diff --git a/pkg/analyzer/lib/src/error/dead_code_verifier.dart b/pkg/analyzer/lib/src/error/dead_code_verifier.dart index 40d859f7f36..0edeacd93ca 100644 --- a/pkg/analyzer/lib/src/error/dead_code_verifier.dart +++ b/pkg/analyzer/lib/src/error/dead_code_verifier.dart @@ -10,14 +10,12 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/error/error.dart'; import 'package:analyzer/error/listener.dart'; -import 'package:analyzer/src/dart/element/type.dart'; import 'package:analyzer/src/dart/resolver/exit_detector.dart'; import 'package:analyzer/src/dart/resolver/scope.dart'; import 'package:analyzer/src/error/codes.dart'; import 'package:analyzer/src/generated/constant.dart'; import 'package:analyzer/src/generated/resolver.dart'; import 'package:analyzer/src/generated/type_system.dart'; -import 'package:analyzer/src/task/strong/checker.dart'; /// A visitor that finds dead code and unused labels. class DeadCodeVerifier extends RecursiveAstVisitor { @@ -30,9 +28,6 @@ class DeadCodeVerifier extends RecursiveAstVisitor { /// The object used to track the usage of labels within a given label scope. _LabelTracker labelTracker; - /// Is `true` if the library being analyzed is non-nullable by default. - final bool _isNonNullableByDefault; - /// Initialize a newly created dead code verifier that will report dead code /// to the given [errorReporter] and will use the given [typeSystem] if one is /// provided. @@ -44,25 +39,13 @@ class DeadCodeVerifier extends RecursiveAstVisitor { isNonNullableByDefault: false, strictInference: false, typeProvider: null, - ), - _isNonNullableByDefault = featureSet.isEnabled(Feature.non_nullable); - - @override - void visitAssignmentExpression(AssignmentExpression node) { - TokenType operatorType = node.operator.type; - if (operatorType == TokenType.QUESTION_QUESTION_EQ) { - _checkForDeadNullCoalesce( - getReadType(node.leftHandSide), node.rightHandSide); - } - super.visitAssignmentExpression(node); - } + ); @override void visitBinaryExpression(BinaryExpression node) { Token operator = node.operator; bool isAmpAmp = operator.type == TokenType.AMPERSAND_AMPERSAND; bool isBarBar = operator.type == TokenType.BAR_BAR; - bool isQuestionQuestion = operator.type == TokenType.QUESTION_QUESTION; if (isAmpAmp || isBarBar) { Expression lhsCondition = node.leftOperand; if (!_isDebugConstant(lhsCondition)) { @@ -105,9 +88,6 @@ class DeadCodeVerifier extends RecursiveAstVisitor { // return null; // } // } - } else if (isQuestionQuestion) { - _checkForDeadNullCoalesce( - getReadType(node.leftOperand), node.rightOperand); } super.visitBinaryExpression(node); } @@ -394,17 +374,6 @@ class DeadCodeVerifier extends RecursiveAstVisitor { } } - void _checkForDeadNullCoalesce(TypeImpl lhsType, Expression rhs) { - if (!_isNonNullableByDefault) return; - - if (_typeSystem.isStrictlyNonNullable(lhsType)) { - _errorReporter.reportErrorForNode( - StaticWarningCode.DEAD_NULL_COALESCE, - rhs, - ); - } - } - /// Given some list of [statements], loop through the list searching for dead /// statements. If [allowMandated] is true, then allow dead statements that /// are mandated by the language spec. This allows for a final break, diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart index bd40631c6c1..ed86ab64468 100644 --- a/pkg/analyzer/lib/src/generated/error_verifier.dart +++ b/pkg/analyzer/lib/src/generated/error_verifier.dart @@ -35,6 +35,7 @@ import 'package:analyzer/src/generated/java_engine.dart'; import 'package:analyzer/src/generated/parser.dart' show ParserErrorCode; import 'package:analyzer/src/generated/resolver.dart'; import 'package:analyzer/src/generated/sdk.dart' show DartSdk, SdkLibrary; +import 'package:analyzer/src/task/strong/checker.dart'; /** * A visitor used to traverse an AST structure looking for additional errors and @@ -342,6 +343,10 @@ class ErrorVerifier extends RecursiveAstVisitor { } else { _checkForArgumentTypeNotAssignableForArgument(rhs); } + if (operatorType == TokenType.QUESTION_QUESTION_EQ) { + _checkForDeadNullCoalesce( + getReadType(node.leftHandSide), node.rightHandSide); + } _checkForAssignmentToFinal(lhs); super.visitAssignmentExpression(node); } @@ -371,6 +376,11 @@ class ErrorVerifier extends RecursiveAstVisitor { _checkForArgumentTypeNotAssignableForArgument(node.rightOperand); } + if (type == TokenType.QUESTION_QUESTION) { + _checkForDeadNullCoalesce( + getReadType(node.leftOperand), node.rightOperand); + } + _checkForUseOfVoidResult(node.leftOperand); super.visitBinaryExpression(node); @@ -2310,6 +2320,17 @@ class ErrorVerifier extends RecursiveAstVisitor { } } + void _checkForDeadNullCoalesce(TypeImpl lhsType, Expression rhs) { + if (!_isNonNullableByDefault) return; + + if (_typeSystem.isStrictlyNonNullable(lhsType)) { + _errorReporter.reportErrorForNode( + StaticWarningCode.DEAD_NULL_COALESCE, + rhs, + ); + } + } + /** * Verify that the given default formal [parameter] is not part of a function * typed parameter. diff --git a/pkg/dev_compiler/tool/dart2js_nnbd_sdk_error_golden.txt b/pkg/dev_compiler/tool/dart2js_nnbd_sdk_error_golden.txt index 68fb6e75fc9..2c440ba7f76 100644 --- a/pkg/dev_compiler/tool/dart2js_nnbd_sdk_error_golden.txt +++ b/pkg/dev_compiler/tool/dart2js_nnbd_sdk_error_golden.txt @@ -27,3 +27,8 @@ ERROR|STATIC_WARNING|NEW_WITH_UNDEFINED_CONSTRUCTOR|lib/collection/collection.da ERROR|STATIC_WARNING|NEW_WITH_UNDEFINED_CONSTRUCTOR|lib/collection/collection.dart|3026|37|5|The class 'List' doesn't have a constructor named 'empty'. ERROR|STATIC_WARNING|NEW_WITH_UNDEFINED_CONSTRUCTOR|lib/internal/internal.dart|1205|37|5|The class 'List' doesn't have a constructor named 'empty'. ERROR|STATIC_WARNING|NEW_WITH_UNDEFINED_CONSTRUCTOR|lib/internal/internal.dart|1663|52|5|The class 'List' doesn't have a constructor named 'empty'. +WARNING|STATIC_WARNING|DEAD_NULL_COALESCE|lib/_http/http.dart|1463|39|5|The left operand can't be null, so the right operand is never executed. +WARNING|STATIC_WARNING|DEAD_NULL_COALESCE|lib/_http/http.dart|8345|60|5|The left operand can't be null, so the right operand is never executed. +WARNING|STATIC_WARNING|DEAD_NULL_COALESCE|lib/_http/http.dart|9272|54|5|The left operand can't be null, so the right operand is never executed. +WARNING|STATIC_WARNING|DEAD_NULL_COALESCE|lib/html/dart2js/html_dart2js.dart|4075|25|2|The left operand can't be null, so the right operand is never executed. +WARNING|STATIC_WARNING|DEAD_NULL_COALESCE|lib/io/io.dart|9167|16|1|The left operand can't be null, so the right operand is never executed. diff --git a/pkg/dev_compiler/tool/dartdevc_nnbd_sdk_error_golden.txt b/pkg/dev_compiler/tool/dartdevc_nnbd_sdk_error_golden.txt index 8b137891791..28cafe0faf7 100644 --- a/pkg/dev_compiler/tool/dartdevc_nnbd_sdk_error_golden.txt +++ b/pkg/dev_compiler/tool/dartdevc_nnbd_sdk_error_golden.txt @@ -1 +1,7 @@ - +WARNING|STATIC_WARNING|DEAD_NULL_COALESCE|lib/_http/http.dart|1463|39|5|The left operand can't be null, so the right operand is never executed. +WARNING|STATIC_WARNING|DEAD_NULL_COALESCE|lib/_http/http.dart|8345|60|5|The left operand can't be null, so the right operand is never executed. +WARNING|STATIC_WARNING|DEAD_NULL_COALESCE|lib/_http/http.dart|9272|54|5|The left operand can't be null, so the right operand is never executed. +WARNING|STATIC_WARNING|DEAD_NULL_COALESCE|lib/collection/collection.dart|1076|46|13|The left operand can't be null, so the right operand is never executed. +WARNING|STATIC_WARNING|DEAD_NULL_COALESCE|lib/developer/developer.dart|332|25|23|The left operand can't be null, so the right operand is never executed. +WARNING|STATIC_WARNING|DEAD_NULL_COALESCE|lib/html/dart2js/html_dart2js.dart|4075|25|2|The left operand can't be null, so the right operand is never executed. +WARNING|STATIC_WARNING|DEAD_NULL_COALESCE|lib/io/io.dart|9167|16|1|The left operand can't be null, so the right operand is never executed. diff --git a/tests/language/nnbd/resolution/question_question_lub_test.dart b/tests/language/nnbd/resolution/question_question_lub_test.dart index 16064414aac..2781b313354 100644 --- a/tests/language/nnbd/resolution/question_question_lub_test.dart +++ b/tests/language/nnbd/resolution/question_question_lub_test.dart @@ -13,10 +13,20 @@ void f1( int? nullableInt, int nonNullInt, ) { - (nullableInt ?? nonNullInt) + 1; //# 00: ok - (nullableInt ?? nullableInt) + 1; //# 01: compile-time error - (nonNullInt ?? nullableInt) + 1; //# 02: compile-time error - (nonNullInt ?? nonNullInt) + 1; //# 03: ok + (nullableInt ?? nonNullInt) + 1; + (nullableInt ?? nullableInt) + 1; +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// [analyzer] STATIC_WARNING.UNCHECKED_USE_OF_NULLABLE_VALUE +// [cfe] unspecified + (nonNullInt ?? nullableInt) + 1; +// ^^^^^^^^^^^ +// [analyzer] STATIC_WARNING.DEAD_NULL_COALESCE +//^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// [analyzer] STATIC_WARNING.UNCHECKED_USE_OF_NULLABLE_VALUE +// [cfe] unspecified + (nonNullInt ?? nonNullInt) + 1; +// ^^^^^^^^^^ +// [analyzer] STATIC_WARNING.DEAD_NULL_COALESCE } // TODO(mfairhurst) add cases with type parameter types diff --git a/tests/language/nnbd/syntax/nullable_type_ambiguous_test.dart b/tests/language/nnbd/syntax/nullable_type_ambiguous_test.dart index 3e5a7185a6b..89ddbd567c4 100644 --- a/tests/language/nnbd/syntax/nullable_type_ambiguous_test.dart +++ b/tests/language/nnbd/syntax/nullable_type_ambiguous_test.dart @@ -37,6 +37,8 @@ main() { // { a is bool ?? true : 3 } is parsed as a map literal { ((a is bool) ?? true) : 3 }. a = true; var x5 = {a is bool ?? true : 3}; + // ^^^^ + // [analyzer] STATIC_WARNING.DEAD_NULL_COALESCE Expect.type>(x5); Map y5 = x5; }