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