blob: 46980c716c3d6823ba2020dbce57f10ff6cf84e2 [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/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/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);
LibraryElement get _definingLibrary => _resolver.definingLibrary;
ErrorReporter get _errorReporter => _resolver.errorReporter;
void resolve(
AnnotationImpl node, List<WhyNotPromotedGetter> whyNotPromotedList) {
node.typeArguments?.accept(_resolver);
_resolve(node, whyNotPromotedList);
}
void _classConstructorInvocation(
AnnotationImpl node,
ClassElement classElement,
SimpleIdentifierImpl? constructorName,
ArgumentList argumentList,
List<WhyNotPromotedGetter> 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<WhyNotPromotedGetter> 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<WhyNotPromotedGetter> whyNotPromotedList,
) {
constructorElement = _resolver.toLegacyElement(constructorElement);
constructorName?.staticElement = constructorElement;
node.element = constructorElement;
var annotationInferrer =
AnnotationInferrer(constructorName: constructorName);
if (constructorElement == null) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.INVALID_ANNOTATION,
node,
);
annotationInferrer.resolveInvocation(
resolver: _resolver,
node: node,
rawType: null,
contextType: null,
whyNotPromotedList: whyNotPromotedList);
return;
}
var elementToInfer = ConstructorElementToInfer(
typeParameters,
constructorElement,
);
var constructorRawType = elementToInfer.asType;
annotationInferrer.resolveInvocation(
resolver: _resolver,
node: node,
rawType: constructorRawType,
contextType: null,
whyNotPromotedList: whyNotPromotedList);
}
void _extensionGetter(
AnnotationImpl node,
ExtensionElement extensionElement,
SimpleIdentifierImpl? getterName,
List<WhyNotPromotedGetter> 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 _localVariable(
AnnotationImpl node,
VariableElement element,
List<WhyNotPromotedGetter> whyNotPromotedList,
) {
if (!element.isConst || node.arguments != null) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.INVALID_ANNOTATION, node);
}
_visitArguments(node, whyNotPromotedList);
}
void _propertyAccessorElement(
AnnotationImpl node,
SimpleIdentifierImpl name,
PropertyAccessorElement element,
List<WhyNotPromotedGetter> whyNotPromotedList,
) {
element = _resolver.toLegacyElement(element);
name.staticElement = element;
node.element = element;
_resolveAnnotationElementGetter(node, element);
_visitArguments(node, whyNotPromotedList);
}
void _resolve(
AnnotationImpl node, List<WhyNotPromotedGetter> 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 = name1.scopeLookupResult!.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;
}
if (element1 is VariableElement) {
_localVariable(node, element1, whyNotPromotedList);
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);
}
}
void _typeAliasConstructorInvocation(
AnnotationImpl node,
TypeAliasElement typeAliasElement,
SimpleIdentifierImpl? constructorName,
InterfaceType aliasedType,
ArgumentList argumentList,
List<WhyNotPromotedGetter> 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<WhyNotPromotedGetter> 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<WhyNotPromotedGetter> whyNotPromotedList) {
var arguments = node.arguments;
if (arguments != null) {
AnnotationInferrer(constructorName: null).resolveInvocation(
resolver: _resolver,
node: node,
rawType: null,
contextType: null,
whyNotPromotedList: whyNotPromotedList);
}
}
}