diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart index 27b279fcbbfea..5095d8db2d114 100644 --- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart +++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart @@ -171,8 +171,8 @@ class MethodInvocationResolver { return; } - if (receiverType == NeverTypeImpl.instance) { - _reportUseOfNeverType(node, receiver); + if (receiverType is NeverTypeImpl) { + _resolveReceiverNever(node, receiver, receiverType); return; } } @@ -290,14 +290,6 @@ class MethodInvocationResolver { ); } - void _reportUseOfNeverType(MethodInvocation node, AstNode errorNode) { - _setDynamicResolution(node); - _resolver.errorReporter.reportErrorForNode( - StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, - errorNode, - ); - } - void _reportUseOfVoidType(MethodInvocation node, AstNode errorNode) { _setDynamicResolution(node); _resolver.errorReporter.reportErrorForNode( @@ -306,12 +298,19 @@ class MethodInvocationResolver { ); } - void _resolveArguments_finishInference(MethodInvocation node) { + /// [InvocationExpression.staticInvokeType] has been set for the [node]. + /// Use it to set context for arguments, and resolve them. + void _resolveArguments(MethodInvocation node) { _inferenceHelper.inferArgumentTypesForInvocation(node); node.argumentList.accept(_resolver); + } + + void _resolveArguments_finishInference(MethodInvocation node) { + _resolveArguments(node); _inferenceHelper.inferGenericInvocationExpression(node); + // TODO(scheglov) Call this only when member lookup failed? var inferred = _inferenceHelper.inferMethodInvocationObject(node); if (!inferred) { @@ -589,6 +588,49 @@ class MethodInvocationResolver { } } + void _resolveReceiverNever( + MethodInvocation node, + Expression receiver, + DartType receiverType, + ) { + _setExplicitTypeArgumentTypes(); + + if (receiverType == NeverTypeImpl.instanceNullable) { + var methodName = node.methodName; + var objectElement = _resolver.typeProvider.objectElement; + var objectMember = objectElement.getMethod(methodName.name); + if (objectMember != null) { + objectMember = _resolver.toLegacyElement(objectMember); + methodName.staticElement = objectMember; + _setResolution( + node, + _elementTypeProvider.getExecutableType(objectMember), + ); + } else { + _setDynamicResolution(node); + _resolver.errorReporter.reportErrorForNode( + StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, + receiver, + ); + } + return; + } + + if (receiverType == NeverTypeImpl.instance) { + node.methodName.staticType = _dynamicType; + node.staticInvokeType = _dynamicType; + node.staticType = NeverTypeImpl.instance; + + _resolveArguments(node); + + _resolver.errorReporter.reportErrorForNode( + StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, + receiver, + ); + return; + } + } + void _resolveReceiverNull( MethodInvocation node, SimpleIdentifier nameNode, String name) { var element = nameScope.lookup(nameNode, _definingLibrary); @@ -958,10 +1000,6 @@ class MethodInvocationResolver { return _reportUseOfVoidType(node, node.methodName); } - if (type == NeverTypeImpl.instance) { - return _reportUseOfNeverType(node, node.methodName); - } - _reportInvocationOfNonFunction(node); } diff --git a/pkg/analyzer/test/src/dart/element/nullable_test.dart b/pkg/analyzer/test/src/dart/element/nullable_test.dart index 9ccf0f5d6b86d..fea9bd50a9576 100644 --- a/pkg/analyzer/test/src/dart/element/nullable_test.dart +++ b/pkg/analyzer/test/src/dart/element/nullable_test.dart @@ -111,6 +111,8 @@ class IsNonNullableTest extends _NullableBase { test_never() { isNonNullable(neverNone); + isNotNonNullable(neverQuestion); + isNonNullable(neverStar); } test_null() { @@ -238,6 +240,8 @@ class IsNullableTest extends _NullableBase { test_never() { isNotNullable(neverNone); + isNullable(neverQuestion); + isNotNullable(neverStar); } test_null() { diff --git a/pkg/analyzer/test/src/diagnostics/invalid_use_of_never_value_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_use_of_never_value_test.dart index f0bc477af5378..7f5bc2733e1bc 100644 --- a/pkg/analyzer/test/src/diagnostics/invalid_use_of_never_value_test.dart +++ b/pkg/analyzer/test/src/diagnostics/invalid_use_of_never_value_test.dart @@ -22,209 +22,252 @@ class InvalidUseOfNeverTest extends DriverResolutionTest { AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()..enabledExperiments = [EnableString.non_nullable]; - test_local_invoked() async { - await assertErrorsInCode(r''' + @failingTest + test_binaryExpression_never_eqEq() async { + // We report this as an error even though CFE does not. + await assertNoErrorsInCode(r''' void main(Never x) { - x(); + x == 0; } -''', [ - error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 23, 1), - ]); +'''); } @failingTest - test_local_neverQuestion_getter_hashCode() async { - // reports undefined getter + test_binaryExpression_never_plus() async { + // We report this as an error even though CFE does not. await assertNoErrorsInCode(r''' -void main(Never? neverQ) { - neverQ.hashCode; +void main(Never x) { + x + 0; } '''); } @failingTest - test_local_neverQuestion_getter_toString() async { - // reports undefined getter + test_binaryExpression_neverQ_eqEq() async { + // We report this as an error even though CFE does not. await assertNoErrorsInCode(r''' -void main() { - (throw '').toString; +void main(Never? x) { + x == 0; +} +'''); + } + + @failingTest + test_binaryExpression_neverQ_plus() async { + // We report this as an error even though CFE does not. + await assertNoErrorsInCode(r''' +void main(Never? x) { + x + 0; } '''); } - test_local_neverQuestion_methodCall_toString() async { + test_conditionalExpression_falseBranch() async { await assertNoErrorsInCode(r''' -void main(Never? neverQ) { - neverQ.toString(); +void main(bool c, Never x) { + c ? 0 : x; } '''); } - @failingTest - test_local_neverQuestion_operator_equals() async { - // We report this as an error even though CFE does not. + test_conditionalExpression_trueBranch() async { await assertNoErrorsInCode(r''' -void main(Never? neverQ) { - neverQ == 0; +void main(bool c, Never x) { + c ? x : 0; } '''); } - @failingTest - test_local_operator_plusPlus_prefix() async { - // Reports 'undefined operator' + test_functionExpressionInvocation_never() async { await assertErrorsInCode(r''' void main(Never x) { - ++x; + x(); } ''', [ - error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 70, 1), + error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 23, 1), ]); } - @failingTest - test_local_operator_plusPlus_suffix() async { - // Reports 'undefined operator' + test_functionExpressionInvocation_neverQ() async { await assertErrorsInCode(r''' -void main(Never x) { - x++; +void main(Never? x) { + x(); } ''', [ - error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 70, 1), + error(StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 24, 1), ]); } - test_member_invoked() async { - await assertErrorsInCode(r''' -class C { - Never get x => throw ''; + test_invocationArgument() async { + await assertNoErrorsInCode(r''' +void main(f, Never x) { + f(x); } +'''); + } -void main() { - C c = C(); - c.x(); + test_methodInvocation_never() async { + await assertErrorsInCode(r''' +void main(Never x) { + x.foo(1 + 2); } ''', [ - error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 69, 3), + error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 23, 1), ]); + + assertMethodInvocation( + findNode.methodInvocation('.foo(1 + 2)'), + null, + 'dynamic', + expectedType: 'Never', + ); + + // Verify that arguments are resolved. + assertType(findNode.binary('1 + 2'), 'int'); } - @failingTest - test_throw_getter_foo() async { - // Reports undefined getter. + test_methodInvocation_never_toString() async { await assertErrorsInCode(r''' -void main() { - (throw '').foo; +void main(Never x) { + x.toString(1 + 2); } ''', [ - error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 70, 1), + error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 23, 1), ]); + + assertMethodInvocation( + findNode.methodInvocation('.toString(1 + 2)'), + null, + 'dynamic', + expectedType: 'Never', + ); + + // Verify that arguments are resolved. + assertType(findNode.binary('1 + 2'), 'int'); } - @failingTest - test_throw_getter_hashCode() async { - // Reports undefined getter. + test_methodInvocation_neverQ_toString() async { await assertErrorsInCode(r''' -void main() { - (throw '').hashCode; +void main(Never? x) { + x.toString(1 + 2); } ''', [ - error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 70, 1), + error(CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS, 34, 7), ]); + + assertMethodInvocation( + findNode.methodInvocation('.toString(1 + 2)'), + typeProvider.objectType.element.getMethod('toString'), + 'String Function()', + expectedType: 'String', + ); + + // Verify that arguments are resolved. + assertType(findNode.binary('1 + 2'), 'int'); } @failingTest - test_throw_getter_toString() async { - // Reports undefined getter (it seems to get confused by the tear-off). + test_postfixExpression_never_plusPlus() async { + // Reports 'undefined operator' await assertErrorsInCode(r''' -void main() { - (throw '').toString; +void main(Never x) { + x++; } ''', [ error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 70, 1), ]); } - test_throw_invoked() async { + @failingTest + test_postfixExpression_neverQ_plusPlus() async { + // Reports 'undefined operator' await assertErrorsInCode(r''' -void main() { - (throw '')(); +void main(Never x) { + x++; } ''', [ - error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 16, 10), + error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 70, 1), ]); } - test_throw_methodCall_foo() async { + @failingTest + test_prefixExpression_never_plusPlus() async { + // Reports 'undefined operator' await assertErrorsInCode(r''' -void main() { - (throw '').foo(); +void main(Never x) { + ++x; } ''', [ - error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 16, 10), + error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 70, 1), ]); } - test_throw_methodCall_toString() async { + @failingTest + test_prefixExpression_neverQ_plusPlus() async { + // Reports 'undefined operator' await assertErrorsInCode(r''' -void main() { - (throw '').toString(); +void main(Never x) { + ++x; } ''', [ - error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 16, 10), + error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 70, 1), ]); } - @failingTest - test_throw_operator_equals() async { - // We report this as an error even though CFE does not. - await assertNoErrorsInCode(r''' -void main() { - (throw '') == 0; -} -'''); - } - - @failingTest - test_throw_operator_plus_lhs() async { - // Currently reports "no such operator" + @FailingTest(reason: 'Types are wrong') + test_propertyAccess_never() async { await assertErrorsInCode(r''' -void main() { - (throw '') + 0; +void main(Never x) { + x.foo; } ''', [ - error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 70, 1), + error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 23, 1), ]); + + assertElementNull(findNode.simple('foo;')); + assertType(findNode.prefixed('x.foo'), 'Never'); } - test_throw_operator_plus_rhs() async { + @failingTest + test_propertyAccess_never_hashCode() async { + // reports undefined getter await assertNoErrorsInCode(r''' -void main() { - 0 + (throw ''); +void main(Never x) { + x.hashCode; } '''); } - test_throw_operator_ternary_falseBranch() async { + @failingTest + test_propertyAccess_never_tearOff_toString() async { + // reports undefined getter await assertNoErrorsInCode(r''' -void f(bool c) { - c ? 0 : (throw ''); +void main(Never x) { + x.toString; } '''); } - test_throw_operator_ternary_trueBranch() async { - await assertNoErrorsInCode(r''' -void f(bool c) { - c ? (throw '') : 0; + @FailingTest(reason: 'Types are wrong') + test_propertyAccess_neverQ() async { + await assertErrorsInCode(r''' +void main(Never x) { + x.foo; } -'''); +''', [ + error(StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, 23, 1), + ]); + + assertElementNull(findNode.simple('foo;')); + assertType(findNode.prefixed('x.foo'), 'Never'); } - test_throw_param() async { + @failingTest + test_propertyAccess_neverQ_hashCode() async { + // reports undefined getter await assertNoErrorsInCode(r''' -void f(x) { - f(throw ''); +void main(Never? x) { + x.hashCode; } '''); } @@ -234,26 +277,26 @@ void f(x) { @reflectiveTest class InvalidUseOfNeverTest_Legacy extends DriverResolutionTest { @failingTest - test_throw_getter_hashCode() async { - // Reports undefined getter. + test_binaryExpression_eqEq() async { + // We report this as an error even though CFE does not. await assertNoErrorsInCode(r''' void main() { - (throw '').hashCode; + (throw '') == 0; } '''); } @failingTest - test_throw_getter_toString() async { - // Reports undefined getter (it seems to get confused by the tear-off). + test_binaryExpression_plus() async { + // We report this as an error even though CFE does not. await assertNoErrorsInCode(r''' void main() { - (throw '').toString; + (throw '') + 0; } '''); } - test_throw_methodCall_toString() async { + test_methodInvocation_toString() async { await assertNoErrorsInCode(r''' void main() { (throw '').toString(); @@ -262,11 +305,21 @@ void main() { } @failingTest - test_throw_operator_equals() async { - // We report this as an error even though CFE does not. + test_propertyAccess_toString() async { + // Reports undefined getter (it seems to get confused by the tear-off). await assertNoErrorsInCode(r''' void main() { - (throw '') == 0; + (throw '').toString; +} +'''); + } + + @failingTest + test_throw_getter_hashCode() async { + // Reports undefined getter. + await assertNoErrorsInCode(r''' +void main() { + (throw '').hashCode; } '''); }