| // 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/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/constant/utilities.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| import 'package:analyzer/src/dart/resolver/property_element_resolver.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; |
| |
| void resolve(AnnotationImpl node) { |
| AstNode parent = node.parent; |
| |
| _resolve1(node); |
| |
| node.constructorName?.accept(_resolver); |
| var element = node.element; |
| if (element is ExecutableElement) { |
| InferenceContext.setType(node.arguments, element.type); |
| } |
| node.typeArguments?.accept(_resolver); |
| node.arguments?.accept(_resolver); |
| |
| var elementAnnotationImpl = |
| node.elementAnnotation as ElementAnnotationImpl?; |
| if (elementAnnotationImpl == null) { |
| // Analyzer ignores annotations on "part of" directives. |
| assert(parent is PartDirective || parent is PartOfDirective); |
| } else { |
| elementAnnotationImpl.annotationAst = _createCloner().cloneNode(node); |
| } |
| } |
| |
| /// Return a newly created cloner that can be used to clone constant |
| /// expressions. |
| /// |
| /// TODO(scheglov) this is duplicate |
| ConstantAstCloner _createCloner() { |
| return ConstantAstCloner(); |
| } |
| |
| InterfaceType _instantiateAnnotationClass(ClassElement element) { |
| return element.instantiate( |
| typeArguments: List.filled( |
| element.typeParameters.length, |
| DynamicTypeImpl.instance, |
| ), |
| nullabilitySuffix: _resolver.noneOrStarSuffix, |
| ); |
| } |
| |
| void _resolve1(AnnotationImpl node) { |
| var nodeName = node.name; |
| |
| if (nodeName is PrefixedIdentifierImpl) { |
| var prefix = nodeName.prefix; |
| var identifier = nodeName.identifier; |
| |
| prefix.accept(_resolver); |
| var prefixElement = prefix.staticElement; |
| |
| if (prefixElement is ClassElement && node.arguments != null) { |
| var element = prefixElement.getNamedConstructor(identifier.name); |
| element = _resolver.toLegacyElement(element); |
| |
| identifier.staticElement = element; |
| // TODO(scheglov) error? |
| } else if (prefixElement is PrefixElement) { |
| var resolver = PropertyElementResolver(_resolver); |
| var result = resolver.resolvePrefixedIdentifier( |
| node: nodeName, |
| hasRead: true, |
| hasWrite: false, |
| forAnnotation: true, |
| ); |
| |
| var element = result.readElement; |
| identifier.staticElement = element; |
| |
| if (element == null) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.UNDEFINED_ANNOTATION, |
| node, |
| [identifier.name], |
| ); |
| } |
| } else { |
| var resolver = PropertyElementResolver(_resolver); |
| var result = resolver.resolvePrefixedIdentifier( |
| node: nodeName, |
| hasRead: true, |
| hasWrite: false, |
| forAnnotation: true, |
| ); |
| |
| var element = result.readElement; |
| identifier.staticElement = element; |
| } |
| } else { |
| var identifier = nodeName as SimpleIdentifierImpl; |
| |
| var resolver = PropertyElementResolver(_resolver); |
| var result = resolver.resolveSimpleIdentifier( |
| node: identifier, |
| hasRead: true, |
| hasWrite: false, |
| ); |
| |
| var element = result.readElement; |
| identifier.staticElement = element; |
| |
| if (element == null) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.UNDEFINED_ANNOTATION, |
| node, |
| [identifier.name], |
| ); |
| } |
| } |
| |
| _resolveAnnotationElement(node); |
| } |
| |
| void _resolveAnnotationConstructorInvocationArguments( |
| AnnotationImpl annotation, ConstructorElement constructor) { |
| var argumentList = annotation.arguments; |
| // error will be reported in ConstantVerifier |
| if (argumentList == null) { |
| return; |
| } |
| // resolve arguments to parameters |
| var parameters = _resolveArgumentsToFunction(argumentList, constructor); |
| if (parameters != null) { |
| argumentList.correspondingStaticParameters = parameters; |
| } |
| } |
| |
| /// Continues resolution of the given [annotation]. |
| void _resolveAnnotationElement(AnnotationImpl annotation) { |
| late final SimpleIdentifier nameNode1; |
| SimpleIdentifierImpl? nameNode2; |
| { |
| Identifier annName = annotation.name; |
| if (annName is PrefixedIdentifierImpl) { |
| nameNode1 = annName.prefix; |
| nameNode2 = annName.identifier; |
| } else { |
| nameNode1 = annName as SimpleIdentifier; |
| nameNode2 = null; |
| } |
| } |
| SimpleIdentifierImpl? nameNode3 = annotation.constructorName; |
| ConstructorElement? constructor; |
| bool undefined = false; |
| // |
| // CONST or Class(args) |
| // |
| if (nameNode2 == null && nameNode3 == null) { |
| var element1 = nameNode1.staticElement; |
| // TODO(scheglov) Must be const. |
| if (element1 is VariableElement) { |
| return; |
| } |
| // CONST |
| if (element1 is PropertyAccessorElement) { |
| _resolveAnnotationElementGetter(annotation, element1); |
| return; |
| } |
| // Class(args) |
| if (element1 is ClassElement) { |
| constructor = _instantiateAnnotationClass(element1) |
| .lookUpConstructor(null, _definingLibrary); |
| constructor = _resolver.toLegacyElement(constructor); |
| } else if (element1 == null) { |
| undefined = true; |
| } |
| } |
| // |
| // prefix.CONST or prefix.Class() or Class.CONST or Class.constructor(args) |
| // |
| if (nameNode2 != null && nameNode3 == null) { |
| var element1 = nameNode1.staticElement; |
| var element2 = nameNode2.staticElement; |
| // Class.CONST - not resolved yet |
| if (element1 is ClassElement) { |
| element2 = element1.lookUpGetter(nameNode2.name, _definingLibrary); |
| element2 = _resolver.toLegacyElement(element2); |
| } |
| // prefix.CONST or Class.CONST |
| if (element2 is PropertyAccessorElement) { |
| nameNode2.staticElement = element2; |
| annotation.element = element2; |
| _resolveAnnotationElementGetter(annotation, element2); |
| return; |
| } |
| // prefix.Class() |
| if (element2 is ClassElement) { |
| constructor = element2.unnamedConstructor; |
| constructor = _resolver.toLegacyElement(constructor); |
| } |
| // Class.constructor(args) |
| if (element1 is ClassElement) { |
| constructor = _instantiateAnnotationClass(element1) |
| .lookUpConstructor(nameNode2.name, _definingLibrary); |
| constructor = _resolver.toLegacyElement(constructor); |
| nameNode2.staticElement = constructor; |
| } |
| if (element1 is PrefixElement && element2 == null) { |
| undefined = true; |
| } |
| if (element1 == null && element2 == null) { |
| undefined = true; |
| } |
| } |
| // |
| // prefix.Class.CONST or prefix.Class.constructor(args) |
| // |
| if (nameNode2 != null && nameNode3 != null) { |
| var element2 = nameNode2.staticElement; |
| // element2 should be ClassElement |
| if (element2 is ClassElement) { |
| String name3 = nameNode3.name; |
| // prefix.Class.CONST |
| var getter = element2.lookUpGetter(name3, _definingLibrary); |
| if (getter != null) { |
| getter = _resolver.toLegacyElement(getter)!; |
| nameNode3.staticElement = getter; |
| annotation.element = getter; |
| _resolveAnnotationElementGetter(annotation, getter); |
| return; |
| } |
| // prefix.Class.constructor(args) |
| constructor = _instantiateAnnotationClass(element2) |
| .lookUpConstructor(name3, _definingLibrary); |
| constructor = _resolver.toLegacyElement(constructor); |
| nameNode3.staticElement = constructor; |
| } else if (element2 == null) { |
| undefined = true; |
| } |
| } |
| // we need constructor |
| if (constructor == null) { |
| if (!undefined) { |
| // If the class was not found then we've already reported the error. |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INVALID_ANNOTATION, annotation); |
| } |
| return; |
| } |
| // record element |
| annotation.element = constructor; |
| // resolve arguments |
| _resolveAnnotationConstructorInvocationArguments(annotation, constructor); |
| } |
| |
| 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); |
| } |
| } |