blob: 078fe3936ed8ad9d924c39726a0a9a61d51d4208 [file] [log] [blame]
// 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/element2.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/extensions.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/type_property_resolver.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/error/nullable_dereference_verifier.dart';
import 'package:analyzer/src/generated/resolver.dart';
/// Helper for resolving [FunctionExpressionInvocation]s.
class FunctionExpressionInvocationResolver {
final ResolverVisitor _resolver;
final TypePropertyResolver _typePropertyResolver;
FunctionExpressionInvocationResolver({
required ResolverVisitor resolver,
}) : _resolver = resolver,
_typePropertyResolver = resolver.typePropertyResolver;
ErrorReporter get _errorReporter => _resolver.errorReporter;
ExtensionMemberResolver get _extensionResolver => _resolver.extensionResolver;
NullableDereferenceVerifier get _nullableDereferenceVerifier =>
_resolver.nullableDereferenceVerifier;
TypeSystemImpl get _typeSystem => _resolver.typeSystem;
void resolve(FunctionExpressionInvocationImpl node,
List<WhyNotPromotedGetter> whyNotPromotedArguments,
{required TypeImpl contextType}) {
var function = node.function;
if (function is ExtensionOverrideImpl) {
_resolveReceiverExtensionOverride(node, function, whyNotPromotedArguments,
contextType: contextType);
return;
}
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(
CompileTimeErrorCode.UNCHECKED_INVOCATION_OF_NULLABLE_VALUE,
function,
);
_resolve(node, receiverType, whyNotPromotedArguments,
contextType: contextType);
return;
}
if (identical(receiverType, NeverTypeImpl.instance)) {
_errorReporter.atNode(
function,
WarningCode.RECEIVER_OF_TYPE_NEVER,
);
_unresolved(node, NeverTypeImpl.instance, whyNotPromotedArguments,
contextType: contextType);
return;
}
var result = _typePropertyResolver.resolve(
receiver: function,
receiverType: receiverType,
name: MethodElement2.CALL_METHOD_NAME,
propertyErrorEntity: function,
nameErrorEntity: function,
);
var callElement = result.getter2;
if (callElement == null) {
if (result.needsGetterError) {
_errorReporter.atNode(
function,
CompileTimeErrorCode.INVOCATION_OF_NON_FUNCTION_EXPRESSION,
);
}
var type = result.isGetterInvalid
? InvalidTypeImpl.instance
: DynamicTypeImpl.instance;
_unresolved(node, type, whyNotPromotedArguments,
contextType: contextType);
return;
}
if (callElement.kind != ElementKind.METHOD) {
_errorReporter.atNode(
function,
CompileTimeErrorCode.INVOCATION_OF_NON_FUNCTION_EXPRESSION,
);
_unresolved(node, InvalidTypeImpl.instance, whyNotPromotedArguments,
contextType: contextType);
return;
}
node.element = callElement;
var rawType = callElement.type;
_resolve(node, rawType, whyNotPromotedArguments, contextType: contextType);
}
/// 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 [CompileTimeErrorCode.USE_OF_VOID_RESULT].
///
// TODO(scheglov): this is duplicate
bool _checkForUseOfVoidResult(Expression expression, DartType type) {
if (!identical(type, VoidTypeImpl.instance)) {
return false;
}
if (expression is MethodInvocation) {
SimpleIdentifier methodName = expression.methodName;
_errorReporter.atNode(
methodName,
CompileTimeErrorCode.USE_OF_VOID_RESULT,
);
} else {
_errorReporter.atNode(
expression,
CompileTimeErrorCode.USE_OF_VOID_RESULT,
);
}
return true;
}
void _resolve(FunctionExpressionInvocationImpl node, FunctionType rawType,
List<WhyNotPromotedGetter> whyNotPromotedArguments,
{required TypeImpl contextType}) {
var returnType = FunctionExpressionInvocationInferrer(
resolver: _resolver,
node: node,
argumentList: node.argumentList,
whyNotPromotedArguments: whyNotPromotedArguments,
contextType: contextType,
).resolveInvocation(
// TODO(paulberry): eliminate this cast by changing the type of
// `rawType`.
rawType: rawType as FunctionTypeImpl);
node.recordStaticType(returnType, resolver: _resolver);
}
void _resolveReceiverExtensionOverride(
FunctionExpressionInvocationImpl node,
ExtensionOverrideImpl function,
List<WhyNotPromotedGetter> whyNotPromotedArguments,
{required TypeImpl contextType}) {
var result = _extensionResolver.getOverrideMember(
function,
MethodElement2.CALL_METHOD_NAME,
);
var callElement = result.getter2;
node.element = callElement;
if (callElement == null) {
_errorReporter.atNode(
function,
CompileTimeErrorCode.INVOCATION_OF_EXTENSION_WITHOUT_CALL,
arguments: [function.name.lexeme],
);
return _unresolved(
node, DynamicTypeImpl.instance, whyNotPromotedArguments,
contextType: contextType);
}
if (callElement.isStatic) {
_errorReporter.atNode(
node.argumentList,
CompileTimeErrorCode.EXTENSION_OVERRIDE_ACCESS_TO_STATIC_MEMBER,
);
}
var rawType = callElement.type;
_resolve(node, rawType, whyNotPromotedArguments, contextType: contextType);
}
void _unresolved(FunctionExpressionInvocationImpl node, TypeImpl type,
List<WhyNotPromotedGetter> whyNotPromotedArguments,
{required TypeImpl contextType}) {
_setExplicitTypeArgumentTypes(node);
FunctionExpressionInvocationInferrer(
resolver: _resolver,
node: node,
argumentList: node.argumentList,
contextType: contextType,
whyNotPromotedArguments: whyNotPromotedArguments)
.resolveInvocation(rawType: null);
node.staticInvokeType = type;
node.recordStaticType(type, resolver: _resolver);
}
/// Inference cannot be done, we still want to fill type argument types.
static void _setExplicitTypeArgumentTypes(
FunctionExpressionInvocationImpl node,
) {
var typeArguments = node.typeArguments;
if (typeArguments != null) {
node.typeArgumentTypes = typeArguments.arguments
.map((typeArgument) => typeArgument.typeOrThrow)
.toList();
} else {
node.typeArgumentTypes = const <TypeImpl>[];
}
}
}