mirror of
https://github.com/dart-lang/sdk
synced 2024-10-02 23:39:19 +00:00
Straighten up invocation of an Object method on dynamic.
Fixes https://github.com/dart-lang/sdk/issues/39609 Change-Id: I9b98d9bff8d4e9b75ab7625323f62eb6b6701a94 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/164256 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
e423e857f8
commit
f9e6e38b21
|
@ -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<DartType> _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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1710,7 +1710,6 @@ C<int, num?> f(List<int> a) => a;
|
|||
await _checkSingleFileChanges(content, expected);
|
||||
}
|
||||
|
||||
@FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/39609')
|
||||
Future<void> 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<void> test_dynamic_toString() async {
|
||||
var content = '''
|
||||
String f(dynamic x) => x.toString();
|
||||
|
|
Loading…
Reference in a new issue