blob: 0793e9b591573172466dbfc1d444378c7dc777ae [file]
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/dart/resolver/extension_member_resolver.dart';
import 'package:analyzer/src/dart/resolver/invocation_inferrer.dart';
import 'package:analyzer/src/dart/resolver/resolution_result.dart';
import 'package:analyzer/src/dart/resolver/type_analyzer.dart';
import 'package:analyzer/src/dart/resolver/type_property_resolver.dart';
import 'package:analyzer/src/dart/type_instantiation_target.dart';
import 'package:analyzer/src/diagnostic/diagnostic.dart' as diag;
import 'package:analyzer/src/error/listener.dart';
import 'package:analyzer/src/error/nullable_dereference_verifier.dart';
/// Helper for resolving [CallInvocation]s.
class CallInvocationResolver {
final TypeAnalyzer _typeAnalyzer;
final TypePropertyResolver _typePropertyResolver;
CallInvocationResolver({required TypeAnalyzer typeAnalyzer})
: _typeAnalyzer = typeAnalyzer,
_typePropertyResolver = typeAnalyzer.typePropertyResolver;
DiagnosticReporter get _diagnosticReporter =>
_typeAnalyzer.diagnosticReporter;
ExtensionMemberResolver get _extensionResolver =>
_typeAnalyzer.extensionResolver;
NullableDereferenceVerifier get _nullableDereferenceVerifier =>
_typeAnalyzer.nullableDereferenceVerifier;
TypeSystemImpl get _typeSystem => _typeAnalyzer.typeSystem;
void resolve(
CallInvocationImpl node,
List<WhyNotPromotedGetter> whyNotPromotedArguments, {
required TypeImpl contextType,
}) {
switch (node.receiver) {
case SuperReferenceImpl receiver:
var result = _typePropertyResolver.resolve(
receiver: receiver,
receiverType: _typeAnalyzer.superLookupType(receiver),
name: MethodElement.CALL_METHOD_NAME,
hasRead: true,
hasWrite: false,
propertyErrorEntity: receiver,
nameErrorEntity: receiver,
);
if (result.getter2 case InternalMethodElement element) {
_resolve(
node,
whyNotPromotedArguments,
contextType: contextType,
target: InvocationTargetExecutableElement(element),
);
} else {
if (result.getterOutcome == LookupOutcome.notFound ||
result.getter2 != null) {
_diagnosticReporter.report(
diag.invocationOfNonFunctionExpression.at(receiver),
);
}
_unresolved(
node,
InvalidTypeImpl.instance,
whyNotPromotedArguments,
contextType: contextType,
);
}
return;
case ExtensionOverride2Impl function:
_resolveReceiverExtensionOverride(
node,
function,
whyNotPromotedArguments,
contextType: contextType,
);
return;
case ExpressionImpl function:
var receiverType = function.typeOrThrow;
if (_checkForUseOfVoidResult(function, receiverType)) {
_unresolved(
node,
DynamicTypeImpl.instance,
whyNotPromotedArguments,
contextType: contextType,
);
return;
}
receiverType = _typeSystem.resolveToBound(receiverType);
if (receiverType is FunctionTypeImpl) {
_nullableDereferenceVerifier.expression(
diag.uncheckedInvocationOfNullableValue,
function,
);
_resolve(
node,
whyNotPromotedArguments,
contextType: contextType,
target: InvocationTargetFunctionTypedExpression(receiverType),
);
return;
}
if (receiverType.isDartCoreFunction) {
_nullableDereferenceVerifier.expression(
diag.uncheckedInvocationOfNullableValue,
function,
);
_unresolved(
node,
DynamicTypeImpl.instance,
whyNotPromotedArguments,
contextType: contextType,
resolution: FunctionInterfaceInvocationResolutionImpl(
type: DynamicTypeImpl.instance,
),
);
return;
}
if (identical(receiverType, NeverTypeImpl.instance)) {
_diagnosticReporter.report(diag.receiverOfTypeNever.at(function));
_unresolved(
node,
NeverTypeImpl.instance,
whyNotPromotedArguments,
contextType: contextType,
);
return;
}
var result = _typePropertyResolver.resolve(
receiver: function,
receiverType: receiverType,
name: MethodElement.CALL_METHOD_NAME,
hasRead: true,
hasWrite: false,
propertyErrorEntity: function,
nameErrorEntity: function,
);
var callElement = result.getter2;
if (result.recordField != null) {
_diagnosticReporter.report(
diag.invocationOfNonFunctionExpression.at(function),
);
_unresolved(
node,
InvalidTypeImpl.instance,
whyNotPromotedArguments,
contextType: contextType,
);
return;
}
if (callElement == null) {
if (result.getterOutcome == LookupOutcome.notFound) {
_diagnosticReporter.report(
diag.invocationOfNonFunctionExpression.at(function),
);
}
var type = result.getterOutcome == LookupOutcome.resolved
? DynamicTypeImpl.instance
: InvalidTypeImpl.instance;
_unresolved(
node,
type,
whyNotPromotedArguments,
candidates: [?result.setter2],
contextType: contextType,
);
return;
}
if (callElement.kind != ElementKind.METHOD) {
_diagnosticReporter.report(
diag.invocationOfNonFunctionExpression.at(function),
);
_unresolved(
node,
InvalidTypeImpl.instance,
whyNotPromotedArguments,
candidates: [callElement],
contextType: contextType,
);
return;
}
_resolve(
node,
whyNotPromotedArguments,
contextType: contextType,
target: InvocationTargetExecutableElement(callElement),
);
}
}
/// Check for situations where the result of a method or function is used,
/// when it returns 'void'. Or, in rare cases, when other types of expressions
/// are void, such as identifiers.
///
/// See [diag.useOfVoidResult].
///
// TODO(scheglov): this is duplicate
bool _checkForUseOfVoidResult(Expression expression, DartType type) {
if (type is! VoidTypeImpl) {
return false;
}
_diagnosticReporter.report(diag.useOfVoidResult.at(expression));
return true;
}
void _resolve(
CallInvocationImpl node,
List<WhyNotPromotedGetter> whyNotPromotedArguments, {
required TypeImpl contextType,
required InvocationTarget target,
}) {
var returnType =
CallInvocationInferrer(
typeAnalyzer: _typeAnalyzer,
node: node,
argumentList: node.argumentList,
whyNotPromotedArguments: whyNotPromotedArguments,
contextType: contextType,
target: target,
).resolveInvocation()
as TypeImpl;
var invokeType = node.staticInvokeType as FunctionTypeImpl;
node.resolution = switch (target) {
InvocationTargetExecutableElement(:var element) =>
ExecutableInvocationResolutionImpl(
element: element as InternalExecutableElement,
invokeType: invokeType,
type: returnType,
),
InvocationTargetFunctionTypedExpression() =>
FunctionTypeInvocationResolutionImpl(
invokeType: invokeType,
type: returnType,
),
_ => throw StateError('Unexpected call invocation target: $target'),
};
node.recordStaticType(returnType, typeAnalyzer: _typeAnalyzer);
}
void _resolveReceiverExtensionOverride(
CallInvocationImpl node,
ExtensionOverride2Impl function,
List<WhyNotPromotedGetter> whyNotPromotedArguments, {
required TypeImpl contextType,
}) {
var result = _extensionResolver.getOverrideMember(
function,
MethodElement.CALL_METHOD_NAME,
);
var callElement = result.getter2;
if (callElement == null) {
_diagnosticReporter.report(
diag.invocationOfExtensionWithoutCall
.withArguments(name: function.name.lexeme)
.at(function),
);
return _unresolved(
node,
DynamicTypeImpl.instance,
whyNotPromotedArguments,
contextType: contextType,
);
}
if (callElement.isStatic) {
_diagnosticReporter.report(
diag.extensionOverrideAccessToStaticMember.at(node.argumentList),
);
}
_resolve(
node,
whyNotPromotedArguments,
contextType: contextType,
target: InvocationTargetExecutableElement(callElement),
);
}
void _unresolved(
CallInvocationImpl node,
TypeImpl type,
List<WhyNotPromotedGetter> whyNotPromotedArguments, {
List<Element> candidates = const [],
required TypeImpl contextType,
InvocationResolutionImpl? resolution,
}) {
_setExplicitTypeArgumentTypes(node);
CallInvocationInferrer(
typeAnalyzer: _typeAnalyzer,
node: node,
argumentList: node.argumentList,
contextType: contextType,
whyNotPromotedArguments: whyNotPromotedArguments,
target: null,
).resolveInvocation();
node.staticInvokeType = type;
node.resolution =
resolution ??
switch (type) {
NeverTypeImpl() => null,
InvalidTypeImpl() => InvalidInvocationResolutionImpl(
candidates: candidates,
recovery: null,
type: type,
),
_ => DynamicInvocationResolutionImpl(type: type),
};
node.recordStaticType(type, typeAnalyzer: _typeAnalyzer);
}
/// Inference cannot be done, we still want to fill type argument types.
static void _setExplicitTypeArgumentTypes(CallInvocationImpl node) {
var typeArguments = node.typeArguments;
if (typeArguments != null) {
node.typeArgumentTypes = typeArguments.arguments
.map((typeArgument) => typeArgument.typeOrThrow)
.toList();
} else {
node.typeArgumentTypes = const <TypeImpl>[];
}
}
}