Skip to content

Commit

Permalink
Consolidate type inference logic for invocations.
Browse files Browse the repository at this point in the history
Moves all of the analyzer's type inference logic for invocations into
a single `resolveInvocation` method, defined on an abstract base class
`InvocationInferrer`.  There are concrete derived classes to
specialize this logic for all of the types of AST nodes require this
sort of inference (Annotation, ExtensionOverride,
FunctionExpressionInvocation, InstanceCreationExpression,
MethodInvocation, RedirectingConstructorInvocation, and
SuperConstructorInvocation), and the special logic for each node type
is slotted into the core algorithm using virtual dispatch.

In addition to making the code more maintainable by reducing code
duplication, this change paves the way toward modifying the core
algorithm to allow inference information to flow between arguments in
a generic function call
(dart-lang/language#731).

Also partially addresses #48500 (Expression.staticParameterElement
sometimes points to a synthetic element).

Change-Id: I82788d58a62b6555589da16163317b6bbddd53c1
Bug: dart-lang/language#731, #48500
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/234864
Reviewed-by: Konstantin Shcheglov <[email protected]>
Commit-Queue: Paul Berry <[email protected]>
  • Loading branch information
stereotype441 authored and Commit Bot committed Mar 6, 2022
1 parent 6f2291a commit 1cdfaa8
Show file tree
Hide file tree
Showing 14 changed files with 721 additions and 909 deletions.
167 changes: 20 additions & 147 deletions pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,25 @@
// 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/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.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/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/dart/resolver/instance_creation_resolver_helper.dart';
import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
import 'package:analyzer/src/dart/resolver/invocation_inferrer.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart';

class AnnotationResolver with InstanceCreationResolverMixin {
class AnnotationResolver {
final ResolverVisitor _resolver;

AnnotationResolver(this._resolver);

@override
ResolverVisitor get resolver => _resolver;

LibraryElement get _definingLibrary => _resolver.definingLibrary;

ErrorReporter get _errorReporter => _resolver.errorReporter;

bool get _genericMetadataIsEnabled =>
_definingLibrary.featureSet.isEnabled(Feature.generic_metadata);

void resolve(
AnnotationImpl node, List<WhyNotPromotedGetter> whyNotPromotedList) {
node.typeArguments?.accept(_resolver);
Expand Down Expand Up @@ -116,137 +105,35 @@ class AnnotationResolver with InstanceCreationResolverMixin {
constructorElement = _resolver.toLegacyElement(constructorElement);
constructorName?.staticElement = constructorElement;
node.element = constructorElement;
var annotationInferrer =
AnnotationInferrer(constructorName: constructorName);

if (constructorElement == null) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.INVALID_ANNOTATION,
node,
);
_resolver.analyzeArgumentList(argumentList, null,
annotationInferrer.resolveInvocation(
resolver: _resolver,
node: node,
rawType: null,
contextType: null,
whyNotPromotedList: whyNotPromotedList);
return;
}

// If no type parameters, the elements are correct.
if (typeParameters.isEmpty) {
var typeArgumentList = node.typeArguments;
if (typeArgumentList != null) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS,
typeArgumentList,
[
typeDisplayName,
typeParameters.length,
typeArgumentList.arguments.length,
],
);
}
_resolveConstructorInvocationArguments(node);
_resolver.analyzeArgumentList(argumentList, constructorElement.parameters,
whyNotPromotedList: whyNotPromotedList);
return;
}

void resolveWithFixedTypeArguments(
List<DartType> typeArguments,
ConstructorElement constructorElement,
) {
var type = instantiateElement(typeArguments);
constructorElement = ConstructorMember.from(constructorElement, type);
constructorName?.staticElement = constructorElement;
node.element = constructorElement;
_resolveConstructorInvocationArguments(node);

_resolver.analyzeArgumentList(argumentList, constructorElement.parameters,
whyNotPromotedList: whyNotPromotedList);
}

if (!_genericMetadataIsEnabled) {
var typeArguments = List.filled(
typeParameters.length,
DynamicTypeImpl.instance,
);
resolveWithFixedTypeArguments(typeArguments, constructorElement);
return;
}

var typeArgumentList = node.typeArguments;
if (typeArgumentList != null) {
List<DartType> typeArguments;
if (typeArgumentList.arguments.length == typeParameters.length) {
typeArguments = typeArgumentList.arguments
.map((element) => element.typeOrThrow)
.toList();
var substitution = Substitution.fromPairs(
typeParameters,
typeArguments,
);
for (var i = 0; i < typeParameters.length; i++) {
var typeParameter = typeParameters[i];
var bound = typeParameter.bound;
if (bound != null) {
bound = substitution.substituteType(bound);
var typeArgument = typeArguments[i];
if (!_resolver.typeSystem.isSubtypeOf(typeArgument, bound)) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS,
typeArgumentList.arguments[i],
[typeArgument, typeParameter.name, bound],
);
}
}
}
} else {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS,
typeArgumentList,
[
typeDisplayName,
typeParameters.length,
typeArgumentList.arguments.length,
],
);
typeArguments = List.filled(
typeParameters.length,
DynamicTypeImpl.instance,
);
}
resolveWithFixedTypeArguments(typeArguments, constructorElement);
return;
}

