blob: 896d2dfd1128b7b2381c578190117b7697264e47 [file] [log] [blame]
// Copyright (c) 2014, 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/ast/precedence.dart';
import 'package:analyzer/dart/ast/syntactic_entity.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/src/dart/ast/ast.dart'
show
ChildEntities,
IdentifierImpl,
PrefixedIdentifierImpl,
SimpleIdentifierImpl;
import 'package:analyzer/src/dart/ast/token.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.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/method_invocation_resolver.dart';
import 'package:analyzer/src/dart/resolver/resolution_result.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/super_context.dart';
import 'package:analyzer/src/task/strong/checker.dart';
/**
* An object used by instances of [ResolverVisitor] to resolve references within
* the AST structure to the elements being referenced. The requirements for the
* element resolver are:
*
* 1. Every [SimpleIdentifier] should be resolved to the element to which it
* refers. Specifically:
* * An identifier within the declaration of that name should resolve to the
* element being declared.
* * An identifier denoting a prefix should resolve to the element
* representing the import that defines the prefix (an [ImportElement]).
* * An identifier denoting a variable should resolve to the element
* representing the variable (a [VariableElement]).
* * An identifier denoting a parameter should resolve to the element
* representing the parameter (a [ParameterElement]).
* * An identifier denoting a field should resolve to the element
* representing the getter or setter being invoked (a
* [PropertyAccessorElement]).
* * An identifier denoting the name of a method or function being invoked
* should resolve to the element representing the method or function (an
* [ExecutableElement]).
* * An identifier denoting a label should resolve to the element
* representing the label (a [LabelElement]).
* The identifiers within directives are exceptions to this rule and are
* covered below.
* 2. Every node containing a token representing an operator that can be
* overridden ( [BinaryExpression], [PrefixExpression], [PostfixExpression])
* should resolve to the element representing the method invoked by that
* operator (a [MethodElement]).
* 3. Every [FunctionExpressionInvocation] should resolve to the element
* representing the function being invoked (a [FunctionElement]). This will
* be the same element as that to which the name is resolved if the function
* has a name, but is provided for those cases where an unnamed function is
* being invoked.
* 4. Every [LibraryDirective] and [PartOfDirective] should resolve to the
* element representing the library being specified by the directive (a
* [LibraryElement]) unless, in the case of a part-of directive, the
* specified library does not exist.
* 5. Every [ImportDirective] and [ExportDirective] should resolve to the
* element representing the library being specified by the directive unless
* the specified library does not exist (an [ImportElement] or
* [ExportElement]).
* 6. The identifier representing the prefix in an [ImportDirective] should
* resolve to the element representing the prefix (a [PrefixElement]).
* 7. The identifiers in the hide and show combinators in [ImportDirective]s
* and [ExportDirective]s should resolve to the elements that are being
* hidden or shown, respectively, unless those names are not defined in the
* specified library (or the specified library does not exist).
* 8. Every [PartDirective] should resolve to the element representing the
* compilation unit being specified by the string unless the specified
* compilation unit does not exist (a [CompilationUnitElement]).
*
* Note that AST nodes that would represent elements that are not defined are
* not resolved to anything. This includes such things as references to
* undeclared variables (which is an error) and names in hide and show
* combinators that are not defined in the imported library (which is not an
* error).
*/
class ElementResolver extends SimpleAstVisitor<void> {
/**
* The manager for the inheritance mappings.
*/
final InheritanceManager3 _inheritance;
/**
* The resolver driving this participant.
*/
final ResolverVisitor _resolver;
/**
* The element for the library containing the compilation unit being visited.
*/
final LibraryElement _definingLibrary;
/**
* The type representing the type 'dynamic'.
*/
DartType _dynamicType;
/**
* The type representing the type 'Type'.
*/
InterfaceType _typeType;
/// Whether constant evaluation errors should be reported during resolution.
@Deprecated('This field is no longer used')
final bool reportConstEvaluationErrors;
/// Helper for extension method resolution.
final ExtensionMemberResolver _extensionResolver;
final MethodInvocationResolver _methodInvocationResolver;
/**
* Initialize a newly created visitor to work for the given [_resolver] to
* resolve the nodes in a compilation unit.
*/
ElementResolver(this._resolver, {this.reportConstEvaluationErrors: true})
: _inheritance = _resolver.inheritance,
_definingLibrary = _resolver.definingLibrary,
_extensionResolver = _resolver.extensionResolver,
_methodInvocationResolver = new MethodInvocationResolver(_resolver) {
_dynamicType = _resolver.typeProvider.dynamicType;
_typeType = _resolver.typeProvider.typeType;
}
/**
* Return `true` iff the current enclosing function is a constant constructor
* declaration.
*/
bool get isInConstConstructor {
ExecutableElement function = _resolver.enclosingFunction;
if (function is ConstructorElement) {
return function.isConst;
}
return false;
}
@override
void visitAssignmentExpression(AssignmentExpression node) {
Token operator = node.operator;
TokenType operatorType = operator.type;
Expression leftHandSide = node.leftHandSide;
DartType staticType = _getStaticType(leftHandSide, read: true);
// For any compound assignments to a void or nullable variable, report it.
// Example: `y += voidFn()`, not allowed.
if (operatorType != TokenType.EQ) {
if (staticType != null && staticType.isVoid) {
_recordUndefinedToken(
null, StaticWarningCode.USE_OF_VOID_RESULT, operator, []);
return;
}
}
if (operatorType != TokenType.AMPERSAND_AMPERSAND_EQ &&
operatorType != TokenType.BAR_BAR_EQ &&
operatorType != TokenType.EQ &&
operatorType != TokenType.QUESTION_QUESTION_EQ) {
operatorType = operatorFromCompoundAssignment(operatorType);
if (leftHandSide != null) {
String methodName = operatorType.lexeme;
// TODO(brianwilkerson) Change the [methodNameNode] from the left hand
// side to the operator.
var result = _newPropertyResolver()
.resolve(leftHandSide, staticType, methodName, leftHandSide);
node.staticElement = result.getter;
if (_shouldReportInvalidMember(staticType, result)) {
_recordUndefinedToken(
staticType.element,
StaticTypeWarningCode.UNDEFINED_OPERATOR,
operator,
[methodName, staticType.displayName]);
}
}
}
}
@override
void visitBinaryExpression(BinaryExpression node) {
Token operator = node.operator;
if (operator.isUserDefinableOperator) {
_resolveBinaryExpression(node, operator.lexeme);
} else if (operator.type == TokenType.BANG_EQ) {
_resolveBinaryExpression(node, TokenType.EQ_EQ.lexeme);
}
}
@override
void visitBreakStatement(BreakStatement node) {
node.target = _lookupBreakOrContinueTarget(node, node.label, false);
}
@override
void visitClassDeclaration(ClassDeclaration node) {
resolveMetadata(node);
}
@override
void visitClassTypeAlias(ClassTypeAlias node) {
resolveMetadata(node);
}
@override
void visitCommentReference(CommentReference node) {
Identifier identifier = node.identifier;
if (identifier is SimpleIdentifier) {
Element element = _resolveSimpleIdentifier(identifier);
if (element == null) {
// TODO(brianwilkerson) Report this error?
// resolver.reportError(
// StaticWarningCode.UNDEFINED_IDENTIFIER,
// simpleIdentifier,
// simpleIdentifier.getName());
} else {
if (element.library == null || element.library != _definingLibrary) {
// TODO(brianwilkerson) Report this error?
}
identifier.staticElement = element;
if (node.newKeyword != null) {
if (element is ClassElement) {
ConstructorElement constructor = element.unnamedConstructor;
if (constructor == null) {
// TODO(brianwilkerson) Report this error.
} else {
identifier.staticElement = constructor;
}
} else {
// TODO(brianwilkerson) Report this error.
}
}
}
} else if (identifier is PrefixedIdentifier) {
SimpleIdentifier prefix = identifier.prefix;
SimpleIdentifier name = identifier.identifier;
Element element = _resolveSimpleIdentifier(prefix);
if (element == null) {
// resolver.reportError(StaticWarningCode.UNDEFINED_IDENTIFIER, prefix, prefix.getName());
} else {
prefix.staticElement = element;
if (element is PrefixElement) {
// TODO(brianwilkerson) Report this error?
element = _resolver.nameScope.lookup(identifier, _definingLibrary);
name.staticElement = element;
return;
}
LibraryElement library = element.library;
if (library == null) {
// TODO(brianwilkerson) We need to understand how the library could
// ever be null.
AnalysisEngine.instance.logger
.logError("Found element with null library: ${element.name}");
} else if (library != _definingLibrary) {
// TODO(brianwilkerson) Report this error.
}
if (node.newKeyword == null) {
if (element is ClassElement) {
name.staticElement = element.getMethod(name.name) ??
element.getGetter(name.name) ??
element.getSetter(name.name) ??
element.getNamedConstructor(name.name);
} else {
// TODO(brianwilkerson) Report this error.
}
} else {
if (element is ClassElement) {
ConstructorElement constructor =
element.getNamedConstructor(name.name);
if (constructor == null) {
// TODO(brianwilkerson) Report this error.
} else {
name.staticElement = constructor;
}
} else {
// TODO(brianwilkerson) Report this error.
}
}
}
}
}
@override
void visitConstructorDeclaration(ConstructorDeclaration node) {
super.visitConstructorDeclaration(node);
ConstructorElement element = node.declaredElement;
if (element is ConstructorElementImpl) {
ConstructorName redirectedNode = node.redirectedConstructor;
if (redirectedNode != null) {
// set redirected factory constructor
ConstructorElement redirectedElement = redirectedNode.staticElement;
element.redirectedConstructor = redirectedElement;
} else {
// set redirected generative constructor
for (ConstructorInitializer initializer in node.initializers) {
if (initializer is RedirectingConstructorInvocation) {
ConstructorElement redirectedElement = initializer.staticElement;
element.redirectedConstructor = redirectedElement;
}
}
}
resolveMetadata(node);
}
}
@override
void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
SimpleIdentifier fieldName = node.fieldName;
ClassElement enclosingClass = _resolver.enclosingClass;
FieldElement fieldElement = enclosingClass.getField(fieldName.name);
fieldName.staticElement = fieldElement;
}
@override
void visitConstructorName(ConstructorName node) {
DartType type = node.type.type;
if (type != null && type.isDynamic) {
// Nothing to do.
} else if (type is InterfaceType) {
// look up ConstructorElement
ConstructorElement constructor;
SimpleIdentifier name = node.name;
if (name == null) {
constructor = type.lookUpConstructor(null, _definingLibrary);
} else {
constructor = type.lookUpConstructor(name.name, _definingLibrary);
name.staticElement = constructor;
}
node.staticElement = constructor;
} else {
// TODO(brianwilkerson) Report these errors.
// ASTNode parent = node.getParent();
// if (parent instanceof InstanceCreationExpression) {
// if (((InstanceCreationExpression) parent).isConst()) {
// // CompileTimeErrorCode.CONST_WITH_NON_TYPE
// } else {
// // StaticWarningCode.NEW_WITH_NON_TYPE
// }
// } else {
// // This is part of a redirecting factory constructor; not sure which error code to use
// }
}
}
@override
void visitContinueStatement(ContinueStatement node) {
node.target = _lookupBreakOrContinueTarget(node, node.label, true);
}
@override
void visitDeclaredIdentifier(DeclaredIdentifier node) {
resolveMetadata(node);
}
@override
void visitEnumDeclaration(EnumDeclaration node) {
resolveMetadata(node);
}
@override
void visitExportDirective(ExportDirective node) {
ExportElement exportElement = node.element;
if (exportElement != null) {
// The element is null when the URI is invalid
// TODO(brianwilkerson) Figure out whether the element can ever be
// something other than an ExportElement
_resolveCombinators(exportElement.exportedLibrary, node.combinators);
resolveMetadata(node);
}
}
@override
void visitFieldFormalParameter(FieldFormalParameter node) {
_resolveMetadataForParameter(node);
super.visitFieldFormalParameter(node);
}
@override
void visitFunctionDeclaration(FunctionDeclaration node) {
resolveMetadata(node);
}
@override
void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
Expression function = node.function;
DartType functionType;
if (function is ExtensionOverride) {
var result = _extensionResolver.getOverrideMember(function, 'call');
var member = result.getter;
if (member == null) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.INVOCATION_OF_EXTENSION_WITHOUT_CALL,
function,
[function.extensionName.name]);
functionType = _resolver.typeProvider.dynamicType;
} else {
if (member.isStatic) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.EXTENSION_OVERRIDE_ACCESS_TO_STATIC_MEMBER,
node.argumentList);
}
node.staticElement = member;
functionType = member.type;
}
} else {
functionType = function.staticType;
}
DartType staticInvokeType =
_instantiateGenericMethod(functionType, node.typeArguments, node);
node.staticInvokeType = staticInvokeType;
List<ParameterElement> parameters =
_computeCorrespondingParameters(node, staticInvokeType);
if (parameters != null) {
node.argumentList.correspondingStaticParameters = parameters;
}
}
@override
void visitFunctionTypeAlias(FunctionTypeAlias node) {
resolveMetadata(node);
}
@override
void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
_resolveMetadataForParameter(node);
}
@override
void visitGenericTypeAlias(GenericTypeAlias node) {
resolveMetadata(node);
return null;
}
@override
void visitImportDirective(ImportDirective node) {
SimpleIdentifier prefixNode = node.prefix;
if (prefixNode != null) {
String prefixName = prefixNode.name;
List<PrefixElement> prefixes = _definingLibrary.prefixes;
int count = prefixes.length;
for (int i = 0; i < count; i++) {
PrefixElement prefixElement = prefixes[i];
if (prefixElement.displayName == prefixName) {
prefixNode.staticElement = prefixElement;
break;
}
}
}
ImportElement importElement = node.element;
if (importElement != null) {
// The element is null when the URI is invalid
LibraryElement library = importElement.importedLibrary;
if (library != null) {
_resolveCombinators(library, node.combinators);
}
resolveMetadata(node);
}
}
@override
void visitIndexExpression(IndexExpression node) {
Expression target = node.realTarget;
DartType staticType = _getStaticType(target);
String getterMethodName = TokenType.INDEX.lexeme;
String setterMethodName = TokenType.INDEX_EQ.lexeme;
ResolutionResult result;
if (target is ExtensionOverride) {
result = _extensionResolver.getOverrideMember(target, getterMethodName);
} else {
result = _newPropertyResolver()
.resolve(target, staticType, getterMethodName, target);
}
bool isInGetterContext = node.inGetterContext();
bool isInSetterContext = node.inSetterContext();
if (isInGetterContext && isInSetterContext) {
node.staticElement = result.setter;
node.auxiliaryElements = AuxiliaryElements(result.getter, null);
} else if (isInGetterContext) {
node.staticElement = result.getter;
} else if (isInSetterContext) {
node.staticElement = result.setter;
}
if (isInGetterContext) {
_checkForUndefinedIndexOperator(
node, target, getterMethodName, result, result.getter, staticType);
}
if (isInSetterContext) {
_checkForUndefinedIndexOperator(
node, target, setterMethodName, result, result.setter, staticType);
}
}
@override
void visitInstanceCreationExpression(InstanceCreationExpression node) {
ConstructorElement invokedConstructor = node.constructorName.staticElement;
node.staticElement = invokedConstructor;
ArgumentList argumentList = node.argumentList;
List<ParameterElement> parameters =
_resolveArgumentsToFunction(argumentList, invokedConstructor);
if (parameters != null) {
argumentList.correspondingStaticParameters = parameters;
}
}
@override
void visitLibraryDirective(LibraryDirective node) {
resolveMetadata(node);
}
@override
void visitMethodDeclaration(MethodDeclaration node) {
resolveMetadata(node);
}
@override
void visitMethodInvocation(MethodInvocation node) {
_methodInvocationResolver.resolve(node);
}
@override
void visitMixinDeclaration(MixinDeclaration node) {
resolveMetadata(node);
}
@override
void visitPartDirective(PartDirective node) {
resolveMetadata(node);
}
@override
void visitPostfixExpression(PostfixExpression node) {
Expression operand = node.operand;
if (node.operator.type == TokenType.BANG) {
// Null-assertion operator (`!`). There's nothing to do, since this is a
// built-in operation (there's no associated operator declaration).
return;
}
String methodName = _getPostfixOperator(node);
DartType staticType = _getStaticType(operand);
var result = _newPropertyResolver()
.resolve(operand, staticType, methodName, operand);
node.staticElement = result.getter;
if (_shouldReportInvalidMember(staticType, result)) {
if (operand is SuperExpression) {
_recordUndefinedToken(
staticType.element,
StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR,
node.operator,
[methodName, staticType.displayName]);
} else {
_recordUndefinedToken(
staticType.element,
StaticTypeWarningCode.UNDEFINED_OPERATOR,
node.operator,
[methodName, staticType.displayName]);
}
}
}
@override
void visitPrefixedIdentifier(PrefixedIdentifier node) {
SimpleIdentifier prefix = node.prefix;
SimpleIdentifier identifier = node.identifier;
//
// First, check the "lib.loadLibrary" case
//
if (identifier.name == FunctionElement.LOAD_LIBRARY_NAME &&
_isDeferredPrefix(prefix)) {
LibraryElement importedLibrary = _getImportedLibrary(prefix);
identifier.staticElement = importedLibrary?.loadLibraryFunction;
return;
}
//
// Check to see whether the prefix is really a prefix.
//
Element prefixElement = prefix.staticElement;
if (prefixElement is PrefixElement) {
Element element = _resolver.nameScope.lookup(node, _definingLibrary);
if (element == null && identifier.inSetterContext()) {
Identifier setterName = new PrefixedIdentifierImpl.temp(
node.prefix,
new SimpleIdentifierImpl(new StringToken(TokenType.STRING,
"${node.identifier.name}=", node.identifier.offset - 1)));
element = _resolver.nameScope.lookup(setterName, _definingLibrary);
}
if (element == null && _resolver.nameScope.shouldIgnoreUndefined(node)) {
return;
}
if (element == null) {
AstNode parent = node.parent;
if (parent is Annotation) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_ANNOTATION,
parent,
[identifier.name]);
} else {
_resolver.errorReporter.reportErrorForNode(
StaticTypeWarningCode.UNDEFINED_PREFIXED_NAME,
identifier,
[identifier.name, prefixElement.name]);
}
return;
}
Element accessor = element;
if (accessor is PropertyAccessorElement && identifier.inSetterContext()) {
PropertyInducingElement variable = accessor.variable;
if (variable != null) {
PropertyAccessorElement setter = variable.setter;
if (setter != null) {
element = setter;
}
}
}
// TODO(brianwilkerson) The prefix needs to be resolved to the element for
// the import that defines the prefix, not the prefix's element.
identifier.staticElement = element;
// Validate annotation element.
AstNode parent = node.parent;
if (parent is Annotation) {
_resolveAnnotationElement(parent);
}
return;
}
// May be annotation, resolve invocation of "const" constructor.
AstNode parent = node.parent;
if (parent is Annotation) {
_resolveAnnotationElement(parent);
return;
}
//
// Otherwise, the prefix is really an expression that happens to be a simple
// identifier and this is really equivalent to a property access node.
//
_resolvePropertyAccess(prefix, identifier, false);
}
@override
void visitPrefixExpression(PrefixExpression node) {
Token operator = node.operator;
TokenType operatorType = operator.type;
if (operatorType.isUserDefinableOperator ||
operatorType == TokenType.PLUS_PLUS ||
operatorType == TokenType.MINUS_MINUS) {
Expression operand = node.operand;
String methodName = _getPrefixOperator(node);
DartType staticType = _getStaticType(operand, read: true);
var result = _newPropertyResolver()
.resolve(operand, staticType, methodName, operand);
node.staticElement = result.getter;
if (_shouldReportInvalidMember(staticType, result)) {
if (operand is SuperExpression) {
_recordUndefinedToken(
staticType.element,
StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR,
operator,
[methodName, staticType.displayName]);
} else {
_recordUndefinedToken(
staticType.element,
StaticTypeWarningCode.UNDEFINED_OPERATOR,
operator,
[methodName, staticType.displayName]);
}
}
}
}
@override
void visitPropertyAccess(PropertyAccess node) {
Expression target = node.realTarget;
if (target is SuperExpression &&
SuperContext.of(target) != SuperContext.valid) {
return;
} else if (target is ExtensionOverride) {
if (node.isCascaded) {
// Report this error and recover by treating it like a non-cascade.
_resolver.errorReporter.reportErrorForToken(
CompileTimeErrorCode.EXTENSION_OVERRIDE_WITH_CASCADE,
node.operator);
}
ExtensionElement element = target.extensionName.staticElement;
SimpleIdentifier propertyName = node.propertyName;
String memberName = propertyName.name;
ExecutableElement member;
var result = _extensionResolver.getOverrideMember(target, memberName);
if (propertyName.inSetterContext()) {
member = result.setter;
if (member == null) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_EXTENSION_SETTER,
propertyName,
[memberName, element.name]);
}
if (propertyName.inGetterContext()) {
PropertyAccessorElement getter = result.getter;
if (getter == null) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_EXTENSION_GETTER,
propertyName,
[memberName, element.name]);
}
propertyName.auxiliaryElements = AuxiliaryElements(getter, null);
}
} else if (propertyName.inGetterContext()) {
member = result.getter;
if (member == null) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_EXTENSION_GETTER,
propertyName,
[memberName, element.name]);
}
}
if (member != null && member.isStatic) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.EXTENSION_OVERRIDE_ACCESS_TO_STATIC_MEMBER,
propertyName);
}
propertyName.staticElement = member;
return;
}
SimpleIdentifier propertyName = node.propertyName;
_resolvePropertyAccess(target, propertyName, node.isCascaded);
}
@override
void visitRedirectingConstructorInvocation(
RedirectingConstructorInvocation node) {
ClassElement enclosingClass = _resolver.enclosingClass;
if (enclosingClass == null) {
// TODO(brianwilkerson) Report this error.
return;
}
SimpleIdentifier name = node.constructorName;
ConstructorElement element;
if (name == null) {
element = enclosingClass.unnamedConstructor;
} else {
element = enclosingClass.getNamedConstructor(name.name);
}
if (element == null) {
// TODO(brianwilkerson) Report this error and decide what element to
// associate with the node.
return;
}
if (name != null) {
name.staticElement = element;
}
node.staticElement = element;
ArgumentList argumentList = node.argumentList;
List<ParameterElement> parameters =
_resolveArgumentsToFunction(argumentList, element);
if (parameters != null) {
argumentList.correspondingStaticParameters = parameters;
}
}
@override
void visitSimpleFormalParameter(SimpleFormalParameter node) {
_resolveMetadataForParameter(node);
}
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
//
// Synthetic identifiers have been already reported during parsing.
//
if (node.isSynthetic) {
return;
}
//
// Ignore nodes that should have been resolved before getting here.
//
if (node.inDeclarationContext()) {
return;
}
if (node.staticElement is LocalVariableElement ||
node.staticElement is ParameterElement) {
return;
}
AstNode parent = node.parent;
if (parent is FieldFormalParameter) {
return;
} else if (parent is ConstructorFieldInitializer &&
parent.fieldName == node) {
return;
} else if (parent is Annotation && parent.constructorName == node) {
return;
}
//
// The name dynamic denotes a Type object even though dynamic is not a
// class.
//
if (node.name == _dynamicType.name) {
node.staticElement = _dynamicType.element;
node.staticType = _typeType;
return;
}
//
// Otherwise, the node should be resolved.
//
Element element = _resolveSimpleIdentifier(node);
ClassElement enclosingClass = _resolver.enclosingClass;
if (_isFactoryConstructorReturnType(node) &&
!identical(element, enclosingClass)) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.INVALID_FACTORY_NAME_NOT_A_CLASS, node);
} else if (_isConstructorReturnType(node) &&
!identical(element, enclosingClass)) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.INVALID_CONSTRUCTOR_NAME, node);
element = null;
} else if (element == null ||
(element is PrefixElement && !_isValidAsPrefix(node))) {
// TODO(brianwilkerson) Recover from this error.
if (_isConstructorReturnType(node)) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.INVALID_CONSTRUCTOR_NAME, node);
} else if (parent is Annotation) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_ANNOTATION, parent, [node.name]);
} else if (element != null) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT,
node,
[element.name]);
} else if (node.name == "await" && _resolver.enclosingFunction != null) {
_recordUndefinedNode(
_resolver.enclosingClass,
StaticWarningCode.UNDEFINED_IDENTIFIER_AWAIT,
node,
[_resolver.enclosingFunction.displayName]);
} else if (!_resolver.nameScope.shouldIgnoreUndefined(node)) {
_recordUndefinedNode(_resolver.enclosingClass,
StaticWarningCode.UNDEFINED_IDENTIFIER, node, [node.name]);
}
}
node.staticElement = element;
if (node.inSetterContext() &&
node.inGetterContext() &&
enclosingClass != null) {
InterfaceType enclosingType = enclosingClass.thisType;
var propertyResolver = _newPropertyResolver();
propertyResolver.resolve(null, enclosingType, node.name, node);
node.auxiliaryElements = AuxiliaryElements(
propertyResolver.result.getter,
null,
);
}
//
// Validate annotation element.
//
if (parent is Annotation) {
_resolveAnnotationElement(parent);
}
}
@override
void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
ClassElementImpl enclosingClass =
AbstractClassElementImpl.getImpl(_resolver.enclosingClass);
if (enclosingClass == null) {
// TODO(brianwilkerson) Report this error.
return;
}
InterfaceType superType = enclosingClass.supertype;
if (superType == null) {
// TODO(brianwilkerson) Report this error.
return;
}
SimpleIdentifier name = node.constructorName;
String superName = name?.name;
ConstructorElement element =
superType.lookUpConstructor(superName, _definingLibrary);
if (element == null || !element.isAccessibleIn(_definingLibrary)) {
if (name != null) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER,
node,
[superType.displayName, name]);
} else {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT,
node,
[superType.displayName]);
}
return;
} else {
if (element.isFactory) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR, node, [element]);
}
}
if (name != null) {
name.staticElement = element;
}
node.staticElement = element;
// TODO(brianwilkerson) Defer this check until we know there's an error (by
// in-lining _resolveArgumentsToFunction below).
ClassDeclaration declaration =
node.thisOrAncestorOfType<ClassDeclaration>();
Identifier superclassName = declaration?.extendsClause?.superclass?.name;
if (superclassName != null &&
_resolver.nameScope.shouldIgnoreUndefined(superclassName)) {
return;
}
ArgumentList argumentList = node.argumentList;
List<ParameterElement> parameters =
_resolveArgumentsToFunction(argumentList, element);
if (parameters != null) {
argumentList.correspondingStaticParameters = parameters;
}
}
@override
void visitSuperExpression(SuperExpression node) {
var context = SuperContext.of(node);
if (context == SuperContext.static) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.SUPER_IN_INVALID_CONTEXT, node);
} else if (context == SuperContext.extension) {
_resolver.errorReporter
.reportErrorForNode(CompileTimeErrorCode.SUPER_IN_EXTENSION, node);
}
super.visitSuperExpression(node);
}
@override
void visitTypeParameter(TypeParameter node) {
resolveMetadata(node);
}
@override
void visitVariableDeclaration(VariableDeclaration node) {
resolveMetadata(node);
}
/// If the [element] is not static, report the error on the [identifier].
void _checkForStaticAccessToInstanceMember(
SimpleIdentifier identifier,
ExecutableElement element,
) {
if (element.isStatic) return;
_resolver.errorReporter.reportErrorForNode(
StaticWarningCode.STATIC_ACCESS_TO_INSTANCE_MEMBER,
identifier,
[identifier.name],
);
}
/**
* Check that the given index [expression] was resolved, otherwise a
* [StaticTypeWarningCode.UNDEFINED_OPERATOR] is generated. The [target] is
* the target of the expression. The [methodName] is the name of the operator
* associated with the context of using of the given index expression.
*/
void _checkForUndefinedIndexOperator(
IndexExpression expression,
Expression target,
String methodName,
ResolutionResult result,
ExecutableElement element,
DartType staticType) {
if (result.isAmbiguous) {
return;
}
if (element != null) {
return;
}
if (target is! ExtensionOverride) {
if (staticType == null || staticType.isDynamic) {
return;
}
}
var leftBracket = expression.leftBracket;
var rightBracket = expression.rightBracket;
var offset = leftBracket.offset;
var length = rightBracket.end - offset;
if (target is ExtensionOverride) {
_resolver.errorReporter.reportErrorForOffset(
CompileTimeErrorCode.UNDEFINED_EXTENSION_OPERATOR,
offset,
length,
[methodName, target.staticElement.name],
);
} else if (target is SuperExpression) {
_resolver.errorReporter.reportErrorForOffset(
StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR,
offset,
length,
[methodName, staticType.displayName],
);
} else if (staticType.isVoid) {
_resolver.errorReporter.reportErrorForOffset(
StaticWarningCode.USE_OF_VOID_RESULT,
offset,
length,
);
} else {
_resolver.errorReporter.reportErrorForOffset(
StaticTypeWarningCode.UNDEFINED_OPERATOR,
offset,
length,
[methodName, staticType.displayName],
);
}
}
/**
* Given an [argumentList] and the executable [element] that will be invoked
* using those arguments, compute the list of parameters that correspond to
* the list of arguments. Return the parameters that correspond to the
* arguments, or `null` if no correspondence could be computed.
*/
List<ParameterElement> _computeCorrespondingParameters(
FunctionExpressionInvocation invocation, DartType type) {
ArgumentList argumentList = invocation.argumentList;
if (type is InterfaceType) {
MethodElement callMethod = invocation.staticElement;
if (callMethod != null) {
return _resolveArgumentsToFunction(argumentList, callMethod);
}
} else if (type is FunctionType) {
return _resolveArgumentsToParameters(argumentList, type.parameters);
}
return null;
}
/**
* Assuming that the given [identifier] is a prefix for a deferred import,
* return the library that is being imported.
*/
LibraryElement _getImportedLibrary(SimpleIdentifier identifier) {
PrefixElement prefixElement = identifier.staticElement as PrefixElement;
List<ImportElement> imports =
prefixElement.enclosingElement.getImportsWithPrefix(prefixElement);
return imports[0].importedLibrary;
}
/**
* Return the name of the method invoked by the given postfix [expression].
*/
String _getPostfixOperator(PostfixExpression expression) {
if (expression.operator.type == TokenType.PLUS_PLUS) {
return TokenType.PLUS.lexeme;
} else if (expression.operator.type == TokenType.MINUS_MINUS) {
return TokenType.MINUS.lexeme;
} else {
throw new UnsupportedError(
'Unsupported postfix operator ${expression.operator.lexeme}');
}
}
/**
* Return the name of the method invoked by the given postfix [expression].
*/
String _getPrefixOperator(PrefixExpression expression) {
Token operator = expression.operator;
TokenType operatorType = operator.type;
if (operatorType == TokenType.PLUS_PLUS) {
return TokenType.PLUS.lexeme;
} else if (operatorType == TokenType.MINUS_MINUS) {
return TokenType.MINUS.lexeme;
} else if (operatorType == TokenType.MINUS) {
return "unary-";
} else {
return operator.lexeme;
}
}
/**
* Return the static type of the given [expression] that is to be used for
* type analysis.
*/
DartType _getStaticType(Expression expression, {bool read: false}) {
if (expression is NullLiteral) {
return _resolver.typeProvider.nullType;
}
DartType type = read ? getReadType(expression) : expression.staticType;
return _resolveTypeParameter(type);
}
/**
* Check for a generic method & apply type arguments if any were passed.
*/
DartType _instantiateGenericMethod(DartType invokeType,
TypeArgumentList typeArguments, FunctionExpressionInvocation invocation) {
DartType parameterizableType;
List<TypeParameterElement> parameters;
if (invokeType is FunctionType) {
parameterizableType = invokeType;
parameters = invokeType.typeFormals;
} else if (invokeType is InterfaceType) {
var propertyResolver = _newPropertyResolver();
propertyResolver.resolve(null, invokeType,
FunctionElement.CALL_METHOD_NAME, invocation.function);
ExecutableElement callMethod = propertyResolver.result.getter;
invocation.staticElement = callMethod;
parameterizableType = callMethod?.type;
parameters = (parameterizableType as FunctionType)?.typeFormals;
}
if (parameterizableType is ParameterizedType) {
NodeList<TypeAnnotation> arguments = typeArguments?.arguments;
if (arguments != null && arguments.length != parameters.length) {
_resolver.errorReporter.reportErrorForNode(
StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD,
invocation,
[parameterizableType, parameters.length, arguments?.length ?? 0]);
// Wrong number of type arguments. Ignore them.
arguments = null;
}
if (parameters.isNotEmpty) {
if (arguments == null) {
return _resolver.typeSystem.instantiateToBounds(parameterizableType);
} else {
return parameterizableType
.instantiate(arguments.map((n) => n.type).toList());
}
}
return parameterizableType;
}
return invokeType;
}
/**
* Return `true` if the given [expression] is a prefix for a deferred import.
*/
bool _isDeferredPrefix(Expression expression) {
if (expression is SimpleIdentifier) {
Element element = expression.staticElement;
if (element is PrefixElement) {
List<ImportElement> imports =
element.enclosingElement.getImportsWithPrefix(element);
if (imports.length != 1) {
return false;
}
return imports[0].isDeferred;
}
}
return false;
}
/**
* Return `true` if the given [node] can validly be resolved to a prefix:
* * it is the prefix in an import directive, or
* * it is the prefix in a prefixed identifier.
*/
bool _isValidAsPrefix(SimpleIdentifier node) {
AstNode parent = node.parent;
if (parent is ImportDirective) {
return identical(parent.prefix, node);
} else if (parent is PrefixedIdentifier) {
return true;
} else if (parent is MethodInvocation) {
return identical(parent.target, node) &&
parent.operator?.type == TokenType.PERIOD;
}
return false;
}
/**
* Return the target of a break or continue statement, and update the static
* element of its label (if any). The [parentNode] is the AST node of the
* break or continue statement. The [labelNode] is the label contained in that
* statement (if any). The flag [isContinue] is `true` if the node being
* visited is a continue statement.
*/
AstNode _lookupBreakOrContinueTarget(
AstNode parentNode, SimpleIdentifier labelNode, bool isContinue) {
if (labelNode == null) {
return _resolver.implicitLabelScope.getTarget(isContinue);
} else {
LabelScope labelScope = _resolver.labelScope;
if (labelScope == null) {
// There are no labels in scope, so by definition the label is
// undefined.
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.LABEL_UNDEFINED, labelNode, [labelNode.name]);
return null;
}
LabelScope definingScope = labelScope.lookup(labelNode.name);
if (definingScope == null) {
// No definition of the given label name could be found in any
// enclosing scope.
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.LABEL_UNDEFINED, labelNode, [labelNode.name]);
return null;
}
// The target has been found.
labelNode.staticElement = definingScope.element;
ExecutableElement labelContainer = definingScope.element
.getAncestor((element) => element is ExecutableElement);
if (!identical(labelContainer, _resolver.enclosingFunction)) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.LABEL_IN_OUTER_SCOPE,
labelNode,
[labelNode.name]);
}
return definingScope.node;
}
}
_PropertyResolver _newPropertyResolver() {
return _PropertyResolver(_resolver.typeProvider, _inheritance,
_definingLibrary, _extensionResolver);
}
/**
* Record that the given [node] is undefined, causing an error to be reported
* if appropriate. The [declaringElement] is the element inside which no
* declaration was found. If this element is a proxy, no error will be
* reported. If null, then an error will always be reported. The [errorCode]
* is the error code to report. The [arguments] are the arguments to the error
* message.
*/
void _recordUndefinedNode(Element declaringElement, ErrorCode errorCode,
AstNode node, List<Object> arguments) {
_resolver.errorReporter.reportErrorForNode(errorCode, node, arguments);
}
/**
* Record that the given [token] is undefined, causing an error to be reported
* if appropriate. The [declaringElement] is the element inside which no
* declaration was found. If this element is a proxy, no error will be
* reported. If null, then an error will always be reported. The [errorCode]
* is the error code to report. The [arguments] are arguments to the error
* message.
*/
void _recordUndefinedToken(Element declaringElement, ErrorCode errorCode,
Token token, List<Object> arguments) {
_resolver.errorReporter.reportErrorForToken(errorCode, token, arguments);
}
void _resolveAnnotationConstructorInvocationArguments(
Annotation annotation, ConstructorElement constructor) {
ArgumentList argumentList = annotation.arguments;
// error will be reported in ConstantVerifier
if (argumentList == null) {
return;
}
// resolve arguments to parameters
List<ParameterElement> parameters =
_resolveArgumentsToFunction(argumentList, constructor);
if (parameters != null) {
argumentList.correspondingStaticParameters = parameters;
}
}
/**
* Continues resolution of the given [annotation].
*/
void _resolveAnnotationElement(Annotation annotation) {
SimpleIdentifier nameNode1;
SimpleIdentifier nameNode2;
{
Identifier annName = annotation.name;
if (annName is PrefixedIdentifier) {
nameNode1 = annName.prefix;
nameNode2 = annName.identifier;
} else {
nameNode1 = annName as SimpleIdentifier;
nameNode2 = null;
}
}
SimpleIdentifier nameNode3 = annotation.constructorName;
ConstructorElement constructor;
bool undefined = false;
//
// CONST or Class(args)
//
if (nameNode1 != null && nameNode2 == null && nameNode3 == null) {
Element element1 = nameNode1.staticElement;
// CONST
if (element1 is PropertyAccessorElement) {
_resolveAnnotationElementGetter(annotation, element1);
return;
}
// Class(args)
if (element1 is ClassElement) {
constructor = element1.type.lookUpConstructor(null, _definingLibrary);
} else if (element1 == null) {
undefined = true;
}
}
//
// prefix.CONST or prefix.Class() or Class.CONST or Class.constructor(args)
//
if (nameNode1 != null && nameNode2 != null && nameNode3 == null) {
Element element1 = nameNode1.staticElement;
Element element2 = nameNode2.staticElement;
// Class.CONST - not resolved yet
if (element1 is ClassElement) {
element2 = element1.lookUpGetter(nameNode2.name, _definingLibrary);
}
// 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;
}
// Class.constructor(args)
if (element1 is ClassElement) {
constructor =
element1.type.lookUpConstructor(nameNode2.name, _definingLibrary);
nameNode2.staticElement = constructor;
}
if (element1 == null && element2 == null) {
undefined = true;
}
}
//
// prefix.Class.CONST or prefix.Class.constructor(args)
//
if (nameNode1 != null && nameNode2 != null && nameNode3 != null) {
Element element2 = nameNode2.staticElement;
// element2 should be ClassElement
if (element2 is ClassElement) {
String name3 = nameNode3.name;
// prefix.Class.CONST
PropertyAccessorElement getter =
element2.lookUpGetter(name3, _definingLibrary);
if (getter != null) {
nameNode3.staticElement = getter;
annotation.element = getter;
_resolveAnnotationElementGetter(annotation, getter);
return;
}
// prefix.Class.constructor(args)
constructor = element2.type.lookUpConstructor(name3, _definingLibrary);
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.
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.INVALID_ANNOTATION, annotation);
}
return;
}
// record element
annotation.element = constructor;
// resolve arguments
_resolveAnnotationConstructorInvocationArguments(annotation, constructor);
}
void _resolveAnnotationElementGetter(
Annotation annotation, PropertyAccessorElement accessorElement) {
// accessor should be synthetic
if (!accessorElement.isSynthetic) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.INVALID_ANNOTATION_GETTER, annotation);
return;
}
// variable should be constant
VariableElement variableElement = accessorElement.variable;
if (!variableElement.isConst) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.INVALID_ANNOTATION, annotation);
return;
}
// no arguments
if (annotation.arguments != null) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.ANNOTATION_WITH_NON_CLASS,
annotation.name,
[annotation.name]);
}
// OK
return;
}
/**
* 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.
*/
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.
*/
List<ParameterElement> _resolveArgumentsToParameters(
ArgumentList argumentList, List<ParameterElement> parameters) {
return ResolverVisitor.resolveArgumentsToParameters(
argumentList, parameters, _resolver.errorReporter.reportErrorForNode);
}
void _resolveBinaryExpression(BinaryExpression node, String methodName) {
Expression leftOperand = node.leftOperand;
if (leftOperand != null) {
if (leftOperand is ExtensionOverride) {
ExtensionElement element = leftOperand.extensionName.staticElement;
MethodElement member = element.getMethod(methodName);
if (member == null) {
_resolver.errorReporter.reportErrorForToken(
CompileTimeErrorCode.UNDEFINED_EXTENSION_OPERATOR,
node.operator,
[methodName, element.name]);
}
node.staticElement = member;
return;
}
DartType leftType = _getStaticType(leftOperand);
ResolutionResult result = _newPropertyResolver()
.resolve(leftOperand, leftType, methodName, node);
node.staticElement = result.getter;
node.staticInvokeType = result.getter?.type;
if (_shouldReportInvalidMember(leftType, result)) {
if (leftOperand is SuperExpression) {
_recordUndefinedToken(
leftType.element,
StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR,
node.operator,
[methodName, leftType.displayName]);
} else {
_recordUndefinedToken(
leftType.element,
StaticTypeWarningCode.UNDEFINED_OPERATOR,
node.operator,
[methodName, leftType.displayName]);
}
}
}
}
/**
* Resolve the names in the given [combinators] in the scope of the given
* [library].
*/
void _resolveCombinators(
LibraryElement library, NodeList<Combinator> combinators) {
if (library == null) {
//
// The library will be null if the directive containing the combinators
// has a URI that is not valid.
//
return;
}
Namespace namespace =
new NamespaceBuilder().createExportNamespaceForLibrary(library);
for (Combinator combinator in combinators) {
NodeList<SimpleIdentifier> names;
if (combinator is HideCombinator) {
names = combinator.hiddenNames;
} else {
names = (combinator as ShowCombinator).shownNames;
}
for (SimpleIdentifier name in names) {
String nameStr = name.name;
Element element = namespace.get(nameStr) ?? namespace.get("$nameStr=");
if (element != null) {
// Ensure that the name always resolves to a top-level variable
// rather than a getter or setter
if (element is PropertyAccessorElement) {
name.staticElement = element.variable;
} else {
name.staticElement = element;
}
}
}
}
}
/**
* Given a [node] that can have annotations associated with it, resolve the
* annotations in the element model representing annotations to the node.
*/
void _resolveMetadataForParameter(NormalFormalParameter node) {
_resolveAnnotations(node.metadata);
}
void _resolvePropertyAccess(
Expression target, SimpleIdentifier propertyName, bool isCascaded) {
DartType staticType = _getStaticType(target);
//
// If this property access is of the form 'E.m' where 'E' is an extension,
// then look for the member in the extension. This does not apply to
// conditional property accesses (i.e. 'C?.m').
//
if (target is Identifier && target.staticElement is ExtensionElement) {
ExtensionElement extension = target.staticElement;
String memberName = propertyName.name;
if (propertyName.inGetterContext()) {
ExecutableElement element;
element ??= extension.getGetter(memberName);
element ??= extension.getMethod(memberName);
if (element != null) {
propertyName.staticElement = element;
_checkForStaticAccessToInstanceMember(propertyName, element);
} else {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_EXTENSION_GETTER,
propertyName,
[memberName, extension.name],
);
}
}
if (propertyName.inSetterContext()) {
var element = extension.getSetter(memberName);
if (element != null) {
propertyName.staticElement = element;
_checkForStaticAccessToInstanceMember(propertyName, element);
} else {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_EXTENSION_SETTER,
propertyName,
[memberName, extension.name],
);
}
}
return;
}
//
// If this property access is of the form 'C.m' where 'C' is a class,
// then we don't call resolveProperty(...) which walks up the class
// hierarchy, instead we just look for the member in the type only. This
// does not apply to conditional property accesses (i.e. 'C?.m').
//
ClassElement typeReference = getTypeReference(target);
if (typeReference != null) {
if (isCascaded) {
typeReference = _typeType.element;
}
if (propertyName.inGetterContext()) {
ExecutableElement element;
if (element == null) {
var getter = typeReference.getGetter(propertyName.name);
if (getter != null && getter.isAccessibleIn(_definingLibrary)) {
element = getter;
}
}
if (element == null) {
var method = typeReference.getMethod(propertyName.name);
if (method != null && method.isAccessibleIn(_definingLibrary)) {
element = method;
}
}
if (element != null) {
propertyName.staticElement = element;
_checkForStaticAccessToInstanceMember(propertyName, element);
} else {
_resolver.errorReporter.reportErrorForNode(
StaticTypeWarningCode.UNDEFINED_GETTER,
propertyName,
[propertyName.name, typeReference.name],
);
}
}
if (propertyName.inSetterContext()) {
ExecutableElement element;
var setter = typeReference.getSetter(propertyName.name);
if (setter != null && setter.isAccessibleIn(_definingLibrary)) {
element = setter;
}
if (element != null) {
propertyName.staticElement = element;
_checkForStaticAccessToInstanceMember(propertyName, element);
} else {
var getter = typeReference.getGetter(propertyName.name);
if (getter != null) {
propertyName.staticElement = getter;
// The error will be reported in ErrorVerifier.
} else {
_resolver.errorReporter.reportErrorForNode(
StaticTypeWarningCode.UNDEFINED_SETTER,
propertyName,
[propertyName.name, typeReference.name],
);
}
}
}
return;
}
if (target is SuperExpression) {
if (staticType is InterfaceTypeImpl) {
if (propertyName.inGetterContext()) {
var element = staticType.lookUpInheritedMember(
propertyName.name, _definingLibrary,
setter: false, concrete: true, forSuperInvocation: true);
if (element != null) {
propertyName.staticElement = element;
} else {
// We were not able to find the concrete dispatch target.
// But we would like to give the user at least some resolution.
// So, we retry without the "concrete" requirement.
element = staticType.lookUpInheritedMember(
propertyName.name, _definingLibrary,
setter: false, concrete: false);
if (element != null) {
propertyName.staticElement = element;
ClassElementImpl receiverSuperClass =
AbstractClassElementImpl.getImpl(
staticType.element.supertype.element,
);
if (!receiverSuperClass.hasNoSuchMethod) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.ABSTRACT_SUPER_MEMBER_REFERENCE,
propertyName,
[element.kind.displayName, propertyName.name],
);
}
} else {
_resolver.errorReporter.reportErrorForNode(
StaticTypeWarningCode.UNDEFINED_SUPER_GETTER,
propertyName,
[propertyName.name, staticType.displayName],
);
}
}
}
if (propertyName.inSetterContext()) {
var element = staticType.lookUpInheritedMember(
propertyName.name, _definingLibrary,
setter: true, concrete: true, forSuperInvocation: true);
if (element != null) {
propertyName.staticElement = element;
} else {
// We were not able to find the concrete dispatch target.
// But we would like to give the user at least some resolution.
// So, we retry without the "concrete" requirement.
element = staticType.lookUpInheritedMember(
propertyName.name, _definingLibrary,
setter: true, concrete: false);
if (element != null) {
propertyName.staticElement = element;
ClassElementImpl receiverSuperClass =
AbstractClassElementImpl.getImpl(
staticType.element.supertype.element,
);
if (!receiverSuperClass.hasNoSuchMethod) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.ABSTRACT_SUPER_MEMBER_REFERENCE,
propertyName,
[element.kind.displayName, propertyName.name],
);
}
} else {
_resolver.errorReporter.reportErrorForNode(
StaticTypeWarningCode.UNDEFINED_SUPER_SETTER,
propertyName,
[propertyName.name, staticType.displayName],
);
}
}
}
}
return;
}
if (staticType == null || staticType.isDynamic) {
return;
}
if (staticType.isVoid) {
_resolver.errorReporter.reportErrorForNode(
StaticWarningCode.USE_OF_VOID_RESULT,
propertyName,
);
return;
}
var result = _newPropertyResolver()
.resolve(target, staticType, propertyName.name, propertyName);
if (propertyName.inGetterContext()) {
var shouldReportUndefinedGetter = false;
if (result.isSingle) {
var getter = result.getter;
if (getter != null) {
propertyName.staticElement = getter;
} else {
shouldReportUndefinedGetter = true;
}
} else if (result.isNone) {
if (staticType is FunctionType &&
propertyName.name == FunctionElement.CALL_METHOD_NAME) {
// Referencing `.call` on a `Function` type is OK.
} else if (staticType is InterfaceType &&
staticType.isDartCoreFunction &&
propertyName.name == FunctionElement.CALL_METHOD_NAME) {
// Referencing `.call` on a `Function` type is OK.
} else {
shouldReportUndefinedGetter = true;
}
}
if (shouldReportUndefinedGetter) {
_resolver.errorReporter.reportErrorForNode(
StaticTypeWarningCode.UNDEFINED_GETTER,
propertyName,
[propertyName.name, staticType.displayName],
);
}
}
if (propertyName.inSetterContext()) {
if (result.isSingle) {
var setter = result.setter;
if (setter != null) {
propertyName.staticElement = setter;
} else {
var getter = result.getter;
propertyName.staticElement = getter;
// A more specific error will be reported in ErrorVerifier.
}
} else if (result.isNone) {
_resolver.errorReporter.reportErrorForNode(
StaticTypeWarningCode.UNDEFINED_SETTER,
propertyName,
[propertyName.name, staticType.displayName],
);
}
}
}
/**
* Resolve the given simple [identifier] if possible. Return the element to
* which it could be resolved, or `null` if it could not be resolved. This
* does not record the results of the resolution.
*/
Element _resolveSimpleIdentifier(SimpleIdentifier identifier) {
Element element = _resolver.nameScope.lookup(identifier, _definingLibrary);
if (element is PropertyAccessorElement && identifier.inSetterContext()) {
PropertyInducingElement variable =
(element as PropertyAccessorElement).variable;
if (variable != null) {
PropertyAccessorElement setter = variable.setter;
if (setter == null) {
//
// Check to see whether there might be a locally defined getter and
// an inherited setter.
//
ClassElement enclosingClass = _resolver.enclosingClass;
if (enclosingClass != null) {
var propertyResolver = _newPropertyResolver();
propertyResolver.resolve(
null, enclosingClass.thisType, identifier.name, identifier);
setter = propertyResolver.result.setter;
}
}
if (setter != null) {
element = setter;
}
}
} else if (element == null &&
(identifier.inSetterContext() ||
identifier.parent is CommentReference)) {
Identifier setterId =
new SyntheticIdentifier('${identifier.name}=', identifier);
element = _resolver.nameScope.lookup(setterId, _definingLibrary);
}
if (element == null) {
InterfaceType enclosingType;
ClassElement enclosingClass = _resolver.enclosingClass;
if (enclosingClass == null) {
var enclosingExtension = _resolver.enclosingExtension;
if (enclosingExtension == null) {
return null;
}
DartType extendedType =
_resolveTypeParameter(enclosingExtension.extendedType);
if (extendedType is InterfaceType) {
enclosingType = extendedType;
} else if (extendedType is FunctionType) {
enclosingType = _resolver.typeProvider.functionType;
} else {
return null;
}
} else {
enclosingType = enclosingClass.thisType;
}
if (element == null && enclosingType != null) {
var propertyResolver = _newPropertyResolver();
propertyResolver.resolve(
null, enclosingType, identifier.name, identifier);
if (identifier.inSetterContext() ||
identifier.parent is CommentReference) {
element = propertyResolver.result.setter;
}
element ??= propertyResolver.result.getter;
}
}
return element;
}
/**
* If the given [type] is a type parameter, resolve it to the type that should
* be used when looking up members. Otherwise, return the original type.
*/
DartType _resolveTypeParameter(DartType type) =>
type?.resolveToBound(_resolver.typeProvider.objectType);
/**
* Return `true` if we should report an error for a [member] lookup that found
* no match on the given [type].
*/
bool _shouldReportInvalidMember(DartType type, ResolutionResult result) =>
type != null && !type.isDynamic && result.isNone;
/**
* Checks whether the given [expression] is a reference to a class. If it is
* then the element representing the class is returned, otherwise `null` is
* returned.
*/
static ClassElement getTypeReference(Expression expression) {
if (expression is Identifier) {
Element staticElement = expression.staticElement;
if (staticElement is ClassElement) {
return staticElement;
}
}
return null;
}
/**
* Given a [node] that can have annotations associated with it, resolve the
* annotations in the element model representing the annotations on the node.
*/
static void resolveMetadata(AnnotatedNode node) {
_resolveAnnotations(node.metadata);
if (node is VariableDeclaration) {
AstNode parent = node.parent;
if (parent is VariableDeclarationList) {
_resolveAnnotations(parent.metadata);
AstNode grandParent = parent.parent;
if (grandParent is FieldDeclaration) {
_resolveAnnotations(grandParent.metadata);
} else if (grandParent is TopLevelVariableDeclaration) {
_resolveAnnotations(grandParent.metadata);
}
}
}
}
/**
* Return `true` if the given [identifier] is the return type of a constructor
* declaration.
*/
static bool _isConstructorReturnType(SimpleIdentifier identifier) {
AstNode parent = identifier.parent;
if (parent is ConstructorDeclaration) {
return identical(parent.returnType, identifier);
}
return false;
}
/**
* Return `true` if the given [identifier] is the return type of a factory
* constructor.
*/
static bool _isFactoryConstructorReturnType(SimpleIdentifier identifier) {
AstNode parent = identifier.parent;
if (parent is ConstructorDeclaration) {
return identical(parent.returnType, identifier) &&
parent.factoryKeyword != null;
}
return false;
}
/**
* Resolve each of the annotations in the given list of [annotations].
*/
static void _resolveAnnotations(NodeList<Annotation> annotations) {
for (Annotation annotation in annotations) {
ElementAnnotationImpl elementAnnotation = annotation.elementAnnotation;
elementAnnotation.element = annotation.element;
}
}
}
/**
* An identifier that can be used to look up names in the lexical scope when
* there is no identifier in the AST structure. There is no identifier in the
* AST when the parser could not distinguish between a method invocation and an
* invocation of a top-level function imported with a prefix.
*/
class SyntheticIdentifier extends IdentifierImpl {
/**
* The name of the synthetic identifier.
*/
@override
final String name;
/**
* The identifier to be highlighted in case of an error
*/
final Identifier targetIdentifier;
/**
* Initialize a newly created synthetic identifier to have the given [name]
* and [targetIdentifier].
*/
SyntheticIdentifier(this.name, this.targetIdentifier);
@override
Token get beginToken => null;
@override
Element get bestElement => null;
@override
Iterable<SyntacticEntity> get childEntities {
// Should never be called, since a SyntheticIdentifier never appears in the
// AST--it is just used for lookup.
assert(false);
return new ChildEntities();
}
@override
Token get endToken => null;
@override
int get length => targetIdentifier.length;
@override
int get offset => targetIdentifier.offset;
@override
Precedence get precedence => Precedence.primary;
@deprecated
@override
Element get propagatedElement => null;
@override
Element get staticElement => null;
@override
E accept<E>(AstVisitor<E> visitor) => null;
@override
void visitChildren(AstVisitor visitor) {}
}
/// Helper for resolving properties (getters, setters, or methods).
class _PropertyResolver {
final TypeProvider _typeProvider;
final InheritanceManager3 _inheritance;
final LibraryElement _definingLibrary;
final ExtensionMemberResolver _extensionResolver;
ResolutionResult result = ResolutionResult.none;
_PropertyResolver(
this._typeProvider,
this._inheritance,
this._definingLibrary,
this._extensionResolver,
);
/// Look up the getter and the setter with the given [name] in the [type].
///
/// The [target] is optional, and used to identify `super`.
///
/// The [errorNode] is used to report the ambiguous extension issue.
ResolutionResult resolve(
Expression target,
DartType type,
String name,
Expression errorNode,
) {
type = _resolveTypeParameter(type);
ExecutableElement typeGetter;
ExecutableElement typeSetter;
void lookupIn(InterfaceType type) {
var isSuper = target is SuperExpression;
if (name == '[]') {
typeGetter = type.lookUpInheritedMethod('[]',
library: _definingLibrary, thisType: !isSuper);
typeSetter = type.lookUpInheritedMethod('[]=',
library: _definingLibrary, thisType: !isSuper);
} else {
typeGetter = type.lookUpInheritedGetter(name,
library: _definingLibrary, thisType: !isSuper);
typeGetter ??= type.lookUpInheritedMethod(name,
library: _definingLibrary, thisType: !isSuper);
typeSetter = type.lookUpInheritedSetter(name,
library: _definingLibrary, thisType: !isSuper);
}
}
if (type is InterfaceType) {
lookupIn(type);
} else if (type is FunctionType) {
lookupIn(_typeProvider.functionType);
} else {
return ResolutionResult.none;
}
if (typeGetter != null || typeSetter != null) {
result = ResolutionResult(getter: typeGetter, setter: typeSetter);
}
if (result.isNone) {
result = _extensionResolver.findExtension(type, name, errorNode);
}
return result;
}
/// If the given [type] is a type parameter, replace it with its bound.
/// Otherwise, return the original type.
DartType _resolveTypeParameter(DartType type) {
return type?.resolveToBound(_typeProvider.objectType);
}
}