Merge branch 'main' into move-do-not-use
diff --git a/lib/src/rules/unreachable_from_main.dart b/lib/src/rules/unreachable_from_main.dart
index e2089a4..c852b0d 100644
--- a/lib/src/rules/unreachable_from_main.dart
+++ b/lib/src/rules/unreachable_from_main.dart
@@ -271,12 +271,16 @@
       return;
     }
 
+    var nodeIsInTypeArgument =
+        node.thisOrAncestorOfType<TypeArgumentList>() != null;
+
     // Any reference to a typedef is reachable, since structural typing is used
     // to match against objects.
     //
     // The return type of an external variable declaration is reachable, since
     // the external implementation can instantiate it.
     if (node.type?.alias != null ||
+        nodeIsInTypeArgument ||
         node.isInExternalVariableTypeOrFunctionReturnType) {
       _addDeclaration(element);
     }
@@ -590,13 +594,23 @@
   }
 }
 
-extension on AstNode {
+extension on NamedType {
+  TypeAnnotation get topmostTypeAnnotation {
+    TypeAnnotation topTypeAnnotation = this;
+    var parent = this.parent;
+    while (parent is TypeAnnotation) {
+      topTypeAnnotation = parent;
+      parent = topTypeAnnotation.parent;
+    }
+    return topTypeAnnotation;
+  }
+
   bool get isInExternalVariableTypeOrFunctionReturnType {
-    var ancestorTypeAnnotation = thisOrAncestorOfType<TypeAnnotation>();
-    if (ancestorTypeAnnotation == null) return false;
-    switch (parent) {
+    var topTypeAnnotation = topmostTypeAnnotation;
+
+    switch (topTypeAnnotation.parent) {
       case MethodDeclaration(:var externalKeyword, :var returnType):
-        return externalKeyword != null && returnType == ancestorTypeAnnotation;
+        return externalKeyword != null && returnType == topTypeAnnotation;
       case VariableDeclarationList(
           parent: FieldDeclaration(:var externalKeyword),
         ):
diff --git a/test/rules/all.dart b/test/rules/all.dart
index fcae757..f0584a2 100644
--- a/test/rules/all.dart
+++ b/test/rules/all.dart
@@ -50,6 +50,7 @@
 import 'directives_ordering_test.dart' as directives_ordering;
 import 'discarded_futures_test.dart' as discarded_futures;
 import 'do_not_use_environment_test.dart' as do_not_use_environment;
+import 'empty_catches_test.dart' as empty_catches;
 import 'empty_statements_test.dart' as empty_statements;
 import 'eol_at_end_of_file_test.dart' as eol_at_end_of_file;
 import 'exhaustive_cases_test.dart' as exhaustive_cases;
@@ -189,6 +190,7 @@
   directives_ordering.main();
   discarded_futures.main();
   do_not_use_environment.main();
+  empty_catches.main();
   empty_statements.main();
   eol_at_end_of_file.main();
   exhaustive_cases.main();
diff --git a/test/rules/empty_catches_test.dart b/test/rules/empty_catches_test.dart
new file mode 100644
index 0000000..07c8e31
--- /dev/null
+++ b/test/rules/empty_catches_test.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../rule_test_support.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(EmptyCatchesTest);
+  });
+}
+
+@reflectiveTest
+class EmptyCatchesTest extends LintRuleTest {
+  @override
+  String get lintRule => 'empty_catches';
+  test_comment() async {
+    await assertNoDiagnostics(r'''
+void foo() {
+  try {
+    throw new Exception();
+  } catch (e) {
+    // Nothing.
+  }
+}
+''');
+  }
+
+  test_emptyCatch() async {
+    await assertDiagnostics(r'''
+void foo() {
+  try {
+    throw Exception();
+  } catch (e) {}
+}
+''', [
+      lint(58, 2),
+    ]);
+  }
+
+  test_statement() async {
+    await assertNoDiagnostics(r'''
+void foo() {
+  try {
+    throw new Exception();
+  } catch (e) {
+    print(e);
+  }
+}
+''');
+  }
+
+  test_underscore() async {
+    await assertNoDiagnostics(r'''
+void foo() {
+  try {
+    throw Exception();
+  } catch (_) {}
+}
+''');
+  }
+}
diff --git a/test/rules/unreachable_from_main_test.dart b/test/rules/unreachable_from_main_test.dart
index 024b107..e151942 100644
--- a/test/rules/unreachable_from_main_test.dart
+++ b/test/rules/unreachable_from_main_test.dart
@@ -253,6 +253,32 @@
 ''');
   }
 
+  test_class_reachable_referencedDeepInTypeAnnotation_externalMethodDeclaration() async {
+    await assertNoDiagnostics(r'''
+void main() {
+  D().f;
+}
+
+class C {}
+
+class D {
+  external C Function() f();
+}
+''');
+  }
+
+  test_class_reachable_referencedDeepInTypeArgument() async {
+    await assertNoDiagnostics(r'''
+void main() {
+  C<D Function()>();
+}
+
+class C<T> {}
+
+class D {}
+''');
+  }
+
   test_class_reachable_referencedInTypeAnnotation_externalFieldDeclaration() async {
     await assertNoDiagnostics(r'''
 void main() {
@@ -281,6 +307,18 @@
 ''');
   }
 
+  test_class_reachable_referencedInTypeArgument() async {
+    await assertNoDiagnostics(r'''
+void main() {
+  C<D>();
+}
+
+class C<T> {}
+
+class D {}
+''');
+  }
+
   test_class_reachableViaAnnotation() async {
     await assertNoDiagnostics(r'''
 void main() {
@@ -425,6 +463,22 @@
     ]);
   }
 
+  test_class_unreachable_referencedInParameter_externalMethodDeclaration() async {
+    await assertDiagnostics(r'''
+void main() {
+  D().f;
+}
+
+class C {}
+
+class D {
+  external f(C c);
+}
+''', [
+      lint(32, 1),
+    ]);
+  }
+
   test_class_unreachable_referencedInTypeAnnotation_fieldDeclaration() async {
     await assertDiagnostics(r'''
 void main() {
diff --git a/test_data/rules/empty_catches.dart b/test_data/rules/empty_catches.dart
deleted file mode 100644
index 26e3009..0000000
--- a/test_data/rules/empty_catches.dart
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// test w/ `dart test -N empty_catches`
-
-void foo() {
-  try {
-    throw new Exception();
-  } catch (_) { } //OK
-
-  try {
-    throw new Exception();
-  } catch (e) { } //LINT
-
-  try {
-    throw new Exception();
-  } catch (e) {
-    // Nothing.
-  } //OK!
-
-  try {
-    throw new Exception();
-  } catch (e) {
-    print(e);
-  } //OK
-
-}