var elementToInfer = ConstructorElementToInfer(
typeParameters,
constructorElement,
);
var inferenceResult = inferArgumentTypes(
inferenceNode: node,
constructorElement: constructorElement,
elementToInfer: elementToInfer,
typeArguments: node.typeArguments,
arguments: node.arguments!,
errorNode: node,
isConst: true,
contextReturnType: null);
if (inferenceResult != null) {
constructorElement = inferenceResult.constructorElement;
}
_resolver.analyzeArgumentList(argumentList, constructorElement.parameters,
whyNotPromotedList: whyNotPromotedList);

var constructorRawType = elementToInfer.asType;

var inferred = _resolver.inferenceHelper.inferGenericInvoke(
node, constructorRawType, typeArgumentList, argumentList, node,
isConst: true, contextReturnType: null)!;

constructorElement = ConstructorMember.from(
constructorElement,
inferred.returnType as InterfaceType,
);
constructorName?.staticElement = constructorElement;
node.element = constructorElement;
_resolveConstructorInvocationArguments(node);
annotationInferrer.resolveInvocation(
resolver: _resolver,
node: node,
rawType: constructorRawType,
contextType: null,
whyNotPromotedList: whyNotPromotedList);
}

void _extensionGetter(
Expand Down Expand Up @@ -446,24 +333,6 @@ class AnnotationResolver with InstanceCreationResolverMixin {
}
}

void _resolveConstructorInvocationArguments(AnnotationImpl node) {
var argumentList = node.arguments;
// error will be reported in ConstantVerifier
if (argumentList == null) {
return;
}
// resolve arguments to parameters
var constructor = node.element;
if (constructor is ConstructorElement) {
argumentList.correspondingStaticParameters =
ResolverVisitor.resolveArgumentsToParameters(
argumentList: argumentList,
parameters: constructor.parameters,
errorReporter: _errorReporter,
);
}
}

