| // 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:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart'; |
| 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/element.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/invocation_inference_helper.dart'; |
| import 'package:analyzer/src/error/codes.dart'; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| |
| class AnnotationResolver { |
| final ResolverVisitor _resolver; |
| |
| AnnotationResolver(this._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<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) { |
| AstNode parent = node.parent; |
| |
| node.typeArguments?.accept(_resolver); |
| _resolve(node, whyNotPromotedList); |
| |
| var elementAnnotationImpl = |
| node.elementAnnotation as ElementAnnotationImpl?; |
| if (elementAnnotationImpl == null) { |
| // Analyzer ignores annotations on "part of" directives. |
| assert(parent is PartDirective || parent is PartOfDirective); |
| } |
| } |
| |
| void _classConstructorInvocation( |
| AnnotationImpl node, |
| ClassElement classElement, |
| SimpleIdentifierImpl? constructorName, |
| ArgumentList argumentList, |
| List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList, |
| ) { |
| ConstructorElement? constructorElement; |
| if (constructorName != null) { |
| constructorElement = classElement.getNamedConstructor( |
| constructorName.name, |
| ); |
| } else { |
| constructorElement = classElement.unnamedConstructor; |
| } |
| |
| _constructorInvocation( |
| node, |
| classElement.name, |
| constructorName, |
| classElement.typeParameters, |
| constructorElement, |
| argumentList, |
| (typeArguments) { |
| return classElement.instantiate( |
| typeArguments: typeArguments, |
| nullabilitySuffix: _resolver.noneOrStarSuffix, |
| ); |
| }, |
| whyNotPromotedList, |
| ); |
| } |
| |
| void _classGetter( |
| AnnotationImpl node, |
| ClassElement classElement, |
| SimpleIdentifierImpl? getterName, |
| List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList, |
| ) { |
| ExecutableElement? getter; |
| if (getterName != null) { |
| getter = classElement.getGetter(getterName.name); |
| getter = _resolver.toLegacyElement(getter); |
| // Recovery, try to find a constructor. |
| getter ??= classElement.getNamedConstructor(getterName.name); |
| } else { |
| getter = classElement.unnamedConstructor; |
| } |
| |
| getterName?.staticElement = getter; |
| node.element = getter; |
| |
| if (getterName != null && getter is PropertyAccessorElement) { |
| _propertyAccessorElement(node, getterName, getter, whyNotPromotedList); |
| _resolveAnnotationElementGetter(node, getter); |
| } else if (getter is! ConstructorElement) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INVALID_ANNOTATION, |
| node, |
| ); |
| } |
| |
| _visitArguments(node, whyNotPromotedList); |
| } |
| |
| void _constructorInvocation( |
| AnnotationImpl node, |
| String typeDisplayName, |
| SimpleIdentifierImpl? constructorName, |
| List<TypeParameterElement> typeParameters, |
| ConstructorElement? constructorElement, |
| ArgumentList argumentList, |
| InterfaceType Function(List<DartType> typeArguments) instantiateElement, |
| List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList, |
| ) { |
| constructorElement = _resolver.toLegacyElement(constructorElement); |
| constructorName?.staticElement = constructorElement; |
| node.element = constructorElement; |
| |
| if (constructorElement == null) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INVALID_ANNOTATION, |
| node, |
| ); |
| _resolver.visitArgumentList(argumentList, |
| 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); |
| InferenceContext.setType(argumentList, constructorElement.type); |
| _resolver.visitArgumentList(argumentList, |
| 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); |
| |
| InferenceContext.setType(argumentList, constructorElement.type); |
| _resolver.visitArgumentList(argumentList, |
| 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; |
| } |
| |
| _resolver.visitArgumentList(argumentList, |
| whyNotPromotedList: whyNotPromotedList); |
| |
| var elementToInfer = ConstructorElementToInfer( |
| typeParameters, |
| constructorElement, |
| ); |
| var constructorRawType = elementToInfer.asType; |
| |
| var inferred = _resolver.inferenceHelper.inferGenericInvoke( |
| node, constructorRawType, typeArgumentList, argumentList, node, |
| isConst: true)!; |
| |
| constructorElement = ConstructorMember.from( |
| constructorElement, |
| inferred.returnType as InterfaceType, |
| ); |
| constructorName?.staticElement = constructorElement; |
| node.element = constructorElement; |
| _resolveConstructorInvocationArguments(node); |
| } |
| |
| void _extensionGetter( |
| AnnotationImpl node, |
| ExtensionElement extensionElement, |
| SimpleIdentifierImpl? getterName, |
| List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList, |
| ) { |
| ExecutableElement? getter; |
| if (getterName != null) { |
| getter = extensionElement.getGetter(getterName.name); |
| getter = _resolver.toLegacyElement(getter); |
| } |
| |
| getterName?.staticElement = getter; |
| node.element = getter; |
| |
| if (getterName != null && getter is PropertyAccessorElement) { |
| _propertyAccessorElement(node, getterName, getter, whyNotPromotedList); |
| _resolveAnnotationElementGetter(node, getter); |
| } else { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INVALID_ANNOTATION, |
| node, |
| ); |
| } |
| |
| _visitArguments(node, whyNotPromotedList); |
| } |
| |
| void _propertyAccessorElement( |
| AnnotationImpl node, |
| SimpleIdentifierImpl name, |
| PropertyAccessorElement element, |
| List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList, |
| ) { |
| element = _resolver.toLegacyElement(element); |
| name.staticElement = element; |
| node.element = element; |
| |
| _resolveAnnotationElementGetter(node, element); |
| _visitArguments(node, whyNotPromotedList); |
| } |
| |
| void _resolve(AnnotationImpl node, |
| List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) { |
| 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 = _resolver.nameScope.lookup(name1.name).getter; |
| name1.staticElement = element1; |
| |
| if (element1 == null) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.UNDEFINED_ANNOTATION, |
| node, |
| [name1.name], |
| ); |
| _visitArguments(node, whyNotPromotedList); |
| return; |
| } |
| |
| // Class(args) or Class.CONST |
| if (element1 is ClassElement) { |
| if (argumentList != null) { |
| _classConstructorInvocation( |
| node, element1, name2, argumentList, whyNotPromotedList); |
| } else { |
| _classGetter(node, element1, name2, whyNotPromotedList); |
| } |
| return; |
| } |
| |
| // Extension.CONST |
| if (element1 is ExtensionElement) { |
| _extensionGetter(node, element1, name2, whyNotPromotedList); |
| return; |
| } |
| |
| // prefix.* |
| if (element1 is PrefixElement) { |
| if (name2 != null) { |
| var element2 = element1.scope.lookup(name2.name).getter; |
| name2.staticElement = element2; |
| // prefix.Class(args) or prefix.Class.CONST |
| if (element2 is ClassElement) { |
| if (argumentList != null) { |
| _classConstructorInvocation( |
| node, element2, name3, argumentList, whyNotPromotedList); |
| } else { |
| _classGetter(node, element2, name3, whyNotPromotedList); |
| } |
| return; |
| } |
| // prefix.Extension.CONST |
| if (element2 is ExtensionElement) { |
| _extensionGetter(node, element2, name3, whyNotPromotedList); |
| return; |
| } |
| // prefix.CONST |
| if (element2 is PropertyAccessorElement) { |
| _propertyAccessorElement(node, name2, element2, whyNotPromotedList); |
| return; |
| } |
| |
| // prefix.TypeAlias(args) or prefix.TypeAlias.CONST |
| if (element2 is TypeAliasElement) { |
| var aliasedType = element2.aliasedType; |
| var argumentList = node.arguments; |
| if (aliasedType is InterfaceType && argumentList != null) { |
| _typeAliasConstructorInvocation(node, element2, name3, aliasedType, |
| argumentList, whyNotPromotedList); |
| } else { |
| _typeAliasGetter(node, element2, name3, whyNotPromotedList); |
| } |
| return; |
| } |
| // undefined |
| if (element2 == null) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.UNDEFINED_ANNOTATION, |
| node, |
| [name2.name], |
| ); |
| _visitArguments(node, whyNotPromotedList); |
| return; |
| } |
| } |
| } |
| |
| // CONST |
| if (element1 is PropertyAccessorElement) { |
| _propertyAccessorElement(node, name1, element1, whyNotPromotedList); |
| return; |
| } |
| |
| // TypeAlias(args) or TypeAlias.CONST |
| if (element1 is TypeAliasElement) { |
| var aliasedType = element1.aliasedType; |
| var argumentList = node.arguments; |
| if (aliasedType is InterfaceType && argumentList != null) { |
| _typeAliasConstructorInvocation(node, element1, name2, aliasedType, |
| argumentList, whyNotPromotedList); |
| } else { |
| _typeAliasGetter(node, element1, name2, whyNotPromotedList); |
| } |
| return; |
| } |
| |
| // TODO(scheglov) Must be const. |
| if (element1 is VariableElement) { |
| return; |
| } |
| |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INVALID_ANNOTATION, |
| node, |
| ); |
| |
| _visitArguments(node, whyNotPromotedList); |
| } |
| |
| void _resolveAnnotationElementGetter( |
| Annotation annotation, PropertyAccessorElement accessorElement) { |
| // The accessor should be synthetic, the variable should be constant, and |
| // there should be no arguments. |
| VariableElement variableElement = accessorElement.variable; |
| if (!accessorElement.isSynthetic || |
| !variableElement.isConst || |
| annotation.arguments != null) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INVALID_ANNOTATION, annotation); |
| } |
| } |
| |
| /// Given an [argumentList] and the [executableElement] that will be invoked |
| /// using those argument, compute the list of parameters that correspond to |
| /// the list of arguments. An error will be reported if any of the arguments |
| /// cannot be matched to a parameter. Return the parameters that correspond to |
| /// the arguments, or `null` if no correspondence could be computed. |
| /// |
| /// TODO(scheglov) this is duplicate |
| List<ParameterElement?>? _resolveArgumentsToFunction( |
| ArgumentList argumentList, ExecutableElement? executableElement) { |
| if (executableElement == null) { |
| return null; |
| } |
| List<ParameterElement> parameters = executableElement.parameters; |
| return _resolveArgumentsToParameters(argumentList, parameters); |
| } |
| |
| /// Given an [argumentList] and the [parameters] related to the element that |
| /// will be invoked using those arguments, compute the list of parameters that |
| /// correspond to the list of arguments. An error will be reported if any of |
| /// the arguments cannot be matched to a parameter. Return the parameters that |
| /// correspond to the arguments. |
| /// |
| /// TODO(scheglov) this is duplicate |
| List<ParameterElement?> _resolveArgumentsToParameters( |
| ArgumentList argumentList, List<ParameterElement> parameters) { |
| return ResolverVisitor.resolveArgumentsToParameters( |
| argumentList, parameters, _errorReporter.reportErrorForNode); |
| } |
| |
| 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) { |
| var parameters = _resolveArgumentsToFunction(argumentList, constructor); |
| if (parameters != null) { |
| argumentList.correspondingStaticParameters = parameters; |
| } |
| } |
| } |
| |
| void _typeAliasConstructorInvocation( |
| AnnotationImpl node, |
| TypeAliasElement typeAliasElement, |
| SimpleIdentifierImpl? constructorName, |
| InterfaceType aliasedType, |
| ArgumentList argumentList, |
| List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList, |
| ) { |
| var constructorElement = aliasedType.lookUpConstructor( |
| constructorName?.name, |
| _definingLibrary, |
| ); |
| |
| _constructorInvocation( |
| node, |
| typeAliasElement.name, |
| constructorName, |
| typeAliasElement.typeParameters, |
| constructorElement, |
| argumentList, |
| (typeArguments) { |
| return typeAliasElement.instantiate( |
| typeArguments: typeArguments, |
| nullabilitySuffix: _resolver.noneOrStarSuffix, |
| ) as InterfaceType; |
| }, |
| whyNotPromotedList, |
| ); |
| } |
| |
| void _typeAliasGetter( |
| AnnotationImpl node, |
| TypeAliasElement typeAliasElement, |
| SimpleIdentifierImpl? getterName, |
| List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList, |
| ) { |
| ExecutableElement? getter; |
| var aliasedType = typeAliasElement.aliasedType; |
| if (aliasedType is InterfaceType) { |
| var classElement = aliasedType.element; |
| if (getterName != null) { |
| getter = classElement.getGetter(getterName.name); |
| getter = _resolver.toLegacyElement(getter); |
| } |
| } |
| |
| getterName?.staticElement = getter; |
| node.element = getter; |
| |
| if (getterName != null && getter is PropertyAccessorElement) { |
| _propertyAccessorElement(node, getterName, getter, whyNotPromotedList); |
| _resolveAnnotationElementGetter(node, getter); |
| } else if (getter is! ConstructorElement) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INVALID_ANNOTATION, |
| node, |
| ); |
| } |
| |
| _visitArguments(node, whyNotPromotedList); |
| } |
| |
| void _visitArguments(AnnotationImpl node, |
| List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) { |
| var arguments = node.arguments; |
| if (arguments != null) { |
| _resolver.visitArgumentList(arguments, |
| whyNotPromotedList: whyNotPromotedList); |
| } |
| } |
| } |