[analyzer] Fix dead code reporting in property access and is/as.
The way dead code detection works in the analyzer is that at various
ad hoc times during the process of resolution (typically before
visiting an expression or statement), the `ResolverVisitor` calls
`checkUnreachableNode`, which asks flow analysis whether the current
point in execution is reachable, and if it isn't, arranges for an
unreachable code warning to be produced.
This wasn't working for property extractions, type casts, and type
tests because there's no subexpression whose execution follows the
subexpression that returns `Never`, so `checkUnreachableNode` wasn't
getting called.
The fix is simple; just add some calls to `checkUnreachableNode` to
cover the RHS of a property access, type cast, or type test.
Note that property accesses in the analyzer can be represented either
as `PrefixedIdentifier` or `PropertyAccess`, so the property access
fix had to be done in two places (and accordingly there are two tests
for it).
Fixes https://github.com/dart-lang/sdk/issues/60247.
Bug: https://github.com/dart-lang/sdk/issues/60247
Change-Id: I49e739784f629b77443ce6d31e67809f48de75be
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/413521
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Auto-Submit: Paul Berry <paulberry@google.com>
diff --git a/pkg/analyzer/lib/src/dart/resolver/prefixed_identifier_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/prefixed_identifier_resolver.dart
index 74f7551..bbd817e 100644
--- a/pkg/analyzer/lib/src/dart/resolver/prefixed_identifier_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/prefixed_identifier_resolver.dart
@@ -29,6 +29,7 @@
}) {
_resolver.analyzeExpression(node.prefix, _resolver.operations.unknownType);
_resolver.popRewrite();
+ _resolver.checkUnreachableNode(node.identifier);
var prefixElement = node.prefix.element;
if (prefixElement is! PrefixElement2) {
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 5c32ff8..ccd913b 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -1822,6 +1822,7 @@
node.expression, SharedTypeSchemaView(UnknownInferredType.instance));
popRewrite();
+ checkUnreachableNode(node.type);
node.type.accept(this);
typeAnalyzer.visitAsExpression(node);
@@ -3070,6 +3071,7 @@
node.expression, SharedTypeSchemaView(UnknownInferredType.instance));
popRewrite();
+ checkUnreachableNode(node.type);
node.type.accept(this);
typeAnalyzer.visitIsExpression(node);
@@ -3455,6 +3457,7 @@
popRewrite();
}
+ checkUnreachableNode(node.propertyName);
_resolvePropertyAccessRhs(node, contextType);
inferenceLogWriter?.exitExpression(node);
}
diff --git a/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart b/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart
index bc166ff..3f38ebc 100644
--- a/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart
@@ -1044,6 +1044,7 @@
}
f(Never a) {
+ // ignore: dead_code
a.foo;
}
''');
diff --git a/pkg/analyzer/test/src/diagnostics/dead_code_test.dart b/pkg/analyzer/test/src/diagnostics/dead_code_test.dart
index facc405..8840364 100644
--- a/pkg/analyzer/test/src/diagnostics/dead_code_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/dead_code_test.dart
@@ -17,6 +17,16 @@
@reflectiveTest
class DeadCodeTest extends PubPackageResolutionTest
with DeadCodeTestCases_Language212 {
+ test_asExpression_type() async {
+ await assertErrorsInCode(r'''
+Never doNotReturn() => throw 0;
+
+test() => doNotReturn() as int;
+''', [
+ error(WarningCode.DEAD_CODE, 60, 4),
+ ]);
+ }
+
test_deadBlock_conditionalElse_recordPropertyAccess() async {
await assertErrorsInCode(r'''
void f(({int x, int y}) p) {
@@ -157,6 +167,17 @@
]);
}
+ test_isExpression_type() async {
+ await assertErrorsInCode(r'''
+Never doNotReturn() => throw 0;
+
+// ignore: unnecessary_type_check_true
+test() => doNotReturn() is int;
+''', [
+ error(WarningCode.DEAD_CODE, 99, 4),
+ ]);
+ }
+
test_localFunction_wildcard() async {
await assertErrorsInCode(r'''
void f() {
@@ -194,6 +215,26 @@
error(WarningCode.DEAD_CODE, 84, 2),
]);
}
+
+ test_prefixedIdentifier_identifier() async {
+ await assertErrorsInCode(r'''
+Never get doNotReturn => throw 0;
+
+test() => doNotReturn.hashCode;
+''', [
+ error(WarningCode.DEAD_CODE, 57, 9),
+ ]);
+ }
+
+ test_propertyAccess_property() async {
+ await assertErrorsInCode(r'''
+Never doNotReturn() => throw 0;
+
+test() => doNotReturn().hashCode;
+''', [
+ error(WarningCode.DEAD_CODE, 57, 9),
+ ]);
+ }
}
@reflectiveTest
diff --git a/pkg/analyzer/test/src/diagnostics/receiver_of_type_never_test.dart b/pkg/analyzer/test/src/diagnostics/receiver_of_type_never_test.dart
index 96440eb..8f95f61 100644
--- a/pkg/analyzer/test/src/diagnostics/receiver_of_type_never_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/receiver_of_type_never_test.dart
@@ -889,6 +889,7 @@
test_propertyAccess_never_read() async {
await assertNoErrorsInCode(r'''
void f(Never x) {
+ // ignore: dead_code
x.foo;
}
''');
@@ -916,6 +917,7 @@
test_propertyAccess_never_read_hashCode() async {
await assertNoErrorsInCode(r'''
void f(Never x) {
+ // ignore: dead_code
x.hashCode;
}
''');
@@ -987,6 +989,7 @@
test_propertyAccess_never_tearOff_toString() async {
await assertNoErrorsInCode(r'''
void f(Never x) {
+ // ignore: dead_code
x.toString;
}
''');
@@ -1142,6 +1145,7 @@
test_propertyAccess_toString() async {
await assertNoErrorsInCode(r'''
void f() {
+ // ignore: dead_code
(throw '').toString;
}
''');
@@ -1171,6 +1175,7 @@
test_throw_getter_hashCode() async {
await assertNoErrorsInCode(r'''
void f() {
+ // ignore: dead_code
(throw '').hashCode;
}
''');