diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart index c22c1e45c02..1b4b7a048b5 100644 --- a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart +++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart @@ -9,7 +9,6 @@ import 'package:analyzer/error/listener.dart'; import 'package:analyzer/src/dart/ast/ast.dart'; import 'package:analyzer/src/dart/element/type.dart'; import 'package:analyzer/src/dart/element/type_algebra.dart'; -import 'package:analyzer/src/dart/element/type_provider.dart'; import 'package:analyzer/src/dart/element/type_system.dart'; import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart'; import 'package:analyzer/src/error/codes.dart'; @@ -22,7 +21,6 @@ class InvocationInferenceHelper { final ErrorReporter _errorReporter; final FlowAnalysisHelper _flowAnalysis; final TypeSystemImpl _typeSystem; - final TypeProviderImpl _typeProvider; final MigrationResolutionHooks _migrationResolutionHooks; List _typeArgumentTypes; @@ -37,7 +35,6 @@ class InvocationInferenceHelper { : _resolver = resolver, _errorReporter = errorReporter, _typeSystem = typeSystem, - _typeProvider = typeSystem.typeProvider, _flowAnalysis = flowAnalysis, _migrationResolutionHooks = migrationResolutionHooks; @@ -157,45 +154,6 @@ class InvocationInferenceHelper { return null; } - /// Given a method invocation [node], attempt to infer a better - /// type for the result if the target is dynamic and the method - /// being called is one of the object methods. - bool inferMethodInvocationObject(MethodInvocation node) { - // If we have a call like `toString()` or `libraryPrefix.toString()`, don't - // infer it. - Expression target = node.realTarget; - if (target == null || - target is SimpleIdentifier && target.staticElement is PrefixElement) { - return false; - } - DartType nodeType = node.staticInvokeType; - if (nodeType == null || - !nodeType.isDynamic || - node.argumentList.arguments.isNotEmpty) { - return false; - } - // Object methods called on dynamic targets can have their types improved. - String name = node.methodName.name; - MethodElement inferredElement = - _typeProvider.objectType.element.getMethod(name); - if (inferredElement == null || inferredElement.isStatic) { - return false; - } - inferredElement = _resolver.toLegacyElement(inferredElement); - DartType inferredType = inferredElement.type; - if (inferredType is FunctionType) { - DartType returnType = inferredType.returnType; - if (inferredType.parameters.isEmpty && - returnType is InterfaceType && - _typeProvider.nonSubtypableClasses.contains(returnType.element)) { - node.staticInvokeType = inferredType; - recordStaticType(node, inferredType.returnType); - return true; - } - } - return false; - } - /// Given an uninstantiated generic function type, referenced by the /// [identifier] in the tear-off [expression], try to infer the instantiated /// generic function type from the surrounding context. 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 951bde71f05..919f54d8df9 100644 --- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart +++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart @@ -150,7 +150,7 @@ class MethodInvocationResolver { } if (receiverType is DynamicTypeImpl) { - _resolveReceiverDynamic(node, name); + _resolveReceiverDynamic(node); return; } @@ -291,15 +291,10 @@ class MethodInvocationResolver { node.methodName.staticType, ); - // TODO(scheglov) Call this only when member lookup failed? - var inferred = _inferenceHelper.inferMethodInvocationObject(node); - - if (!inferred) { - DartType staticStaticType = _inferenceHelper.computeInvokeReturnType( - node.staticInvokeType, - ); - _inferenceHelper.recordStaticType(node, staticStaticType); - } + DartType staticStaticType = _inferenceHelper.computeInvokeReturnType( + node.staticInvokeType, + ); + _inferenceHelper.recordStaticType(node, staticStaticType); } /// Given that we are accessing a property of the given [classElement] with the @@ -387,8 +382,33 @@ class MethodInvocationResolver { _setResolution(node, member.type); } - void _resolveReceiverDynamic(MethodInvocation node, String name) { - _setDynamicResolution(node); + void _resolveReceiverDynamic(MethodInvocationImpl node) { + var nameNode = node.methodName; + + var objectElement = _typeSystem.typeProvider.objectElement; + var target = objectElement.getMethod(nameNode.name); + + var hasMatchingObjectMethod = false; + if (target is MethodElement) { + var arguments = node.argumentList.arguments; + hasMatchingObjectMethod = arguments.length == target.parameters.length && + !arguments.any((e) => e is NamedExpression); + if (hasMatchingObjectMethod) { + target = _resolver.toLegacyElement(target); + nameNode.staticElement = target; + node.staticInvokeType = target.type; + node.staticType = target.returnType; + } + } + + if (!hasMatchingObjectMethod) { + nameNode.staticType = DynamicTypeImpl.instance; + node.staticInvokeType = DynamicTypeImpl.instance; + node.staticType = DynamicTypeImpl.instance; + } + + _setExplicitTypeArgumentTypes(); + node.argumentList.accept(_resolver); } void _resolveReceiverFunctionType(MethodInvocation node, Expression receiver, diff --git a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart index 0f36155d59c..d83065c072a 100644 --- a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart @@ -1165,11 +1165,12 @@ main() { ''', [ error(CompileTimeErrorCode.USE_OF_VOID_RESULT, 23, 3), ]); - // TODO(scheglov) Resolve fully, or don't resolve at all. - assertMethodInvocation( + assertMethodInvocation2( findNode.methodInvocation('toString()'), - null, - 'String Function()', + element: null, + typeArgumentTypes: [], + invokeType: 'dynamic', + type: 'dynamic', ); } @@ -1182,16 +1183,16 @@ main() { ''', [ error(CompileTimeErrorCode.USE_OF_VOID_RESULT, 23, 3), ]); - // TODO(scheglov) Resolve fully, or don't resolve at all. - assertMethodInvocation( + assertMethodInvocation2( findNode.methodInvocation('toString()'), - null, - 'String Function()', + element: null, + typeArgumentTypes: [], + invokeType: 'dynamic', + type: 'dynamic', ); } test_error_useOfVoidResult_receiver_withNull() async { - var question = typeToStringWithNullability ? '?' : ''; await assertErrorsInCode(r''' main() { void foo; @@ -1200,12 +1201,12 @@ main() { ''', [ error(CompileTimeErrorCode.USE_OF_VOID_RESULT, 23, 3), ]); - // TODO(scheglov) Resolve fully, or don't resolve at all. - assertMethodInvocation( + assertMethodInvocation2( findNode.methodInvocation('toString()'), - null, - 'String Function()', - expectedType: 'String$question', + element: null, + typeArgumentTypes: [], + invokeType: 'dynamic', + type: 'dynamic', ); } @@ -1944,14 +1945,39 @@ main() { assertType(foo, 'void Function(int)'); } - test_objectMethodOnDynamic() async { + test_objectMethodOnDynamic_argumentsDontMatch() async { await assertNoErrorsInCode(r''' -main() { - var v; - v.toString(42); +void f(a, int b) { + a.toString(b); } '''); - _assertUnresolvedMethodInvocation('toString(42);'); + assertMethodInvocation2( + findNode.methodInvocation('toString(b)'), + element: null, + typeArgumentTypes: [], + invokeType: 'dynamic', + type: 'dynamic', + ); + + assertType(findNode.simple('b);'), 'int'); + } + + test_objectMethodOnDynamic_argumentsMatch() async { + await assertNoErrorsInCode(r''' +void f(a) { + a.toString(); +} +'''); + assertMethodInvocation2( + findNode.methodInvocation('toString()'), + element: elementMatcher( + objectElement.getMethod('toString'), + isLegacy: isNullSafetySdkAndLegacyLibrary, + ), + typeArgumentTypes: [], + invokeType: 'String Function()', + type: 'String', + ); } test_objectMethodOnFunction() async { diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart index 34fd1395221..5a8f2bb9419 100644 --- a/pkg/nnbd_migration/test/api_test.dart +++ b/pkg/nnbd_migration/test/api_test.dart @@ -1710,7 +1710,6 @@ C f(List a) => a; await _checkSingleFileChanges(content, expected); } - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/39609') Future test_dynamic_dispatch_to_object_method() async { var content = ''' String f(dynamic x) => x.toString(); @@ -1787,7 +1786,6 @@ main() { await _checkSingleFileChanges(content, expected); } - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/39609') Future test_dynamic_toString() async { var content = ''' String f(dynamic x) => x.toString();