blob: 9099004925eba3b42bdc80155c7dddb9865a882f [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/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.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/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_constraint_gatherer.dart';
import 'package:analyzer/src/dart/element/type_schema.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 {
final ResolverVisitor _resolver;
AnnotationResolver(this._resolver);
LibraryElementImpl get _definingLibrary => _resolver.definingLibrary;
DiagnosticReporter get _diagnosticReporter => _resolver.diagnosticReporter;
void resolve(
AnnotationImpl node,
List<WhyNotPromotedGetter> whyNotPromotedArguments,
) {
node.typeArguments?.accept(_resolver);
_resolve(node, whyNotPromotedArguments);
}
void _classConstructorInvocation(
AnnotationImpl node,
InterfaceElementImpl classElement,
SimpleIdentifierImpl? constructorName,
ArgumentListImpl argumentList,
List<WhyNotPromotedGetter> whyNotPromotedArguments,
) {
ConstructorElementMixin2? constructorElement;
if (constructorName != null) {
constructorElement = classElement.getNamedConstructor2(
constructorName.name,
);
} else {
constructorElement = classElement.unnamedConstructor2;
}
_constructorInvocation(
node,
classElement.name3!,
constructorName,
classElement.typeParameters2,
constructorElement,
argumentList,
(typeArguments) {
return classElement.instantiateImpl(
typeArguments: typeArguments,
nullabilitySuffix: NullabilitySuffix.none,
);
},
whyNotPromotedArguments,
);
}
void _classGetter(
AnnotationImpl node,
InterfaceElement classElement,
SimpleIdentifierImpl? getterName,
List<WhyNotPromotedGetter> whyNotPromotedArguments,
) {
ExecutableElement? getter;
if (getterName != null) {
getter = classElement.getGetter(getterName.name);
// Recovery, try to find a constructor.
getter ??= classElement.getNamedConstructor2(getterName.name);
} else {
getter = classElement.unnamedConstructor2;
}
getterName?.element = getter;
node.element2 = getter;
if (getterName != null && getter is PropertyAccessorElement) {
_propertyAccessorElement(
node,
getterName,
getter,
whyNotPromotedArguments,
);
_resolveAnnotationElementGetter(node, getter);
} else if (getter is! ConstructorElement) {
_diagnosticReporter.atNode(node, CompileTimeErrorCode.INVALID_ANNOTATION);
}
_visitArguments(
node,
whyNotPromotedArguments,
dataForTesting: _resolver.inferenceHelper.dataForTesting,
);
}
void _constructorInvocation(
AnnotationImpl node,
String typeDisplayName,
SimpleIdentifierImpl? constructorName,
List<TypeParameterElementImpl> typeParameters,
ConstructorElementMixin2? constructorElement,
ArgumentListImpl argumentList,
InterfaceType Function(List<TypeImpl> typeArguments) instantiateElement,
List<WhyNotPromotedGetter> whyNotPromotedArguments,
) {
constructorName?.element = constructorElement;
node.element2 = constructorElement;
if (constructorElement == null) {
_diagnosticReporter.atNode(node, CompileTimeErrorCode.INVALID_ANNOTATION);
AnnotationInferrer(
resolver: _resolver,
node: node,
argumentList: argumentList,
contextType: UnknownInferredType.instance,
whyNotPromotedArguments: whyNotPromotedArguments,
constructorName: constructorName,
).resolveInvocation(rawType: null);
return;
}
var elementToInfer = ConstructorElementToInfer(
typeParameters,
constructorElement,
);
var constructorRawType = elementToInfer.asType;
AnnotationInferrer(
resolver: _resolver,
node: node,
argumentList: argumentList,
contextType: UnknownInferredType.instance,
whyNotPromotedArguments: whyNotPromotedArguments,
constructorName: constructorName,
).resolveInvocation(
// TODO(paulberry): eliminate this cast by changing the type of
// `ConstructorElementToInfer.asType`.
rawType: constructorRawType as FunctionTypeImpl,
);
}
void _extensionGetter(
AnnotationImpl node,
ExtensionElement extensionElement,
SimpleIdentifierImpl? getterName,
List<WhyNotPromotedGetter> whyNotPromotedArguments,
) {
ExecutableElement? getter;
if (getterName != null) {
getter = extensionElement.getGetter(getterName.name);
}
getterName?.element = getter;
node.element2 = getter;
if (getterName != null && getter is PropertyAccessorElement) {
_propertyAccessorElement(
node,
getterName,
getter,
whyNotPromotedArguments,
);
_resolveAnnotationElementGetter(node, getter);
} else {
_diagnosticReporter.atNode(node, CompileTimeErrorCode.INVALID_ANNOTATION);
}
_visitArguments(
node,
whyNotPromotedArguments,
dataForTesting: _resolver.inferenceHelper.dataForTesting,
);
}
void _localVariable(
AnnotationImpl node,
VariableElement element,
List<WhyNotPromotedGetter> whyNotPromotedArguments,
) {
if (!element.isConst || node.arguments != null) {
_diagnosticReporter.atNode(node, CompileTimeErrorCode.INVALID_ANNOTATION);
}
_visitArguments(
node,
whyNotPromotedArguments,
dataForTesting: _resolver.inferenceHelper.dataForTesting,
);
}
void _propertyAccessorElement(
AnnotationImpl node,
SimpleIdentifierImpl name,
PropertyAccessorElement element,
List<WhyNotPromotedGetter> whyNotPromotedArguments,
) {
name.element = element;
node.element2 = element;
_resolveAnnotationElementGetter(node, element);
_visitArguments(
node,
whyNotPromotedArguments,
dataForTesting: _resolver.inferenceHelper.dataForTesting,
);
}
void _resolve(
AnnotationImpl node,
List<WhyNotPromotedGetter> whyNotPromotedArguments,
) {
SimpleIdentifierImpl name1;
SimpleIdentifierImpl? name2;
SimpleIdentifierImpl? name3;
var nameNode = node.name;
if (nameNode is PrefixedIdentifierImpl) {
name1 = nameNode.prefix;
name2 = nameNode.identifier;
name3 = node.constructorName;
} else {
name1 = nameNode as SimpleIdentifierImpl;
name2 = node.constructorName;
}
var argumentList = node.arguments;
var element1 = name1.scopeLookupResult!.getter2;
name1.element = element1;
if (element1 == null) {
_diagnosticReporter.atNode(
node,
CompileTimeErrorCode.UNDEFINED_ANNOTATION,
arguments: [name1.name],
);
_visitArguments(
node,
whyNotPromotedArguments,
dataForTesting: _resolver.inferenceHelper.dataForTesting,
);
return;
}
// Class(args) or Class.CONST
if (element1 is InterfaceElementImpl) {
if (argumentList != null) {
_classConstructorInvocation(
node,
element1,
name2,
argumentList,
whyNotPromotedArguments,
);
} else {
_classGetter(node, element1, name2, whyNotPromotedArguments);
}
return;
}
// Extension.CONST
if (element1 is ExtensionElement) {
_extensionGetter(node, element1, name2, whyNotPromotedArguments);
return;
}
// prefix.*
if (element1 is PrefixElement) {
if (name2 != null) {
var element = element1.scope.lookup(name2.name).getter2;
name2.element = element;
// prefix.Class(args) or prefix.Class.CONST
if (element is InterfaceElementImpl) {
if (element is ClassElement && argumentList != null) {
_classConstructorInvocation(
node,
element,
name3,
argumentList,
whyNotPromotedArguments,
);
} else {
_classGetter(node, element, name3, whyNotPromotedArguments);
}
return;
}
// prefix.Extension.CONST
if (element is ExtensionElement) {
_extensionGetter(node, element, name3, whyNotPromotedArguments);
return;
}
// prefix.CONST
if (element is PropertyAccessorElement) {
_propertyAccessorElement(
node,
name2,
element,
whyNotPromotedArguments,
);
return;
}
// prefix.TypeAlias(args) or prefix.TypeAlias.CONST
if (element is TypeAliasElementImpl) {
var aliasedType = element.aliasedType;
var argumentList = node.arguments;
if (aliasedType is InterfaceTypeImpl && argumentList != null) {
_typeAliasConstructorInvocation(
node,
element,
name3,
aliasedType,
argumentList,
whyNotPromotedArguments,
dataForTesting: _resolver.inferenceHelper.dataForTesting,
);
} else {
_typeAliasGetter(node, element, name3, whyNotPromotedArguments);
}
return;
}
// undefined
if (element == null) {
_diagnosticReporter.atNode(
node,
CompileTimeErrorCode.UNDEFINED_ANNOTATION,
arguments: [name2.name],
);
_visitArguments(
node,
whyNotPromotedArguments,
dataForTesting: _resolver.inferenceHelper.dataForTesting,
);
return;
}
}
}
// CONST
if (element1 is PropertyAccessorElement) {
_propertyAccessorElement(node, name1, element1, whyNotPromotedArguments);
return;
}
// TypeAlias(args) or TypeAlias.CONST
if (element1 is TypeAliasElementImpl) {
var aliasedType = element1.aliasedType;
var argumentList = node.arguments;
if (aliasedType is InterfaceTypeImpl && argumentList != null) {
_typeAliasConstructorInvocation(
node,
element1,
name2,
aliasedType,
argumentList,
whyNotPromotedArguments,
dataForTesting: _resolver.inferenceHelper.dataForTesting,
);
} else {
_typeAliasGetter(node, element1, name2, whyNotPromotedArguments);
}
return;
}
if (element1 is VariableElement) {
_localVariable(node, element1, whyNotPromotedArguments);
return;
}
_diagnosticReporter.atNode(node, CompileTimeErrorCode.INVALID_ANNOTATION);
_visitArguments(
node,
whyNotPromotedArguments,
dataForTesting: _resolver.inferenceHelper.dataForTesting,
);
}
void _resolveAnnotationElementGetter(
Annotation annotation,
PropertyAccessorElement accessorElement,
) {
// The accessor should be synthetic, the variable should be constant, and
// there should be no arguments.
var variableElement = accessorElement.variable3;
if (variableElement == null) {
return;
}
if (!accessorElement.isSynthetic ||
!variableElement.isConst ||
annotation.arguments != null) {
_diagnosticReporter.atNode(
annotation,
CompileTimeErrorCode.INVALID_ANNOTATION,
);
}
}
void _typeAliasConstructorInvocation(
AnnotationImpl node,
TypeAliasElementImpl typeAliasElement,
SimpleIdentifierImpl? constructorName,
InterfaceTypeImpl aliasedType,
ArgumentListImpl argumentList,
List<WhyNotPromotedGetter> whyNotPromotedArguments, {
required TypeConstraintGenerationDataForTesting? dataForTesting,
}) {
var constructorElement = aliasedType.lookUpConstructor(
constructorName?.name,
_definingLibrary,
);
_constructorInvocation(
node,
typeAliasElement.name3!,
constructorName,
typeAliasElement.typeParameters2,
constructorElement,
argumentList,
(typeArguments) {
return typeAliasElement.instantiateImpl(
typeArguments: typeArguments,
nullabilitySuffix: NullabilitySuffix.none,
)
as InterfaceType;
},
whyNotPromotedArguments,
);
}
void _typeAliasGetter(
AnnotationImpl node,
TypeAliasElement typeAliasElement,
SimpleIdentifierImpl? getterName,
List<WhyNotPromotedGetter> whyNotPromotedArguments,
) {
ExecutableElement? getter;
var aliasedType = typeAliasElement.aliasedType;
if (aliasedType is InterfaceType) {
var classElement = aliasedType.element;
if (getterName != null) {
getter = classElement.getGetter(getterName.name);
}
}
getterName?.element = getter;
node.element2 = getter;
if (getterName != null && getter is PropertyAccessorElement) {
_propertyAccessorElement(
node,
getterName,
getter,
whyNotPromotedArguments,
);
_resolveAnnotationElementGetter(node, getter);
} else if (getter is! ConstructorElement) {
_diagnosticReporter.atNode(node, CompileTimeErrorCode.INVALID_ANNOTATION);
}
_visitArguments(
node,
whyNotPromotedArguments,
dataForTesting: _resolver.inferenceHelper.dataForTesting,
);
}
void _visitArguments(
AnnotationImpl node,
List<WhyNotPromotedGetter> whyNotPromotedArguments, {
required TypeConstraintGenerationDataForTesting? dataForTesting,
}) {
var arguments = node.arguments;
if (arguments != null) {
AnnotationInferrer(
resolver: _resolver,
node: node,
argumentList: arguments,
contextType: UnknownInferredType.instance,
whyNotPromotedArguments: whyNotPromotedArguments,
constructorName: null,
).resolveInvocation(rawType: null);
}
}
}