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
-
-}