void _typeAliasConstructorInvocation(
AnnotationImpl node,
TypeAliasElement typeAliasElement,
Expand Down Expand Up @@ -530,7 +399,11 @@ class AnnotationResolver with InstanceCreationResolverMixin {
AnnotationImpl node, List<WhyNotPromotedGetter> whyNotPromotedList) {
var arguments = node.arguments;
if (arguments != null) {
_resolver.analyzeArgumentList(arguments, null,
AnnotationInferrer(constructorName: null).resolveInvocation(
resolver: _resolver,
node: node,
rawType: null,
contextType: null,
whyNotPromotedList: whyNotPromotedList);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ class ConstructorReferenceResolver {
//
// Otherwise we'll have a ConstructorElement, and we can skip inference
// because there's nothing to infer in a non-generic type.
if (elementToInfer != null) {
if (elementToInfer != null &&
elementToInfer.typeParameters.isNotEmpty &&
constructorName.type.typeArguments == null) {
// TODO(leafp): Currently, we may re-infer types here, since we
// sometimes resolve multiple times. We should really check that we
// have not already inferred something. However, the obvious ways to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/resolver/extension_member_resolver.dart';
import 'package:analyzer/src/dart/resolver/invocation_inference_helper.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';
Expand Down Expand Up @@ -59,7 +60,8 @@ class FunctionExpressionInvocationResolver {
}

if (_checkForUseOfVoidResult(function, receiverType)) {
_unresolved(node, DynamicTypeImpl.instance, whyNotPromotedList);
_unresolved(node, DynamicTypeImpl.instance, whyNotPromotedList,
contextType: contextType);
return;
}

Expand All @@ -77,11 +79,13 @@ class FunctionExpressionInvocationResolver {
if (identical(receiverType, NeverTypeImpl.instance)) {
_errorReporter.reportErrorForNode(
HintCode.RECEIVER_OF_TYPE_NEVER, function);
_unresolved(node, NeverTypeImpl.instance, whyNotPromotedList);
_unresolved(node, NeverTypeImpl.instance, whyNotPromotedList,
contextType: contextType);
return;
}

_unresolved(node, DynamicTypeImpl.instance, whyNotPromotedList);
_unresolved(node, DynamicTypeImpl.instance, whyNotPromotedList,
contextType: contextType);
}

/// Check for situations where the result of a method or function is used,
Expand Down Expand Up @@ -111,26 +115,19 @@ class FunctionExpressionInvocationResolver {
void _resolve(FunctionExpressionInvocationImpl node, FunctionType rawType,
List<WhyNotPromotedGetter> whyNotPromotedList,
{required DartType? contextType}) {
_inferenceHelper.resolveFunctionExpressionInvocation(
var returnType =
const FunctionExpressionInvocationInferrer().resolveInvocation(
resolver: _resolver,
node: node,
rawType: rawType,
whyNotPromotedList: whyNotPromotedList,
contextType: contextType,
);

var returnType = _inferenceHelper.computeInvokeReturnType(
node.staticInvokeType,
);
_inferenceHelper.recordStaticType(node, returnType,
contextType: contextType);
}

void _resolveArguments(FunctionExpressionInvocationImpl node,
List<WhyNotPromotedGetter> whyNotPromotedList) {
_resolver.analyzeArgumentList(node.argumentList, null,
whyNotPromotedList: whyNotPromotedList);
}

void _resolveReceiverExtensionOverride(FunctionExpressionInvocationImpl node,
ExtensionOverride function, List<WhyNotPromotedGetter> whyNotPromotedList,
{required DartType? contextType}) {
Expand All @@ -147,7 +144,8 @@ class FunctionExpressionInvocationResolver {
function,
[function.extensionName.name],
);
return _unresolved(node, DynamicTypeImpl.instance, whyNotPromotedList);
return _unresolved(node, DynamicTypeImpl.instance, whyNotPromotedList,
contextType: contextType);
}

if (callElement.isStatic) {
Expand Down Expand Up @@ -183,7 +181,8 @@ class FunctionExpressionInvocationResolver {
function,
);
}
_unresolved(node, DynamicTypeImpl.instance, whyNotPromotedList);
_unresolved(node, DynamicTypeImpl.instance, whyNotPromotedList,
contextType: contextType);
return;
}

Expand All @@ -192,7 +191,8 @@ class FunctionExpressionInvocationResolver {
CompileTimeErrorCode.INVOCATION_OF_NON_FUNCTION_EXPRESSION,
function,
);
_unresolved(node, DynamicTypeImpl.instance, whyNotPromotedList);
_unresolved(node, DynamicTypeImpl.instance, whyNotPromotedList,
contextType: contextType);
return;
}

Expand All @@ -202,9 +202,15 @@ class FunctionExpressionInvocationResolver {
}

void _unresolved(FunctionExpressionInvocationImpl node, DartType type,
List<WhyNotPromotedGetter> whyNotPromotedList) {
List<WhyNotPromotedGetter> whyNotPromotedList,
{required DartType? contextType}) {
_setExplicitTypeArgumentTypes(node);
_resolveArguments(node, whyNotPromotedList);
const FunctionExpressionInvocationInferrer().resolveInvocation(
resolver: _resolver,
node: node,
rawType: null,
contextType: contextType,
whyNotPromotedList: whyNotPromotedList);
node.staticInvokeType = DynamicTypeImpl.instance;
node.staticType = type;
}
Expand Down
Loading

0 comments on commit 1cdfaa8

Please sign in to comment.