Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Unified Diff: packages/analyzer/lib/src/generated/resolver.dart

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « packages/analyzer/lib/src/generated/parser.dart ('k') | packages/analyzer/lib/src/generated/scanner.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: packages/analyzer/lib/src/generated/resolver.dart
diff --git a/packages/analyzer/lib/src/generated/resolver.dart b/packages/analyzer/lib/src/generated/resolver.dart
index 47a68ee9fd1bd1b16266b070b1e7f5d5d7126706..3e1dc6bfb3ff04166ad35619c0e6ca7e38eda4ec 100644
--- a/packages/analyzer/lib/src/generated/resolver.dart
+++ b/packages/analyzer/lib/src/generated/resolver.dart
@@ -2,51 +2,39 @@
// 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.
-library engine.resolver;
+library analyzer.src.generated.resolver;
import 'dart:collection';
-import 'ast.dart';
-import 'constant.dart';
-import 'element.dart';
-import 'element_resolver.dart';
-import 'engine.dart';
-import 'error.dart';
-import 'error_verifier.dart';
-import 'html.dart' as ht;
-import 'java_core.dart';
-import 'java_engine.dart';
-import 'scanner.dart' as sc;
-import 'sdk.dart' show DartSdk, SdkLibrary;
-import 'source.dart';
-import 'static_type_analyzer.dart';
-import 'utilities_dart.dart';
-
-/**
- * Callback signature used by ImplicitConstructorBuilder to register
- * computations to be performed, and their dependencies. A call to this
- * callback indicates that [computation] may be used to compute implicit
- * constructors for [classElement], but that the computation may not be invoked
- * until after implicit constructors have been built for [superclassElement].
- */
-typedef void ImplicitConstructorBuilderCallback(ClassElement classElement,
- ClassElement superclassElement, void computation());
-
-typedef LibraryResolver LibraryResolverFactory(AnalysisContext context);
-
-typedef ResolverVisitor ResolverVisitorFactory(
- Library library, Source source, TypeProvider typeProvider);
-
-typedef StaticTypeAnalyzer StaticTypeAnalyzerFactory(ResolverVisitor visitor);
-
-typedef TypeResolverVisitor TypeResolverVisitorFactory(
- Library library, Source source, TypeProvider typeProvider);
-
-typedef void VoidFunction();
-
-typedef bool _GuardedSubtypeChecker<T>(T t1, T t2, Set<Element> visited);
-
-typedef bool _SubtypeChecker<T>(T t1, T t2);
+import 'package:analyzer/dart/ast/ast.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/dart/element/visitor.dart';
+import 'package:analyzer/error/error.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/exception/exception.dart';
+import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
+import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer/src/dart/element/utilities.dart';
+import 'package:analyzer/src/dart/resolver/inheritance_manager.dart';
+import 'package:analyzer/src/dart/resolver/scope.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/constant.dart';
+import 'package:analyzer/src/generated/element_resolver.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/error_verifier.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/generated/static_type_analyzer.dart';
+import 'package:analyzer/src/generated/type_system.dart';
+import 'package:analyzer/src/generated/utilities_dart.dart';
+
+export 'package:analyzer/src/dart/resolver/inheritance_manager.dart';
+export 'package:analyzer/src/dart/resolver/scope.dart';
+export 'package:analyzer/src/generated/type_system.dart';
/**
* Instances of the class `BestPracticesVerifier` traverse an AST structure looking for
@@ -63,13 +51,24 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
* The class containing the AST nodes being visited, or `null` if we are not in the scope of
* a class.
*/
- ClassElement _enclosingClass;
+ ClassElementImpl _enclosingClass;
+
+ /**
+ * A flag indicating whether a surrounding member (compilation unit or class)
+ * is deprecated.
+ */
+ bool inDeprecatedMember;
/**
* The error reporter by which errors will be reported.
*/
final ErrorReporter _errorReporter;
+ /**
+ * The type [Null].
+ */
+ final InterfaceType _nullType;
+
/**
* The type Future<Null>, which is needed for determining whether it is safe
* to have a bare "return;" in an async method.
@@ -81,18 +80,52 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
*/
TypeSystem _typeSystem;
+ /**
+ * The current library
+ */
+ LibraryElement _currentLibrary;
+
+ /**
+ * The inheritance manager used to find overridden methods.
+ */
+ InheritanceManager _manager;
+
/**
* Create a new instance of the [BestPracticesVerifier].
*
* @param errorReporter the error reporter
*/
BestPracticesVerifier(this._errorReporter, TypeProvider typeProvider,
+ this._currentLibrary, this._manager,
{TypeSystem typeSystem})
- : _futureNullType = typeProvider.futureNullType,
- _typeSystem = (typeSystem != null) ? typeSystem : new TypeSystemImpl();
+ : _nullType = typeProvider.nullType,
+ _futureNullType = typeProvider.futureNullType,
+ _typeSystem = typeSystem ?? new TypeSystemImpl() {
+ inDeprecatedMember = _currentLibrary.isDeprecated;
+ }
+
+ @override
+ Object visitAnnotation(Annotation node) {
+ if (node.elementAnnotation?.isFactory == true) {
+ AstNode parent = node.parent;
+ if (parent is MethodDeclaration) {
+ _checkForInvalidFactory(parent);
+ } else {
+ _errorReporter
+ .reportErrorForNode(HintCode.INVALID_FACTORY_ANNOTATION, node, []);
+ }
+ }
+ return super.visitAnnotation(node);
+ }
@override
Object visitArgumentList(ArgumentList node) {
+ for (Expression argument in node.arguments) {
+ ParameterElement parameter = argument.bestParameterElement;
+ if (parameter?.parameterKind == ParameterKind.POSITIONAL) {
+ _checkForDeprecatedMemberUse(parameter, argument);
+ }
+ }
_checkForArgumentTypesNotAssignableInList(node);
return super.visitArgumentList(node);
}
@@ -103,10 +136,16 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
return super.visitAsExpression(node);
}
+ @override
+ Object visitAssertStatement(AssertStatement node) {
+ _checkForPossibleNullCondition(node.condition);
+ return super.visitAssertStatement(node);
+ }
+
@override
Object visitAssignmentExpression(AssignmentExpression node) {
- sc.TokenType operatorType = node.operator.type;
- if (operatorType == sc.TokenType.EQ) {
+ TokenType operatorType = node.operator.type;
+ if (operatorType == TokenType.EQ) {
_checkForUseOfVoidResult(node.rightHandSide);
_checkForInvalidAssignment(node.leftHandSide, node.rightHandSide);
} else {
@@ -124,15 +163,47 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
@override
Object visitClassDeclaration(ClassDeclaration node) {
- ClassElement outerClass = _enclosingClass;
+ ClassElementImpl outerClass = _enclosingClass;
+ bool wasInDeprecatedMember = inDeprecatedMember;
+ ClassElement element = AbstractClassElementImpl.getImpl(node.element);
+ if (element != null && element.isDeprecated) {
+ inDeprecatedMember = true;
+ }
try {
- _enclosingClass = node.element;
+ _enclosingClass = element;
// Commented out until we decide that we want this hint in the analyzer
// checkForOverrideEqualsButNotHashCode(node);
return super.visitClassDeclaration(node);
} finally {
_enclosingClass = outerClass;
+ inDeprecatedMember = wasInDeprecatedMember;
+ }
+ }
+
+ @override
+ Object visitConditionalExpression(ConditionalExpression node) {
+ _checkForPossibleNullCondition(node.condition);
+ return super.visitConditionalExpression(node);
+ }
+
+ @override
+ Object visitConstructorDeclaration(ConstructorDeclaration node) {
+ if (node.element.isFactory) {
+ if (node.body is BlockFunctionBody) {
+ // Check the block for a return statement, if not, create the hint.
+ if (!ExitDetector.exits(node.body)) {
+ _errorReporter.reportErrorForNode(
+ HintCode.MISSING_RETURN, node, [node.returnType.name]);
+ }
+ }
}
+ return super.visitConstructorDeclaration(node);
+ }
+
+ @override
+ Object visitDoStatement(DoStatement node) {
+ _checkForPossibleNullCondition(node.condition);
+ return super.visitDoStatement(node);
}
@override
@@ -141,20 +212,39 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
return super.visitExportDirective(node);
}
+ @override
+ Object visitForStatement(ForStatement node) {
+ _checkForPossibleNullCondition(node.condition);
+ return super.visitForStatement(node);
+ }
+
@override
Object visitFunctionDeclaration(FunctionDeclaration node) {
- _checkForMissingReturn(node.returnType, node.functionExpression.body);
- return super.visitFunctionDeclaration(node);
+ bool wasInDeprecatedMember = inDeprecatedMember;
+ ExecutableElement element = node.element;
+ if (element != null && element.isDeprecated) {
+ inDeprecatedMember = true;
+ }
+ try {
+ _checkForMissingReturn(node.returnType, node.functionExpression.body);
+ return super.visitFunctionDeclaration(node);
+ } finally {
+ inDeprecatedMember = wasInDeprecatedMember;
+ }
+ }
+
+ @override
+ Object visitIfStatement(IfStatement node) {
+ _checkForPossibleNullCondition(node.condition);
+ return super.visitIfStatement(node);
}
@override
Object visitImportDirective(ImportDirective node) {
_checkForDeprecatedMemberUse(node.uriElement, node);
ImportElement importElement = node.element;
- if (importElement != null) {
- if (importElement.isDeferred) {
- _checkForLoadLibraryFunction(node, importElement);
- }
+ if (importElement != null && importElement.isDeferred) {
+ _checkForLoadLibraryFunction(node, importElement);
}
return super.visitImportDirective(node);
}
@@ -179,10 +269,35 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
@override
Object visitMethodDeclaration(MethodDeclaration node) {
- // This was determined to not be a good hint, see: dartbug.com/16029
- //checkForOverridingPrivateMember(node);
- _checkForMissingReturn(node.returnType, node.body);
- return super.visitMethodDeclaration(node);
+ bool wasInDeprecatedMember = inDeprecatedMember;
+ ExecutableElement element = node.element;
+ if (element != null && element.isDeprecated) {
+ inDeprecatedMember = true;
+ }
+ try {
+ // This was determined to not be a good hint, see: dartbug.com/16029
+ //checkForOverridingPrivateMember(node);
+ _checkForMissingReturn(node.returnType, node.body);
+ _checkForUnnecessaryNoSuchMethod(node);
+ return super.visitMethodDeclaration(node);
+ } finally {
+ inDeprecatedMember = wasInDeprecatedMember;
+ }
+ }
+
+ @override
+ Object visitMethodInvocation(MethodInvocation node) {
+ Expression realTarget = node.realTarget;
+ _checkForAbstractSuperMemberReference(realTarget, node.methodName);
+ _checkForCanBeNullAfterNullAware(
+ realTarget, node.operator, null, node.methodName);
+ DartType staticInvokeType = node.staticInvokeType;
+ if (staticInvokeType is InterfaceType) {
+ MethodElement methodElement = staticInvokeType.lookUpMethod(
+ FunctionElement.CALL_METHOD_NAME, _currentLibrary);
+ _checkForDeprecatedMemberUse(methodElement, node);
+ }
+ return super.visitMethodInvocation(node);
}
@override
@@ -197,6 +312,15 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
return super.visitPrefixExpression(node);
}
+ @override
+ Object visitPropertyAccess(PropertyAccess node) {
+ Expression realTarget = node.realTarget;
+ _checkForAbstractSuperMemberReference(realTarget, node.propertyName);
+ _checkForCanBeNullAfterNullAware(
+ realTarget, node.operator, node.propertyName, null);
+ return super.visitPropertyAccess(node);
+ }
+
@override
Object visitRedirectingConstructorInvocation(
RedirectingConstructorInvocation node) {
@@ -207,6 +331,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
@override
Object visitSimpleIdentifier(SimpleIdentifier node) {
_checkForDeprecatedMemberUseAtIdentifier(node);
+ _checkForInvalidProtectedMemberAccess(node);
return super.visitSimpleIdentifier(node);
}
@@ -223,6 +348,12 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
return super.visitVariableDeclaration(node);
}
+ @override
+ Object visitWhileStatement(WhileStatement node) {
+ _checkForPossibleNullCondition(node.condition);
+ return super.visitWhileStatement(node);
+ }
+
/**
* Check for the passed is expression for the unnecessary type check hint codes as well as null
* checks expressed using an is expression.
@@ -243,7 +374,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
}
String rhsNameStr = typeName.name.name;
// if x is dynamic
- if (rhsType.isDynamic && rhsNameStr == sc.Keyword.DYNAMIC.syntax) {
+ if (rhsType.isDynamic && rhsNameStr == Keyword.DYNAMIC.syntax) {
if (node.notOperator == null) {
// the is case
_errorReporter.reportErrorForNode(
@@ -256,8 +387,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
return true;
}
Element rhsElement = rhsType.element;
- LibraryElement libraryElement =
- rhsElement != null ? rhsElement.library : null;
+ LibraryElement libraryElement = rhsElement?.library;
if (libraryElement != null && libraryElement.isDartCore) {
// if x is Object or null is Null
if (rhsType.isObject ||
@@ -287,6 +417,34 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
return false;
}
+ void _checkForAbstractSuperMemberReference(
+ Expression target, SimpleIdentifier name) {
+ if (target is SuperExpression) {
+ Element element = name.staticElement;
+ if (element is ExecutableElement && element.isAbstract) {
+ if (!_enclosingClass.hasNoSuchMethod) {
+ ExecutableElement concrete = null;
+ if (element.kind == ElementKind.METHOD) {
+ concrete = _enclosingClass.lookUpInheritedConcreteMethod(
+ element.displayName, _currentLibrary);
+ } else if (element.kind == ElementKind.GETTER) {
+ concrete = _enclosingClass.lookUpInheritedConcreteGetter(
+ element.displayName, _currentLibrary);
+ } else if (element.kind == ElementKind.SETTER) {
+ concrete = _enclosingClass.lookUpInheritedConcreteSetter(
+ element.displayName, _currentLibrary);
+ }
+ if (concrete == null) {
+ _errorReporter.reportTypeErrorForNode(
+ HintCode.ABSTRACT_SUPER_MEMBER_REFERENCE,
+ name,
+ [element.kind.displayName, name.name]);
+ }
+ }
+ }
+ }
+ }
+
/**
* This verifies that the passed expression can be assigned to its corresponding parameters.
*
@@ -325,11 +483,8 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
// Hint case: test propagated type information
//
// Compute the best types to use.
- DartType expectedBestType = expectedPropagatedType != null
- ? expectedPropagatedType
- : expectedStaticType;
- DartType actualBestType =
- actualPropagatedType != null ? actualPropagatedType : actualStaticType;
+ DartType expectedBestType = expectedPropagatedType ?? expectedStaticType;
+ DartType actualBestType = actualPropagatedType ?? actualStaticType;
if (actualBestType != null && expectedBestType != null) {
if (!_typeSystem.isAssignableTo(actualBestType, expectedBestType)) {
_errorReporter.reportTypeErrorForNode(
@@ -354,13 +509,10 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
return false;
}
ParameterElement staticParameterElement = argument.staticParameterElement;
- DartType staticParameterType =
- staticParameterElement == null ? null : staticParameterElement.type;
+ DartType staticParameterType = staticParameterElement?.type;
ParameterElement propagatedParameterElement =
argument.propagatedParameterElement;
- DartType propagatedParameterType = propagatedParameterElement == null
- ? null
- : propagatedParameterElement.type;
+ DartType propagatedParameterType = propagatedParameterElement?.type;
return _checkForArgumentTypeNotAssignableWithExpectedTypes(
argument,
staticParameterType,
@@ -414,6 +566,44 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
return problemReported;
}
+ /**
+ * Produce a hint if the given [target] could have a value of `null`, and
+ * [identifier] is not a name of a getter or a method that exists in the
+ * class [Null].
+ */
+ void _checkForCanBeNullAfterNullAware(Expression target, Token operator,
+ SimpleIdentifier propertyName, SimpleIdentifier methodName) {
+ if (operator?.type == TokenType.QUESTION_PERIOD) {
+ return;
+ }
+ bool isNullTypeMember() {
+ if (propertyName != null) {
+ String name = propertyName.name;
+ return _nullType.lookUpGetter(name, _currentLibrary) != null;
+ }
+ if (methodName != null) {
+ String name = methodName.name;
+ return _nullType.lookUpMethod(name, _currentLibrary) != null;
+ }
+ return false;
+ }
+
+ target = target?.unParenthesized;
+ if (target is MethodInvocation) {
+ if (target.operator?.type == TokenType.QUESTION_PERIOD &&
+ !isNullTypeMember()) {
+ _errorReporter.reportErrorForNode(
+ HintCode.CAN_BE_NULL_AFTER_NULL_AWARE, target);
+ }
+ } else if (target is PropertyAccess) {
+ if (target.operator.type == TokenType.QUESTION_PERIOD &&
+ !isNullTypeMember()) {
+ _errorReporter.reportErrorForNode(
+ HintCode.CAN_BE_NULL_AFTER_NULL_AWARE, target);
+ }
+ }
+ }
+
/**
* Given some [Element], look at the associated metadata and report the use of the member if
* it is declared as deprecated.
@@ -423,24 +613,40 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
* @return `true` if and only if a hint code is generated on the passed node
* See [HintCode.DEPRECATED_MEMBER_USE].
*/
- bool _checkForDeprecatedMemberUse(Element element, AstNode node) {
- if (element != null && element.isDeprecated) {
+ void _checkForDeprecatedMemberUse(Element element, AstNode node) {
+ bool isDeprecated(Element element) {
+ if (element == null) {
+ return false;
+ } else if (element is PropertyAccessorElement && element.isSynthetic) {
+ // TODO(brianwilkerson) Why isn't this the implementation for PropertyAccessorElement?
+ Element variable = element.variable;
+ if (variable == null) {
+ return false;
+ }
+ return variable.isDeprecated;
+ }
+ return element.isDeprecated;
+ }
+
+ if (!inDeprecatedMember && isDeprecated(element)) {
String displayName = element.displayName;
if (element is ConstructorElement) {
// TODO(jwren) We should modify ConstructorElement.getDisplayName(),
// or have the logic centralized elsewhere, instead of doing this logic
// here.
- ConstructorElement constructorElement = element;
- displayName = constructorElement.enclosingElement.displayName;
- if (!constructorElement.displayName.isEmpty) {
- displayName = "$displayName.${constructorElement.displayName}";
+ displayName = element.enclosingElement.displayName;
+ if (!element.displayName.isEmpty) {
+ displayName = "$displayName.${element.displayName}";
}
+ } else if (displayName == FunctionElement.CALL_METHOD_NAME &&
+ node is MethodInvocation &&
+ node.staticInvokeType is InterfaceType) {
+ displayName =
+ "${node.staticInvokeType.displayName}.${element.displayName}";
}
_errorReporter.reportErrorForNode(
HintCode.DEPRECATED_MEMBER_USE, node, [displayName]);
- return true;
}
- return false;
}
/**
@@ -457,18 +663,20 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
* @return `true` if and only if a hint code is generated on the passed node
* See [HintCode.DEPRECATED_MEMBER_USE].
*/
- bool _checkForDeprecatedMemberUseAtIdentifier(SimpleIdentifier identifier) {
+ void _checkForDeprecatedMemberUseAtIdentifier(SimpleIdentifier identifier) {
if (identifier.inDeclarationContext()) {
- return false;
+ return;
}
AstNode parent = identifier.parent;
if ((parent is ConstructorName && identical(identifier, parent.name)) ||
+ (parent is ConstructorDeclaration &&
+ identical(identifier, parent.returnType)) ||
(parent is SuperConstructorInvocation &&
identical(identifier, parent.constructorName)) ||
parent is HideCombinator) {
- return false;
+ return;
}
- return _checkForDeprecatedMemberUse(identifier.bestElement, identifier);
+ _checkForDeprecatedMemberUse(identifier.bestElement, identifier);
}
/**
@@ -480,7 +688,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
*/
bool _checkForDivisionOptimizationHint(BinaryExpression node) {
// Return if the operator is not '/'
- if (node.operator.type != sc.TokenType.SLASH) {
+ if (node.operator.type != TokenType.SLASH) {
return false;
}
// Return if the '/' operator is not defined in core, or if we don't know
@@ -494,16 +702,16 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
return false;
}
// Report error if the (x/y) has toInt() invoked on it
- if (node.parent is ParenthesizedExpression) {
+ AstNode parent = node.parent;
+ if (parent is ParenthesizedExpression) {
ParenthesizedExpression parenthesizedExpression =
- _wrapParenthesizedExpression(node.parent as ParenthesizedExpression);
- if (parenthesizedExpression.parent is MethodInvocation) {
- MethodInvocation methodInvocation =
- parenthesizedExpression.parent as MethodInvocation;
- if (_TO_INT_METHOD_NAME == methodInvocation.methodName.name &&
- methodInvocation.argumentList.arguments.isEmpty) {
+ _wrapParenthesizedExpression(parent);
+ AstNode grandParent = parenthesizedExpression.parent;
+ if (grandParent is MethodInvocation) {
+ if (_TO_INT_METHOD_NAME == grandParent.methodName.name &&
+ grandParent.argumentList.arguments.isEmpty) {
_errorReporter.reportErrorForNode(
- HintCode.DIVISION_OPTIMIZATION, methodInvocation);
+ HintCode.DIVISION_OPTIMIZATION, grandParent);
return true;
}
}
@@ -546,6 +754,91 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
return false;
}
+ void _checkForInvalidFactory(MethodDeclaration decl) {
+ // Check declaration.
+ // Note that null return types are expected to be flagged by other analyses.
+ DartType returnType = decl.returnType?.type;
+ if (returnType is VoidType) {
+ _errorReporter.reportErrorForNode(HintCode.INVALID_FACTORY_METHOD_DECL,
+ decl.name, [decl.name.toString()]);
+ return;
+ }
+
+ // Check implementation.
+
+ FunctionBody body = decl.body;
+ if (body is EmptyFunctionBody) {
+ // Abstract methods are OK.
+ return;
+ }
+
+ // `new Foo()` or `null`.
+ bool factoryExpression(Expression expression) =>
+ expression is InstanceCreationExpression || expression is NullLiteral;
+
+ if (body is ExpressionFunctionBody && factoryExpression(body.expression)) {
+ return;
+ } else if (body is BlockFunctionBody) {
+ NodeList<Statement> statements = body.block.statements;
+ if (statements.isNotEmpty) {
+ Statement last = statements.last;
+ if (last is ReturnStatement && factoryExpression(last.expression)) {
+ return;
+ }
+ }
+ }
+
+ _errorReporter.reportErrorForNode(HintCode.INVALID_FACTORY_METHOD_IMPL,
+ decl.name, [decl.name.toString()]);
+ }
+
+ /**
+ * Produces a hint if the given identifier is a protected closure, field or
+ * getter/setter, method closure or invocation accessed outside a subclass.
+ */
+ void _checkForInvalidProtectedMemberAccess(SimpleIdentifier identifier) {
+ if (identifier.inDeclarationContext()) {
+ return;
+ }
+
+ bool isProtected(Element element) {
+ if (element is PropertyAccessorElement &&
+ element.enclosingElement is ClassElement &&
+ (element.isProtected || element.variable.isProtected)) {
+ return true;
+ }
+ if (element is MethodElement &&
+ element.enclosingElement is ClassElement &&
+ element.isProtected) {
+ return true;
+ }
+ return false;
+ }
+
+ bool inCommentReference(SimpleIdentifier identifier) =>
+ identifier.getAncestor((AstNode node) => node is CommentReference) !=
+ null;
+
+ bool inCurrentLibrary(Element element) =>
+ element.library == _currentLibrary;
+
+ Element element = identifier.bestElement;
+ if (isProtected(element) &&
+ !inCurrentLibrary(element) &&
+ !inCommentReference(identifier)) {
+ ClassElement definingClass = element.enclosingElement;
+ ClassDeclaration accessingClass =
+ identifier.getAncestor((AstNode node) => node is ClassDeclaration);
+ if (accessingClass == null ||
+ !_hasTypeOrSuperType(accessingClass.element, definingClass.type)) {
+ _errorReporter.reportErrorForNode(
+ HintCode.INVALID_USE_OF_PROTECTED_MEMBER,
+ identifier,
+ [identifier.name.toString(), definingClass.name]);
+ }
+ }
+ }
+
/**
* Check that the imported library does not define a loadLibrary function. The import has already
* been determined to be deferred when this is called.
@@ -584,68 +877,90 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
* @return `true` if and only if a hint code is generated on the passed node
* See [HintCode.MISSING_RETURN].
*/
- bool _checkForMissingReturn(TypeName returnType, FunctionBody body) {
+ void _checkForMissingReturn(TypeName returnType, FunctionBody body) {
// Check that the method or function has a return type, and a function body
if (returnType == null || body == null) {
- return false;
+ return;
}
// Check that the body is a BlockFunctionBody
- if (body is! BlockFunctionBody) {
- return false;
- }
- // Generators are never required to have a return statement.
- if (body.isGenerator) {
- return false;
+ if (body is BlockFunctionBody) {
+ // Generators are never required to have a return statement.
+ if (body.isGenerator) {
+ return;
+ }
+ // Check that the type is resolvable, and is not "void"
+ DartType returnTypeType = returnType.type;
+ if (returnTypeType == null || returnTypeType.isVoid) {
+ return;
+ }
+ // For async, give no hint if Future<Null> is assignable to the return
+ // type.
+ if (body.isAsynchronous &&
+ _typeSystem.isAssignableTo(_futureNullType, returnTypeType)) {
+ return;
+ }
+ // Check the block for a return statement, if not, create the hint
+ if (!ExitDetector.exits(body)) {
+ _errorReporter.reportErrorForNode(
+ HintCode.MISSING_RETURN, returnType, [returnTypeType.displayName]);
+ }
}
- // Check that the type is resolvable, and is not "void"
- DartType returnTypeType = returnType.type;
- if (returnTypeType == null || returnTypeType.isVoid) {
- return false;
+ }
+
+ /**
+ * Produce a hint if the given [condition] could have a value of `null`.
+ */
+ void _checkForPossibleNullCondition(Expression condition) {
+ condition = condition?.unParenthesized;
+ if (condition is BinaryExpression) {
+ _checkForPossibleNullConditionInBinaryExpression(condition);
+ } else if (condition is PrefixExpression) {
+ _checkForPossibleNullConditionInPrefixExpression(condition);
+ } else {
+ _checkForPossibleNullConditionInSimpleExpression(condition);
}
- // For async, give no hint if Future<Null> is assignable to the return
- // type.
- if (body.isAsynchronous &&
- _typeSystem.isAssignableTo(_futureNullType, returnTypeType)) {
- return false;
+ }
+
+ /**
+ * Produce a hint if any of the parts of the given binary [condition] could
+ * have a value of `null`.
+ */
+ void _checkForPossibleNullConditionInBinaryExpression(
+ BinaryExpression condition) {
+ TokenType type = condition.operator?.type;
+ if (type == TokenType.AMPERSAND_AMPERSAND || type == TokenType.BAR_BAR) {
+ _checkForPossibleNullCondition(condition.leftOperand);
+ _checkForPossibleNullCondition(condition.rightOperand);
}
- // Check the block for a return statement, if not, create the hint
- BlockFunctionBody blockFunctionBody = body as BlockFunctionBody;
- if (!ExitDetector.exits(blockFunctionBody)) {
- _errorReporter.reportErrorForNode(
- HintCode.MISSING_RETURN, returnType, [returnTypeType.displayName]);
- return true;
+ }
+
+ /**
+ * Produce a hint if the operand of the given prefix [condition] could
+ * have a value of `null`.
+ */
+ void _checkForPossibleNullConditionInPrefixExpression(
+ PrefixExpression condition) {
+ if (condition.operator?.type == TokenType.BANG) {
+ _checkForPossibleNullCondition(condition.operand);
}
- return false;
}
/**
- * Check for the passed class declaration for the
- * [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE] hint code.
- *
- * @param node the class declaration to check
- * @return `true` if and only if a hint code is generated on the passed node
- * See [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE].
+ * Produce a hint if the given [condition] could have a value of `null`.
*/
-// bool _checkForOverrideEqualsButNotHashCode(ClassDeclaration node) {
-// ClassElement classElement = node.element;
-// if (classElement == null) {
-// return false;
-// }
-// MethodElement equalsOperatorMethodElement =
-// classElement.getMethod(sc.TokenType.EQ_EQ.lexeme);
-// if (equalsOperatorMethodElement != null) {
-// PropertyAccessorElement hashCodeElement =
-// classElement.getGetter(_HASHCODE_GETTER_NAME);
-// if (hashCodeElement == null) {
-// _errorReporter.reportErrorForNode(
-// HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE,
-// node.name,
-// [classElement.displayName]);
-// return true;
-// }
-// }
-// return false;
-// }
+ void _checkForPossibleNullConditionInSimpleExpression(Expression condition) {
+ if (condition is MethodInvocation) {
+ if (condition.operator?.type == TokenType.QUESTION_PERIOD) {
+ _errorReporter.reportErrorForNode(
+ HintCode.NULL_AWARE_IN_CONDITION, condition);
+ }
+ } else if (condition is PropertyAccess) {
+ if (condition.operator?.type == TokenType.QUESTION_PERIOD) {
+ _errorReporter.reportErrorForNode(
+ HintCode.NULL_AWARE_IN_CONDITION, condition);
+ }
+ }
+ }
/**
* Check for the passed as expression for the [HintCode.UNNECESSARY_CAST] hint code.
@@ -697,34 +1012,116 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
}
/**
- * Check for situations where the result of a method or function is used, when it returns 'void'.
- *
- * TODO(jwren) Many other situations of use could be covered. We currently cover the cases var x =
- * m() and x = m(), but we could also cover cases such as m().x, m()[k], a + m(), f(m()), return
- * m().
+ * Generate a hint for `noSuchMethod` methods that do nothing except of
+ * calling another `noSuchMethod` that is not defined by `Object`.
*
- * @param node expression on the RHS of some assignment
* @return `true` if and only if a hint code is generated on the passed node
- * See [HintCode.USE_OF_VOID_RESULT].
+ * See [HintCode.UNNECESSARY_NO_SUCH_METHOD].
*/
- bool _checkForUseOfVoidResult(Expression expression) {
- if (expression == null || expression is! MethodInvocation) {
+ bool _checkForUnnecessaryNoSuchMethod(MethodDeclaration node) {
+ if (node.name.name != FunctionElement.NO_SUCH_METHOD_METHOD_NAME) {
return false;
}
- MethodInvocation methodInvocation = expression as MethodInvocation;
- if (identical(methodInvocation.staticType, VoidTypeImpl.instance)) {
- SimpleIdentifier methodName = methodInvocation.methodName;
- _errorReporter.reportErrorForNode(
- HintCode.USE_OF_VOID_RESULT, methodName, [methodName.name]);
- return true;
+ bool isNonObjectNoSuchMethodInvocation(Expression invocation) {
+ if (invocation is MethodInvocation &&
+ invocation.target is SuperExpression &&
+ invocation.argumentList.arguments.length == 1) {
+ SimpleIdentifier name = invocation.methodName;
+ if (name.name == FunctionElement.NO_SUCH_METHOD_METHOD_NAME) {
+ Element methodElement = name.staticElement;
+ Element classElement = methodElement?.enclosingElement;
+ return methodElement is MethodElement &&
+ classElement is ClassElement &&
+ !classElement.type.isObject;
+ }
+ }
+ return false;
}
- return false;
- }
- /**
- * Given a parenthesized expression, this returns the parent (or recursively grand-parent) of the
- * expression that is a parenthesized expression, but whose parent is not a parenthesized
- * expression.
+ FunctionBody body = node.body;
+ if (body is ExpressionFunctionBody) {
+ if (isNonObjectNoSuchMethodInvocation(body.expression)) {
+ _errorReporter.reportErrorForNode(
+ HintCode.UNNECESSARY_NO_SUCH_METHOD, node);
+ return true;
+ }
+ } else if (body is BlockFunctionBody) {
+ List<Statement> statements = body.block.statements;
+ if (statements.length == 1) {
+ Statement returnStatement = statements.first;
+ if (returnStatement is ReturnStatement &&
+ isNonObjectNoSuchMethodInvocation(returnStatement.expression)) {
+ _errorReporter.reportErrorForNode(
+ HintCode.UNNECESSARY_NO_SUCH_METHOD, node);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check for situations where the result of a method or function is used, when
+ * it returns 'void'.
+ *
+ * See [HintCode.USE_OF_VOID_RESULT].
+ */
+ void _checkForUseOfVoidResult(Expression expression) {
+ // TODO(jwren) Many other situations of use could be covered. We currently
+ // cover the cases var x = m() and x = m(), but we could also cover cases
+ // such as m().x, m()[k], a + m(), f(m()), return m().
+ if (expression is MethodInvocation) {
+ if (identical(expression.staticType, VoidTypeImpl.instance)) {
+ SimpleIdentifier methodName = expression.methodName;
+ _errorReporter.reportErrorForNode(
+ HintCode.USE_OF_VOID_RESULT, methodName, [methodName.name]);
+ }
+ }
+ }
+
+ /**
+ * Check for the passed class declaration for the
+ * [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE] hint code.
+ *
+ * @param node the class declaration to check
+ * @return `true` if and only if a hint code is generated on the passed node
+ * See [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE].
+ */
+// bool _checkForOverrideEqualsButNotHashCode(ClassDeclaration node) {
+// ClassElement classElement = node.element;
+// if (classElement == null) {
+// return false;
+// }
+// MethodElement equalsOperatorMethodElement =
+// classElement.getMethod(sc.TokenType.EQ_EQ.lexeme);
+// if (equalsOperatorMethodElement != null) {
+// PropertyAccessorElement hashCodeElement =
+// classElement.getGetter(_HASHCODE_GETTER_NAME);
+// if (hashCodeElement == null) {
+// _errorReporter.reportErrorForNode(
+// HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE,
+// node.name,
+// [classElement.displayName]);
+// return true;
+// }
+// }
+// return false;
+// }
+
+ bool _hasTypeOrSuperType(ClassElement element, InterfaceType type) {
+ if (element == null) {
+ return false;
+ }
+ ClassElement typeElement = type.element;
+ return element == typeElement ||
+ element.allSupertypes
+ .any((InterfaceType t) => t.element == typeElement);
+ }
+
+ /**
+ * Given a parenthesized expression, this returns the parent (or recursively grand-parent) of the
+ * expression that is a parenthesized expression, but whose parent is not a parenthesized
+ * expression.
*
* For example given the code `(((e)))`: `(e) -> (((e)))`.
*
@@ -734,103 +1131,76 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
*/
static ParenthesizedExpression _wrapParenthesizedExpression(
ParenthesizedExpression parenthesizedExpression) {
- if (parenthesizedExpression.parent is ParenthesizedExpression) {
- return _wrapParenthesizedExpression(
- parenthesizedExpression.parent as ParenthesizedExpression);
+ AstNode parent = parenthesizedExpression.parent;
+ if (parent is ParenthesizedExpression) {
+ return _wrapParenthesizedExpression(parent);
}
return parenthesizedExpression;
}
}
/**
- * Instances of the class `ClassScope` implement the scope defined by a class.
+ * Utilities for [LibraryElementImpl] building.
*/
-class ClassScope extends EnclosedScope {
+class BuildLibraryElementUtils {
/**
- * Initialize a newly created scope enclosed within another scope.
- *
- * @param enclosingScope the scope in which this scope is lexically enclosed
- * @param typeElement the element representing the type represented by this scope
+ * Look through all of the compilation units defined for the given [library],
+ * looking for getters and setters that are defined in different compilation
+ * units but that have the same names. If any are found, make sure that they
+ * have the same variable element.
*/
- ClassScope(Scope enclosingScope, ClassElement typeElement)
- : super(enclosingScope) {
- if (typeElement == null) {
- throw new IllegalArgumentException("class element cannot be null");
- }
- _defineMembers(typeElement);
- }
-
- @override
- AnalysisError getErrorForDuplicate(Element existing, Element duplicate) {
- if (existing is PropertyAccessorElement && duplicate is MethodElement) {
- if (existing.nameOffset < duplicate.nameOffset) {
- return new AnalysisError(
- duplicate.source,
- duplicate.nameOffset,
- duplicate.nameLength,
- CompileTimeErrorCode.METHOD_AND_GETTER_WITH_SAME_NAME,
- [existing.displayName]);
- } else {
- return new AnalysisError(
- existing.source,
- existing.nameOffset,
- existing.nameLength,
- CompileTimeErrorCode.GETTER_AND_METHOD_WITH_SAME_NAME,
- [existing.displayName]);
- }
+ static void patchTopLevelAccessors(LibraryElementImpl library) {
+ // Without parts getters/setters already share the same variable element.
+ List<CompilationUnitElement> parts = library.parts;
+ if (parts.isEmpty) {
+ return;
}
- return super.getErrorForDuplicate(existing, duplicate);
- }
-
- /**
- * Define the instance members defined by the class.
- *
- * @param typeElement the element representing the type represented by this scope
- */
- void _defineMembers(ClassElement typeElement) {
- for (PropertyAccessorElement accessor in typeElement.accessors) {
- define(accessor);
+ // Collect getters and setters.
+ HashMap<String, PropertyAccessorElement> getters =
+ new HashMap<String, PropertyAccessorElement>();
+ List<PropertyAccessorElement> setters = <PropertyAccessorElement>[];
+ _collectAccessors(getters, setters, library.definingCompilationUnit);
+ int partLength = parts.length;
+ for (int i = 0; i < partLength; i++) {
+ CompilationUnitElement unit = parts[i];
+ _collectAccessors(getters, setters, unit);
}
- for (MethodElement method in typeElement.methods) {
- define(method);
+ // Move every setter to the corresponding getter's variable (if exists).
+ int setterLength = setters.length;
+ for (int j = 0; j < setterLength; j++) {
+ PropertyAccessorElement setter = setters[j];
+ PropertyAccessorElement getter = getters[setter.displayName];
+ if (getter != null) {
+ TopLevelVariableElementImpl variable = getter.variable;
+ TopLevelVariableElementImpl setterVariable = setter.variable;
+ CompilationUnitElementImpl setterUnit = setterVariable.enclosingElement;
+ setterUnit.replaceTopLevelVariable(setterVariable, variable);
+ variable.setter = setter;
+ (setter as PropertyAccessorElementImpl).variable = variable;
+ }
}
}
-}
-/**
- * A `CompilationUnitBuilder` builds an element model for a single compilation
- * unit.
- */
-class CompilationUnitBuilder {
/**
- * Build the compilation unit element for the given [source] based on the
- * compilation [unit] associated with the source. Throw an AnalysisException
- * if the element could not be built. [librarySource] is the source for the
- * containing library.
+ * Add all of the non-synthetic [getters] and [setters] defined in the given
+ * [unit] that have no corresponding accessor to one of the given collections.
*/
- CompilationUnitElementImpl buildCompilationUnit(
- Source source, CompilationUnit unit, Source librarySource) {
- return PerformanceStatistics.resolve.makeCurrentWhile(() {
- if (unit == null) {
- return null;
+ static void _collectAccessors(Map<String, PropertyAccessorElement> getters,
+ List<PropertyAccessorElement> setters, CompilationUnitElement unit) {
+ List<PropertyAccessorElement> accessors = unit.accessors;
+ int length = accessors.length;
+ for (int i = 0; i < length; i++) {
+ PropertyAccessorElement accessor = accessors[i];
+ if (accessor.isGetter) {
+ if (!accessor.isSynthetic && accessor.correspondingSetter == null) {
+ getters[accessor.displayName] = accessor;
+ }
+ } else {
+ if (!accessor.isSynthetic && accessor.correspondingGetter == null) {
+ setters.add(accessor);
+ }
}
- ElementHolder holder = new ElementHolder();
- ElementBuilder builder = new ElementBuilder(holder);
- unit.accept(builder);
- CompilationUnitElementImpl element =
- new CompilationUnitElementImpl(source.shortName);
- element.accessors = holder.accessors;
- element.enums = holder.enums;
- element.functions = holder.functions;
- element.source = source;
- element.librarySource = librarySource;
- element.typeAliases = holder.typeAliases;
- element.types = holder.types;
- element.topLevelVariables = holder.topLevelVariables;
- unit.element = element;
- holder.validate();
- return element;
- });
+ }
}
}
@@ -906,9 +1276,8 @@ class ConstantVerifier extends RecursiveAstVisitor<Object> {
// check annotation creation
Element element = node.element;
if (element is ConstructorElement) {
- ConstructorElement constructorElement = element;
- // should 'const' constructor
- if (!constructorElement.isConst) {
+ // should be 'const' constructor
+ if (!element.isConst) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.NON_CONSTANT_ANNOTATION_CONSTRUCTOR, node);
return null;
@@ -976,8 +1345,10 @@ class ConstantVerifier extends RecursiveAstVisitor<Object> {
result =
_validate(element, CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT);
if (result != null) {
- _reportErrorIfFromDeferredLibrary(element,
- CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT_FROM_DEFERRED_LIBRARY);
+ _reportErrorIfFromDeferredLibrary(
+ element,
+ CompileTimeErrorCode
+ .NON_CONSTANT_LIST_ELEMENT_FROM_DEFERRED_LIBRARY);
}
}
}
@@ -1000,8 +1371,10 @@ class ConstantVerifier extends RecursiveAstVisitor<Object> {
DartObjectImpl valueResult = _validate(
valueExpression, CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE);
if (valueResult != null) {
- _reportErrorIfFromDeferredLibrary(valueExpression,
- CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE_FROM_DEFERRED_LIBRARY);
+ _reportErrorIfFromDeferredLibrary(
+ valueExpression,
+ CompileTimeErrorCode
+ .NON_CONSTANT_MAP_VALUE_FROM_DEFERRED_LIBRARY);
}
if (keyResult != null) {
_reportErrorIfFromDeferredLibrary(key,
@@ -1014,7 +1387,8 @@ class ConstantVerifier extends RecursiveAstVisitor<Object> {
DartType type = keyResult.type;
if (_implementsEqualsWhenNotAllowed(type)) {
_errorReporter.reportErrorForNode(
- CompileTimeErrorCode.CONST_MAP_KEY_EXPRESSION_TYPE_IMPLEMENTS_EQUALS,
+ CompileTimeErrorCode
+ .CONST_MAP_KEY_EXPRESSION_TYPE_IMPLEMENTS_EQUALS,
key,
[type.displayName]);
}
@@ -1041,9 +1415,10 @@ class ConstantVerifier extends RecursiveAstVisitor<Object> {
}
}
if (reportEqualKeys) {
- for (Expression key in invalidKeys) {
+ int length = invalidKeys.length;
+ for (int i = 0; i < length; i++) {
_errorReporter.reportErrorForNode(
- StaticWarningCode.EQUAL_KEYS_IN_MAP, key);
+ StaticWarningCode.EQUAL_KEYS_IN_MAP, invalidKeys[i]);
}
}
return null;
@@ -1066,13 +1441,14 @@ class ConstantVerifier extends RecursiveAstVisitor<Object> {
DartType firstType = null;
for (SwitchMember switchMember in switchMembers) {
if (switchMember is SwitchCase) {
- SwitchCase switchCase = switchMember;
- Expression expression = switchCase.expression;
+ Expression expression = switchMember.expression;
DartObjectImpl caseResult = _validate(
expression, CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION);
if (caseResult != null) {
- _reportErrorIfFromDeferredLibrary(expression,
- CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION_FROM_DEFERRED_LIBRARY);
+ _reportErrorIfFromDeferredLibrary(
+ expression,
+ CompileTimeErrorCode
+ .NON_CONSTANT_CASE_EXPRESSION_FROM_DEFERRED_LIBRARY);
DartObject value = caseResult;
if (firstType == null) {
firstType = value.type;
@@ -1112,8 +1488,10 @@ class ConstantVerifier extends RecursiveAstVisitor<Object> {
}
_reportErrors(result.errors,
CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE);
- _reportErrorIfFromDeferredLibrary(initializer,
- CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE_FROM_DEFERRED_LIBRARY);
+ _reportErrorIfFromDeferredLibrary(
+ initializer,
+ CompileTimeErrorCode
+ .CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE_FROM_DEFERRED_LIBRARY);
}
return null;
}
@@ -1153,18 +1531,17 @@ class ConstantVerifier extends RecursiveAstVisitor<Object> {
}
// prepare ClassElement
Element element = type.element;
- if (element is! ClassElement) {
- return false;
- }
- ClassElement classElement = element as ClassElement;
- // lookup for ==
- MethodElement method =
- classElement.lookUpConcreteMethod("==", _currentLibrary);
- if (method == null || method.enclosingElement.type.isObject) {
- return false;
+ if (element is ClassElement) {
+ // lookup for ==
+ MethodElement method =
+ element.lookUpConcreteMethod("==", _currentLibrary);
+ if (method == null || method.enclosingElement.type.isObject) {
+ return false;
+ }
+ // there is == that we don't like
+ return true;
}
- // there is == that we don't like
- return true;
+ return false;
}
/**
@@ -1193,7 +1570,9 @@ class ConstantVerifier extends RecursiveAstVisitor<Object> {
* @param errorCode the error code to be used
*/
void _reportErrors(List<AnalysisError> errors, ErrorCode errorCode) {
- for (AnalysisError data in errors) {
+ int length = errors.length;
+ for (int i = 0; i < length; i++) {
+ AnalysisError data = errors[i];
ErrorCode dataErrorCode = data.errorCode;
if (identical(dataErrorCode,
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION) ||
@@ -1206,10 +1585,14 @@ class ConstantVerifier extends RecursiveAstVisitor<Object> {
identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM) ||
identical(dataErrorCode,
CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT) ||
- identical(dataErrorCode,
- CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH) ||
- identical(dataErrorCode,
- CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH) ||
+ identical(
+ dataErrorCode,
+ CheckedModeCompileTimeErrorCode
+ .CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH) ||
+ identical(
+ dataErrorCode,
+ CheckedModeCompileTimeErrorCode
+ .CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH) ||
identical(dataErrorCode,
CheckedModeCompileTimeErrorCode.VARIABLE_TYPE_MISMATCH)) {
_errorReporter.reportError(data);
@@ -1247,11 +1630,10 @@ class ConstantVerifier extends RecursiveAstVisitor<Object> {
*/
void _validateConstantArguments(ArgumentList argumentList) {
for (Expression argument in argumentList.arguments) {
- if (argument is NamedExpression) {
- argument = (argument as NamedExpression).expression;
- }
+ Expression realArgument =
+ argument is NamedExpression ? argument.expression : argument;
_validate(
- argument, CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT);
+ realArgument, CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT);
}
}
@@ -1267,19 +1649,16 @@ class ConstantVerifier extends RecursiveAstVisitor<Object> {
NodeList<ConstructorInitializer> initializers = constructor.initializers;
for (ConstructorInitializer initializer in initializers) {
if (initializer is ConstructorFieldInitializer) {
- ConstructorFieldInitializer fieldInitializer = initializer;
_validateInitializerExpression(
- parameterElements, fieldInitializer.expression);
+ parameterElements, initializer.expression);
}
if (initializer is RedirectingConstructorInvocation) {
- RedirectingConstructorInvocation invocation = initializer;
_validateInitializerInvocationArguments(
- parameterElements, invocation.argumentList);
+ parameterElements, initializer.argumentList);
}
if (initializer is SuperConstructorInvocation) {
- SuperConstructorInvocation invocation = initializer;
_validateInitializerInvocationArguments(
- parameterElements, invocation.argumentList);
+ parameterElements, initializer.argumentList);
}
}
}
@@ -1296,8 +1675,7 @@ class ConstantVerifier extends RecursiveAstVisitor<Object> {
}
for (FormalParameter parameter in parameters.parameters) {
if (parameter is DefaultFormalParameter) {
- DefaultFormalParameter defaultParameter = parameter;
- Expression defaultValue = defaultParameter.defaultValue;
+ Expression defaultValue = parameter.defaultValue;
DartObjectImpl result;
if (defaultValue == null) {
result =
@@ -1306,8 +1684,10 @@ class ConstantVerifier extends RecursiveAstVisitor<Object> {
result = _validate(
defaultValue, CompileTimeErrorCode.NON_CONSTANT_DEFAULT_VALUE);
if (result != null) {
- _reportErrorIfFromDeferredLibrary(defaultValue,
- CompileTimeErrorCode.NON_CONSTANT_DEFAULT_VALUE_FROM_DEFERRED_LIBRARY);
+ _reportErrorIfFromDeferredLibrary(
+ defaultValue,
+ CompileTimeErrorCode
+ .NON_CONSTANT_DEFAULT_VALUE_FROM_DEFERRED_LIBRARY);
}
}
VariableElementImpl element = parameter.element as VariableElementImpl;
@@ -1328,29 +1708,27 @@ class ConstantVerifier extends RecursiveAstVisitor<Object> {
ClassDeclaration classDeclaration, ConstructorDeclaration errorSite) {
NodeList<ClassMember> members = classDeclaration.members;
for (ClassMember member in members) {
- if (member is FieldDeclaration) {
- FieldDeclaration fieldDeclaration = member;
- if (!fieldDeclaration.isStatic) {
- for (VariableDeclaration variableDeclaration
- in fieldDeclaration.fields.variables) {
- Expression initializer = variableDeclaration.initializer;
- if (initializer != null) {
- // Ignore any errors produced during validation--if the constant
- // can't be eavluated we'll just report a single error.
- AnalysisErrorListener errorListener =
- AnalysisErrorListener.NULL_LISTENER;
- ErrorReporter subErrorReporter =
- new ErrorReporter(errorListener, _errorReporter.source);
- DartObjectImpl result = initializer.accept(new ConstantVisitor(
- new ConstantEvaluationEngine(_typeProvider, declaredVariables,
- typeSystem: _typeSystem),
- subErrorReporter));
- if (result == null) {
- _errorReporter.reportErrorForNode(
- CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_FIELD_INITIALIZED_BY_NON_CONST,
- errorSite,
- [variableDeclaration.name.name]);
- }
+ if (member is FieldDeclaration && !member.isStatic) {
+ for (VariableDeclaration variableDeclaration
+ in member.fields.variables) {
+ Expression initializer = variableDeclaration.initializer;
+ if (initializer != null) {
+ // Ignore any errors produced during validation--if the constant
+ // can't be eavluated we'll just report a single error.
+ AnalysisErrorListener errorListener =
+ AnalysisErrorListener.NULL_LISTENER;
+ ErrorReporter subErrorReporter =
+ new ErrorReporter(errorListener, _errorReporter.source);
+ DartObjectImpl result = initializer.accept(new ConstantVisitor(
+ new ConstantEvaluationEngine(_typeProvider, declaredVariables,
+ typeSystem: _typeSystem),
+ subErrorReporter));
+ if (result == null) {
+ _errorReporter.reportErrorForNode(
+ CompileTimeErrorCode
+ .CONST_CONSTRUCTOR_WITH_FIELD_INITIALIZED_BY_NON_CONST,
+ errorSite,
+ [variableDeclaration.name.name]);
}
}
}
@@ -1377,8 +1755,10 @@ class ConstantVerifier extends RecursiveAstVisitor<Object> {
_reportErrors(errorListener.errors,
CompileTimeErrorCode.NON_CONSTANT_VALUE_IN_INITIALIZER);
if (result != null) {
- _reportErrorIfFromDeferredLibrary(expression,
- CompileTimeErrorCode.NON_CONSTANT_VALUE_IN_INITIALIZER_FROM_DEFERRED_LIBRARY);
+ _reportErrorIfFromDeferredLibrary(
+ expression,
+ CompileTimeErrorCode
+ .NON_CONSTANT_VALUE_IN_INITIALIZER_FROM_DEFERRED_LIBRARY);
}
}
@@ -1459,8 +1839,8 @@ class Dart2JSVerifier extends RecursiveAstVisitor<Object> {
bool _checkForIsDoubleHints(IsExpression node) {
TypeName typeName = node.type;
DartType type = typeName.type;
- if (type != null && type.element != null) {
- Element element = type.element;
+ Element element = type?.element;
+ if (element != null) {
String typeNameStr = element.name;
LibraryElement libraryElement = element.library;
// if (typeNameStr.equals(INT_TYPE_NAME) && libraryElement != null
@@ -1508,32 +1888,32 @@ class DeadCodeVerifier extends RecursiveAstVisitor<Object> {
* @param errorReporter the error reporter
*/
DeadCodeVerifier(this._errorReporter, {TypeSystem typeSystem})
- : this._typeSystem =
- (typeSystem != null) ? typeSystem : new TypeSystemImpl();
+ : this._typeSystem = typeSystem ?? new TypeSystemImpl();
@override
Object visitBinaryExpression(BinaryExpression node) {
- sc.Token operator = node.operator;
- bool isAmpAmp = operator.type == sc.TokenType.AMPERSAND_AMPERSAND;
- bool isBarBar = operator.type == sc.TokenType.BAR_BAR;
+ Token operator = node.operator;
+ bool isAmpAmp = operator.type == TokenType.AMPERSAND_AMPERSAND;
+ bool isBarBar = operator.type == TokenType.BAR_BAR;
if (isAmpAmp || isBarBar) {
Expression lhsCondition = node.leftOperand;
if (!_isDebugConstant(lhsCondition)) {
EvaluationResultImpl lhsResult = _getConstantBooleanValue(lhsCondition);
if (lhsResult != null) {
- if (lhsResult.value.toBoolValue() == true && isBarBar) {
+ bool value = lhsResult.value.toBoolValue();
+ if (value == true && isBarBar) {
// report error on else block: true || !e!
_errorReporter.reportErrorForNode(
HintCode.DEAD_CODE, node.rightOperand);
// only visit the LHS:
- _safelyVisit(lhsCondition);
+ lhsCondition?.accept(this);
return null;
- } else if (lhsResult.value.toBoolValue() == false && isAmpAmp) {
+ } else if (value == false && isAmpAmp) {
// report error on if block: false && !e!
_errorReporter.reportErrorForNode(
HintCode.DEAD_CODE, node.rightOperand);
// only visit the LHS:
- _safelyVisit(lhsCondition);
+ lhsCondition?.accept(this);
return null;
}
}
@@ -1547,13 +1927,13 @@ class DeadCodeVerifier extends RecursiveAstVisitor<Object> {
// // report error on else block: !e! || true
// errorReporter.reportError(HintCode.DEAD_CODE, node.getRightOperand());
// // only visit the RHS:
-// safelyVisit(rhsCondition);
+// rhsCondition?.accept(this);
// return null;
// } else if (rhsResult == ValidResult.RESULT_FALSE && isAmpAmp) {
// // report error on if block: !e! && false
// errorReporter.reportError(HintCode.DEAD_CODE, node.getRightOperand());
// // only visit the RHS:
-// safelyVisit(rhsCondition);
+// rhsCondition?.accept(this);
// return null;
// }
// }
@@ -1577,7 +1957,7 @@ class DeadCodeVerifier extends RecursiveAstVisitor<Object> {
@override
Object visitConditionalExpression(ConditionalExpression node) {
Expression conditionExpression = node.condition;
- _safelyVisit(conditionExpression);
+ conditionExpression?.accept(this);
if (!_isDebugConstant(conditionExpression)) {
EvaluationResultImpl result =
_getConstantBooleanValue(conditionExpression);
@@ -1586,13 +1966,13 @@ class DeadCodeVerifier extends RecursiveAstVisitor<Object> {
// report error on else block: true ? 1 : !2!
_errorReporter.reportErrorForNode(
HintCode.DEAD_CODE, node.elseExpression);
- _safelyVisit(node.thenExpression);
+ node.thenExpression?.accept(this);
return null;
} else {
// report error on if block: false ? !1! : 2
_errorReporter.reportErrorForNode(
HintCode.DEAD_CODE, node.thenExpression);
- _safelyVisit(node.elseExpression);
+ node.elseExpression?.accept(this);
return null;
}
}
@@ -1600,10 +1980,25 @@ class DeadCodeVerifier extends RecursiveAstVisitor<Object> {
return super.visitConditionalExpression(node);
}
+ @override
+ Object visitExportDirective(ExportDirective node) {
+ ExportElement exportElement = node.element;
+ if (exportElement != null) {
+ // The element is null when the URI is invalid
+ LibraryElement library = exportElement.exportedLibrary;
+ if (library != null && !library.isSynthetic) {
+ for (Combinator combinator in node.combinators) {
+ _checkCombinator(exportElement.exportedLibrary, combinator);
+ }
+ }
+ }
+ return super.visitExportDirective(node);
+ }
+
@override
Object visitIfStatement(IfStatement node) {
Expression conditionExpression = node.condition;
- _safelyVisit(conditionExpression);
+ conditionExpression?.accept(this);
if (!_isDebugConstant(conditionExpression)) {
EvaluationResultImpl result =
_getConstantBooleanValue(conditionExpression);
@@ -1614,14 +2009,14 @@ class DeadCodeVerifier extends RecursiveAstVisitor<Object> {
if (elseStatement != null) {
_errorReporter.reportErrorForNode(
HintCode.DEAD_CODE, elseStatement);
- _safelyVisit(node.thenStatement);
+ node.thenStatement?.accept(this);
return null;
}
} else {
// report error on if block: if (false) {!} else {}
_errorReporter.reportErrorForNode(
HintCode.DEAD_CODE, node.thenStatement);
- _safelyVisit(node.elseStatement);
+ node.elseStatement?.accept(this);
return null;
}
}
@@ -1629,22 +2024,38 @@ class DeadCodeVerifier extends RecursiveAstVisitor<Object> {
return super.visitIfStatement(node);
}
+ @override
+ Object visitImportDirective(ImportDirective node) {
+ ImportElement importElement = node.element;
+ if (importElement != null) {
+ // The element is null when the URI is invalid, but not when the URI is
+ // valid but refers to a non-existent file.
+ LibraryElement library = importElement.importedLibrary;
+ if (library != null && !library.isSynthetic) {
+ for (Combinator combinator in node.combinators) {
+ _checkCombinator(library, combinator);
+ }
+ }
+ }
+ return super.visitImportDirective(node);
+ }
+
@override
Object visitSwitchCase(SwitchCase node) {
- _checkForDeadStatementsInNodeList(node.statements);
+ _checkForDeadStatementsInNodeList(node.statements, allowMandated: true);
return super.visitSwitchCase(node);
}
@override
Object visitSwitchDefault(SwitchDefault node) {
- _checkForDeadStatementsInNodeList(node.statements);
+ _checkForDeadStatementsInNodeList(node.statements, allowMandated: true);
return super.visitSwitchDefault(node);
}
@override
Object visitTryStatement(TryStatement node) {
- _safelyVisit(node.body);
- _safelyVisit(node.finallyBlock);
+ node.body?.accept(this);
+ node.finallyBlock?.accept(this);
NodeList<CatchClause> catchClauses = node.catchClauses;
int numOfCatchClauses = catchClauses.length;
List<DartType> visitedTypes = new List<DartType>();
@@ -1653,15 +2064,14 @@ class DeadCodeVerifier extends RecursiveAstVisitor<Object> {
if (catchClause.onKeyword != null) {
// on-catch clause found, verify that the exception type is not a
// subtype of a previous on-catch exception type
- TypeName typeName = catchClause.exceptionType;
- if (typeName != null && typeName.type != null) {
- DartType currentType = typeName.type;
+ DartType currentType = catchClause.exceptionType?.type;
+ if (currentType != null) {
if (currentType.isObject) {
// Found catch clause clause that has Object as an exception type,
// this is equivalent to having a catch clause that doesn't have an
// exception type, visit the block, but generate an error on any
// following catch clauses (and don't visit them).
- _safelyVisit(catchClause);
+ catchClause?.accept(this);
if (i + 1 != numOfCatchClauses) {
// this catch clause is not the last in the try statement
CatchClause nextCatchClause = catchClauses[i + 1];
@@ -1673,7 +2083,9 @@ class DeadCodeVerifier extends RecursiveAstVisitor<Object> {
return null;
}
}
- for (DartType type in visitedTypes) {
+ int length = visitedTypes.length;
+ for (int j = 0; j < length; j++) {
+ DartType type = visitedTypes[j];
if (_typeSystem.isSubtypeOf(currentType, type)) {
CatchClause lastCatchClause = catchClauses[numOfCatchClauses - 1];
int offset = catchClause.offset;
@@ -1688,12 +2100,12 @@ class DeadCodeVerifier extends RecursiveAstVisitor<Object> {
}
visitedTypes.add(currentType);
}
- _safelyVisit(catchClause);
+ catchClause?.accept(this);
} else {
// Found catch clause clause that doesn't have an exception type,
// visit the block, but generate an error on any following catch clauses
// (and don't visit them).
- _safelyVisit(catchClause);
+ catchClause?.accept(this);
if (i + 1 != numOfCatchClauses) {
// this catch clause is not the last in the try statement
CatchClause nextCatchClause = catchClauses[i + 1];
@@ -1712,7 +2124,7 @@ class DeadCodeVerifier extends RecursiveAstVisitor<Object> {
@override
Object visitWhileStatement(WhileStatement node) {
Expression conditionExpression = node.condition;
- _safelyVisit(conditionExpression);
+ conditionExpression?.accept(this);
if (!_isDebugConstant(conditionExpression)) {
EvaluationResultImpl result =
_getConstantBooleanValue(conditionExpression);
@@ -1724,30 +2136,73 @@ class DeadCodeVerifier extends RecursiveAstVisitor<Object> {
}
}
}
- _safelyVisit(node.body);
+ node.body?.accept(this);
return null;
}
+ /**
+ * Resolve the names in the given [combinator] in the scope of the given
+ * [library].
+ */
+ void _checkCombinator(LibraryElement library, Combinator combinator) {
+ Namespace namespace =
+ new NamespaceBuilder().createExportNamespaceForLibrary(library);
+ NodeList<SimpleIdentifier> names;
+ ErrorCode hintCode;
+ if (combinator is HideCombinator) {
+ names = combinator.hiddenNames;
+ hintCode = HintCode.UNDEFINED_HIDDEN_NAME;
+ } else {
+ names = (combinator as ShowCombinator).shownNames;
+ hintCode = HintCode.UNDEFINED_SHOWN_NAME;
+ }
+ for (SimpleIdentifier name in names) {
+ String nameStr = name.name;
+ Element element = namespace.get(nameStr);
+ if (element == null) {
+ element = namespace.get("$nameStr=");
+ }
+ if (element == null) {
+ _errorReporter
+ .reportErrorForNode(hintCode, name, [library.identifier, nameStr]);
+ }
+ }
+ }
+
/**
* Given some [NodeList] of [Statement]s, from either a [Block] or
- * [SwitchMember], this loops through the list in reverse order searching for statements
- * after a return, unlabeled break or unlabeled continue statement to mark them as dead code.
+ * [SwitchMember], this loops through the list searching for dead statements.
*
* @param statements some ordered list of statements in a [Block] or [SwitchMember]
+ * @param allowMandated allow dead statements mandated by the language spec.
+ * This allows for a final break, continue, return, or throw statement
+ * at the end of a switch case, that are mandated by the language spec.
*/
- void _checkForDeadStatementsInNodeList(NodeList<Statement> statements) {
+ void _checkForDeadStatementsInNodeList(NodeList<Statement> statements,
+ {bool allowMandated: false}) {
+ bool statementExits(Statement statement) {
+ if (statement is BreakStatement) {
+ return statement.label == null;
+ } else if (statement is ContinueStatement) {
+ return statement.label == null;
+ }
+ return ExitDetector.exits(statement);
+ }
+
int size = statements.length;
for (int i = 0; i < size; i++) {
Statement currentStatement = statements[i];
- _safelyVisit(currentStatement);
- bool returnOrBreakingStatement = currentStatement is ReturnStatement ||
- (currentStatement is BreakStatement &&
- currentStatement.label == null) ||
- (currentStatement is ContinueStatement &&
- currentStatement.label == null);
- if (returnOrBreakingStatement && i != size - 1) {
+ currentStatement?.accept(this);
+ if (statementExits(currentStatement) && i != size - 1) {
Statement nextStatement = statements[i + 1];
Statement lastStatement = statements[size - 1];
+ // If mandated statements are allowed, and only the last statement is
+ // dead, and it's a BreakStatement, then assume it is a statement
+ // mandated by the language spec, there to avoid a
+ // CASE_BLOCK_NOT_TERMINATED error.
+ if (allowMandated && i == size - 2 && nextStatement is BreakStatement) {
+ return;
+ }
int offset = nextStatement.offset;
int length = lastStatement.end - offset;
_errorReporter.reportErrorForOffset(HintCode.DEAD_CODE, offset, length);
@@ -1799,11 +2254,9 @@ class DeadCodeVerifier extends RecursiveAstVisitor<Object> {
bool _isDebugConstant(Expression expression) {
Element element = null;
if (expression is Identifier) {
- Identifier identifier = expression;
- element = identifier.staticElement;
+ element = expression.staticElement;
} else if (expression is PropertyAccess) {
- PropertyAccess propertyAccess = expression;
- element = propertyAccess.propertyName.staticElement;
+ element = expression.propertyName.staticElement;
}
if (element is PropertyAccessorElement) {
PropertyInducingElement variable = element.variable;
@@ -1811,64 +2264,69 @@ class DeadCodeVerifier extends RecursiveAstVisitor<Object> {
}
return false;
}
-
- /**
- * If the given node is not `null`, visit this instance of the dead code verifier.
- *
- * @param node the node to be visited
- */
- void _safelyVisit(AstNode node) {
- if (node != null) {
- node.accept(this);
- }
- }
}
/**
- * Instances of the class `DeclarationResolver` are used to resolve declarations in an AST
- * structure to already built elements.
+ * A visitor that resolves declarations in an AST structure to already built
+ * elements.
+ *
+ * The resulting AST must have everything resolved that would have been resolved
+ * by a [CompilationUnitBuilder] (that is, must be a valid [RESOLVED_UNIT1]).
+ * This class must not assume that the [CompilationUnitElement] passed to it is
+ * any more complete than a [COMPILATION_UNIT_ELEMENT].
*/
-class DeclarationResolver extends RecursiveAstVisitor<Object> {
+class DeclarationResolver extends RecursiveAstVisitor<Object>
+ with ExistingElementResolver {
/**
- * The compilation unit containing the AST nodes being visited.
+ * The analysis context containing the sources to be analyzed.
+ */
+ AnalysisContext _context;
+
+ /**
+ * The elements that are reachable from the compilation unit element. When a
+ * compilation unit has been resolved, this set should be empty.
*/
- CompilationUnitElement _enclosingUnit;
+ Set<Element> _expectedElements;
/**
- * The function type alias containing the AST nodes being visited, or `null` if we are not
- * in the scope of a function type alias.
+ * The function type alias containing the AST nodes being visited, or `null`
+ * if we are not in the scope of a function type alias.
*/
FunctionTypeAliasElement _enclosingAlias;
/**
- * The class containing the AST nodes being visited, or `null` if we are not in the scope of
- * a class.
+ * The class containing the AST nodes being visited, or `null` if we are not
+ * in the scope of a class.
*/
ClassElement _enclosingClass;
/**
- * The method or function containing the AST nodes being visited, or `null` if we are not in
- * the scope of a method or function.
+ * The method or function containing the AST nodes being visited, or `null` if
+ * we are not in the scope of a method or function.
*/
ExecutableElement _enclosingExecutable;
/**
- * The parameter containing the AST nodes being visited, or `null` if we are not in the
- * scope of a parameter.
+ * The parameter containing the AST nodes being visited, or `null` if we are
+ * not in the scope of a parameter.
*/
ParameterElement _enclosingParameter;
/**
- * Resolve the declarations within the given compilation unit to the elements rooted at the given
- * element.
- *
- * @param unit the compilation unit to be resolved
- * @param element the root of the element model used to resolve the AST nodes
+ * Resolve the declarations within the given compilation [unit] to the
+ * elements rooted at the given [element]. Throw an [ElementMismatchException]
+ * if the element model and compilation unit do not match each other.
*/
void resolve(CompilationUnit unit, CompilationUnitElement element) {
+ _context = element.context;
+ ElementGatherer gatherer = new ElementGatherer();
+ element.accept(gatherer);
+ _expectedElements = gatherer.elements;
_enclosingUnit = element;
+ _expectedElements.remove(element);
unit.element = element;
unit.accept(this);
+ _validateResolution();
}
@override
@@ -1892,7 +2350,9 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
try {
SimpleIdentifier className = node.name;
_enclosingClass = _findIdentifier(_enclosingUnit.types, className);
- return super.visitClassDeclaration(node);
+ super.visitClassDeclaration(node);
+ _resolveMetadata(node, node.metadata, _enclosingClass);
+ return null;
} finally {
_enclosingClass = outerClass;
}
@@ -1904,7 +2364,9 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
try {
SimpleIdentifier className = node.name;
_enclosingClass = _findIdentifier(_enclosingUnit.types, className);
- return super.visitClassTypeAlias(node);
+ super.visitClassTypeAlias(node);
+ _resolveMetadata(node, node.metadata, _enclosingClass);
+ return null;
} finally {
_enclosingClass = outerClass;
}
@@ -1917,13 +2379,24 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
SimpleIdentifier constructorName = node.name;
if (constructorName == null) {
_enclosingExecutable = _enclosingClass.unnamedConstructor;
+ if (_enclosingExecutable == null) {
+ _mismatch('Could not find default constructor', node);
+ }
} else {
_enclosingExecutable =
_enclosingClass.getNamedConstructor(constructorName.name);
+ if (_enclosingExecutable == null) {
+ _mismatch(
+ 'Could not find constructor element with name "${constructorName.name}',
+ node);
+ }
constructorName.staticElement = _enclosingExecutable;
}
+ _expectedElements.remove(_enclosingExecutable);
node.element = _enclosingExecutable as ConstructorElement;
- return super.visitConstructorDeclaration(node);
+ super.visitConstructorDeclaration(node);
+ _resolveMetadata(node, node.metadata, _enclosingExecutable);
+ return null;
} finally {
_enclosingExecutable = outerExecutable;
}
@@ -1932,8 +2405,11 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
@override
Object visitDeclaredIdentifier(DeclaredIdentifier node) {
SimpleIdentifier variableName = node.identifier;
- _findIdentifier(_enclosingExecutable.localVariables, variableName);
- return super.visitDeclaredIdentifier(node);
+ Element element =
+ _findIdentifier(_enclosingExecutable.localVariables, variableName);
+ super.visitDeclaredIdentifier(node);
+ _resolveMetadata(node, node.metadata, element);
+ return null;
}
@override
@@ -1944,11 +2420,7 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
if (defaultValue != null) {
ExecutableElement outerExecutable = _enclosingExecutable;
try {
- if (element == null) {
- // TODO(brianwilkerson) Report this internal error.
- } else {
- _enclosingExecutable = element.initializer;
- }
+ _enclosingExecutable = element.initializer;
defaultValue.accept(this);
} finally {
_enclosingExecutable = outerExecutable;
@@ -1957,7 +2429,9 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
ParameterElement outerParameter = _enclosingParameter;
try {
_enclosingParameter = element;
- return super.visitDefaultFormalParameter(node);
+ super.visitDefaultFormalParameter(node);
+ _resolveMetadata(node, node.metadata, element);
+ return null;
} finally {
_enclosingParameter = outerParameter;
}
@@ -1971,21 +2445,24 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
for (EnumConstantDeclaration constant in node.constants) {
_findIdentifier(constants, constant.name);
}
- return super.visitEnumDeclaration(node);
+ super.visitEnumDeclaration(node);
+ _resolveMetadata(node, node.metadata, enclosingEnum);
+ return null;
}
@override
Object visitExportDirective(ExportDirective node) {
- String uri = _getStringValue(node.uri);
- if (uri != null) {
- LibraryElement library = _enclosingUnit.library;
- ExportElement exportElement = _findExport(
- library.exports,
- _enclosingUnit.context.sourceFactory
- .resolveUri(_enclosingUnit.source, uri));
- node.element = exportElement;
- }
- return super.visitExportDirective(node);
+ super.visitExportDirective(node);
+ _resolveAnnotations(
+ node, node.metadata, _enclosingUnit.getAnnotations(node.offset));
+ return null;
+ }
+
+ @override
+ Object visitFieldDeclaration(FieldDeclaration node) {
+ super.visitFieldDeclaration(node);
+ _resolveMetadata(node, node.metadata, node.fields.variables[0].element);
+ return null;
}
@override
@@ -1996,7 +2473,9 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
ParameterElement outerParameter = _enclosingParameter;
try {
_enclosingParameter = element;
- return super.visitFieldFormalParameter(node);
+ super.visitFieldFormalParameter(node);
+ _resolveMetadata(node, node.metadata, element);
+ return null;
} finally {
_enclosingParameter = outerParameter;
}
@@ -2010,7 +2489,7 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
ExecutableElement outerExecutable = _enclosingExecutable;
try {
SimpleIdentifier functionName = node.name;
- sc.Token property = node.propertyKeyword;
+ Token property = node.propertyKeyword;
if (property == null) {
if (_enclosingExecutable != null) {
_enclosingExecutable =
@@ -2020,16 +2499,32 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
_findIdentifier(_enclosingUnit.functions, functionName);
}
} else {
- PropertyAccessorElement accessor =
- _findIdentifier(_enclosingUnit.accessors, functionName);
- if ((property as sc.KeywordToken).keyword == sc.Keyword.SET) {
- accessor = accessor.variable.setter;
- functionName.staticElement = accessor;
+ if (_enclosingExecutable != null) {
+ _enclosingExecutable =
+ _findIdentifier(_enclosingExecutable.functions, functionName);
+ } else {
+ List<PropertyAccessorElement> accessors;
+ if (_enclosingClass != null) {
+ accessors = _enclosingClass.accessors;
+ } else {
+ accessors = _enclosingUnit.accessors;
+ }
+ PropertyAccessorElement accessor;
+ if (property.keyword == Keyword.GET) {
+ accessor = _findIdentifier(accessors, functionName);
+ } else if (property.keyword == Keyword.SET) {
+ accessor = _findWithNameAndOffset(accessors, functionName,
+ functionName.name + '=', functionName.offset);
+ _expectedElements.remove(accessor);
+ functionName.staticElement = accessor;
+ }
+ _enclosingExecutable = accessor;
}
- _enclosingExecutable = accessor;
}
node.functionExpression.element = _enclosingExecutable;
- return super.visitFunctionDeclaration(node);
+ super.visitFunctionDeclaration(node);
+ _resolveMetadata(node, node.metadata, _enclosingExecutable);
+ return null;
} finally {
_enclosingExecutable = outerExecutable;
}
@@ -2038,8 +2533,9 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
@override
Object visitFunctionExpression(FunctionExpression node) {
if (node.parent is! FunctionDeclaration) {
- FunctionElement element =
- _findAtOffset(_enclosingExecutable.functions, node.beginToken.offset);
+ FunctionElement element = _findAtOffset(
+ _enclosingExecutable.functions, node, node.beginToken.offset);
+ _expectedElements.remove(element);
node.element = element;
}
ExecutableElement outerExecutable = _enclosingExecutable;
@@ -2058,7 +2554,9 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
SimpleIdentifier aliasName = node.name;
_enclosingAlias =
_findIdentifier(_enclosingUnit.functionTypeAliases, aliasName);
- return super.visitFunctionTypeAlias(node);
+ super.visitFunctionTypeAlias(node);
+ _resolveMetadata(node, node.metadata, _enclosingAlias);
+ return null;
} finally {
_enclosingAlias = outerAlias;
}
@@ -2072,7 +2570,9 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
ParameterElement outerParameter = _enclosingParameter;
try {
_enclosingParameter = element;
- return super.visitFunctionTypedFormalParameter(node);
+ super.visitFunctionTypedFormalParameter(node);
+ _resolveMetadata(node, node.metadata, _enclosingParameter);
+ return null;
} finally {
_enclosingParameter = outerParameter;
}
@@ -2083,17 +2583,10 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
@override
Object visitImportDirective(ImportDirective node) {
- String uri = _getStringValue(node.uri);
- if (uri != null) {
- LibraryElement library = _enclosingUnit.library;
- ImportElement importElement = _findImport(
- library.imports,
- _enclosingUnit.context.sourceFactory
- .resolveUri(_enclosingUnit.source, uri),
- node.prefix);
- node.element = importElement;
- }
- return super.visitImportDirective(node);
+ super.visitImportDirective(node);
+ _resolveAnnotations(
+ node, node.metadata, _enclosingUnit.getAnnotations(node.offset));
+ return null;
}
@override
@@ -2107,31 +2600,44 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
@override
Object visitLibraryDirective(LibraryDirective node) {
- node.element = _enclosingUnit.library;
- return super.visitLibraryDirective(node);
+ super.visitLibraryDirective(node);
+ _resolveAnnotations(
+ node, node.metadata, _enclosingUnit.getAnnotations(node.offset));
+ return null;
}
@override
Object visitMethodDeclaration(MethodDeclaration node) {
ExecutableElement outerExecutable = _enclosingExecutable;
try {
- sc.Token property = node.propertyKeyword;
+ Token property = node.propertyKeyword;
SimpleIdentifier methodName = node.name;
String nameOfMethod = methodName.name;
if (property == null) {
- _enclosingExecutable = _findWithNameAndOffset(
- _enclosingClass.methods, nameOfMethod, methodName.offset);
+ String elementName = nameOfMethod == '-' &&
+ node.parameters != null &&
+ node.parameters.parameters.isEmpty
+ ? 'unary-'
+ : nameOfMethod;
+ _enclosingExecutable = _findWithNameAndOffset(_enclosingClass.methods,
+ methodName, elementName, methodName.offset);
+ _expectedElements.remove(_enclosingExecutable);
methodName.staticElement = _enclosingExecutable;
} else {
- PropertyAccessorElement accessor =
- _findIdentifier(_enclosingClass.accessors, methodName);
- if ((property as sc.KeywordToken).keyword == sc.Keyword.SET) {
- accessor = accessor.variable.setter;
+ PropertyAccessorElement accessor;
+ if (property.keyword == Keyword.GET) {
+ accessor = _findIdentifier(_enclosingClass.accessors, methodName);
+ } else if (property.keyword == Keyword.SET) {
+ accessor = _findWithNameAndOffset(_enclosingClass.accessors,
+ methodName, nameOfMethod + '=', methodName.offset);
+ _expectedElements.remove(accessor);
methodName.staticElement = accessor;
}
_enclosingExecutable = accessor;
}
- return super.visitMethodDeclaration(node);
+ super.visitMethodDeclaration(node);
+ _resolveMetadata(node, node.metadata, _enclosingExecutable);
+ return null;
} finally {
_enclosingExecutable = outerExecutable;
}
@@ -2139,13 +2645,10 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
@override
Object visitPartDirective(PartDirective node) {
- String uri = _getStringValue(node.uri);
- if (uri != null) {
- Source partSource = _enclosingUnit.context.sourceFactory
- .resolveUri(_enclosingUnit.source, uri);
- node.element = _findPart(_enclosingUnit.library.parts, partSource);
- }
- return super.visitPartDirective(node);
+ super.visitPartDirective(node);
+ _resolveAnnotations(
+ node, node.metadata, _enclosingUnit.getAnnotations(node.offset));
+ return null;
}
@override
@@ -2162,7 +2665,9 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
ParameterElement outerParameter = _enclosingParameter;
try {
_enclosingParameter = element;
- return super.visitSimpleFormalParameter(node);
+ super.visitSimpleFormalParameter(node);
+ _resolveMetadata(node, node.metadata, element);
+ return null;
} finally {
_enclosingParameter = outerParameter;
}
@@ -2188,15 +2693,40 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
return super.visitSwitchDefault(node);
}
+ @override
+ Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+ super.visitTopLevelVariableDeclaration(node);
+ _resolveMetadata(node, node.metadata, node.variables.variables[0].element);
+ return null;
+ }
+
@override
Object visitTypeParameter(TypeParameter node) {
SimpleIdentifier parameterName = node.name;
- if (_enclosingClass != null) {
- _findIdentifier(_enclosingClass.typeParameters, parameterName);
- } else if (_enclosingAlias != null) {
- _findIdentifier(_enclosingAlias.typeParameters, parameterName);
+ Element element = null;
+ if (_enclosingExecutable != null) {
+ element = _findIdentifier(
+ _enclosingExecutable.typeParameters, parameterName,
+ required: false);
}
- return super.visitTypeParameter(node);
+ if (element == null) {
+ if (_enclosingClass != null) {
+ element =
+ _findIdentifier(_enclosingClass.typeParameters, parameterName);
+ } else if (_enclosingAlias != null) {
+ element =
+ _findIdentifier(_enclosingAlias.typeParameters, parameterName);
+ }
+ }
+ if (element == null) {
+ String name = parameterName.name;
+ int offset = parameterName.offset;
+ _mismatch(
+ 'Could not find type parameter with name "$name" at $offset', node);
+ }
+ super.visitTypeParameter(node);
+ _resolveMetadata(node, node.metadata, element);
+ return null;
}
@override
@@ -2204,11 +2734,13 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
VariableElement element = null;
SimpleIdentifier variableName = node.name;
if (_enclosingExecutable != null) {
- element =
- _findIdentifier(_enclosingExecutable.localVariables, variableName);
+ element = _findIdentifier(
+ _enclosingExecutable.localVariables, variableName,
+ required: false);
}
if (element == null && _enclosingClass != null) {
- element = _findIdentifier(_enclosingClass.fields, variableName);
+ element = _findIdentifier(_enclosingClass.fields, variableName,
+ required: false);
}
if (element == null && _enclosingUnit != null) {
element = _findIdentifier(_enclosingUnit.topLevelVariables, variableName);
@@ -2217,11 +2749,7 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
if (initializer != null) {
ExecutableElement outerExecutable = _enclosingExecutable;
try {
- if (element == null) {
- // TODO(brianwilkerson) Report this internal error.
- } else {
- _enclosingExecutable = element.initializer;
- }
+ _enclosingExecutable = element.initializer;
return super.visitVariableDeclaration(node);
} finally {
_enclosingExecutable = outerExecutable;
@@ -2230,123 +2758,87 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
return super.visitVariableDeclaration(node);
}
- /**
- * Return the element in the given array of elements that was created for the declaration at the
- * given offset. This method should only be used when there is no name
- *
- * @param elements the elements of the appropriate kind that exist in the current context
- * @param offset the offset of the name of the element to be returned
- * @return the element at the given offset
- */
- Element _findAtOffset(List<Element> elements, int offset) =>
- _findWithNameAndOffset(elements, "", offset);
-
- /**
- * Return the export element from the given array whose library has the given source, or
- * `null` if there is no such export.
- *
- * @param exports the export elements being searched
- * @param source the source of the library associated with the export element to being searched
- * for
- * @return the export element whose library has the given source
- */
- ExportElement _findExport(List<ExportElement> exports, Source source) {
- for (ExportElement export in exports) {
- if (export.exportedLibrary.source == source) {
- return export;
- }
+ @override
+ Object visitVariableDeclarationList(VariableDeclarationList node) {
+ super.visitVariableDeclarationList(node);
+ if (node.parent is! FieldDeclaration &&
+ node.parent is! TopLevelVariableDeclaration) {
+ _resolveMetadata(node, node.metadata, node.variables[0].element);
}
return null;
}
/**
- * Return the element in the given array of elements that was created for the declaration with the
- * given name.
+ * Return the element in the given list of [elements] that was created for the
+ * declaration at the given [offset]. Throw an [ElementMismatchException] if
+ * an element at that offset cannot be found.
*
- * @param elements the elements of the appropriate kind that exist in the current context
- * @param identifier the name node in the declaration of the element to be returned
- * @return the element created for the declaration with the given name
+ * This method should only be used when there is no name associated with the
+ * node.
*/
- Element _findIdentifier(List<Element> elements, SimpleIdentifier identifier) {
- Element element =
- _findWithNameAndOffset(elements, identifier.name, identifier.offset);
+ Element _findAtOffset(List<Element> elements, AstNode node, int offset) =>
+ _findWithNameAndOffset(elements, node, '', offset);
+
+ /**
+ * Return the element in the given list of [elements] that was created for the
+ * declaration with the given [identifier]. As a side-effect, associate the
+ * returned element with the identifier. Throw an [ElementMismatchException]
+ * if an element corresponding to the identifier cannot be found unless
+ * [required] is `false`, in which case return `null`.
+ */
+ Element _findIdentifier(List<Element> elements, SimpleIdentifier identifier,
+ {bool required: true}) {
+ Element element = _findWithNameAndOffset(
+ elements, identifier, identifier.name, identifier.offset,
+ required: required);
+ _expectedElements.remove(element);
identifier.staticElement = element;
return element;
}
/**
- * Return the import element from the given array whose library has the given source and that has
- * the given prefix, or `null` if there is no such import.
- *
- * @param imports the import elements being searched
- * @param source the source of the library associated with the import element to being searched
- * for
- * @param prefix the prefix with which the library was imported
- * @return the import element whose library has the given source and prefix
+ * Return the element in the given list of [elements] that was created for the
+ * declaration with the given [name] at the given [offset]. Throw an
+ * [ElementMismatchException] if an element corresponding to the identifier
+ * cannot be found unless [required] is `false`, in which case return `null`.
*/
- ImportElement _findImport(
- List<ImportElement> imports, Source source, SimpleIdentifier prefix) {
- for (ImportElement element in imports) {
- if (element.importedLibrary.source == source) {
- PrefixElement prefixElement = element.prefix;
- if (prefix == null) {
- if (prefixElement == null) {
- return element;
- }
- } else {
- if (prefixElement != null &&
- prefix.name == prefixElement.displayName) {
- return element;
- }
- }
+ Element _findWithNameAndOffset(
+ List<Element> elements, AstNode node, String name, int offset,
+ {bool required: true}) {
+ int length = elements.length;
+ for (int i = 0; i < length; i++) {
+ Element element = elements[i];
+ if (element.nameOffset == offset && element.name == name) {
+ return element;
}
}
- return null;
- }
-
- /**
- * Return the element for the part with the given source, or `null` if there is no element
- * for the given source.
- *
- * @param parts the elements for the parts
- * @param partSource the source for the part whose element is to be returned
- * @return the element for the part with the given source
- */
- CompilationUnitElement _findPart(
- List<CompilationUnitElement> parts, Source partSource) {
- for (CompilationUnitElement part in parts) {
- if (part.source == partSource) {
- return part;
- }
+ if (!required) {
+ return null;
}
- return null;
- }
-
- /**
- * Return the element in the given array of elements that was created for the declaration with the
- * given name at the given offset.
- *
- * @param elements the elements of the appropriate kind that exist in the current context
- * @param name the name of the element to be returned
- * @param offset the offset of the name of the element to be returned
- * @return the element with the given name and offset
- */
- Element _findWithNameAndOffset(
- List<Element> elements, String name, int offset) {
- for (Element element in elements) {
- if (element.nameOffset == offset && element.displayName == name) {
- return element;
+ for (int i = 0; i < length; i++) {
+ Element element = elements[i];
+ if (element.name == name) {
+ _mismatch(
+ 'Found element with name "$name" at ${element.nameOffset}, '
+ 'but expected offset of $offset',
+ node);
+ }
+ if (element.nameOffset == offset) {
+ _mismatch(
+ 'Found element with name "${element.name}" at $offset, '
+ 'but expected element with name "$name"',
+ node);
}
}
- return null;
+ _mismatch('Could not find element with name "$name" at $offset', node);
+ return null; // Never reached
}
/**
- * Search the most closely enclosing list of parameters for a parameter with the given name.
- *
- * @param node the node defining the parameter with the given name
- * @param parameterName the name of the parameter being searched for
- * @return the element representing the parameter with that name
+ * Search the most closely enclosing list of parameter elements for a
+ * parameter, defined by the given [node], with the given [parameterName].
+ * Return the element that was found, or throw an [ElementMismatchException]
+ * if an element corresponding to the identifier cannot be found.
*/
ParameterElement _getElementForParameter(
FormalParameter node, SimpleIdentifier parameterName) {
@@ -2360,1327 +2852,375 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
if (parameters == null && _enclosingAlias != null) {
parameters = _enclosingAlias.parameters;
}
- ParameterElement element =
- parameters == null ? null : _findIdentifier(parameters, parameterName);
- if (element == null) {
+ if (parameters == null) {
StringBuffer buffer = new StringBuffer();
- buffer.writeln("Invalid state found in the Analysis Engine:");
+ buffer.writeln('Could not find parameter in enclosing scope');
buffer.writeln(
- "DeclarationResolver.getElementForParameter() is visiting a parameter that does not appear to be in a method or function.");
- buffer.writeln("Ancestors:");
- AstNode parent = node.parent;
- while (parent != null) {
- buffer.writeln(parent.runtimeType.toString());
- buffer.writeln("---------");
- parent = parent.parent;
- }
- AnalysisEngine.instance.logger.logError(buffer.toString(),
- new CaughtException(new AnalysisException(), null));
+ '(_enclosingParameter == null) == ${_enclosingParameter == null}');
+ buffer.writeln(
+ '(_enclosingExecutable == null) == ${_enclosingExecutable == null}');
+ buffer.writeln('(_enclosingAlias == null) == ${_enclosingAlias == null}');
+ _mismatch(buffer.toString(), parameterName);
}
- return element;
+ return _findIdentifier(parameters, parameterName);
}
/**
- * Return the value of the given string literal, or `null` if the string is not a constant
- * string without any string interpolation.
- *
- * @param literal the string literal whose value is to be returned
- * @return the value of the given string literal
+ * Associate each of the annotation [nodes] with the corresponding
+ * [ElementAnnotation] in [annotations]. If there is a problem, report it
+ * against the given [parent] node.
*/
- String _getStringValue(StringLiteral literal) {
- if (literal is StringInterpolation) {
- return null;
+ void _resolveAnnotations(AstNode parent, NodeList<Annotation> nodes,
+ List<ElementAnnotation> annotations) {
+ int nodeCount = nodes.length;
+ if (nodeCount != annotations.length) {
+ _mismatch(
+ 'Found $nodeCount annotation nodes and '
+ '${annotations.length} element annotations',
+ parent);
+ }
+ for (int i = 0; i < nodeCount; i++) {
+ nodes[i].elementAnnotation = annotations[i];
}
- return literal.stringValue;
}
-}
-
-/**
- * Instances of the class `ElementBuilder` traverse an AST structure and build the element
- * model representing the AST structure.
- */
-class ElementBuilder extends RecursiveAstVisitor<Object> {
- /**
- * The element holder associated with the element that is currently being built.
- */
- ElementHolder _currentHolder;
-
- /**
- * A flag indicating whether a variable declaration is in the context of a field declaration.
- */
- bool _inFieldContext = false;
-
- /**
- * A flag indicating whether a variable declaration is within the body of a method or function.
- */
- bool _inFunction = false;
/**
- * A flag indicating whether the class currently being visited can be used as a mixin.
- */
- bool _isValidMixin = false;
-
- /**
- * A collection holding the function types defined in a class that need to have their type
- * arguments set to the types of the type parameters for the class, or `null` if we are not
- * currently processing nodes within a class.
- */
- List<FunctionTypeImpl> _functionTypesToFix = null;
-
- /**
- * A table mapping field names to field elements for the fields defined in the current class, or
- * `null` if we are not in the scope of a class.
+ * If [element] is not `null`, associate each of the annotation [nodes] with
+ * the corresponding [ElementAnnotation] in [element.metadata]. If there is a
+ * problem, report it against the given [parent] node.
+ *
+ * If [element] is `null`, do nothing--this allows us to be robust in the
+ * case where we are operating on an element model that hasn't been fully
+ * built.
*/
- HashMap<String, FieldElement> _fieldMap;
+ void _resolveMetadata(
+ AstNode parent, NodeList<Annotation> nodes, Element element) {
+ if (element != null) {
+ _resolveAnnotations(parent, nodes, element.metadata);
+ }
+ }
/**
- * Initialize a newly created element builder to build the elements for a compilation unit.
- *
- * @param initialHolder the element holder associated with the compilation unit being built
+ * Throw an exception if there are non-synthetic elements in the element model
+ * that were not associated with an AST node.
*/
- ElementBuilder(ElementHolder initialHolder) {
- _currentHolder = initialHolder;
+ void _validateResolution() {
+ if (_expectedElements.isNotEmpty) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.write(_expectedElements.length);
+ buffer.writeln(' unmatched elements found:');
+ for (Element element in _expectedElements) {
+ buffer.write(' ');
+ buffer.writeln(element);
+ }
+ throw new ElementMismatchException(buffer.toString());
+ }
}
+}
+
+/**
+ * A visitor that resolves directives in an AST structure to already built
+ * elements.
+ *
+ * The resulting AST must have everything resolved that would have been resolved
+ * by a [DirectiveElementBuilder].
+ */
+class DirectiveResolver extends SimpleAstVisitor {
+ LibraryElement _enclosingLibrary;
@override
- Object visitBlock(Block node) {
- bool wasInField = _inFieldContext;
- _inFieldContext = false;
- try {
- node.visitChildren(this);
- } finally {
- _inFieldContext = wasInField;
+ void visitCompilationUnit(CompilationUnit node) {
+ _enclosingLibrary = node.element.library;
+ for (Directive directive in node.directives) {
+ directive.accept(this);
}
- return null;
}
@override
- Object visitCatchClause(CatchClause node) {
- SimpleIdentifier exceptionParameter = node.exceptionParameter;
- if (exceptionParameter != null) {
- // exception
- LocalVariableElementImpl exception =
- new LocalVariableElementImpl.forNode(exceptionParameter);
- if (node.exceptionType == null) {
- exception.hasImplicitType = true;
- }
- _currentHolder.addLocalVariable(exception);
- exceptionParameter.staticElement = exception;
- // stack trace
- SimpleIdentifier stackTraceParameter = node.stackTraceParameter;
- if (stackTraceParameter != null) {
- LocalVariableElementImpl stackTrace =
- new LocalVariableElementImpl.forNode(stackTraceParameter);
- _currentHolder.addLocalVariable(stackTrace);
- stackTraceParameter.staticElement = stackTrace;
+ void visitExportDirective(ExportDirective node) {
+ int nodeOffset = node.offset;
+ node.element = null;
+ for (ExportElement element in _enclosingLibrary.exports) {
+ if (element.nameOffset == nodeOffset) {
+ node.element = element;
+ break;
}
}
- return super.visitCatchClause(node);
}
@override
- Object visitClassDeclaration(ClassDeclaration node) {
- ElementHolder holder = new ElementHolder();
- _isValidMixin = true;
- _functionTypesToFix = new List<FunctionTypeImpl>();
- //
- // Process field declarations before constructors and methods so that field
- // formal parameters can be correctly resolved to their fields.
- //
- ElementHolder previousHolder = _currentHolder;
- _currentHolder = holder;
- try {
- List<ClassMember> nonFields = new List<ClassMember>();
- node.visitChildren(
- new _ElementBuilder_visitClassDeclaration(this, nonFields));
- _buildFieldMap(holder.fieldsWithoutFlushing);
- int count = nonFields.length;
- for (int i = 0; i < count; i++) {
- nonFields[i].accept(this);
+ void visitImportDirective(ImportDirective node) {
+ int nodeOffset = node.offset;
+ node.element = null;
+ for (ImportElement element in _enclosingLibrary.imports) {
+ if (element.nameOffset == nodeOffset) {
+ node.element = element;
+ break;
}
- } finally {
- _currentHolder = previousHolder;
- }
- SimpleIdentifier className = node.name;
- ClassElementImpl element = new ClassElementImpl.forNode(className);
- List<TypeParameterElement> typeParameters = holder.typeParameters;
- List<DartType> typeArguments = _createTypeParameterTypes(typeParameters);
- InterfaceTypeImpl interfaceType = new InterfaceTypeImpl(element);
- interfaceType.typeArguments = typeArguments;
- element.type = interfaceType;
- List<ConstructorElement> constructors = holder.constructors;
- if (constructors.length == 0) {
- //
- // Create the default constructor.
- //
- constructors = _createDefaultConstructors(interfaceType);
- }
- _setDocRange(element, node);
- element.abstract = node.isAbstract;
- element.accessors = holder.accessors;
- element.constructors = constructors;
- element.fields = holder.fields;
- element.methods = holder.methods;
- element.typeParameters = typeParameters;
- element.validMixin = _isValidMixin;
- int functionTypeCount = _functionTypesToFix.length;
- for (int i = 0; i < functionTypeCount; i++) {
- _functionTypesToFix[i].typeArguments = typeArguments;
- }
- _functionTypesToFix = null;
- _currentHolder.addType(element);
- className.staticElement = element;
- _fieldMap = null;
- holder.validate();
- return null;
- }
-
- /**
- * Implementation of this method should be synchronized with
- * [visitClassDeclaration].
- */
- void visitClassDeclarationIncrementally(ClassDeclaration node) {
- //
- // Process field declarations before constructors and methods so that field
- // formal parameters can be correctly resolved to their fields.
- //
- ClassElement classElement = node.element;
- _buildFieldMap(classElement.fields);
+ }
}
@override
- Object visitClassTypeAlias(ClassTypeAlias node) {
- ElementHolder holder = new ElementHolder();
- _functionTypesToFix = new List<FunctionTypeImpl>();
- _visitChildren(holder, node);
- SimpleIdentifier className = node.name;
- ClassElementImpl element = new ClassElementImpl.forNode(className);
- element.abstract = node.abstractKeyword != null;
- element.mixinApplication = true;
- List<TypeParameterElement> typeParameters = holder.typeParameters;
- element.typeParameters = typeParameters;
- List<DartType> typeArguments = _createTypeParameterTypes(typeParameters);
- InterfaceTypeImpl interfaceType = new InterfaceTypeImpl(element);
- interfaceType.typeArguments = typeArguments;
- element.type = interfaceType;
- // set default constructor
- for (FunctionTypeImpl functionType in _functionTypesToFix) {
- functionType.typeArguments = typeArguments;
- }
- _functionTypesToFix = null;
- _currentHolder.addType(element);
- className.staticElement = element;
- holder.validate();
- return null;
+ void visitLibraryDirective(LibraryDirective node) {
+ node.element = _enclosingLibrary;
}
+}
- @override
- Object visitConstructorDeclaration(ConstructorDeclaration node) {
- _isValidMixin = false;
- ElementHolder holder = new ElementHolder();
- bool wasInFunction = _inFunction;
- _inFunction = true;
- try {
- _visitChildren(holder, node);
- } finally {
- _inFunction = wasInFunction;
- }
- FunctionBody body = node.body;
- SimpleIdentifier constructorName = node.name;
- ConstructorElementImpl element =
- new ConstructorElementImpl.forNode(constructorName);
- _setDocRange(element, node);
- if (node.externalKeyword != null) {
- element.external = true;
- }
- if (node.factoryKeyword != null) {
- element.factory = true;
- }
- element.functions = holder.functions;
- element.labels = holder.labels;
- element.localVariables = holder.localVariables;
- element.parameters = holder.parameters;
- element.const2 = node.constKeyword != null;
- if (body.isAsynchronous) {
- element.asynchronous = true;
- }
- if (body.isGenerator) {
- element.generator = true;
- }
- _currentHolder.addConstructor(element);
- node.element = element;
- if (constructorName == null) {
- Identifier returnType = node.returnType;
- if (returnType != null) {
- element.nameOffset = returnType.offset;
- element.nameEnd = returnType.end;
- }
- } else {
- constructorName.staticElement = element;
- element.periodOffset = node.period.offset;
- element.nameEnd = constructorName.end;
+/**
+ * Instances of the class `ElementHolder` hold on to elements created while traversing an AST
+ * structure so that they can be accessed when creating their enclosing element.
+ */
+class ElementHolder {
+ List<PropertyAccessorElement> _accessors;
+
+ List<ConstructorElement> _constructors;
+
+ List<ClassElement> _enums;
+
+ List<FieldElement> _fields;
+
+ List<FunctionElement> _functions;
+
+ List<LabelElement> _labels;
+
+ List<LocalVariableElement> _localVariables;
+
+ List<MethodElement> _methods;
+
+ List<ParameterElement> _parameters;
+
+ List<TopLevelVariableElement> _topLevelVariables;
+
+ List<ClassElement> _types;
+
+ List<FunctionTypeAliasElement> _typeAliases;
+
+ List<TypeParameterElement> _typeParameters;
+
+ List<PropertyAccessorElement> get accessors {
+ if (_accessors == null) {
+ return PropertyAccessorElement.EMPTY_LIST;
}
- holder.validate();
- return null;
+ List<PropertyAccessorElement> result = _accessors;
+ _accessors = null;
+ return result;
}
- @override
- Object visitDeclaredIdentifier(DeclaredIdentifier node) {
- SimpleIdentifier variableName = node.identifier;
- LocalVariableElementImpl element =
- new LocalVariableElementImpl.forNode(variableName);
- ForEachStatement statement = node.parent as ForEachStatement;
- int declarationEnd = node.offset + node.length;
- int statementEnd = statement.offset + statement.length;
- element.setVisibleRange(declarationEnd, statementEnd - declarationEnd - 1);
- element.const3 = node.isConst;
- element.final2 = node.isFinal;
- if (node.type == null) {
- element.hasImplicitType = true;
+ List<ConstructorElement> get constructors {
+ if (_constructors == null) {
+ return ConstructorElement.EMPTY_LIST;
}
- _currentHolder.addLocalVariable(element);
- variableName.staticElement = element;
- return super.visitDeclaredIdentifier(node);
+ List<ConstructorElement> result = _constructors;
+ _constructors = null;
+ return result;
}
- @override
- Object visitDefaultFormalParameter(DefaultFormalParameter node) {
- ElementHolder holder = new ElementHolder();
- NormalFormalParameter normalParameter = node.parameter;
- SimpleIdentifier parameterName = normalParameter.identifier;
- ParameterElementImpl parameter;
- if (normalParameter is FieldFormalParameter) {
- parameter = new DefaultFieldFormalParameterElementImpl(parameterName);
- FieldElement field =
- _fieldMap == null ? null : _fieldMap[parameterName.name];
- if (field != null) {
- (parameter as DefaultFieldFormalParameterElementImpl).field = field;
- }
- } else {
- parameter = new DefaultParameterElementImpl(parameterName);
+ List<ClassElement> get enums {
+ if (_enums == null) {
+ return ClassElement.EMPTY_LIST;
}
- parameter.const3 = node.isConst;
- parameter.final2 = node.isFinal;
- parameter.parameterKind = node.kind;
- // set initializer, default value range
- Expression defaultValue = node.defaultValue;
- if (defaultValue != null) {
- _visit(holder, defaultValue);
- FunctionElementImpl initializer =
- new FunctionElementImpl.forOffset(defaultValue.beginToken.offset);
- initializer.functions = holder.functions;
- initializer.labels = holder.labels;
- initializer.localVariables = holder.localVariables;
- initializer.parameters = holder.parameters;
- initializer.synthetic = true;
- parameter.initializer = initializer;
- parameter.defaultValueCode = defaultValue.toSource();
- }
- // visible range
- _setParameterVisibleRange(node, parameter);
- if (normalParameter is SimpleFormalParameter &&
- normalParameter.type == null) {
- parameter.hasImplicitType = true;
- }
- _currentHolder.addParameter(parameter);
- parameterName.staticElement = parameter;
- normalParameter.accept(this);
- holder.validate();
- return null;
+ List<ClassElement> result = _enums;
+ _enums = null;
+ return result;
}
- @override
- Object visitEnumDeclaration(EnumDeclaration node) {
- SimpleIdentifier enumName = node.name;
- ClassElementImpl enumElement = new ClassElementImpl.forNode(enumName);
- enumElement.enum2 = true;
- _setDocRange(enumElement, node);
- InterfaceTypeImpl enumType = new InterfaceTypeImpl(enumElement);
- enumElement.type = enumType;
- // The equivalent code for enums in the spec shows a single constructor,
- // but that constructor is not callable (since it is a compile-time error
- // to subclass, mix-in, implement, or explicitly instantiate an enum). So
- // we represent this as having no constructors.
- enumElement.constructors = ConstructorElement.EMPTY_LIST;
- _currentHolder.addEnum(enumElement);
- enumName.staticElement = enumElement;
- return super.visitEnumDeclaration(node);
+ List<FieldElement> get fields {
+ if (_fields == null) {
+ return FieldElement.EMPTY_LIST;
+ }
+ List<FieldElement> result = _fields;
+ _fields = null;
+ return result;
}
- @override
- Object visitFieldDeclaration(FieldDeclaration node) {
- bool wasInField = _inFieldContext;
- _inFieldContext = true;
- try {
- node.visitChildren(this);
- } finally {
- _inFieldContext = wasInField;
+ List<FieldElement> get fieldsWithoutFlushing {
+ if (_fields == null) {
+ return FieldElement.EMPTY_LIST;
}
- return null;
+ List<FieldElement> result = _fields;
+ return result;
}
- @override
- Object visitFieldFormalParameter(FieldFormalParameter node) {
- if (node.parent is! DefaultFormalParameter) {
- SimpleIdentifier parameterName = node.identifier;
- FieldElement field =
- _fieldMap == null ? null : _fieldMap[parameterName.name];
- FieldFormalParameterElementImpl parameter =
- new FieldFormalParameterElementImpl(parameterName);
- parameter.const3 = node.isConst;
- parameter.final2 = node.isFinal;
- parameter.parameterKind = node.kind;
- if (field != null) {
- parameter.field = field;
- }
- _currentHolder.addParameter(parameter);
- parameterName.staticElement = parameter;
+ List<FunctionElement> get functions {
+ if (_functions == null) {
+ return FunctionElement.EMPTY_LIST;
}
- //
- // The children of this parameter include any parameters defined on the type
- // of this parameter.
- //
- ElementHolder holder = new ElementHolder();
- _visitChildren(holder, node);
- ParameterElementImpl element = node.element;
- element.parameters = holder.parameters;
- element.typeParameters = holder.typeParameters;
- holder.validate();
- return null;
+ List<FunctionElement> result = _functions;
+ _functions = null;
+ return result;
}
- @override
- Object visitFunctionDeclaration(FunctionDeclaration node) {
- FunctionExpression expression = node.functionExpression;
- if (expression != null) {
- ElementHolder holder = new ElementHolder();
- bool wasInFunction = _inFunction;
- _inFunction = true;
- try {
- _visitChildren(holder, node);
- } finally {
- _inFunction = wasInFunction;
- }
- FunctionBody body = expression.body;
- sc.Token property = node.propertyKeyword;
- if (property == null || _inFunction) {
- SimpleIdentifier functionName = node.name;
- FunctionElementImpl element =
- new FunctionElementImpl.forNode(functionName);
- _setDocRange(element, node);
- if (node.externalKeyword != null) {
- element.external = true;
- }
- element.functions = holder.functions;
- element.labels = holder.labels;
- element.localVariables = holder.localVariables;
- element.parameters = holder.parameters;
- element.typeParameters = holder.typeParameters;
- if (body.isAsynchronous) {
- element.asynchronous = true;
- }
- if (body.isGenerator) {
- element.generator = true;
- }
- if (_inFunction) {
- Block enclosingBlock = node.getAncestor((node) => node is Block);
- if (enclosingBlock != null) {
- int functionEnd = node.offset + node.length;
- int blockEnd = enclosingBlock.offset + enclosingBlock.length;
- element.setVisibleRange(functionEnd, blockEnd - functionEnd - 1);
- }
- }
- if (node.returnType == null) {
- element.hasImplicitReturnType = true;
- }
- _currentHolder.addFunction(element);
- expression.element = element;
- functionName.staticElement = element;
- } else {
- SimpleIdentifier propertyNameNode = node.name;
- if (propertyNameNode == null) {
- // TODO(brianwilkerson) Report this internal error.
- return null;
- }
- String propertyName = propertyNameNode.name;
- TopLevelVariableElementImpl variable = _currentHolder
- .getTopLevelVariable(propertyName) as TopLevelVariableElementImpl;
- if (variable == null) {
- variable = new TopLevelVariableElementImpl(node.name.name, -1);
- variable.final2 = true;
- variable.synthetic = true;
- _currentHolder.addTopLevelVariable(variable);
- }
- if (node.isGetter) {
- PropertyAccessorElementImpl getter =
- new PropertyAccessorElementImpl.forNode(propertyNameNode);
- _setDocRange(getter, node);
- if (node.externalKeyword != null) {
- getter.external = true;
- }
- getter.functions = holder.functions;
- getter.labels = holder.labels;
- getter.localVariables = holder.localVariables;
- if (body.isAsynchronous) {
- getter.asynchronous = true;
- }
- if (body.isGenerator) {
- getter.generator = true;
- }
- getter.variable = variable;
- getter.getter = true;
- getter.static = true;
- variable.getter = getter;
- if (node.returnType == null) {
- getter.hasImplicitReturnType = true;
- }
- _currentHolder.addAccessor(getter);
- expression.element = getter;
- propertyNameNode.staticElement = getter;
- } else {
- PropertyAccessorElementImpl setter =
- new PropertyAccessorElementImpl.forNode(propertyNameNode);
- _setDocRange(setter, node);
- if (node.externalKeyword != null) {
- setter.external = true;
- }
- setter.functions = holder.functions;
- setter.labels = holder.labels;
- setter.localVariables = holder.localVariables;
- setter.parameters = holder.parameters;
- if (body.isAsynchronous) {
- setter.asynchronous = true;
- }
- if (body.isGenerator) {
- setter.generator = true;
- }
- setter.variable = variable;
- setter.setter = true;
- setter.static = true;
- variable.setter = setter;
- variable.final2 = false;
- _currentHolder.addAccessor(setter);
- expression.element = setter;
- propertyNameNode.staticElement = setter;
- }
- }
- holder.validate();
+ List<LabelElement> get labels {
+ if (_labels == null) {
+ return LabelElement.EMPTY_LIST;
}
- return null;
+ List<LabelElement> result = _labels;
+ _labels = null;
+ return result;
}
- @override
- Object visitFunctionExpression(FunctionExpression node) {
- if (node.parent is FunctionDeclaration) {
- // visitFunctionDeclaration has already created the element for the
- // declaration. We just need to visit children.
- return super.visitFunctionExpression(node);
- }
- ElementHolder holder = new ElementHolder();
- bool wasInFunction = _inFunction;
- _inFunction = true;
- try {
- _visitChildren(holder, node);
- } finally {
- _inFunction = wasInFunction;
+ List<LocalVariableElement> get localVariables {
+ if (_localVariables == null) {
+ return LocalVariableElement.EMPTY_LIST;
}
- FunctionBody body = node.body;
- FunctionElementImpl element =
- new FunctionElementImpl.forOffset(node.beginToken.offset);
- element.functions = holder.functions;
- element.labels = holder.labels;
- element.localVariables = holder.localVariables;
- element.parameters = holder.parameters;
- element.typeParameters = holder.typeParameters;
- if (body.isAsynchronous) {
- element.asynchronous = true;
- }
- if (body.isGenerator) {
- element.generator = true;
- }
- if (_inFunction) {
- Block enclosingBlock = node.getAncestor((node) => node is Block);
- if (enclosingBlock != null) {
- int functionEnd = node.offset + node.length;
- int blockEnd = enclosingBlock.offset + enclosingBlock.length;
- element.setVisibleRange(functionEnd, blockEnd - functionEnd - 1);
- }
- }
- FunctionTypeImpl type = new FunctionTypeImpl(element);
- if (_functionTypesToFix != null) {
- _functionTypesToFix.add(type);
- }
- element.type = type;
- element.hasImplicitReturnType = true;
- _currentHolder.addFunction(element);
- node.element = element;
- holder.validate();
- return null;
+ List<LocalVariableElement> result = _localVariables;
+ _localVariables = null;
+ return result;
}
- @override
- Object visitFunctionTypeAlias(FunctionTypeAlias node) {
- ElementHolder holder = new ElementHolder();
- _visitChildren(holder, node);
- SimpleIdentifier aliasName = node.name;
- List<ParameterElement> parameters = holder.parameters;
- List<TypeParameterElement> typeParameters = holder.typeParameters;
- FunctionTypeAliasElementImpl element =
- new FunctionTypeAliasElementImpl.forNode(aliasName);
- _setDocRange(element, node);
- element.parameters = parameters;
- element.typeParameters = typeParameters;
- FunctionTypeImpl type = new FunctionTypeImpl.forTypedef(element);
- type.typeArguments = _createTypeParameterTypes(typeParameters);
- element.type = type;
- _currentHolder.addTypeAlias(element);
- aliasName.staticElement = element;
- holder.validate();
- return null;
+ List<MethodElement> get methods {
+ if (_methods == null) {
+ return MethodElement.EMPTY_LIST;
+ }
+ List<MethodElement> result = _methods;
+ _methods = null;
+ return result;
}
- @override
- Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
- if (node.parent is! DefaultFormalParameter) {
- SimpleIdentifier parameterName = node.identifier;
- ParameterElementImpl parameter =
- new ParameterElementImpl.forNode(parameterName);
- parameter.parameterKind = node.kind;
- _setParameterVisibleRange(node, parameter);
- _currentHolder.addParameter(parameter);
- parameterName.staticElement = parameter;
+ List<ParameterElement> get parameters {
+ if (_parameters == null) {
+ return ParameterElement.EMPTY_LIST;
}
- //
- // The children of this parameter include any parameters defined on the type
- //of this parameter.
- //
- ElementHolder holder = new ElementHolder();
- _visitChildren(holder, node);
- ParameterElementImpl element = node.element;
- element.parameters = holder.parameters;
- element.typeParameters = holder.typeParameters;
- holder.validate();
- return null;
+ List<ParameterElement> result = _parameters;
+ _parameters = null;
+ return result;
}
- @override
- Object visitLabeledStatement(LabeledStatement node) {
- bool onSwitchStatement = node.statement is SwitchStatement;
- for (Label label in node.labels) {
- SimpleIdentifier labelName = label.label;
- LabelElementImpl element =
- new LabelElementImpl(labelName, onSwitchStatement, false);
- _currentHolder.addLabel(element);
- labelName.staticElement = element;
+ List<TopLevelVariableElement> get topLevelVariables {
+ if (_topLevelVariables == null) {
+ return TopLevelVariableElement.EMPTY_LIST;
}
- return super.visitLabeledStatement(node);
+ List<TopLevelVariableElement> result = _topLevelVariables;
+ _topLevelVariables = null;
+ return result;
}
- @override
- Object visitMethodDeclaration(MethodDeclaration node) {
- try {
- ElementHolder holder = new ElementHolder();
- bool wasInFunction = _inFunction;
- _inFunction = true;
- try {
- _visitChildren(holder, node);
- } finally {
- _inFunction = wasInFunction;
- }
- bool isStatic = node.isStatic;
- sc.Token property = node.propertyKeyword;
- FunctionBody body = node.body;
- if (property == null) {
- SimpleIdentifier methodName = node.name;
- String nameOfMethod = methodName.name;
- if (nameOfMethod == sc.TokenType.MINUS.lexeme &&
- node.parameters.parameters.length == 0) {
- nameOfMethod = "unary-";
- }
- MethodElementImpl element =
- new MethodElementImpl(nameOfMethod, methodName.offset);
- _setDocRange(element, node);
- element.abstract = node.isAbstract;
- if (node.externalKeyword != null) {
- element.external = true;
- }
- element.functions = holder.functions;
- element.labels = holder.labels;
- element.localVariables = holder.localVariables;
- element.parameters = holder.parameters;
- element.static = isStatic;
- element.typeParameters = holder.typeParameters;
- if (body.isAsynchronous) {
- element.asynchronous = true;
- }
- if (body.isGenerator) {
- element.generator = true;
- }
- if (node.returnType == null) {
- element.hasImplicitReturnType = true;
- }
- _currentHolder.addMethod(element);
- methodName.staticElement = element;
- } else {
- SimpleIdentifier propertyNameNode = node.name;
- String propertyName = propertyNameNode.name;
- FieldElementImpl field =
- _currentHolder.getField(propertyName) as FieldElementImpl;
- if (field == null) {
- field = new FieldElementImpl(node.name.name, -1);
- field.final2 = true;
- field.static = isStatic;
- field.synthetic = true;
- _currentHolder.addField(field);
- }
- if (node.isGetter) {
- PropertyAccessorElementImpl getter =
- new PropertyAccessorElementImpl.forNode(propertyNameNode);
- _setDocRange(getter, node);
- if (node.externalKeyword != null) {
- getter.external = true;
- }
- getter.functions = holder.functions;
- getter.labels = holder.labels;
- getter.localVariables = holder.localVariables;
- if (body.isAsynchronous) {
- getter.asynchronous = true;
- }
- if (body.isGenerator) {
- getter.generator = true;
- }
- getter.variable = field;
- getter.abstract = node.isAbstract;
- getter.getter = true;
- getter.static = isStatic;
- field.getter = getter;
- if (node.returnType == null) {
- getter.hasImplicitReturnType = true;
- }
- _currentHolder.addAccessor(getter);
- propertyNameNode.staticElement = getter;
- } else {
- PropertyAccessorElementImpl setter =
- new PropertyAccessorElementImpl.forNode(propertyNameNode);
- _setDocRange(setter, node);
- if (node.externalKeyword != null) {
- setter.external = true;
- }
- setter.functions = holder.functions;
- setter.labels = holder.labels;
- setter.localVariables = holder.localVariables;
- setter.parameters = holder.parameters;
- if (body.isAsynchronous) {
- setter.asynchronous = true;
- }
- if (body.isGenerator) {
- setter.generator = true;
- }
- setter.variable = field;
- setter.abstract = node.isAbstract;
- setter.setter = true;
- setter.static = isStatic;
- field.setter = setter;
- field.final2 = false;
- _currentHolder.addAccessor(setter);
- propertyNameNode.staticElement = setter;
- }
- }
- holder.validate();
- } catch (exception, stackTrace) {
- if (node.name.staticElement == null) {
- ClassDeclaration classNode =
- node.getAncestor((node) => node is ClassDeclaration);
- StringBuffer buffer = new StringBuffer();
- buffer.write("The element for the method ");
- buffer.write(node.name);
- buffer.write(" in ");
- buffer.write(classNode.name);
- buffer.write(" was not set while trying to build the element model.");
- AnalysisEngine.instance.logger.logError(
- buffer.toString(), new CaughtException(exception, stackTrace));
- } else {
- String message =
- "Exception caught in ElementBuilder.visitMethodDeclaration()";
- AnalysisEngine.instance.logger
- .logError(message, new CaughtException(exception, stackTrace));
- }
- } finally {
- if (node.name.staticElement == null) {
- ClassDeclaration classNode =
- node.getAncestor((node) => node is ClassDeclaration);
- StringBuffer buffer = new StringBuffer();
- buffer.write("The element for the method ");
- buffer.write(node.name);
- buffer.write(" in ");
- buffer.write(classNode.name);
- buffer.write(" was not set while trying to resolve types.");
- AnalysisEngine.instance.logger.logError(
- buffer.toString(),
- new CaughtException(
- new AnalysisException(buffer.toString()), null));
- }
+ List<FunctionTypeAliasElement> get typeAliases {
+ if (_typeAliases == null) {
+ return FunctionTypeAliasElement.EMPTY_LIST;
}
- return null;
+ List<FunctionTypeAliasElement> result = _typeAliases;
+ _typeAliases = null;
+ return result;
}
- @override
- Object visitSimpleFormalParameter(SimpleFormalParameter node) {
- if (node.parent is! DefaultFormalParameter) {
- SimpleIdentifier parameterName = node.identifier;
- ParameterElementImpl parameter =
- new ParameterElementImpl.forNode(parameterName);
- parameter.const3 = node.isConst;
- parameter.final2 = node.isFinal;
- parameter.parameterKind = node.kind;
- _setParameterVisibleRange(node, parameter);
- if (node.type == null) {
- parameter.hasImplicitType = true;
- }
- _currentHolder.addParameter(parameter);
- parameterName.staticElement = parameter;
+ List<TypeParameterElement> get typeParameters {
+ if (_typeParameters == null) {
+ return TypeParameterElement.EMPTY_LIST;
}
- return super.visitSimpleFormalParameter(node);
- }
-
- @override
- Object visitSuperExpression(SuperExpression node) {
- _isValidMixin = false;
- return super.visitSuperExpression(node);
+ List<TypeParameterElement> result = _typeParameters;
+ _typeParameters = null;
+ return result;
}
- @override
- Object visitSwitchCase(SwitchCase node) {
- for (Label label in node.labels) {
- SimpleIdentifier labelName = label.label;
- LabelElementImpl element = new LabelElementImpl(labelName, false, true);
- _currentHolder.addLabel(element);
- labelName.staticElement = element;
+ List<ClassElement> get types {
+ if (_types == null) {
+ return ClassElement.EMPTY_LIST;
}
- return super.visitSwitchCase(node);
+ List<ClassElement> result = _types;
+ _types = null;
+ return result;
}
- @override
- Object visitSwitchDefault(SwitchDefault node) {
- for (Label label in node.labels) {
- SimpleIdentifier labelName = label.label;
- LabelElementImpl element = new LabelElementImpl(labelName, false, true);
- _currentHolder.addLabel(element);
- labelName.staticElement = element;
+ void addAccessor(PropertyAccessorElement element) {
+ if (_accessors == null) {
+ _accessors = new List<PropertyAccessorElement>();
}
- return super.visitSwitchDefault(node);
- }
-
- @override
- Object visitTypeParameter(TypeParameter node) {
- SimpleIdentifier parameterName = node.name;
- TypeParameterElementImpl typeParameter =
- new TypeParameterElementImpl.forNode(parameterName);
- TypeParameterTypeImpl typeParameterType =
- new TypeParameterTypeImpl(typeParameter);
- typeParameter.type = typeParameterType;
- _currentHolder.addTypeParameter(typeParameter);
- parameterName.staticElement = typeParameter;
- return super.visitTypeParameter(node);
+ _accessors.add(element);
}
- @override
- Object visitVariableDeclaration(VariableDeclaration node) {
- bool isConst = node.isConst;
- bool isFinal = node.isFinal;
- bool hasInitializer = node.initializer != null;
- VariableElementImpl element;
- if (_inFieldContext) {
- SimpleIdentifier fieldName = node.name;
- FieldElementImpl field;
- if ((isConst || isFinal) && hasInitializer) {
- field = new ConstFieldElementImpl.forNode(fieldName);
- } else {
- field = new FieldElementImpl.forNode(fieldName);
- }
- element = field;
- if (node.parent.parent is FieldDeclaration) {
- _setDocRange(element, node.parent.parent);
- }
- if ((node.parent as VariableDeclarationList).type == null) {
- field.hasImplicitType = true;
- }
- _currentHolder.addField(field);
- fieldName.staticElement = field;
- } else if (_inFunction) {
- SimpleIdentifier variableName = node.name;
- LocalVariableElementImpl variable;
- if (isConst && hasInitializer) {
- variable = new ConstLocalVariableElementImpl.forNode(variableName);
- } else {
- variable = new LocalVariableElementImpl.forNode(variableName);
- }
- element = variable;
- Block enclosingBlock = node.getAncestor((node) => node is Block);
- // TODO(brianwilkerson) This isn't right for variables declared in a for
- // loop.
- variable.setVisibleRange(enclosingBlock.offset, enclosingBlock.length);
- if ((node.parent as VariableDeclarationList).type == null) {
- variable.hasImplicitType = true;
- }
- _currentHolder.addLocalVariable(variable);
- variableName.staticElement = element;
- } else {
- SimpleIdentifier variableName = node.name;
- TopLevelVariableElementImpl variable;
- if (isConst && hasInitializer) {
- variable = new ConstTopLevelVariableElementImpl(variableName);
- } else {
- variable = new TopLevelVariableElementImpl.forNode(variableName);
- }
- element = variable;
- if (node.parent.parent is TopLevelVariableDeclaration) {
- _setDocRange(element, node.parent.parent);
- }
- if ((node.parent as VariableDeclarationList).type == null) {
- variable.hasImplicitType = true;
- }
- _currentHolder.addTopLevelVariable(variable);
- variableName.staticElement = element;
- }
- element.const3 = isConst;
- element.final2 = isFinal;
- if (hasInitializer) {
- ElementHolder holder = new ElementHolder();
- bool wasInFieldContext = _inFieldContext;
- _inFieldContext = false;
- try {
- _visit(holder, node.initializer);
- } finally {
- _inFieldContext = wasInFieldContext;
- }
- FunctionElementImpl initializer =
- new FunctionElementImpl.forOffset(node.initializer.beginToken.offset);
- initializer.functions = holder.functions;
- initializer.labels = holder.labels;
- initializer.localVariables = holder.localVariables;
- initializer.synthetic = true;
- element.initializer = initializer;
- holder.validate();
- }
- if (element is PropertyInducingElementImpl) {
- if (_inFieldContext) {
- (element as FieldElementImpl).static =
- (node.parent.parent as FieldDeclaration).isStatic;
- }
- PropertyAccessorElementImpl getter =
- new PropertyAccessorElementImpl.forVariable(element);
- getter.getter = true;
- if (element.hasImplicitType) {
- getter.hasImplicitReturnType = true;
- }
- _currentHolder.addAccessor(getter);
- element.getter = getter;
- if (!isConst && !isFinal) {
- PropertyAccessorElementImpl setter =
- new PropertyAccessorElementImpl.forVariable(element);
- setter.setter = true;
- ParameterElementImpl parameter =
- new ParameterElementImpl("_${element.name}", element.nameOffset);
- parameter.synthetic = true;
- parameter.parameterKind = ParameterKind.REQUIRED;
- setter.parameters = <ParameterElement>[parameter];
- _currentHolder.addAccessor(setter);
- element.setter = setter;
- }
+ void addConstructor(ConstructorElement element) {
+ if (_constructors == null) {
+ _constructors = new List<ConstructorElement>();
}
- return null;
+ _constructors.add(element);
}
- /**
- * Build the table mapping field names to field elements for the fields defined in the current
- * class.
- *
- * @param fields the field elements defined in the current class
- */
- void _buildFieldMap(List<FieldElement> fields) {
- _fieldMap = new HashMap<String, FieldElement>();
- int count = fields.length;
- for (int i = 0; i < count; i++) {
- FieldElement field = fields[i];
- _fieldMap[field.name] = field;
+ void addEnum(ClassElement element) {
+ if (_enums == null) {
+ _enums = new List<ClassElement>();
}
+ _enums.add(element);
}
- /**
- * Creates the [ConstructorElement]s array with the single default constructor element.
- *
- * @param interfaceType the interface type for which to create a default constructor
- * @return the [ConstructorElement]s array with the single default constructor element
- */
- List<ConstructorElement> _createDefaultConstructors(
- InterfaceTypeImpl interfaceType) {
- ConstructorElementImpl constructor =
- new ConstructorElementImpl.forNode(null);
- constructor.synthetic = true;
- constructor.returnType = interfaceType;
- FunctionTypeImpl type = new FunctionTypeImpl(constructor);
- _functionTypesToFix.add(type);
- constructor.type = type;
- return <ConstructorElement>[constructor];
- }
-
- /**
- * Create the types associated with the given type parameters, setting the type of each type
- * parameter, and return an array of types corresponding to the given parameters.
- *
- * @param typeParameters the type parameters for which types are to be created
- * @return an array of types corresponding to the given parameters
- */
- List<DartType> _createTypeParameterTypes(
- List<TypeParameterElement> typeParameters) {
- int typeParameterCount = typeParameters.length;
- List<DartType> typeArguments = new List<DartType>(typeParameterCount);
- for (int i = 0; i < typeParameterCount; i++) {
- TypeParameterElementImpl typeParameter =
- typeParameters[i] as TypeParameterElementImpl;
- TypeParameterTypeImpl typeParameterType =
- new TypeParameterTypeImpl(typeParameter);
- typeParameter.type = typeParameterType;
- typeArguments[i] = typeParameterType;
+ void addField(FieldElement element) {
+ if (_fields == null) {
+ _fields = new List<FieldElement>();
}
- return typeArguments;
+ _fields.add(element);
}
- /**
- * Return the body of the function that contains the given parameter, or `null` if no
- * function body could be found.
- *
- * @param node the parameter contained in the function whose body is to be returned
- * @return the body of the function that contains the given parameter
- */
- FunctionBody _getFunctionBody(FormalParameter node) {
- AstNode parent = node.parent;
- while (parent != null) {
- if (parent is ConstructorDeclaration) {
- return parent.body;
- } else if (parent is FunctionExpression) {
- return parent.body;
- } else if (parent is MethodDeclaration) {
- return parent.body;
- }
- parent = parent.parent;
+ void addFunction(FunctionElement element) {
+ if (_functions == null) {
+ _functions = new List<FunctionElement>();
}
- return null;
+ _functions.add(element);
}
- /**
- * If the given [node] has a documentation comment, remember its range
- * into the given [element].
- */
- void _setDocRange(ElementImpl element, AnnotatedNode node) {
- Comment comment = node.documentationComment;
- if (comment != null && comment.isDocumentation) {
- element.setDocRange(comment.offset, comment.length);
+ void addLabel(LabelElement element) {
+ if (_labels == null) {
+ _labels = new List<LabelElement>();
}
+ _labels.add(element);
}
- /**
- * Sets the visible source range for formal parameter.
- */
- void _setParameterVisibleRange(
- FormalParameter node, ParameterElementImpl element) {
- FunctionBody body = _getFunctionBody(node);
- if (body != null) {
- element.setVisibleRange(body.offset, body.length);
+ void addLocalVariable(LocalVariableElement element) {
+ if (_localVariables == null) {
+ _localVariables = new List<LocalVariableElement>();
}
+ _localVariables.add(element);
}
- /**
- * Make the given holder be the current holder while visiting the given node.
- *
- * @param holder the holder that will gather elements that are built while visiting the children
- * @param node the node to be visited
- */
- void _visit(ElementHolder holder, AstNode node) {
- if (node != null) {
- ElementHolder previousHolder = _currentHolder;
- _currentHolder = holder;
- try {
- node.accept(this);
- } finally {
- _currentHolder = previousHolder;
- }
+ void addMethod(MethodElement element) {
+ if (_methods == null) {
+ _methods = new List<MethodElement>();
}
+ _methods.add(element);
}
- /**
- * Make the given holder be the current holder while visiting the children of the given node.
- *
- * @param holder the holder that will gather elements that are built while visiting the children
- * @param node the node whose children are to be visited
- */
- void _visitChildren(ElementHolder holder, AstNode node) {
- if (node != null) {
- ElementHolder previousHolder = _currentHolder;
- _currentHolder = holder;
- try {
- node.visitChildren(this);
- } finally {
- _currentHolder = previousHolder;
- }
+ void addParameter(ParameterElement element) {
+ if (_parameters == null) {
+ _parameters = new List<ParameterElement>();
}
+ _parameters.add(element);
}
-}
-
-/**
- * Instances of the class `ElementHolder` hold on to elements created while traversing an AST
- * structure so that they can be accessed when creating their enclosing element.
- */
-class ElementHolder {
- List<PropertyAccessorElement> _accessors;
-
- List<ConstructorElement> _constructors;
-
- List<ClassElement> _enums;
-
- List<FieldElement> _fields;
-
- List<FunctionElement> _functions;
-
- List<LabelElement> _labels;
-
- List<LocalVariableElement> _localVariables;
-
- List<MethodElement> _methods;
-
- List<ParameterElement> _parameters;
-
- List<TopLevelVariableElement> _topLevelVariables;
-
- List<ClassElement> _types;
-
- List<FunctionTypeAliasElement> _typeAliases;
-
- List<TypeParameterElement> _typeParameters;
- List<PropertyAccessorElement> get accessors {
- if (_accessors == null) {
- return PropertyAccessorElement.EMPTY_LIST;
+ void addTopLevelVariable(TopLevelVariableElement element) {
+ if (_topLevelVariables == null) {
+ _topLevelVariables = new List<TopLevelVariableElement>();
}
- List<PropertyAccessorElement> result = _accessors;
- _accessors = null;
- return result;
+ _topLevelVariables.add(element);
}
- List<ConstructorElement> get constructors {
- if (_constructors == null) {
- return ConstructorElement.EMPTY_LIST;
+ void addType(ClassElement element) {
+ if (_types == null) {
+ _types = new List<ClassElement>();
}
- List<ConstructorElement> result = _constructors;
- _constructors = null;
- return result;
+ _types.add(element);
}
- List<ClassElement> get enums {
- if (_enums == null) {
- return ClassElement.EMPTY_LIST;
+ void addTypeAlias(FunctionTypeAliasElement element) {
+ if (_typeAliases == null) {
+ _typeAliases = new List<FunctionTypeAliasElement>();
}
- List<ClassElement> result = _enums;
- _enums = null;
- return result;
+ _typeAliases.add(element);
}
- List<FieldElement> get fields {
- if (_fields == null) {
- return FieldElement.EMPTY_LIST;
+ void addTypeParameter(TypeParameterElement element) {
+ if (_typeParameters == null) {
+ _typeParameters = new List<TypeParameterElement>();
}
- List<FieldElement> result = _fields;
- _fields = null;
- return result;
+ _typeParameters.add(element);
}
- List<FieldElement> get fieldsWithoutFlushing {
- if (_fields == null) {
- return FieldElement.EMPTY_LIST;
- }
- List<FieldElement> result = _fields;
- return result;
- }
-
- List<FunctionElement> get functions {
- if (_functions == null) {
- return FunctionElement.EMPTY_LIST;
- }
- List<FunctionElement> result = _functions;
- _functions = null;
- return result;
- }
-
- List<LabelElement> get labels {
- if (_labels == null) {
- return LabelElement.EMPTY_LIST;
- }
- List<LabelElement> result = _labels;
- _labels = null;
- return result;
- }
-
- List<LocalVariableElement> get localVariables {
- if (_localVariables == null) {
- return LocalVariableElement.EMPTY_LIST;
- }
- List<LocalVariableElement> result = _localVariables;
- _localVariables = null;
- return result;
- }
-
- List<MethodElement> get methods {
- if (_methods == null) {
- return MethodElement.EMPTY_LIST;
- }
- List<MethodElement> result = _methods;
- _methods = null;
- return result;
- }
-
- List<ParameterElement> get parameters {
- if (_parameters == null) {
- return ParameterElement.EMPTY_LIST;
- }
- List<ParameterElement> result = _parameters;
- _parameters = null;
- return result;
- }
-
- List<TopLevelVariableElement> get topLevelVariables {
- if (_topLevelVariables == null) {
- return TopLevelVariableElement.EMPTY_LIST;
- }
- List<TopLevelVariableElement> result = _topLevelVariables;
- _topLevelVariables = null;
- return result;
- }
-
- List<FunctionTypeAliasElement> get typeAliases {
- if (_typeAliases == null) {
- return FunctionTypeAliasElement.EMPTY_LIST;
- }
- List<FunctionTypeAliasElement> result = _typeAliases;
- _typeAliases = null;
- return result;
- }
-
- List<TypeParameterElement> get typeParameters {
- if (_typeParameters == null) {
- return TypeParameterElement.EMPTY_LIST;
- }
- List<TypeParameterElement> result = _typeParameters;
- _typeParameters = null;
- return result;
- }
-
- List<ClassElement> get types {
- if (_types == null) {
- return ClassElement.EMPTY_LIST;
- }
- List<ClassElement> result = _types;
- _types = null;
- return result;
- }
-
- void addAccessor(PropertyAccessorElement element) {
- if (_accessors == null) {
- _accessors = new List<PropertyAccessorElement>();
- }
- _accessors.add(element);
- }
-
- void addConstructor(ConstructorElement element) {
- if (_constructors == null) {
- _constructors = new List<ConstructorElement>();
- }
- _constructors.add(element);
- }
-
- void addEnum(ClassElement element) {
- if (_enums == null) {
- _enums = new List<ClassElement>();
- }
- _enums.add(element);
- }
-
- void addField(FieldElement element) {
- if (_fields == null) {
- _fields = new List<FieldElement>();
- }
- _fields.add(element);
- }
-
- void addFunction(FunctionElement element) {
- if (_functions == null) {
- _functions = new List<FunctionElement>();
- }
- _functions.add(element);
- }
-
- void addLabel(LabelElement element) {
- if (_labels == null) {
- _labels = new List<LabelElement>();
- }
- _labels.add(element);
- }
-
- void addLocalVariable(LocalVariableElement element) {
- if (_localVariables == null) {
- _localVariables = new List<LocalVariableElement>();
- }
- _localVariables.add(element);
- }
-
- void addMethod(MethodElement element) {
- if (_methods == null) {
- _methods = new List<MethodElement>();
- }
- _methods.add(element);
- }
-
- void addParameter(ParameterElement element) {
- if (_parameters == null) {
- _parameters = new List<ParameterElement>();
- }
- _parameters.add(element);
- }
-
- void addTopLevelVariable(TopLevelVariableElement element) {
- if (_topLevelVariables == null) {
- _topLevelVariables = new List<TopLevelVariableElement>();
- }
- _topLevelVariables.add(element);
- }
-
- void addType(ClassElement element) {
- if (_types == null) {
- _types = new List<ClassElement>();
- }
- _types.add(element);
- }
-
- void addTypeAlias(FunctionTypeAliasElement element) {
- if (_typeAliases == null) {
- _typeAliases = new List<FunctionTypeAliasElement>();
- }
- _typeAliases.add(element);
- }
-
- void addTypeParameter(TypeParameterElement element) {
- if (_typeParameters == null) {
- _typeParameters = new List<TypeParameterElement>();
- }
- _typeParameters.add(element);
- }
-
- FieldElement getField(String fieldName) {
+ FieldElement getField(String fieldName, {bool synthetic: false}) {
if (_fields == null) {
return null;
}
- for (FieldElement field in _fields) {
- if (field.name == fieldName) {
+ int length = _fields.length;
+ for (int i = 0; i < length; i++) {
+ FieldElement field = _fields[i];
+ if (field.name == fieldName && field.isSynthetic == synthetic) {
return field;
}
}
@@ -3691,7 +3231,9 @@ class ElementHolder {
if (_topLevelVariables == null) {
return null;
}
- for (TopLevelVariableElement variable in _topLevelVariables) {
+ int length = _topLevelVariables.length;
+ for (int i = 0; i < length; i++) {
+ TopLevelVariableElement variable = _topLevelVariables[i];
if (variable.name == variableName) {
return variable;
}
@@ -3789,77 +3331,13 @@ class ElementHolder {
}
}
-/**
- * Instances of the class `EnclosedScope` implement a scope that is lexically enclosed in
- * another scope.
- */
-class EnclosedScope extends Scope {
- /**
- * The scope in which this scope is lexically enclosed.
- */
- final Scope enclosingScope;
-
- /**
- * A table mapping names that will be defined in this scope, but right now are not initialized.
- * According to the scoping rules these names are hidden, even if they were defined in an outer
- * scope.
- */
- HashMap<String, Element> _hiddenElements = new HashMap<String, Element>();
-
- /**
- * A flag indicating whether there are any names defined in this scope.
- */
- bool _hasHiddenName = false;
-
- /**
- * Initialize a newly created scope enclosed within another scope.
- *
- * @param enclosingScope the scope in which this scope is lexically enclosed
- */
- EnclosedScope(this.enclosingScope);
-
- @override
- AnalysisErrorListener get errorListener => enclosingScope.errorListener;
-
+class ElementMismatchException extends AnalysisException {
/**
- * Record that given element is declared in this scope, but hasn't been initialized yet, so it is
- * error to use. If there is already an element with the given name defined in an outer scope,
- * then it will become unavailable.
- *
- * @param element the element declared, but not initialized in this scope
+ * Initialize a newly created exception to have the given [message] and
+ * [cause].
*/
- void hide(Element element) {
- if (element != null) {
- String name = element.name;
- if (name != null && !name.isEmpty) {
- _hiddenElements[name] = element;
- _hasHiddenName = true;
- }
- }
- }
-
- @override
- Element internalLookup(
- Identifier identifier, String name, LibraryElement referencingLibrary) {
- Element element = localLookup(name, referencingLibrary);
- if (element != null) {
- return element;
- }
- // May be there is a hidden Element.
- if (_hasHiddenName) {
- Element hiddenElement = _hiddenElements[name];
- if (hiddenElement != null) {
- errorListener.onError(new AnalysisError(
- getSource(identifier),
- identifier.offset,
- identifier.length,
- CompileTimeErrorCode.REFERENCED_BEFORE_DECLARATION, []));
- return hiddenElement;
- }
- }
- // Check enclosing scope.
- return enclosingScope.internalLookup(identifier, name, referencingLibrary);
- }
+ ElementMismatchException(String message, [CaughtException cause = null])
+ : super(message, cause);
}
/**
@@ -3885,9 +3363,8 @@ class EnumMemberBuilder extends RecursiveAstVisitor<Object> {
//
// Finish building the enum.
//
- ClassElementImpl enumElement = node.name.staticElement as ClassElementImpl;
+ EnumElementImpl enumElement = node.name.staticElement as EnumElementImpl;
InterfaceType enumType = enumElement.type;
- enumElement.supertype = _typeProvider.objectType;
//
// Populate the fields.
//
@@ -3905,7 +3382,7 @@ class EnumMemberBuilder extends RecursiveAstVisitor<Object> {
valuesField.static = true;
valuesField.const3 = true;
valuesField.synthetic = true;
- valuesField.type = _typeProvider.listType.substitute4(<DartType>[enumType]);
+ valuesField.type = _typeProvider.listType.instantiate(<DartType>[enumType]);
fields.add(valuesField);
getters.add(_createGetter(valuesField));
//
@@ -3915,12 +3392,8 @@ class EnumMemberBuilder extends RecursiveAstVisitor<Object> {
List<DartObjectImpl> constantValues = new List<DartObjectImpl>();
int constantCount = constants.length;
for (int i = 0; i < constantCount; i++) {
- SimpleIdentifier constantName = constants[i].name;
- FieldElementImpl constantField =
- new ConstFieldElementImpl.forNode(constantName);
- constantField.static = true;
- constantField.const3 = true;
- constantField.type = enumType;
+ EnumConstantDeclaration constant = constants[i];
+ FieldElementImpl constantField = constant.name.staticElement;
//
// Create a value for the constant.
//
@@ -3932,8 +3405,7 @@ class EnumMemberBuilder extends RecursiveAstVisitor<Object> {
constantValues.add(value);
constantField.evaluationResult = new EvaluationResultImpl(value);
fields.add(constantField);
- getters.add(_createGetter(constantField));
- constantName.staticElement = constantField;
+ getters.add(constantField.getter);
}
//
// Build the value of the 'values' field.
@@ -3951,19 +3423,45 @@ class EnumMemberBuilder extends RecursiveAstVisitor<Object> {
}
/**
- * Create a getter that corresponds to the given field.
- *
- * @param field the field for which a getter is to be created
- * @return the getter that was created
+ * Create a getter that corresponds to the given [field].
*/
PropertyAccessorElement _createGetter(FieldElementImpl field) {
- PropertyAccessorElementImpl getter =
- new PropertyAccessorElementImpl.forVariable(field);
- getter.getter = true;
- getter.returnType = field.type;
- getter.type = new FunctionTypeImpl(getter);
- field.getter = getter;
- return getter;
+ return new PropertyAccessorElementImpl_ImplicitGetter(field);
+ }
+}
+
+/**
+ * A mixin for classes that use an existing element model to resolve a portion
+ * of an AST structure.
+ */
+class ExistingElementResolver {
+ /**
+ * The compilation unit containing the AST nodes being visited.
+ */
+ CompilationUnitElementImpl _enclosingUnit;
+
+ /**
+ * Throw an [ElementMismatchException] to report that the element model and the
+ * AST do not match. The [message] will have the path to the given [node]
+ * appended to it.
+ */
+ void _mismatch(String message, AstNode node) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.write('Mismatch in ');
+ buffer.write(runtimeType);
+ buffer.write(' while resolving ');
+ buffer.writeln(_enclosingUnit?.source?.fullName);
+ buffer.writeln(message);
+ buffer.write('Path to root:');
+ String separator = ' ';
+ AstNode parent = node;
+ while (parent != null) {
+ buffer.write(separator);
+ buffer.write(parent.runtimeType.toString());
+ separator = ', ';
+ parent = parent.parent;
+ }
+ throw new ElementMismatchException(buffer.toString());
}
}
@@ -3979,6 +3477,17 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
*/
bool _enclosingBlockContainsBreak = false;
+ /**
+ * Set to `true` when a `continue` is encountered, and reset to `false` when a
+ * `do`, `while`, `for` or `switch` block is entered.
+ */
+ bool _enclosingBlockContainsContinue = false;
+
+ /**
+ * Add node when a labelled `break` is encountered.
+ */
+ Set<AstNode> _enclosingBlockBreaksLabel = new Set<AstNode>();
+
@override
bool visitArgumentList(ArgumentList node) =>
_visitExpressions(node.arguments);
@@ -3995,11 +3504,14 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
if (_nodeExits(leftHandSide)) {
return true;
}
- if (node.operator.type == sc.TokenType.QUESTION_QUESTION_EQ) {
+ TokenType operatorType = node.operator.type;
+ if (operatorType == TokenType.AMPERSAND_AMPERSAND_EQ ||
+ operatorType == TokenType.BAR_BAR_EQ ||
+ operatorType == TokenType.QUESTION_QUESTION_EQ) {
return false;
}
if (leftHandSide is PropertyAccess &&
- leftHandSide.operator.type == sc.TokenType.QUESTION_PERIOD) {
+ leftHandSide.operator.type == TokenType.QUESTION_PERIOD) {
return false;
}
return _nodeExits(node.rightHandSide);
@@ -4013,16 +3525,15 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
bool visitBinaryExpression(BinaryExpression node) {
Expression lhsExpression = node.leftOperand;
Expression rhsExpression = node.rightOperand;
- sc.TokenType operatorType = node.operator.type;
+ TokenType operatorType = node.operator.type;
// If the operator is ||, then only consider the RHS of the binary
// expression if the left hand side is the false literal.
// TODO(jwren) Do we want to take constant expressions into account,
// evaluate if(false) {} differently than if(<condition>), when <condition>
// evaluates to a constant false value?
- if (operatorType == sc.TokenType.BAR_BAR) {
+ if (operatorType == TokenType.BAR_BAR) {
if (lhsExpression is BooleanLiteral) {
- BooleanLiteral booleanLiteral = lhsExpression;
- if (!booleanLiteral.value) {
+ if (!lhsExpression.value) {
return _nodeExits(rhsExpression);
}
}
@@ -4030,10 +3541,9 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
}
// If the operator is &&, then only consider the RHS of the binary
// expression if the left hand side is the true literal.
- if (operatorType == sc.TokenType.AMPERSAND_AMPERSAND) {
+ if (operatorType == TokenType.AMPERSAND_AMPERSAND) {
if (lhsExpression is BooleanLiteral) {
- BooleanLiteral booleanLiteral = lhsExpression;
- if (booleanLiteral.value) {
+ if (lhsExpression.value) {
return _nodeExits(rhsExpression);
}
}
@@ -4041,7 +3551,7 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
}
// If the operator is ??, then don't consider the RHS of the binary
// expression.
- if (operatorType == sc.TokenType.QUESTION_QUESTION) {
+ if (operatorType == TokenType.QUESTION_QUESTION) {
return _nodeExits(lhsExpression);
}
return _nodeExits(lhsExpression) || _nodeExits(rhsExpression);
@@ -4056,6 +3566,9 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
@override
bool visitBreakStatement(BreakStatement node) {
_enclosingBlockContainsBreak = true;
+ if (node.label != null) {
+ _enclosingBlockBreaksLabel.add(node.target);
+ }
return false;
}
@@ -4081,31 +3594,41 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
}
@override
- bool visitContinueStatement(ContinueStatement node) => false;
+ bool visitContinueStatement(ContinueStatement node) {
+ _enclosingBlockContainsContinue = true;
+ return false;
+ }
@override
bool visitDoStatement(DoStatement node) {
bool outerBreakValue = _enclosingBlockContainsBreak;
+ bool outerContinueValue = _enclosingBlockContainsContinue;
_enclosingBlockContainsBreak = false;
+ _enclosingBlockContainsContinue = false;
try {
+ bool bodyExits = _nodeExits(node.body);
+ bool containsBreakOrContinue =
+ _enclosingBlockContainsBreak || _enclosingBlockContainsContinue;
+ // Even if we determine that the body "exits", there might be break or
+ // continue statements that actually mean it _doesn't_ always exit.
+ if (bodyExits && !containsBreakOrContinue) {
+ return true;
+ }
Expression conditionExpression = node.condition;
if (_nodeExits(conditionExpression)) {
return true;
}
// TODO(jwren) Do we want to take all constant expressions into account?
if (conditionExpression is BooleanLiteral) {
- BooleanLiteral booleanLiteral = conditionExpression;
- // If do {} while (true), and the body doesn't return or the body
- // doesn't have a break, then return true.
- bool blockReturns = _nodeExits(node.body);
- if (booleanLiteral.value &&
- (blockReturns || !_enclosingBlockContainsBreak)) {
+ // If do {} while (true), and the body doesn't break, then return true.
+ if (conditionExpression.value && !_enclosingBlockContainsBreak) {
return true;
}
}
return false;
} finally {
_enclosingBlockContainsBreak = outerBreakValue;
+ _enclosingBlockContainsContinue = outerContinueValue;
}
}
@@ -4146,13 +3669,13 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
if (_visitExpressions(node.updaters)) {
return true;
}
+ bool blockReturns = _nodeExits(node.body);
// TODO(jwren) Do we want to take all constant expressions into account?
// If for(; true; ) (or for(;;)), and the body doesn't return or the body
// doesn't have a break, then return true.
bool implicitOrExplictTrue = conditionExpression == null ||
(conditionExpression is BooleanLiteral && conditionExpression.value);
if (implicitOrExplictTrue) {
- bool blockReturns = _nodeExits(node.body);
if (blockReturns || !_enclosingBlockContainsBreak) {
return true;
}
@@ -4191,19 +3714,20 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
}
// TODO(jwren) Do we want to take all constant expressions into account?
if (conditionExpression is BooleanLiteral) {
- BooleanLiteral booleanLiteral = conditionExpression;
- if (booleanLiteral.value) {
- // if(true) ...
+ if (conditionExpression.value) {
+ // if (true) ...
return _nodeExits(thenStatement);
} else if (elseStatement != null) {
// if (false) ...
return _nodeExits(elseStatement);
}
}
+ bool thenExits = _nodeExits(thenStatement);
+ bool elseExits = _nodeExits(elseStatement);
if (thenStatement == null || elseStatement == null) {
return false;
}
- return _nodeExits(thenStatement) && _nodeExits(elseStatement);
+ return thenExits && elseExits;
}
@override
@@ -4229,8 +3753,16 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
bool visitLabel(Label node) => false;
@override
- bool visitLabeledStatement(LabeledStatement node) =>
- node.statement.accept(this);
+ bool visitLabeledStatement(LabeledStatement node) {
+ try {
+ bool statementExits = _nodeExits(node.statement);
+ bool neverBrokeFromLabel =
+ !_enclosingBlockBreaksLabel.contains(node.statement);
+ return statementExits && neverBrokeFromLabel;
+ } finally {
+ _enclosingBlockBreaksLabel.remove(node.statement);
+ }
+ }
@override
bool visitLiteral(Literal node) => false;
@@ -4242,7 +3774,7 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
if (target.accept(this)) {
return true;
}
- if (node.operator.type == sc.TokenType.QUESTION_PERIOD) {
+ if (node.operator.type == TokenType.QUESTION_PERIOD) {
return false;
}
}
@@ -4294,36 +3826,30 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
_enclosingBlockContainsBreak = false;
try {
bool hasDefault = false;
+ bool hasNonExitingCase = false;
List<SwitchMember> members = node.members;
for (int i = 0; i < members.length; i++) {
SwitchMember switchMember = members[i];
if (switchMember is SwitchDefault) {
hasDefault = true;
- // If this is the last member and there are no statements, return
- // false
+ // If this is the last member and there are no statements, then it
+ // does not exit.
if (switchMember.statements.isEmpty && i + 1 == members.length) {
- return false;
+ hasNonExitingCase = true;
+ continue;
}
}
- // For switch members with no statements, don't visit the children,
- // otherwise, return false if no return is found in the children
- // statements.
+ // For switch members with no statements, don't visit the children.
+ // Otherwise, if there children statements don't exit, mark this as a
+ // non-exiting case.
if (!switchMember.statements.isEmpty && !switchMember.accept(this)) {
- return false;
+ hasNonExitingCase = true;
}
}
- // All of the members exit, determine whether there are possible cases
- // that are not caught by the members.
- DartType type = node.expression == null ? null : node.expression.bestType;
- if (type is InterfaceType) {
- ClassElement element = type.element;
- if (element != null && element.isEnum) {
- // If some of the enum values are not covered, then a warning will
- // have already been generated, so there's no point in generating a
- // hint.
- return true;
- }
+ if (hasNonExitingCase) {
+ return false;
}
+ // As all cases exit, return whether that list includes `default`.
return hasDefault;
} finally {
_enclosingBlockContainsBreak = outerBreakValue;
@@ -4338,14 +3864,18 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
@override
bool visitTryStatement(TryStatement node) {
- if (_nodeExits(node.body)) {
+ if (_nodeExits(node.finallyBlock)) {
return true;
}
- Block finallyBlock = node.finallyBlock;
- if (_nodeExits(finallyBlock)) {
- return true;
+ if (!_nodeExits(node.body)) {
+ return false;
}
- return false;
+ for (CatchClause c in node.catchClauses) {
+ if (!_nodeExits(c.body)) {
+ return false;
+ }
+ }
+ return true;
}
@override
@@ -4384,14 +3914,21 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
if (conditionExpression.accept(this)) {
return true;
}
+ node.body.accept(this);
// TODO(jwren) Do we want to take all constant expressions into account?
if (conditionExpression is BooleanLiteral) {
- BooleanLiteral booleanLiteral = conditionExpression;
- // If while(true), and the body doesn't return or the body doesn't have
- // a break, then return true.
- bool blockReturns = node.body.accept(this);
- if (booleanLiteral.value &&
- (blockReturns || !_enclosingBlockContainsBreak)) {
+ // If while(true), and the body doesn't have a break, then return true.
+ // The body might be found to exit, but if there are any break
+ // statements, then it is a faulty finding. In other words:
+ //
+ // * If the body exits, and does not contain a break statement, then
+ // it exits.
+ // * If the body does not exit, and does not contain a break statement,
+ // then it loops infinitely (also an exit).
+ //
+ // As both conditions forbid any break statements to be found, the logic
+ // just boils down to checking [_enclosingBlockContainsBreak].
+ if (conditionExpression.value && !_enclosingBlockContainsBreak) {
return true;
}
}
@@ -4401,6 +3938,9 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
}
}
+ @override
+ bool visitYieldStatement(YieldStatement node) => _nodeExits(node.expression);
+
/**
* Return `true` if the given node exits.
*
@@ -4424,7 +3964,7 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
}
bool _visitStatements(NodeList<Statement> statements) {
- for (int i = statements.length - 1; i >= 0; i--) {
+ for (int i = 0; i < statements.length; i++) {
if (statements[i].accept(this)) {
return true;
}
@@ -4450,103 +3990,6 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
}
}
-/**
- * The scope defined by a function.
- */
-class FunctionScope extends EnclosedScope {
- /**
- * The element representing the function that defines this scope.
- */
- final ExecutableElement _functionElement;
-
- /**
- * A flag indicating whether the parameters have already been defined, used to
- * prevent the parameters from being defined multiple times.
- */
- bool _parametersDefined = false;
-
- /**
- * Initialize a newly created scope enclosed within the [enclosingScope] that
- * represents the given [_functionElement].
- */
- FunctionScope(Scope enclosingScope, this._functionElement)
- : super(new EnclosedScope(new EnclosedScope(enclosingScope))) {
- if (_functionElement == null) {
- throw new IllegalArgumentException("function element cannot be null");
- }
- _defineTypeParameters();
- }
-
- /**
- * Define the parameters for the given function in the scope that encloses
- * this function.
- */
- void defineParameters() {
- if (_parametersDefined) {
- return;
- }
- _parametersDefined = true;
- Scope parameterScope = enclosingScope;
- for (ParameterElement parameter in _functionElement.parameters) {
- if (!parameter.isInitializingFormal) {
- parameterScope.define(parameter);
- }
- }
- }
-
- /**
- * Define the type parameters for the function.
- */
- void _defineTypeParameters() {
- Scope typeParameterScope = enclosingScope.enclosingScope;
- for (TypeParameterElement typeParameter
- in _functionElement.typeParameters) {
- typeParameterScope.define(typeParameter);
- }
- }
-}
-
-/**
- * The scope defined by a function type alias.
- */
-class FunctionTypeScope extends EnclosedScope {
- final FunctionTypeAliasElement _typeElement;
-
- bool _parametersDefined = false;
-
- /**
- * Initialize a newly created scope enclosed within the [enclosingScope] that
- * represents the given [_typeElement].
- */
- FunctionTypeScope(Scope enclosingScope, this._typeElement)
- : super(new EnclosedScope(enclosingScope)) {
- _defineTypeParameters();
- }
-
- /**
- * Define the parameters for the function type alias.
- */
- void defineParameters() {
- if (_parametersDefined) {
- return;
- }
- _parametersDefined = true;
- for (ParameterElement parameter in _typeElement.parameters) {
- define(parameter);
- }
- }
-
- /**
- * Define the type parameters for the function type alias.
- */
- void _defineTypeParameters() {
- Scope typeParameterScope = enclosingScope;
- for (TypeParameterElement typeParameter in _typeElement.typeParameters) {
- typeParameterScope.define(typeParameter);
- }
- }
-}
-
/**
* A visitor that visits ASTs and fills [UsedImportedElements].
*/
@@ -4558,54 +4001,80 @@ class GatherUsedImportedElementsVisitor extends RecursiveAstVisitor {
@override
void visitExportDirective(ExportDirective node) {
- _visitMetadata(node.metadata);
+ _visitDirective(node);
}
@override
void visitImportDirective(ImportDirective node) {
- _visitMetadata(node.metadata);
+ _visitDirective(node);
}
@override
void visitLibraryDirective(LibraryDirective node) {
- _visitMetadata(node.metadata);
+ _visitDirective(node);
}
@override
- void visitPrefixedIdentifier(PrefixedIdentifier node) {
- // If the prefixed identifier references some A.B, where A is a library
- // prefix, then we can lookup the associated ImportDirective in
- // prefixElementMap and remove it from the unusedImports list.
- SimpleIdentifier prefixIdentifier = node.prefix;
- Element element = prefixIdentifier.staticElement;
- if (element is PrefixElement) {
- usedElements.prefixes.add(element);
- return;
+ void visitSimpleIdentifier(SimpleIdentifier node) {
+ _visitIdentifier(node, node.staticElement);
+ }
+
+ /**
+ * If the given [identifier] is prefixed with a [PrefixElement], fill the
+ * corresponding `UsedImportedElements.prefixMap` entry and return `true`.
+ */
+ bool _recordPrefixMap(SimpleIdentifier identifier, Element element) {
+ bool recordIfTargetIsPrefixElement(Expression target) {
+ if (target is SimpleIdentifier && target.staticElement is PrefixElement) {
+ List<Element> prefixedElements = usedElements.prefixMap
+ .putIfAbsent(target.staticElement, () => <Element>[]);
+ prefixedElements.add(element);
+ return true;
+ }
+ return false;
+ }
+
+ AstNode parent = identifier.parent;
+ if (parent is MethodInvocation && parent.methodName == identifier) {
+ return recordIfTargetIsPrefixElement(parent.target);
+ }
+ if (parent is PrefixedIdentifier && parent.identifier == identifier) {
+ return recordIfTargetIsPrefixElement(parent.prefix);
}
- // Otherwise, pass the prefixed identifier element and name onto
- // visitIdentifier.
- _visitIdentifier(element, prefixIdentifier.name);
+ return false;
}
- @override
- void visitSimpleIdentifier(SimpleIdentifier node) {
- _visitIdentifier(node.staticElement, node.name);
+ /**
+ * Visit identifiers used by the given [directive].
+ */
+ void _visitDirective(Directive directive) {
+ directive.documentationComment?.accept(this);
+ directive.metadata.accept(this);
}
- void _visitIdentifier(Element element, String name) {
+ void _visitIdentifier(SimpleIdentifier identifier, Element element) {
if (element == null) {
return;
}
// If the element is multiply defined then call this method recursively for
// each of the conflicting elements.
if (element is MultiplyDefinedElement) {
- MultiplyDefinedElement multiplyDefinedElement = element;
- for (Element elt in multiplyDefinedElement.conflictingElements) {
- _visitIdentifier(elt, name);
+ List<Element> conflictingElements = element.conflictingElements;
+ int length = conflictingElements.length;
+ for (int i = 0; i < length; i++) {
+ Element elt = conflictingElements[i];
+ _visitIdentifier(identifier, elt);
}
return;
- } else if (element is PrefixElement) {
- usedElements.prefixes.add(element);
+ }
+
+ // Record `importPrefix.identifier` into 'prefixMap'.
+ if (_recordPrefixMap(identifier, element)) {
+ return;
+ }
+
+ if (element is PrefixElement) {
+ usedElements.prefixMap.putIfAbsent(element, () => <Element>[]);
return;
} else if (element.enclosingElement is! CompilationUnitElement) {
// Identifiers that aren't a prefix element and whose enclosing element
@@ -4626,20 +4095,6 @@ class GatherUsedImportedElementsVisitor extends RecursiveAstVisitor {
// Remember the element.
usedElements.elements.add(element);
}
-
- /**
- * Given some [NodeList] of [Annotation]s, ensure that the identifiers are visited by
- * this visitor. Specifically, this covers the cases where AST nodes don't have their identifiers
- * visited by this visitor, but still need their annotations visited.
- *
- * @param annotations the list of annotations to visit
- */
- void _visitMetadata(NodeList<Annotation> annotations) {
- int count = annotations.length;
- for (int i = 0; i < count; i++) {
- annotations[i].accept(this);
- }
- }
}
/**
@@ -4764,7 +4219,10 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor {
return;
}
if (parent2 is VariableDeclarationList) {
- return;
+ // If it's a field's type, it still counts as used.
+ if (parent2.parent is! FieldDeclaration) {
+ return;
+ }
}
}
}
@@ -4824,13 +4282,16 @@ class HintGenerator {
_usedImportedElementsVisitor =
new GatherUsedImportedElementsVisitor(_library);
_enableDart2JSHints = _context.analysisOptions.dart2jsHint;
- _manager = new InheritanceManager(_compilationUnits[0].element.library);
+ _manager =
+ new InheritanceManager(_library, includeAbstractFromSuperclasses: true);
_usedLocalElementsVisitor = new GatherUsedLocalElementsVisitor(_library);
}
void generateForLibrary() {
PerformanceStatistics.hints.makeCurrentWhile(() {
- for (CompilationUnit unit in _compilationUnits) {
+ int length = _compilationUnits.length;
+ for (int i = 0; i < length; i++) {
+ CompilationUnit unit = _compilationUnits[i];
CompilationUnitElement element = unit.element;
if (element != null) {
_generateForCompilationUnit(unit, element.source);
@@ -4846,6 +4307,7 @@ class HintGenerator {
.removeUsedElements(_usedImportedElementsVisitor.usedElements);
importsVerifier.generateDuplicateImportHints(definingUnitErrorReporter);
importsVerifier.generateUnusedImportHints(definingUnitErrorReporter);
+ importsVerifier.generateUnusedShownNameHints(definingUnitErrorReporter);
}
_library.accept(new UnusedLocalElementsVerifier(
_errorListener, _usedLocalElementsVisitor.usedElements));
@@ -4864,7 +4326,8 @@ class HintGenerator {
unit.accept(new Dart2JSVerifier(errorReporter));
}
// Dart best practices
- unit.accept(new BestPracticesVerifier(errorReporter, _context.typeProvider,
+ unit.accept(new BestPracticesVerifier(
+ errorReporter, _context.typeProvider, _library, _manager,
typeSystem: _context.typeSystem));
unit.accept(new OverrideVerifier(errorReporter, _manager));
// Find to-do comments
@@ -4877,679 +4340,253 @@ class HintGenerator {
}
/**
- * Instances of the class {@code HtmlTagInfo} record information about the tags used in an HTML
- * file.
+ * Instances of the class `ImportsVerifier` visit all of the referenced libraries in the source code
+ * verifying that all of the imports are used, otherwise a [HintCode.UNUSED_IMPORT] hint is
+ * generated with [generateUnusedImportHints].
+ *
+ * Additionally, [generateDuplicateImportHints] generates [HintCode.DUPLICATE_IMPORT] hints and
+ * [HintCode.UNUSED_SHOWN_NAME] hints.
+ *
+ * While this class does not yet have support for an "Organize Imports" action, this logic built up
+ * in this class could be used for such an action in the future.
*/
-class HtmlTagInfo {
- /**
- * An array containing all of the tags used in the HTML file.
- */
- List<String> allTags;
-
- /**
- * A table mapping the id's defined in the HTML file to an array containing the names of tags with
- * that identifier.
- */
- HashMap<String, String> idToTagMap;
-
- /**
- * A table mapping the classes defined in the HTML file to an array containing the names of tags
- * with that class.
- */
- HashMap<String, List<String>> classToTagsMap;
-
- /**
- * Initialize a newly created information holder to hold the given information about the tags in
- * an HTML file.
- *
- * @param allTags an array containing all of the tags used in the HTML file
- * @param idToTagMap a table mapping the id's defined in the HTML file to an array containing the
- * names of tags with that identifier
- * @param classToTagsMap a table mapping the classes defined in the HTML file to an array
- * containing the names of tags with that class
- */
- HtmlTagInfo(this.allTags, this.idToTagMap, this.classToTagsMap);
-
+class ImportsVerifier {
/**
- * Return an array containing the tags that have the given class, or {@code null} if there are no
- * such tags.
+ * A list of [ImportDirective]s that the current library imports, but does not use.
*
- * @return an array containing the tags that have the given class
- */
- List<String> getTagsWithClass(String identifier) {
- return classToTagsMap[identifier];
- }
-
- /**
- * Return the tag that has the given identifier, or {@code null} if there is no such tag (the
- * identifier is not defined).
+ * As identifiers are visited by this visitor and an import has been identified as being used
+ * by the library, the [ImportDirective] is removed from this list. After all the sources in the
+ * library have been evaluated, this list represents the set of unused imports.
*
- * @return the tag that has the given identifier
- */
- String getTagWithId(String identifier) {
- return idToTagMap[identifier];
- }
-}
-
-/**
- * Instances of the class {@code HtmlTagInfoBuilder} gather information about the tags used in one
- * or more HTML structures.
- */
-class HtmlTagInfoBuilder implements ht.XmlVisitor {
- /**
- * The name of the 'id' attribute.
- */
- static final String ID_ATTRIBUTE = "id";
-
- /**
- * The name of the 'class' attribute.
+ * See [ImportsVerifier.generateUnusedImportErrors].
*/
- static final String ID_CLASS = "class";
+ final List<ImportDirective> _unusedImports = <ImportDirective>[];
/**
- * A set containing all of the tag names used in the HTML.
+ * After the list of [unusedImports] has been computed, this list is a proper subset of the
+ * unused imports that are listed more than once.
*/
- HashSet<String> tagSet = new HashSet<String>();
+ final List<ImportDirective> _duplicateImports = <ImportDirective>[];
/**
- * A table mapping the id's that are defined to the tag name with that id.
+ * This is a map between the set of [LibraryElement]s that the current library imports, and the
+ * list of [ImportDirective]s that import each [LibraryElement]. In cases where the current
+ * library imports a library with a single directive (such as `import lib1.dart;`), the library
+ * element will map to a list of one [ImportDirective], which will then be removed from the
+ * [unusedImports] list. In cases where the current library imports a library with multiple
+ * directives (such as `import lib1.dart; import lib1.dart show C;`), the [LibraryElement] will
+ * be mapped to a list of the import directives, and the namespace will need to be used to
+ * compute the correct [ImportDirective] being used; see [_namespaceMap].
*/
- HashMap<String, String> idMap = new HashMap<String, String>();
+ final HashMap<LibraryElement, List<ImportDirective>> _libraryMap =
+ new HashMap<LibraryElement, List<ImportDirective>>();
/**
- * A table mapping the classes that are defined to a set of the tag names with that class.
+ * In cases where there is more than one import directive per library element, this mapping is
+ * used to determine which of the multiple import directives are used by generating a
+ * [Namespace] for each of the imports to do lookups in the same way that they are done from
+ * the [ElementResolver].
*/
- HashMap<String, HashSet<String>> classMap =
- new HashMap<String, HashSet<String>>();
+ final HashMap<ImportDirective, Namespace> _namespaceMap =
+ new HashMap<ImportDirective, Namespace>();
/**
- * Initialize a newly created HTML tag info builder.
+ * This is a map between prefix elements and the import directives from which they are derived. In
+ * cases where a type is referenced via a prefix element, the import directive can be marked as
+ * used (removed from the unusedImports) by looking at the resolved `lib` in `lib.X`,
+ * instead of looking at which library the `lib.X` resolves.
+ *
+ * TODO (jwren) Since multiple [ImportDirective]s can share the same [PrefixElement],
+ * it is possible to have an unreported unused import in situations where two imports use the same
+ * prefix and at least one import directive is used.
*/
- HtmlTagInfoBuilder();
+ final HashMap<PrefixElement, List<ImportDirective>> _prefixElementMap =
+ new HashMap<PrefixElement, List<ImportDirective>>();
/**
- * Create a tag information holder holding all of the information gathered about the tags in the
- * HTML structures that were visited.
+ * A map of identifiers that the current library's imports show, but that the library does not
+ * use.
+ *
+ * Each import directive maps to a list of the identifiers that are imported via the "show"
+ * keyword.
+ *
+ * As each identifier is visited by this visitor, it is identified as being used by the library,
+ * and the identifier is removed from this map (under the import that imported it). After all the
+ * sources in the library have been evaluated, each list in this map's values present the set of
+ * unused shown elements.
*
- * @return the information gathered about the tags in the visited HTML structures
+ * See [ImportsVerifier.generateUnusedShownNameHints].
*/
- HtmlTagInfo getTagInfo() {
- List<String> allTags = tagSet.toList();
- HashMap<String, List<String>> classToTagsMap =
- new HashMap<String, List<String>>();
- classMap.forEach((String key, Set<String> tags) {
- classToTagsMap[key] = tags.toList();
- });
- return new HtmlTagInfo(allTags, idMap, classToTagsMap);
- }
-
- @override
- visitHtmlScriptTagNode(ht.HtmlScriptTagNode node) {
- visitXmlTagNode(node);
- }
-
- @override
- visitHtmlUnit(ht.HtmlUnit node) {
- node.visitChildren(this);
- }
-
- @override
- visitXmlAttributeNode(ht.XmlAttributeNode node) {}
+ final HashMap<ImportDirective, List<SimpleIdentifier>> _unusedShownNamesMap =
+ new HashMap<ImportDirective, List<SimpleIdentifier>>();
- @override
- visitXmlTagNode(ht.XmlTagNode node) {
- node.visitChildren(this);
- String tagName = node.tag;
- tagSet.add(tagName);
- for (ht.XmlAttributeNode attribute in node.attributes) {
- String attributeName = attribute.name;
- if (attributeName == ID_ATTRIBUTE) {
- String attributeValue = attribute.text;
- if (attributeValue != null) {
- String tag = idMap[attributeValue];
- if (tag == null) {
- idMap[attributeValue] = tagName;
- } else {
-// reportError(HtmlWarningCode.MULTIPLY_DEFINED_ID, valueToken);
+ void addImports(CompilationUnit node) {
+ for (Directive directive in node.directives) {
+ if (directive is ImportDirective) {
+ LibraryElement libraryElement = directive.uriElement;
+ if (libraryElement == null) {
+ continue;
+ }
+ _unusedImports.add(directive);
+ //
+ // Initialize prefixElementMap
+ //
+ if (directive.asKeyword != null) {
+ SimpleIdentifier prefixIdentifier = directive.prefix;
+ if (prefixIdentifier != null) {
+ Element element = prefixIdentifier.staticElement;
+ if (element is PrefixElement) {
+ List<ImportDirective> list = _prefixElementMap[element];
+ if (list == null) {
+ list = new List<ImportDirective>();
+ _prefixElementMap[element] = list;
+ }
+ list.add(directive);
+ }
+ // TODO (jwren) Can the element ever not be a PrefixElement?
}
}
- } else if (attributeName == ID_CLASS) {
- String attributeValue = attribute.text;
- if (attributeValue != null) {
- HashSet<String> tagList = classMap[attributeValue];
- if (tagList == null) {
- tagList = new HashSet<String>();
- classMap[attributeValue] = tagList;
+ //
+ // Initialize libraryMap: libraryElement -> importDirective
+ //
+ _putIntoLibraryMap(libraryElement, directive);
+ //
+ // For this new addition to the libraryMap, also recursively add any
+ // exports from the libraryElement.
+ //
+ _addAdditionalLibrariesForExports(
+ libraryElement, directive, new HashSet<LibraryElement>());
+ _addShownNames(directive);
+ }
+ }
+ if (_unusedImports.length > 1) {
+ // order the list of unusedImports to find duplicates in faster than
+ // O(n^2) time
+ List<ImportDirective> importDirectiveArray =
+ new List<ImportDirective>.from(_unusedImports);
+ importDirectiveArray.sort(ImportDirective.COMPARATOR);
+ ImportDirective currentDirective = importDirectiveArray[0];
+ for (int i = 1; i < importDirectiveArray.length; i++) {
+ ImportDirective nextDirective = importDirectiveArray[i];
+ if (ImportDirective.COMPARATOR(currentDirective, nextDirective) == 0) {
+ // Add either the currentDirective or nextDirective depending on which
+ // comes second, this guarantees that the first of the duplicates
+ // won't be highlighted.
+ if (currentDirective.offset < nextDirective.offset) {
+ _duplicateImports.add(nextDirective);
} else {
-// reportError(HtmlWarningCode.MULTIPLY_DEFINED_ID, valueToken);
+ _duplicateImports.add(currentDirective);
}
- tagList.add(tagName);
}
+ currentDirective = nextDirective;
}
}
}
-// /**
-// * Report an error with the given error code at the given location. Use the given arguments to
-// * compose the error message.
-// *
-// * @param errorCode the error code of the error to be reported
-// * @param offset the offset of the first character to be highlighted
-// * @param length the number of characters to be highlighted
-// * @param arguments the arguments used to compose the error message
-// */
-// private void reportError(ErrorCode errorCode, Token token, Object... arguments) {
-// errorListener.onError(new AnalysisError(
-// htmlElement.getSource(),
-// token.getOffset(),
-// token.getLength(),
-// errorCode,
-// arguments));
-// }
-//
-// /**
-// * Report an error with the given error code at the given location. Use the given arguments to
-// * compose the error message.
-// *
-// * @param errorCode the error code of the error to be reported
-// * @param offset the offset of the first character to be highlighted
-// * @param length the number of characters to be highlighted
-// * @param arguments the arguments used to compose the error message
-// */
-// private void reportError(ErrorCode errorCode, int offset, int length, Object... arguments) {
-// errorListener.onError(new AnalysisError(
-// htmlElement.getSource(),
-// offset,
-// length,
-// errorCode,
-// arguments));
-// }
-}
-
-/**
- * Instances of the class `HtmlUnitBuilder` build an element model for a single HTML unit.
- */
-class HtmlUnitBuilder implements ht.XmlVisitor<Object> {
- static String _SRC = "src";
-
- /**
- * The analysis context in which the element model will be built.
- */
- final InternalAnalysisContext _context;
-
- /**
- * The error listener to which errors will be reported.
- */
- RecordingErrorListener _errorListener;
-
- /**
- * The HTML element being built.
- */
- HtmlElementImpl _htmlElement;
-
- /**
- * The elements in the path from the HTML unit to the current tag node.
- */
- List<ht.XmlTagNode> _parentNodes;
-
- /**
- * The script elements being built.
- */
- List<HtmlScriptElement> _scripts;
-
/**
- * A set of the libraries that were resolved while resolving the HTML unit.
- */
- Set<Library> _resolvedLibraries = new HashSet<Library>();
-
- /**
- * Initialize a newly created HTML unit builder.
+ * Any time after the defining compilation unit has been visited by this visitor, this method can
+ * be called to report an [HintCode.DUPLICATE_IMPORT] hint for each of the import directives
+ * in the [duplicateImports] list.
*
- * @param context the analysis context in which the element model will be built
+ * @param errorReporter the error reporter to report the set of [HintCode.DUPLICATE_IMPORT]
+ * hints to
*/
- HtmlUnitBuilder(this._context) {
- this._errorListener = new RecordingErrorListener();
+ void generateDuplicateImportHints(ErrorReporter errorReporter) {
+ int length = _duplicateImports.length;
+ for (int i = 0; i < length; i++) {
+ errorReporter.reportErrorForNode(
+ HintCode.DUPLICATE_IMPORT, _duplicateImports[i].uri);
+ }
}
/**
- * Return the listener to which analysis errors will be reported.
+ * Report an [HintCode.UNUSED_IMPORT] hint for each unused import.
*
- * @return the listener to which analysis errors will be reported
- */
- RecordingErrorListener get errorListener => _errorListener;
-
- /**
- * Return an array containing information about all of the libraries that were resolved.
+ * Only call this method after all of the compilation units have been visited by this visitor.
*
- * @return an array containing the libraries that were resolved
+ * @param errorReporter the error reporter used to report the set of [HintCode.UNUSED_IMPORT]
+ * hints
*/
- Set<Library> get resolvedLibraries => _resolvedLibraries;
+ void generateUnusedImportHints(ErrorReporter errorReporter) {
+ int length = _unusedImports.length;
+ for (int i = 0; i < length; i++) {
+ ImportDirective unusedImport = _unusedImports[i];
+ // Check that the imported URI exists and isn't dart:core
+ ImportElement importElement = unusedImport.element;
+ if (importElement != null) {
+ LibraryElement libraryElement = importElement.importedLibrary;
+ if (libraryElement == null ||
+ libraryElement.isDartCore ||
+ libraryElement.isSynthetic) {
+ continue;
+ }
+ }
+ errorReporter.reportErrorForNode(
+ HintCode.UNUSED_IMPORT, unusedImport.uri);
+ }
+ }
/**
- * Build the HTML element for the given source.
+ * Report an [HintCode.UNUSED_SHOWN_NAME] hint for each unused shown name.
+ *
+ * Only call this method after all of the compilation units have been visited by this visitor.
*
- * @param source the source describing the compilation unit
- * @param unit the AST structure representing the HTML
- * @throws AnalysisException if the analysis could not be performed
+ * @param errorReporter the error reporter used to report the set of [HintCode.UNUSED_SHOWN_NAME]
+ * hints
*/
- HtmlElementImpl buildHtmlElement(Source source, ht.HtmlUnit unit) {
- HtmlElementImpl result = new HtmlElementImpl(_context, source.shortName);
- result.source = source;
- _htmlElement = result;
- unit.accept(this);
- _htmlElement = null;
- unit.element = result;
- return result;
+ void generateUnusedShownNameHints(ErrorReporter reporter) {
+ _unusedShownNamesMap.forEach(
+ (ImportDirective importDirective, List<SimpleIdentifier> identifiers) {
+ if (_unusedImports.contains(importDirective)) {
+ // This import is actually wholly unused, not just one or more shown names from it.
+ // This is then an "unused import", rather than unused shown names.
+ return;
+ }
+ int length = identifiers.length;
+ for (int i = 0; i < length; i++) {
+ Identifier identifier = identifiers[i];
+ reporter.reportErrorForNode(
+ HintCode.UNUSED_SHOWN_NAME, identifier, [identifier.name]);
+ }
+ });
}
- @override
- Object visitHtmlScriptTagNode(ht.HtmlScriptTagNode node) {
- if (_parentNodes.contains(node)) {
- return _reportCircularity(node);
- }
- _parentNodes.add(node);
- try {
- Source htmlSource = _htmlElement.source;
- ht.XmlAttributeNode scriptAttribute = _getScriptSourcePath(node);
- String scriptSourcePath =
- scriptAttribute == null ? null : scriptAttribute.text;
- if (node.attributeEnd.type == ht.TokenType.GT &&
- scriptSourcePath == null) {
- EmbeddedHtmlScriptElementImpl script =
- new EmbeddedHtmlScriptElementImpl(node);
- try {
- LibraryResolver resolver = new LibraryResolver(_context);
- LibraryElementImpl library =
- resolver.resolveEmbeddedLibrary(htmlSource, node.script, true);
- script.scriptLibrary = library;
- _resolvedLibraries.addAll(resolver.resolvedLibraries);
- _errorListener.addAll(resolver.errorListener);
- } on AnalysisException catch (exception, stackTrace) {
- //TODO (danrubel): Handle or forward the exception
- AnalysisEngine.instance.logger.logError(
- "Could not resolve script tag",
- new CaughtException(exception, stackTrace));
- }
- node.scriptElement = script;
- _scripts.add(script);
- } else {
- ExternalHtmlScriptElementImpl script =
- new ExternalHtmlScriptElementImpl(node);
- if (scriptSourcePath != null) {
- try {
- scriptSourcePath = Uri.encodeFull(scriptSourcePath);
- // Force an exception to be thrown if the URI is invalid so that we
- // can report the problem.
- parseUriWithException(scriptSourcePath);
- Source scriptSource =
- _context.sourceFactory.resolveUri(htmlSource, scriptSourcePath);
- script.scriptSource = scriptSource;
- if (!_context.exists(scriptSource)) {
- _reportValueError(HtmlWarningCode.URI_DOES_NOT_EXIST,
- scriptAttribute, [scriptSourcePath]);
- }
- } on URISyntaxException {
- _reportValueError(HtmlWarningCode.INVALID_URI, scriptAttribute,
- [scriptSourcePath]);
- }
- }
- node.scriptElement = script;
- _scripts.add(script);
- }
- } finally {
- _parentNodes.remove(node);
- }
- return null;
- }
-
- @override
- Object visitHtmlUnit(ht.HtmlUnit node) {
- _parentNodes = new List<ht.XmlTagNode>();
- _scripts = new List<HtmlScriptElement>();
- try {
- node.visitChildren(this);
- _htmlElement.scripts = new List.from(_scripts);
- } finally {
- _scripts = null;
- _parentNodes = null;
- }
- return null;
- }
-
- @override
- Object visitXmlAttributeNode(ht.XmlAttributeNode node) => null;
-
- @override
- Object visitXmlTagNode(ht.XmlTagNode node) {
- if (_parentNodes.contains(node)) {
- return _reportCircularity(node);
- }
- _parentNodes.add(node);
- try {
- node.visitChildren(this);
- } finally {
- _parentNodes.remove(node);
- }
- return null;
- }
-
- /**
- * Return the first source attribute for the given tag node, or `null` if it does not exist.
- *
- * @param node the node containing attributes
- * @return the source attribute contained in the given tag
- */
- ht.XmlAttributeNode _getScriptSourcePath(ht.XmlTagNode node) {
- for (ht.XmlAttributeNode attribute in node.attributes) {
- if (attribute.name == _SRC) {
- return attribute;
- }
- }
- return null;
- }
-
- Object _reportCircularity(ht.XmlTagNode node) {
- //
- // This should not be possible, but we have an error report that suggests
- // that it happened at least once. This code will guard against infinite
- // recursion and might help us identify the cause of the issue.
- //
- StringBuffer buffer = new StringBuffer();
- buffer.write("Found circularity in XML nodes: ");
- bool first = true;
- for (ht.XmlTagNode pathNode in _parentNodes) {
- if (first) {
- first = false;
- } else {
- buffer.write(", ");
- }
- String tagName = pathNode.tag;
- if (identical(pathNode, node)) {
- buffer.write("*");
- buffer.write(tagName);
- buffer.write("*");
- } else {
- buffer.write(tagName);
- }
- }
- AnalysisEngine.instance.logger.logError(buffer.toString());
- return null;
- }
-
- /**
- * Report an error with the given error code at the given location. Use the given arguments to
- * compose the error message.
- *
- * @param errorCode the error code of the error to be reported
- * @param offset the offset of the first character to be highlighted
- * @param length the number of characters to be highlighted
- * @param arguments the arguments used to compose the error message
- */
- void _reportErrorForOffset(
- ErrorCode errorCode, int offset, int length, List<Object> arguments) {
- _errorListener.onError(new AnalysisError(
- _htmlElement.source, offset, length, errorCode, arguments));
- }
-
- /**
- * Report an error with the given error code at the location of the value of the given attribute.
- * Use the given arguments to compose the error message.
- *
- * @param errorCode the error code of the error to be reported
- * @param offset the offset of the first character to be highlighted
- * @param length the number of characters to be highlighted
- * @param arguments the arguments used to compose the error message
- */
- void _reportValueError(ErrorCode errorCode, ht.XmlAttributeNode attribute,
- List<Object> arguments) {
- int offset = attribute.valueToken.offset + 1;
- int length = attribute.valueToken.length - 2;
- _reportErrorForOffset(errorCode, offset, length, arguments);
- }
-}
-
-/**
- * Instances of the class `ImplicitLabelScope` represent the scope statements
- * that can be the target of unlabeled break and continue statements.
- */
-class ImplicitLabelScope {
- /**
- * The implicit label scope associated with the top level of a function.
- */
- static const ImplicitLabelScope ROOT = const ImplicitLabelScope._(null, null);
-
- /**
- * The implicit label scope enclosing this implicit label scope.
- */
- final ImplicitLabelScope outerScope;
-
- /**
- * The statement that acts as a target for break and/or continue statements
- * at this scoping level.
- */
- final Statement statement;
-
- /**
- * Private constructor.
- */
- const ImplicitLabelScope._(this.outerScope, this.statement);
-
- /**
- * Get the statement which should be the target of an unlabeled `break` or
- * `continue` statement, or `null` if there is no appropriate target.
- */
- Statement getTarget(bool isContinue) {
- if (outerScope == null) {
- // This scope represents the toplevel of a function body, so it doesn't
- // match either break or continue.
- return null;
- }
- if (isContinue && statement is SwitchStatement) {
- return outerScope.getTarget(isContinue);
- }
- return statement;
- }
-
- /**
- * Initialize a newly created scope to represent a switch statement or loop
- * nested within the current scope. [statement] is the statement associated
- * with the newly created scope.
- */
- ImplicitLabelScope nest(Statement statement) =>
- new ImplicitLabelScope._(this, statement);
-}
-
-/**
- * Instances of the class `ImportsVerifier` visit all of the referenced libraries in the
- * source code verifying that all of the imports are used, otherwise a
- * [HintCode.UNUSED_IMPORT] is generated with
- * [generateUnusedImportHints].
- *
- * While this class does not yet have support for an "Organize Imports" action, this logic built up
- * in this class could be used for such an action in the future.
- */
-class ImportsVerifier /*extends RecursiveAstVisitor<Object>*/ {
- /**
- * A list of [ImportDirective]s that the current library imports, as identifiers are visited
- * by this visitor and an import has been identified as being used by the library, the
- * [ImportDirective] is removed from this list. After all the sources in the library have
- * been evaluated, this list represents the set of unused imports.
- *
- * See [ImportsVerifier.generateUnusedImportErrors].
- */
- final List<ImportDirective> _unusedImports = <ImportDirective>[];
-
- /**
- * After the list of [unusedImports] has been computed, this list is a proper subset of the
- * unused imports that are listed more than once.
- */
- final List<ImportDirective> _duplicateImports = <ImportDirective>[];
-
- /**
- * This is a map between the set of [LibraryElement]s that the current library imports, and
- * a list of [ImportDirective]s that imports the library. In cases where the current library
- * imports a library with a single directive (such as `import lib1.dart;`), the library
- * element will map to a list of one [ImportDirective], which will then be removed from the
- * [unusedImports] list. In cases where the current library imports a library with multiple
- * directives (such as `import lib1.dart; import lib1.dart show C;`), the
- * [LibraryElement] will be mapped to a list of the import directives, and the namespace
- * will need to be used to compute the correct [ImportDirective] being used, see
- * [namespaceMap].
- */
- final HashMap<LibraryElement, List<ImportDirective>> _libraryMap =
- new HashMap<LibraryElement, List<ImportDirective>>();
-
- /**
- * In cases where there is more than one import directive per library element, this mapping is
- * used to determine which of the multiple import directives are used by generating a
- * [Namespace] for each of the imports to do lookups in the same way that they are done from
- * the [ElementResolver].
- */
- final HashMap<ImportDirective, Namespace> _namespaceMap =
- new HashMap<ImportDirective, Namespace>();
-
- /**
- * This is a map between prefix elements and the import directives from which they are derived. In
- * cases where a type is referenced via a prefix element, the import directive can be marked as
- * used (removed from the unusedImports) by looking at the resolved `lib` in `lib.X`,
- * instead of looking at which library the `lib.X` resolves.
- *
- * TODO (jwren) Since multiple [ImportDirective]s can share the same [PrefixElement],
- * it is possible to have an unreported unused import in situations where two imports use the same
- * prefix and at least one import directive is used.
- */
- final HashMap<PrefixElement, List<ImportDirective>> _prefixElementMap =
- new HashMap<PrefixElement, List<ImportDirective>>();
-
- void addImports(CompilationUnit node) {
- for (Directive directive in node.directives) {
- if (directive is ImportDirective) {
- ImportDirective importDirective = directive;
- LibraryElement libraryElement = importDirective.uriElement;
- if (libraryElement != null) {
- _unusedImports.add(importDirective);
- //
- // Initialize prefixElementMap
- //
- if (importDirective.asKeyword != null) {
- SimpleIdentifier prefixIdentifier = importDirective.prefix;
- if (prefixIdentifier != null) {
- Element element = prefixIdentifier.staticElement;
- if (element is PrefixElement) {
- PrefixElement prefixElementKey = element;
- List<ImportDirective> list =
- _prefixElementMap[prefixElementKey];
- if (list == null) {
- list = new List<ImportDirective>();
- _prefixElementMap[prefixElementKey] = list;
- }
- list.add(importDirective);
- }
- // TODO (jwren) Can the element ever not be a PrefixElement?
- }
- }
- //
- // Initialize libraryMap: libraryElement -> importDirective
- //
- _putIntoLibraryMap(libraryElement, importDirective);
- //
- // For this new addition to the libraryMap, also recursively add any
- // exports from the libraryElement.
- //
- _addAdditionalLibrariesForExports(
- libraryElement, importDirective, new List<LibraryElement>());
- }
- }
- }
- if (_unusedImports.length > 1) {
- // order the list of unusedImports to find duplicates in faster than
- // O(n^2) time
- List<ImportDirective> importDirectiveArray =
- new List<ImportDirective>.from(_unusedImports);
- importDirectiveArray.sort(ImportDirective.COMPARATOR);
- ImportDirective currentDirective = importDirectiveArray[0];
- for (int i = 1; i < importDirectiveArray.length; i++) {
- ImportDirective nextDirective = importDirectiveArray[i];
- if (ImportDirective.COMPARATOR(currentDirective, nextDirective) == 0) {
- // Add either the currentDirective or nextDirective depending on which
- // comes second, this guarantees that the first of the duplicates
- // won't be highlighted.
- if (currentDirective.offset < nextDirective.offset) {
- _duplicateImports.add(nextDirective);
- } else {
- _duplicateImports.add(currentDirective);
- }
- }
- currentDirective = nextDirective;
- }
- }
- }
-
- /**
- * Any time after the defining compilation unit has been visited by this visitor, this method can
- * be called to report an [HintCode.DUPLICATE_IMPORT] hint for each of the import directives
- * in the [duplicateImports] list.
- *
- * @param errorReporter the error reporter to report the set of [HintCode.DUPLICATE_IMPORT]
- * hints to
- */
- void generateDuplicateImportHints(ErrorReporter errorReporter) {
- for (ImportDirective duplicateImport in _duplicateImports) {
- errorReporter.reportErrorForNode(
- HintCode.DUPLICATE_IMPORT, duplicateImport.uri);
- }
- }
-
- /**
- * After all of the compilation units have been visited by this visitor, this method can be called
- * to report an [HintCode.UNUSED_IMPORT] hint for each of the import directives in the
- * [unusedImports] list.
- *
- * @param errorReporter the error reporter to report the set of [HintCode.UNUSED_IMPORT]
- * hints to
- */
- void generateUnusedImportHints(ErrorReporter errorReporter) {
- for (ImportDirective unusedImport in _unusedImports) {
- // Check that the import isn't dart:core
- ImportElement importElement = unusedImport.element;
- if (importElement != null) {
- LibraryElement libraryElement = importElement.importedLibrary;
- if (libraryElement != null && libraryElement.isDartCore) {
- continue;
- }
- }
- errorReporter.reportErrorForNode(
- HintCode.UNUSED_IMPORT, unusedImport.uri);
- }
- }
-
- /**
- * Remove elements from [_unusedImports] using the given [usedElements].
- */
- void removeUsedElements(UsedImportedElements usedElements) {
- // Stop if all the imports are known to be used.
- if (_unusedImports.isEmpty) {
- return;
+ /**
+ * Remove elements from [_unusedImports] using the given [usedElements].
+ */
+ void removeUsedElements(UsedImportedElements usedElements) {
+ // Stop if all the imports and shown names are known to be used.
+ if (_unusedImports.isEmpty && _unusedShownNamesMap.isEmpty) {
+ return;
}
// Process import prefixes.
- for (PrefixElement prefix in usedElements.prefixes) {
+ usedElements.prefixMap
+ .forEach((PrefixElement prefix, List<Element> elements) {
List<ImportDirective> importDirectives = _prefixElementMap[prefix];
if (importDirectives != null) {
- for (ImportDirective importDirective in importDirectives) {
+ int importLength = importDirectives.length;
+ for (int i = 0; i < importLength; i++) {
+ ImportDirective importDirective = importDirectives[i];
_unusedImports.remove(importDirective);
+ int elementLength = elements.length;
+ for (int j = 0; j < elementLength; j++) {
+ Element element = elements[j];
+ _removeFromUnusedShownNamesMap(element, importDirective);
+ }
}
}
- }
+ });
// Process top-level elements.
for (Element element in usedElements.elements) {
- // Stop if all the imports are known to be used.
- if (_unusedImports.isEmpty) {
+ // Stop if all the imports and shown names are known to be used.
+ if (_unusedImports.isEmpty && _unusedShownNamesMap.isEmpty) {
return;
}
- // Prepare import directives for this library.
+ // Prepare import directives for this element's library.
LibraryElement library = element.library;
List<ImportDirective> importsLibrary = _libraryMap[library];
if (importsLibrary == null) {
+ // element's library is not imported. Must be the current library.
continue;
}
// If there is only one import directive for this library, then it must be
@@ -5558,14 +4595,16 @@ class ImportsVerifier /*extends RecursiveAstVisitor<Object>*/ {
if (importsLibrary.length == 1) {
ImportDirective usedImportDirective = importsLibrary[0];
_unusedImports.remove(usedImportDirective);
+ _removeFromUnusedShownNamesMap(element, usedImportDirective);
continue;
}
// Otherwise, find import directives using namespaces.
String name = element.displayName;
for (ImportDirective importDirective in importsLibrary) {
Namespace namespace = _computeNamespace(importDirective);
- if (namespace != null && namespace.get(name) != null) {
+ if (namespace?.get(name) != null) {
_unusedImports.remove(importDirective);
+ _removeFromUnusedShownNamesMap(element, importDirective);
}
}
}
@@ -5575,22 +4614,46 @@ class ImportsVerifier /*extends RecursiveAstVisitor<Object>*/ {
* Recursively add any exported library elements into the [libraryMap].
*/
void _addAdditionalLibrariesForExports(LibraryElement library,
- ImportDirective importDirective, List<LibraryElement> exportPath) {
- if (exportPath.contains(library)) {
+ ImportDirective importDirective, Set<LibraryElement> visitedLibraries) {
+ if (!visitedLibraries.add(library)) {
return;
}
- exportPath.add(library);
- for (LibraryElement exportedLibraryElt in library.exportedLibraries) {
- _putIntoLibraryMap(exportedLibraryElt, importDirective);
+ List<ExportElement> exports = library.exports;
+ int length = exports.length;
+ for (int i = 0; i < length; i++) {
+ ExportElement exportElt = exports[i];
+ LibraryElement exportedLibrary = exportElt.exportedLibrary;
+ _putIntoLibraryMap(exportedLibrary, importDirective);
_addAdditionalLibrariesForExports(
- exportedLibraryElt, importDirective, exportPath);
+ exportedLibrary, importDirective, visitedLibraries);
+ }
+ }
+
+ /**
+ * Add every shown name from [importDirective] into [_unusedShownNamesMap].
+ */
+ void _addShownNames(ImportDirective importDirective) {
+ if (importDirective.combinators == null) {
+ return;
+ }
+ List<SimpleIdentifier> identifiers = new List<SimpleIdentifier>();
+ _unusedShownNamesMap[importDirective] = identifiers;
+ for (Combinator combinator in importDirective.combinators) {
+ if (combinator is ShowCombinator) {
+ for (SimpleIdentifier name in combinator.shownNames) {
+ if (name.staticElement != null) {
+ identifiers.add(name);
+ }
+ }
+ }
}
}
/**
- * Lookup and return the [Namespace] from the [namespaceMap], if the map does not
- * have the computed namespace, compute it and cache it in the map. If the import directive is not
- * resolved or is not resolvable, `null` is returned.
+ * Lookup and return the [Namespace] from the [_namespaceMap].
+ *
+ * If the map does not have the computed namespace, compute it and cache it in the map. If
+ * [importDirective] is not resolved or is not resolvable, `null` is returned.
*
* @param importDirective the import directive used to compute the returned namespace
* @return the computed or looked up [Namespace]
@@ -5625,1065 +4688,366 @@ class ImportsVerifier /*extends RecursiveAstVisitor<Object>*/ {
}
importList.add(importDirective);
}
+
+ /**
+ * Remove [element] from the list of names shown by [importDirective].
+ */
+ void _removeFromUnusedShownNamesMap(
+ Element element, ImportDirective importDirective) {
+ List<SimpleIdentifier> identifiers = _unusedShownNamesMap[importDirective];
+ if (identifiers == null) {
+ return;
+ }
+ int length = identifiers.length;
+ for (int i = 0; i < length; i++) {
+ Identifier identifier = identifiers[i];
+ if (element is PropertyAccessorElement) {
+ // If the getter or setter of a variable is used, then the variable (the
+ // shown name) is used.
+ if (identifier.staticElement == element.variable) {
+ identifiers.remove(identifier);
+ break;
+ }
+ } else {
+ if (identifier.staticElement == element) {
+ identifiers.remove(identifier);
+ break;
+ }
+ }
+ }
+ if (identifiers.isEmpty) {
+ _unusedShownNamesMap.remove(importDirective);
+ }
+ }
}
/**
- * Instances of the class `InheritanceManager` manage the knowledge of where class members
- * (methods, getters & setters) are inherited from.
+ * Maintains and manages contextual type information used for
+ * inferring types.
*/
-class InheritanceManager {
- /**
- * The [LibraryElement] that is managed by this manager.
- */
- LibraryElement _library;
+class InferenceContext {
+ // TODO(leafp): Consider replacing these node properties with a
+ // hash table help in an instance of this class.
+ static const String _typeProperty =
+ 'analyzer.src.generated.InferenceContext.contextType';
/**
- * This is a mapping between each [ClassElement] and a map between the [String] member
- * names and the associated [ExecutableElement] in the mixin and superclass chain.
+ * The error listener on which to record inference information.
*/
- HashMap<ClassElement, MemberMap> _classLookup;
+ final ErrorReporter _errorReporter;
/**
- * This is a mapping between each [ClassElement] and a map between the [String] member
- * names and the associated [ExecutableElement] in the interface set.
+ * If true, emit hints when types are inferred
*/
- HashMap<ClassElement, MemberMap> _interfaceLookup;
+ final bool _inferenceHints;
/**
- * A map between each visited [ClassElement] and the set of [AnalysisError]s found on
- * the class element.
+ * Type provider, needed for type matching.
*/
- HashMap<ClassElement, HashSet<AnalysisError>> _errorsInClassElement =
- new HashMap<ClassElement, HashSet<AnalysisError>>();
+ final TypeProvider _typeProvider;
/**
- * Initialize a newly created inheritance manager.
- *
- * @param library the library element context that the inheritance mappings are being generated
+ * The type system in use.
*/
- InheritanceManager(LibraryElement library) {
- this._library = library;
- _classLookup = new HashMap<ClassElement, MemberMap>();
- _interfaceLookup = new HashMap<ClassElement, MemberMap>();
- }
+ final TypeSystem _typeSystem;
/**
- * Set the new library element context.
+ * When no context type is available, this will track the least upper bound
+ * of all return statements in a lambda.
*
- * @param library the new library element
+ * This will always be kept in sync with [_returnStack].
*/
- void set libraryElement(LibraryElement library) {
- this._library = library;
- }
+ final List<DartType> _inferredReturn = <DartType>[];
/**
- * Return the set of [AnalysisError]s found on the passed [ClassElement], or
- * `null` if there are none.
- *
- * @param classElt the class element to query
- * @return the set of [AnalysisError]s found on the passed [ClassElement], or
- * `null` if there are none
+ * A stack of return types for all of the enclosing
+ * functions and methods.
*/
- HashSet<AnalysisError> getErrors(ClassElement classElt) =>
- _errorsInClassElement[classElt];
+ final List<DartType> _returnStack = <DartType>[];
- /**
- * Get and return a mapping between the set of all string names of the members inherited from the
- * passed [ClassElement] superclass hierarchy, and the associated [ExecutableElement].
- *
- * @param classElt the class element to query
- * @return a mapping between the set of all members inherited from the passed [ClassElement]
- * superclass hierarchy, and the associated [ExecutableElement]
- */
- MemberMap getMapOfMembersInheritedFromClasses(ClassElement classElt) =>
- _computeClassChainLookupMap(classElt, new HashSet<ClassElement>());
+ InferenceContext._(this._errorReporter, TypeProvider typeProvider,
+ this._typeSystem, this._inferenceHints)
+ : _typeProvider = typeProvider;
/**
- * Get and return a mapping between the set of all string names of the members inherited from the
- * passed [ClassElement] interface hierarchy, and the associated [ExecutableElement].
+ * Get the return type of the current enclosing function, if any.
*
- * @param classElt the class element to query
- * @return a mapping between the set of all string names of the members inherited from the passed
- * [ClassElement] interface hierarchy, and the associated [ExecutableElement].
+ * The type returned for a function is the type that is expected
+ * to be used in a return or yield context. For ordinary functions
+ * this is the same as the return type of the function. For async
+ * functions returning Future<T> and for generator functions
+ * returning Stream<T> or Iterable<T>, this is T.
*/
- MemberMap getMapOfMembersInheritedFromInterfaces(ClassElement classElt) =>
- _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>());
+ DartType get returnContext =>
+ _returnStack.isNotEmpty ? _returnStack.last : null;
/**
- * Given some [ClassElement] and some member name, this returns the
- * [ExecutableElement] that the class inherits from the mixins,
- * superclasses or interfaces, that has the member name, if no member is inherited `null` is
- * returned.
+ * Records the type of the expression of a return statement.
*
- * @param classElt the class element to query
- * @param memberName the name of the executable element to find and return
- * @return the inherited executable element with the member name, or `null` if no such
- * member exists
+ * This will be used for inferring a block bodied lambda, if no context
+ * type was available.
*/
- ExecutableElement lookupInheritance(
- ClassElement classElt, String memberName) {
- if (memberName == null || memberName.isEmpty) {
- return null;
- }
- ExecutableElement executable = _computeClassChainLookupMap(
- classElt, new HashSet<ClassElement>()).get(memberName);
- if (executable == null) {
- return _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>())
- .get(memberName);
+ void addReturnOrYieldType(DartType type) {
+ if (_returnStack.isEmpty) {
+ return;
}
- return executable;
+
+ DartType inferred = _inferredReturn.last;
+ inferred = _typeSystem.getLeastUpperBound(_typeProvider, type, inferred);
+ _inferredReturn[_inferredReturn.length - 1] = inferred;
}
/**
- * Given some [ClassElement] and some member name, this returns the
- * [ExecutableElement] that the class either declares itself, or
- * inherits, that has the member name, if no member is inherited `null` is returned.
+ * Match type [t1] against type [t2] as follows.
+ * If `t1 = I<dynamic, ..., dynamic>`, then look for a supertype
+ * of t1 of the form `K<S0, ..., Sm>` where `t2 = K<S0', ..., Sm'>`
+ * If the supertype exists, use the constraints `S0 <: S0', ... Sm <: Sm'`
+ * to derive a concrete instantation for I of the form `<T0, ..., Tn>`,
+ * such that `I<T0, .., Tn> <: t2`
+ */
+ List<DartType> matchTypes(DartType t1, DartType t2) =>
+ (t1 is InterfaceType && t2 is InterfaceType) ? _matchTypes(t1, t2) : null;
+
+ /**
+ * Pop a return type off of the return stack.
*
- * @param classElt the class element to query
- * @param memberName the name of the executable element to find and return
- * @return the inherited executable element with the member name, or `null` if no such
- * member exists
+ * Also record any inferred return type using [setType], unless this node
+ * already has a context type. This recorded type will be the least upper
+ * bound of all types added with [addReturnOrYieldType].
*/
- ExecutableElement lookupMember(ClassElement classElt, String memberName) {
- ExecutableElement element = _lookupMemberInClass(classElt, memberName);
- if (element != null) {
- return element;
+ void popReturnContext(BlockFunctionBody node) {
+ if (_returnStack.isNotEmpty && _inferredReturn.isNotEmpty) {
+ DartType context = _returnStack.removeLast() ?? DynamicTypeImpl.instance;
+ DartType inferred = _inferredReturn.removeLast();
+ if (inferred.isBottom) {
+ return;
+ }
+
+ if (context is FutureUnionType) {
+ // Try and match the Future type first.
+ if (_typeSystem.isSubtypeOf(inferred, context.futureOfType) ||
+ _typeSystem.isSubtypeOf(inferred, context.type)) {
+ setType(node, inferred);
+ }
+ } else if (_typeSystem.isSubtypeOf(inferred, context)) {
+ setType(node, inferred);
+ }
+ } else {
+ assert(false);
}
- return lookupInheritance(classElt, memberName);
}
/**
- * Given some [InterfaceType] and some member name, this returns the
- * [FunctionType] of the [ExecutableElement] that the
- * class either declares itself, or inherits, that has the member name, if no member is inherited
- * `null` is returned. The returned [FunctionType] has all type
- * parameters substituted with corresponding type arguments from the given [InterfaceType].
- *
- * @param interfaceType the interface type to query
- * @param memberName the name of the executable element to find and return
- * @return the member's function type, or `null` if no such member exists
+ * Push a block function body's return type onto the return stack.
*/
- FunctionType lookupMemberType(
- InterfaceType interfaceType, String memberName) {
- ExecutableElement iteratorMember =
- lookupMember(interfaceType.element, memberName);
- if (iteratorMember == null) {
- return null;
- }
- return substituteTypeArgumentsInMemberFromInheritance(
- iteratorMember.type, memberName, interfaceType);
- }
-
- /**
- * Determine the set of methods which is overridden by the given class member. If no member is
- * inherited, an empty list is returned. If one of the inherited members is a
- * [MultiplyInheritedExecutableElement], then it is expanded into its constituent inherited
- * elements.
- *
- * @param classElt the class to query
- * @param memberName the name of the class member to query
- * @return a list of overridden methods
- */
- List<ExecutableElement> lookupOverrides(
- ClassElement classElt, String memberName) {
- List<ExecutableElement> result = new List<ExecutableElement>();
- if (memberName == null || memberName.isEmpty) {
- return result;
- }
- List<MemberMap> interfaceMaps =
- _gatherInterfaceLookupMaps(classElt, new HashSet<ClassElement>());
- if (interfaceMaps != null) {
- for (MemberMap interfaceMap in interfaceMaps) {
- ExecutableElement overriddenElement = interfaceMap.get(memberName);
- if (overriddenElement != null) {
- if (overriddenElement is MultiplyInheritedExecutableElement) {
- MultiplyInheritedExecutableElement multiplyInheritedElement =
- overriddenElement;
- for (ExecutableElement element
- in multiplyInheritedElement.inheritedElements) {
- result.add(element);
- }
- } else {
- result.add(overriddenElement);
- }
- }
- }
- }
- return result;
+ void pushReturnContext(BlockFunctionBody node) {
+ _returnStack.add(getContext(node));
+ _inferredReturn.add(BottomTypeImpl.instance);
}
/**
- * This method takes some inherited [FunctionType], and resolves all the parameterized types
- * in the function type, dependent on the class in which it is being overridden.
- *
- * @param baseFunctionType the function type that is being overridden
- * @param memberName the name of the member, this is used to lookup the inheritance path of the
- * override
- * @param definingType the type that is overriding the member
- * @return the passed function type with any parameterized types substituted
- */
- FunctionType substituteTypeArgumentsInMemberFromInheritance(
- FunctionType baseFunctionType,
- String memberName,
- InterfaceType definingType) {
- // if the baseFunctionType is null, or does not have any parameters,
- // return it.
- if (baseFunctionType == null ||
- baseFunctionType.typeArguments.length == 0) {
- return baseFunctionType;
- }
- // First, generate the path from the defining type to the overridden member
- Queue<InterfaceType> inheritancePath = new Queue<InterfaceType>();
- _computeInheritancePath(inheritancePath, definingType, memberName);
- if (inheritancePath == null || inheritancePath.isEmpty) {
- // TODO(jwren) log analysis engine error
- return baseFunctionType;
- }
- FunctionType functionTypeToReturn = baseFunctionType;
- // loop backward through the list substituting as we go:
- while (!inheritancePath.isEmpty) {
- InterfaceType lastType = inheritancePath.removeLast();
- List<DartType> parameterTypes = lastType.element.type.typeArguments;
- List<DartType> argumentTypes = lastType.typeArguments;
- functionTypeToReturn =
- functionTypeToReturn.substitute2(argumentTypes, parameterTypes);
- }
- return functionTypeToReturn;
- }
-
- /**
- * Compute and return a mapping between the set of all string names of the members inherited from
- * the passed [ClassElement] superclass hierarchy, and the associated
- * [ExecutableElement].
- *
- * @param classElt the class element to query
- * @param visitedClasses a set of visited classes passed back into this method when it calls
- * itself recursively
- * @return a mapping between the set of all string names of the members inherited from the passed
- * [ClassElement] superclass hierarchy, and the associated [ExecutableElement]
- */
- MemberMap _computeClassChainLookupMap(
- ClassElement classElt, HashSet<ClassElement> visitedClasses) {
- MemberMap resultMap = _classLookup[classElt];
- if (resultMap != null) {
- return resultMap;
- } else {
- resultMap = new MemberMap();
+ * Place an info node into the error stream indicating that a
+ * [type] has been inferred as the type of [node].
+ */
+ void recordInference(Expression node, DartType type) {
+ if (!_inferenceHints) {
+ return;
}
- ClassElement superclassElt = null;
- InterfaceType supertype = classElt.supertype;
- if (supertype != null) {
- superclassElt = supertype.element;
+
+ ErrorCode error;
+ if (node is Literal) {
+ error = StrongModeCode.INFERRED_TYPE_LITERAL;
+ } else if (node is InstanceCreationExpression) {
+ error = StrongModeCode.INFERRED_TYPE_ALLOCATION;
+ } else if (node is FunctionExpression) {
+ error = StrongModeCode.INFERRED_TYPE_CLOSURE;
} else {
- // classElt is Object
- _classLookup[classElt] = resultMap;
- return resultMap;
- }
- if (superclassElt != null) {
- if (!visitedClasses.contains(superclassElt)) {
- visitedClasses.add(superclassElt);
- try {
- resultMap = new MemberMap.from(
- _computeClassChainLookupMap(superclassElt, visitedClasses));
- //
- // Substitute the super types down the hierarchy.
- //
- _substituteTypeParametersDownHierarchy(supertype, resultMap);
- //
- // Include the members from the superclass in the resultMap.
- //
- _recordMapWithClassMembers(resultMap, supertype, false);
- } finally {
- visitedClasses.remove(superclassElt);
- }
- } else {
- // This case happens only when the superclass was previously visited and
- // not in the lookup, meaning this is meant to shorten the compute for
- // recursive cases.
- _classLookup[superclassElt] = resultMap;
- return resultMap;
- }
+ error = StrongModeCode.INFERRED_TYPE;
}
- //
- // Include the members from the mixins in the resultMap. If there are
- // multiple mixins, visit them in the order listed so that methods in later
- // mixins will overwrite identically-named methods in earlier mixins.
- //
- List<InterfaceType> mixins = classElt.mixins;
- for (InterfaceType mixin in mixins) {
- ClassElement mixinElement = mixin.element;
- if (mixinElement != null) {
- if (!visitedClasses.contains(mixinElement)) {
- visitedClasses.add(mixinElement);
- try {
- MemberMap map = new MemberMap.from(
- _computeClassChainLookupMap(mixinElement, visitedClasses));
- //
- // Substitute the super types down the hierarchy.
- //
- _substituteTypeParametersDownHierarchy(mixin, map);
- //
- // Include the members from the superclass in the resultMap.
- //
- _recordMapWithClassMembers(map, mixin, false);
- //
- // Add the members from map into result map.
- //
- for (int j = 0; j < map.size; j++) {
- String key = map.getKey(j);
- ExecutableElement value = map.getValue(j);
- if (key != null) {
- ClassElement definingClass = value
- .getAncestor((Element element) => element is ClassElement);
- if (!definingClass.type.isObject) {
- ExecutableElement existingValue = resultMap.get(key);
- if (existingValue == null ||
- (existingValue != null && !_isAbstract(value))) {
- resultMap.put(key, value);
- }
- }
- }
- }
- } finally {
- visitedClasses.remove(mixinElement);
- }
- } else {
- // This case happens only when the superclass was previously visited
- // and not in the lookup, meaning this is meant to shorten the compute
- // for recursive cases.
- _classLookup[mixinElement] = resultMap;
- return resultMap;
- }
- }
- }
- _classLookup[classElt] = resultMap;
- return resultMap;
+
+ _errorReporter.reportErrorForNode(error, node, [node, type]);
}
- /**
- * Compute and return the inheritance path given the context of a type and a member that is
- * overridden in the inheritance path (for which the type is in the path).
- *
- * @param chain the inheritance path that is built up as this method calls itself recursively,
- * when this method is called an empty [LinkedList] should be provided
- * @param currentType the current type in the inheritance path
- * @param memberName the name of the member that is being looked up the inheritance path
- */
- void _computeInheritancePath(Queue<InterfaceType> chain,
- InterfaceType currentType, String memberName) {
- // TODO (jwren) create a public version of this method which doesn't require
- // the initial chain to be provided, then provided tests for this
- // functionality in InheritanceManagerTest
- chain.add(currentType);
- ClassElement classElt = currentType.element;
- InterfaceType supertype = classElt.supertype;
- // Base case- reached Object
- if (supertype == null) {
- // Looked up the chain all the way to Object, return null.
- // This should never happen.
- return;
+ List<DartType> _matchTypes(InterfaceType t1, InterfaceType t2) {
+ if (t1 == t2) {
+ return t2.typeArguments;
}
- // If we are done, return the chain
- // We are not done if this is the first recursive call on this method.
- if (chain.length != 1) {
- // We are done however if the member is in this classElt
- if (_lookupMemberInClass(classElt, memberName) != null) {
- return;
- }
+ List<DartType> tArgs1 = t1.typeArguments;
+ List<DartType> tArgs2 = t2.typeArguments;
+ // If t1 isn't a raw type, bail out
+ if (tArgs1 != null && tArgs1.any((t) => !t.isDynamic)) {
+ return null;
}
- // Mixins- note that mixins call lookupMemberInClass, not lookupMember
- List<InterfaceType> mixins = classElt.mixins;
- for (int i = mixins.length - 1; i >= 0; i--) {
- ClassElement mixinElement = mixins[i].element;
- if (mixinElement != null) {
- ExecutableElement elt = _lookupMemberInClass(mixinElement, memberName);
- if (elt != null) {
- // this is equivalent (but faster than) calling this method
- // recursively
- // (return computeInheritancePath(chain, mixins[i], memberName);)
- chain.add(mixins[i]);
- return;
+
+ // This is our inferred type argument list. We start at all dynamic,
+ // and fill in with inferred types when we reach a match.
+ List<DartType> actuals =
+ new List<DartType>.filled(tArgs1.length, _typeProvider.dynamicType);
+
+ // When we find the supertype of t1 with the same
+ // classname as t2 (see below), we have the following:
+ // If t1 is an instantiation of a class T1<X0, ..., Xn>
+ // and t2 is an instantiation of a class T2<Y0, ...., Ym>
+ // of the form t2 = T2<S0, ..., Sm>
+ // then we want to choose instantiations for the Xi
+ // T0, ..., Tn such that T1<T0, ..., Tn> <: t2 .
+ // To find this, we simply instantate T1 with
+ // X0, ..., Xn, and then find its superclass
+ // T2<T0', ..., Tn'>. We then solve the constraint
+ // set T0' <: S0, ..., Tn' <: Sn for the Xi.
+ // Currently, we only handle constraints where
+ // the Ti' is one of the Xi'. If there are multiple
+ // constraints on some Xi, we choose the lower of the
+ // two (if it exists).
+ bool permute(List<DartType> permutedArgs) {
+ if (permutedArgs == null) {
+ return false;
+ }
+ List<TypeParameterElement> ps = t1.typeParameters;
+ List<DartType> ts = ps.map((p) => p.type).toList();
+ for (int i = 0; i < permutedArgs.length; i++) {
+ DartType tVar = permutedArgs[i];
+ DartType tActual = tArgs2[i];
+ int index = ts.indexOf(tVar);
+ if (index >= 0 && _typeSystem.isSubtypeOf(tActual, actuals[index])) {
+ actuals[index] = tActual;
}
}
+ return actuals.any((x) => !x.isDynamic);
}
- // Superclass
- ClassElement superclassElt = supertype.element;
- if (lookupMember(superclassElt, memberName) != null) {
- _computeInheritancePath(chain, supertype, memberName);
- return;
- }
- // Interfaces
- List<InterfaceType> interfaces = classElt.interfaces;
- for (InterfaceType interfaceType in interfaces) {
- ClassElement interfaceElement = interfaceType.element;
- if (interfaceElement != null &&
- lookupMember(interfaceElement, memberName) != null) {
- _computeInheritancePath(chain, interfaceType, memberName);
- return;
+
+ // Look for the first supertype of t1 with the same class name as t2.
+ bool match(InterfaceType t1, Set<Element> visited) {
+ if (t1.element == t2.element) {
+ return permute(t1.typeArguments);
}
- }
- }
- /**
- * Compute and return a mapping between the set of all string names of the members inherited from
- * the passed [ClassElement] interface hierarchy, and the associated
- * [ExecutableElement].
- *
- * @param classElt the class element to query
- * @param visitedInterfaces a set of visited classes passed back into this method when it calls
- * itself recursively
- * @return a mapping between the set of all string names of the members inherited from the passed
- * [ClassElement] interface hierarchy, and the associated [ExecutableElement]
- */
- MemberMap _computeInterfaceLookupMap(
- ClassElement classElt, HashSet<ClassElement> visitedInterfaces) {
- MemberMap resultMap = _interfaceLookup[classElt];
- if (resultMap != null) {
- return resultMap;
- }
- List<MemberMap> lookupMaps =
- _gatherInterfaceLookupMaps(classElt, visitedInterfaces);
- if (lookupMaps == null) {
- resultMap = new MemberMap();
- } else {
- HashMap<String, List<ExecutableElement>> unionMap =
- _unionInterfaceLookupMaps(lookupMaps);
- resultMap = _resolveInheritanceLookup(classElt, unionMap);
- }
- _interfaceLookup[classElt] = resultMap;
- return resultMap;
- }
-
- /**
- * Collect a list of interface lookup maps whose elements correspond to all of the classes
- * directly above [classElt] in the class hierarchy (the direct superclass if any, all
- * mixins, and all direct superinterfaces). Each item in the list is the interface lookup map
- * returned by [computeInterfaceLookupMap] for the corresponding super, except with type
- * parameters appropriately substituted.
- *
- * @param classElt the class element to query
- * @param visitedInterfaces a set of visited classes passed back into this method when it calls
- * itself recursively
- * @return `null` if there was a problem (such as a loop in the class hierarchy) or if there
- * are no classes above this one in the class hierarchy. Otherwise, a list of interface
- * lookup maps.
- */
- List<MemberMap> _gatherInterfaceLookupMaps(
- ClassElement classElt, HashSet<ClassElement> visitedInterfaces) {
- InterfaceType supertype = classElt.supertype;
- ClassElement superclassElement =
- supertype != null ? supertype.element : null;
- List<InterfaceType> mixins = classElt.mixins;
- List<InterfaceType> interfaces = classElt.interfaces;
- // Recursively collect the list of mappings from all of the interface types
- List<MemberMap> lookupMaps = new List<MemberMap>();
- //
- // Superclass element
- //
- if (superclassElement != null) {
- if (!visitedInterfaces.contains(superclassElement)) {
- try {
- visitedInterfaces.add(superclassElement);
- //
- // Recursively compute the map for the super type.
- //
- MemberMap map =
- _computeInterfaceLookupMap(superclassElement, visitedInterfaces);
- map = new MemberMap.from(map);
- //
- // Substitute the super type down the hierarchy.
- //
- _substituteTypeParametersDownHierarchy(supertype, map);
- //
- // Add any members from the super type into the map as well.
- //
- _recordMapWithClassMembers(map, supertype, true);
- lookupMaps.add(map);
- } finally {
- visitedInterfaces.remove(superclassElement);
- }
- } else {
- return null;
+ if (t1 == _typeProvider.objectType) {
+ return false;
}
- }
- //
- // Mixin elements
- //
- for (int i = mixins.length - 1; i >= 0; i--) {
- InterfaceType mixinType = mixins[i];
- ClassElement mixinElement = mixinType.element;
- if (mixinElement != null) {
- if (!visitedInterfaces.contains(mixinElement)) {
- try {
- visitedInterfaces.add(mixinElement);
- //
- // Recursively compute the map for the mixin.
- //
- MemberMap map =
- _computeInterfaceLookupMap(mixinElement, visitedInterfaces);
- map = new MemberMap.from(map);
- //
- // Substitute the mixin type down the hierarchy.
- //
- _substituteTypeParametersDownHierarchy(mixinType, map);
- //
- // Add any members from the mixin type into the map as well.
- //
- _recordMapWithClassMembers(map, mixinType, true);
- lookupMaps.add(map);
- } finally {
- visitedInterfaces.remove(mixinElement);
+
+ Element element = t1.element;
+ if (visited == null) {
+ visited = new HashSet<Element>();
+ }
+ if (element == null || !visited.add(element)) {
+ return false;
+ }
+ try {
+ if (match(t1.superclass, visited)) {
+ return true;
+ }
+
+ List<InterfaceType> mixins = t1.mixins;
+ int mixinLength = mixins.length;
+ for (int i = 0; i < mixinLength; i++) {
+ if (match(mixins[i], visited)) {
+ return true;
}
- } else {
- return null;
}
- }
- }
- //
- // Interface elements
- //
- for (InterfaceType interfaceType in interfaces) {
- ClassElement interfaceElement = interfaceType.element;
- if (interfaceElement != null) {
- if (!visitedInterfaces.contains(interfaceElement)) {
- try {
- visitedInterfaces.add(interfaceElement);
- //
- // Recursively compute the map for the interfaces.
- //
- MemberMap map =
- _computeInterfaceLookupMap(interfaceElement, visitedInterfaces);
- map = new MemberMap.from(map);
- //
- // Substitute the supertypes down the hierarchy
- //
- _substituteTypeParametersDownHierarchy(interfaceType, map);
- //
- // And add any members from the interface into the map as well.
- //
- _recordMapWithClassMembers(map, interfaceType, true);
- lookupMaps.add(map);
- } finally {
- visitedInterfaces.remove(interfaceElement);
+
+ List<InterfaceType> interfaces = t1.interfaces;
+ int interfaceLength = interfaces.length;
+ for (int j = 0; j < interfaceLength; j++) {
+ if (match(interfaces[j], visited)) {
+ return true;
}
- } else {
- return null;
}
+ } finally {
+ visited.remove(element);
}
+ return false;
}
- if (lookupMaps.length == 0) {
- return null;
- }
- return lookupMaps;
- }
- /**
- * Given some [ClassElement], this method finds and returns the [ExecutableElement] of
- * the passed name in the class element. Static members, members in super types and members not
- * accessible from the current library are not considered.
- *
- * @param classElt the class element to query
- * @param memberName the name of the member to lookup in the class
- * @return the found [ExecutableElement], or `null` if no such member was found
- */
- ExecutableElement _lookupMemberInClass(
- ClassElement classElt, String memberName) {
- List<MethodElement> methods = classElt.methods;
- for (MethodElement method in methods) {
- if (memberName == method.name &&
- method.isAccessibleIn(_library) &&
- !method.isStatic) {
- return method;
- }
+ // We have that t1 = T1<dynamic, ..., dynamic>.
+ // To match t1 against t2, we use the uninstantiated version
+ // of t1, essentially treating it as an instantiation with
+ // fresh variables, and solve for the variables.
+ // t1.element.type will be of the form T1<X0, ..., Xn>
+ if (!match(t1.element.type, null)) {
+ return null;
}
- List<PropertyAccessorElement> accessors = classElt.accessors;
- for (PropertyAccessorElement accessor in accessors) {
- if (memberName == accessor.name &&
- accessor.isAccessibleIn(_library) &&
- !accessor.isStatic) {
- return accessor;
- }
+ DartType newT1 = t1.element.type.instantiate(actuals);
+ // If we found a solution, return it.
+ if (_typeSystem.isSubtypeOf(newT1, t2)) {
+ return actuals;
}
return null;
}
/**
- * Record the passed map with the set of all members (methods, getters and setters) in the type
- * into the passed map.
- *
- * @param map some non-`null` map to put the methods and accessors from the passed
- * [ClassElement] into
- * @param type the type that will be recorded into the passed map
- * @param doIncludeAbstract `true` if abstract members will be put into the map
- */
- void _recordMapWithClassMembers(
- MemberMap map, InterfaceType type, bool doIncludeAbstract) {
- List<MethodElement> methods = type.methods;
- for (MethodElement method in methods) {
- if (method.isAccessibleIn(_library) &&
- !method.isStatic &&
- (doIncludeAbstract || !method.isAbstract)) {
- map.put(method.name, method);
- }
- }
- List<PropertyAccessorElement> accessors = type.accessors;
- for (PropertyAccessorElement accessor in accessors) {
- if (accessor.isAccessibleIn(_library) &&
- !accessor.isStatic &&
- (doIncludeAbstract || !accessor.isAbstract)) {
- map.put(accessor.name, accessor);
- }
- }
- }
-
- /**
- * This method is used to report errors on when they are found computing inheritance information.
- * See [ErrorVerifier.checkForInconsistentMethodInheritance] to see where these generated
- * error codes are reported back into the analysis engine.
- *
- * @param classElt the location of the source for which the exception occurred
- * @param offset the offset of the location of the error
- * @param length the length of the location of the error
- * @param errorCode the error code to be associated with this error
- * @param arguments the arguments used to build the error message
+ * Clear the type information assocated with [node].
*/
- void _reportError(ClassElement classElt, int offset, int length,
- ErrorCode errorCode, List<Object> arguments) {
- HashSet<AnalysisError> errorSet = _errorsInClassElement[classElt];
- if (errorSet == null) {
- errorSet = new HashSet<AnalysisError>();
- _errorsInClassElement[classElt] = errorSet;
- }
- errorSet.add(new AnalysisError(
- classElt.source, offset, length, errorCode, arguments));
+ static void clearType(AstNode node) {
+ node?.setProperty(_typeProperty, null);
}
/**
- * Given the set of methods defined by classes above [classElt] in the class hierarchy,
- * apply the appropriate inheritance rules to determine those methods inherited by or overridden
- * by [classElt]. Also report static warnings
- * [StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE] and
- * [StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD] if appropriate.
+ * Look for contextual type information attached to [node]. Returns
+ * the type if found, otherwise null.
*
- * @param classElt the class element to query.
- * @param unionMap a mapping from method name to the set of unique (in terms of signature) methods
- * defined in superclasses of [classElt].
- * @return the inheritance lookup map for [classElt].
+ * If [node] has a contextual union type like `T | Future<T>` this will be
+ * returned. You can use [getType] if you prefer to only get the `T`.
*/
- MemberMap _resolveInheritanceLookup(ClassElement classElt,
- HashMap<String, List<ExecutableElement>> unionMap) {
- MemberMap resultMap = new MemberMap();
- unionMap.forEach((String key, List<ExecutableElement> list) {
- int numOfEltsWithMatchingNames = list.length;
- if (numOfEltsWithMatchingNames == 1) {
- //
- // Example: class A inherits only 1 method named 'm'.
- // Since it is the only such method, it is inherited.
- // Another example: class A inherits 2 methods named 'm' from 2
- // different interfaces, but they both have the same signature, so it is
- // the method inherited.
- //
- resultMap.put(key, list[0]);
- } else {
- //
- // Then numOfEltsWithMatchingNames > 1, check for the warning cases.
- //
- bool allMethods = true;
- bool allSetters = true;
- bool allGetters = true;
- for (ExecutableElement executableElement in list) {
- if (executableElement is PropertyAccessorElement) {
- allMethods = false;
- if (executableElement.isSetter) {
- allGetters = false;
- } else {
- allSetters = false;
- }
- } else {
- allGetters = false;
- allSetters = false;
- }
- }
- //
- // If there isn't a mixture of methods with getters, then continue,
- // otherwise create a warning.
- //
- if (allMethods || allGetters || allSetters) {
- //
- // Compute the element whose type is the subtype of all of the other
- // types.
- //
- List<ExecutableElement> elements = new List.from(list);
- List<FunctionType> executableElementTypes =
- new List<FunctionType>(numOfEltsWithMatchingNames);
- for (int i = 0; i < numOfEltsWithMatchingNames; i++) {
- executableElementTypes[i] = elements[i].type;
- }
- List<int> subtypesOfAllOtherTypesIndexes = new List<int>();
- for (int i = 0; i < numOfEltsWithMatchingNames; i++) {
- FunctionType subtype = executableElementTypes[i];
- if (subtype == null) {
- continue;
- }
- bool subtypeOfAllTypes = true;
- TypeSystem typeSystem = _library.context.typeSystem;
- for (int j = 0;
- j < numOfEltsWithMatchingNames && subtypeOfAllTypes;
- j++) {
- if (i != j) {
- if (!typeSystem.isSubtypeOf(
- subtype, executableElementTypes[j])) {
- subtypeOfAllTypes = false;
- break;
- }
- }
- }
- if (subtypeOfAllTypes) {
- subtypesOfAllOtherTypesIndexes.add(i);
- }
- }
- //
- // The following is split into three cases determined by the number of
- // elements in subtypesOfAllOtherTypes
- //
- if (subtypesOfAllOtherTypesIndexes.length == 1) {
- //
- // Example: class A inherited only 2 method named 'm'.
- // One has the function type '() -> dynamic' and one has the
- // function type '([int]) -> dynamic'. Since the second method is a
- // subtype of all the others, it is the inherited method.
- // Tests: InheritanceManagerTest.
- // test_getMapOfMembersInheritedFromInterfaces_union_oneSubtype_*
- //
- resultMap.put(key, elements[subtypesOfAllOtherTypesIndexes[0]]);
- } else {
- if (subtypesOfAllOtherTypesIndexes.isEmpty) {
- //
- // Determine if the current class has a method or accessor with
- // the member name, if it does then then this class does not
- // "inherit" from any of the supertypes. See issue 16134.
- //
- bool classHasMember = false;
- if (allMethods) {
- classHasMember = classElt.getMethod(key) != null;
- } else {
- List<PropertyAccessorElement> accessors = classElt.accessors;
- for (int i = 0; i < accessors.length; i++) {
- if (accessors[i].name == key) {
- classHasMember = true;
- }
- }
- }
- //
- // Example: class A inherited only 2 method named 'm'.
- // One has the function type '() -> int' and one has the function
- // type '() -> String'. Since neither is a subtype of the other,
- // we create a warning, and have this class inherit nothing.
- //
- if (!classHasMember) {
- String firstTwoFuntionTypesStr =
- "${executableElementTypes[0]}, ${executableElementTypes[1]}";
- _reportError(
- classElt,
- classElt.nameOffset,
- classElt.nameLength,
- StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE,
- [key, firstTwoFuntionTypesStr]);
- }
- } else {
- //
- // Example: class A inherits 2 methods named 'm'.
- // One has the function type '(int) -> dynamic' and one has the
- // function type '(num) -> dynamic'. Since they are both a subtype
- // of the other, a synthetic function '(dynamic) -> dynamic' is
- // inherited.
- // Tests: test_getMapOfMembersInheritedFromInterfaces_
- // union_multipleSubtypes_*
- //
- List<ExecutableElement> elementArrayToMerge = new List<
- ExecutableElement>(subtypesOfAllOtherTypesIndexes.length);
- for (int i = 0; i < elementArrayToMerge.length; i++) {
- elementArrayToMerge[i] =
- elements[subtypesOfAllOtherTypesIndexes[i]];
- }
- ExecutableElement mergedExecutableElement =
- _computeMergedExecutableElement(elementArrayToMerge);
- resultMap.put(key, mergedExecutableElement);
- }
- }
- } else {
- _reportError(
- classElt,
- classElt.nameOffset,
- classElt.nameLength,
- StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD,
- [key]);
- }
- }
- });
- return resultMap;
- }
+ static DartType getContext(AstNode node) => node?.getProperty(_typeProperty);
/**
- * Loop through all of the members in some [MemberMap], performing type parameter
- * substitutions using a passed supertype.
+ * Look for a single contextual type attached to [node], and returns the type
+ * if found, otherwise null.
*
- * @param superType the supertype to substitute into the members of the [MemberMap]
- * @param map the MemberMap to perform the substitutions on
+ * If [node] has a contextual union type like `T | Future<T>` this will
+ * simplify it to only return `T`. If the caller can handle a union type,
+ * [getContext] should be used instead.
*/
- void _substituteTypeParametersDownHierarchy(
- InterfaceType superType, MemberMap map) {
- for (int i = 0; i < map.size; i++) {
- ExecutableElement executableElement = map.getValue(i);
- if (executableElement is MethodMember) {
- executableElement =
- MethodMember.from(executableElement as MethodMember, superType);
- map.setValue(i, executableElement);
- } else if (executableElement is PropertyAccessorMember) {
- executableElement = PropertyAccessorMember.from(
- executableElement as PropertyAccessorMember, superType);
- map.setValue(i, executableElement);
- }
+ static DartType getType(AstNode node) {
+ DartType t = getContext(node);
+ if (t is FutureUnionType) {
+ return t.type;
}
+ return t;
}
/**
- * Union all of the [lookupMaps] together into a single map, grouping the ExecutableElements
- * into a list where none of the elements are equal where equality is determined by having equal
- * function types. (We also take note too of the kind of the element: ()->int and () -> int may
- * not be equal if one is a getter and the other is a method.)
- *
- * @param lookupMaps the maps to be unioned together.
- * @return the resulting union map.
+ * Like [getContext] but expands a union type into a list of types.
*/
- HashMap<String, List<ExecutableElement>> _unionInterfaceLookupMaps(
- List<MemberMap> lookupMaps) {
- HashMap<String, List<ExecutableElement>> unionMap =
- new HashMap<String, List<ExecutableElement>>();
- for (MemberMap lookupMap in lookupMaps) {
- int lookupMapSize = lookupMap.size;
- for (int i = 0; i < lookupMapSize; i++) {
- // Get the string key, if null, break.
- String key = lookupMap.getKey(i);
- if (key == null) {
- break;
- }
- // Get the list value out of the unionMap
- List<ExecutableElement> list = unionMap[key];
- // If we haven't created such a map for this key yet, do create it and
- // put the list entry into the unionMap.
- if (list == null) {
- list = new List<ExecutableElement>();
- unionMap[key] = list;
- }
- // Fetch the entry out of this lookupMap
- ExecutableElement newExecutableElementEntry = lookupMap.getValue(i);
- if (list.isEmpty) {
- // If the list is empty, just the new value
- list.add(newExecutableElementEntry);
- } else {
- // Otherwise, only add the newExecutableElementEntry if it isn't
- // already in the list, this covers situation where a class inherits
- // two methods (or two getters) that are identical.
- bool alreadyInList = false;
- bool isMethod1 = newExecutableElementEntry is MethodElement;
- for (ExecutableElement executableElementInList in list) {
- bool isMethod2 = executableElementInList is MethodElement;
- if (isMethod1 == isMethod2 &&
- executableElementInList.type ==
- newExecutableElementEntry.type) {
- alreadyInList = true;
- break;
- }
- }
- if (!alreadyInList) {
- list.add(newExecutableElementEntry);
- }
- }
- }
- }
- return unionMap;
- }
-
- /**
- * Given some array of [ExecutableElement]s, this method creates a synthetic element as
- * described in 8.1.1:
- *
- * Let <i>numberOfPositionals</i>(<i>f</i>) denote the number of positional parameters of a
- * function <i>f</i>, and let <i>numberOfRequiredParams</i>(<i>f</i>) denote the number of
- * required parameters of a function <i>f</i>. Furthermore, let <i>s</i> denote the set of all
- * named parameters of the <i>m<sub>1</sub>, &hellip;, m<sub>k</sub></i>. Then let
- * * <i>h = max(numberOfPositionals(m<sub>i</sub>)),</i>
- * * <i>r = min(numberOfRequiredParams(m<sub>i</sub>)), for all <i>i</i>, 1 <= i <= k.</i>
- * Then <i>I</i> has a method named <i>n</i>, with <i>r</i> required parameters of type
- * <b>dynamic</b>, <i>h</i> positional parameters of type <b>dynamic</b>, named parameters
- * <i>s</i> of type <b>dynamic</b> and return type <b>dynamic</b>.
- *
- */
- static ExecutableElement _computeMergedExecutableElement(
- List<ExecutableElement> elementArrayToMerge) {
- int h = _getNumOfPositionalParameters(elementArrayToMerge[0]);
- int r = _getNumOfRequiredParameters(elementArrayToMerge[0]);
- Set<String> namedParametersList = new HashSet<String>();
- for (int i = 1; i < elementArrayToMerge.length; i++) {
- ExecutableElement element = elementArrayToMerge[i];
- int numOfPositionalParams = _getNumOfPositionalParameters(element);
- if (h < numOfPositionalParams) {
- h = numOfPositionalParams;
- }
- int numOfRequiredParams = _getNumOfRequiredParameters(element);
- if (r > numOfRequiredParams) {
- r = numOfRequiredParams;
- }
- namedParametersList.addAll(_getNamedParameterNames(element));
- }
- return _createSyntheticExecutableElement(
- elementArrayToMerge,
- elementArrayToMerge[0].displayName,
- r,
- h - r,
- new List.from(namedParametersList));
- }
-
- /**
- * Used by [computeMergedExecutableElement] to actually create the
- * synthetic element.
- *
- * @param elementArrayToMerge the array used to create the synthetic element
- * @param name the name of the method, getter or setter
- * @param numOfRequiredParameters the number of required parameters
- * @param numOfPositionalParameters the number of positional parameters
- * @param namedParameters the list of [String]s that are the named parameters
- * @return the created synthetic element
- */
- static ExecutableElement _createSyntheticExecutableElement(
- List<ExecutableElement> elementArrayToMerge,
- String name,
- int numOfRequiredParameters,
- int numOfPositionalParameters,
- List<String> namedParameters) {
- DynamicTypeImpl dynamicType = DynamicTypeImpl.instance;
- SimpleIdentifier nameIdentifier = new SimpleIdentifier(
- new sc.StringToken(sc.TokenType.IDENTIFIER, name, 0));
- ExecutableElementImpl executable;
- if (elementArrayToMerge[0] is MethodElement) {
- MultiplyInheritedMethodElementImpl unionedMethod =
- new MultiplyInheritedMethodElementImpl(nameIdentifier);
- unionedMethod.inheritedElements = elementArrayToMerge;
- executable = unionedMethod;
- } else {
- MultiplyInheritedPropertyAccessorElementImpl unionedPropertyAccessor =
- new MultiplyInheritedPropertyAccessorElementImpl(nameIdentifier);
- unionedPropertyAccessor.getter =
- (elementArrayToMerge[0] as PropertyAccessorElement).isGetter;
- unionedPropertyAccessor.setter =
- (elementArrayToMerge[0] as PropertyAccessorElement).isSetter;
- unionedPropertyAccessor.inheritedElements = elementArrayToMerge;
- executable = unionedPropertyAccessor;
+ static Iterable<DartType> getTypes(AstNode node) {
+ DartType t = getContext(node);
+ if (t == null) {
+ return DartType.EMPTY_LIST;
}
- int numOfParameters = numOfRequiredParameters +
- numOfPositionalParameters +
- namedParameters.length;
- List<ParameterElement> parameters =
- new List<ParameterElement>(numOfParameters);
- int i = 0;
- for (int j = 0; j < numOfRequiredParameters; j++, i++) {
- ParameterElementImpl parameter = new ParameterElementImpl("", 0);
- parameter.type = dynamicType;
- parameter.parameterKind = ParameterKind.REQUIRED;
- parameters[i] = parameter;
- }
- for (int k = 0; k < numOfPositionalParameters; k++, i++) {
- ParameterElementImpl parameter = new ParameterElementImpl("", 0);
- parameter.type = dynamicType;
- parameter.parameterKind = ParameterKind.POSITIONAL;
- parameters[i] = parameter;
- }
- for (int m = 0; m < namedParameters.length; m++, i++) {
- ParameterElementImpl parameter =
- new ParameterElementImpl(namedParameters[m], 0);
- parameter.type = dynamicType;
- parameter.parameterKind = ParameterKind.NAMED;
- parameters[i] = parameter;
- }
- executable.returnType = dynamicType;
- executable.parameters = parameters;
- FunctionTypeImpl methodType = new FunctionTypeImpl(executable);
- executable.type = methodType;
- return executable;
- }
-
- /**
- * Given some [ExecutableElement], return the list of named parameters.
- */
- static List<String> _getNamedParameterNames(
- ExecutableElement executableElement) {
- List<String> namedParameterNames = new List<String>();
- List<ParameterElement> parameters = executableElement.parameters;
- for (int i = 0; i < parameters.length; i++) {
- ParameterElement parameterElement = parameters[i];
- if (parameterElement.parameterKind == ParameterKind.NAMED) {
- namedParameterNames.add(parameterElement.name);
- }
+ if (t is FutureUnionType) {
+ return t.types;
}
- return namedParameterNames;
+ return <DartType>[t];
}
/**
- * Given some [ExecutableElement] return the number of parameters of the specified kind.
+ * Attach contextual type information [type] to [node] for use during
+ * inference.
*/
- static int _getNumOfParameters(
- ExecutableElement executableElement, ParameterKind parameterKind) {
- int parameterCount = 0;
- List<ParameterElement> parameters = executableElement.parameters;
- for (int i = 0; i < parameters.length; i++) {
- ParameterElement parameterElement = parameters[i];
- if (parameterElement.parameterKind == parameterKind) {
- parameterCount++;
- }
+ static void setType(AstNode node, DartType type) {
+ if (type == null || type.isDynamic) {
+ clearType(node);
+ } else {
+ node?.setProperty(_typeProperty, type);
}
- return parameterCount;
}
/**
- * Given some [ExecutableElement] return the number of positional parameters.
- *
- * Note: by positional we mean [ParameterKind.REQUIRED] or [ParameterKind.POSITIONAL].
- */
- static int _getNumOfPositionalParameters(
- ExecutableElement executableElement) =>
- _getNumOfParameters(executableElement, ParameterKind.REQUIRED) +
- _getNumOfParameters(executableElement, ParameterKind.POSITIONAL);
-
- /**
- * Given some [ExecutableElement] return the number of required parameters.
- */
- static int _getNumOfRequiredParameters(ExecutableElement executableElement) =>
- _getNumOfParameters(executableElement, ParameterKind.REQUIRED);
-
- /**
- * Given some [ExecutableElement] returns `true` if it is an abstract member of a
- * class.
- *
- * @param executableElement some [ExecutableElement] to evaluate
- * @return `true` if the given element is an abstract member of a class
+ * Attach contextual type information [type] to [node] for use during
+ * inference.
*/
- static bool _isAbstract(ExecutableElement executableElement) {
- if (executableElement is MethodElement) {
- return executableElement.isAbstract;
- } else if (executableElement is PropertyAccessorElement) {
- return executableElement.isAbstract;
- }
- return false;
+ static void setTypeFromNode(AstNode innerNode, AstNode outerNode) {
+ setType(innerNode, getContext(outerNode));
}
}
/**
- * This enum holds one of four states of a field initialization state through a constructor
- * signature, not initialized, initialized in the field declaration, initialized in the field
- * formal, and finally, initialized in the initializers list.
+ * The four states of a field initialization state through a constructor
+ * signature, not initialized, initialized in the field declaration, initialized
+ * in the field formal, and finally, initialized in the initializers list.
*/
-class INIT_STATE extends Enum<INIT_STATE> {
+class INIT_STATE implements Comparable<INIT_STATE> {
static const INIT_STATE NOT_INIT = const INIT_STATE('NOT_INIT', 0);
static const INIT_STATE INIT_IN_DECLARATION =
@@ -6702,3077 +5066,341 @@ class INIT_STATE extends Enum<INIT_STATE> {
INIT_IN_INITIALIZERS
];
- const INIT_STATE(String name, int ordinal) : super(name, ordinal);
-}
-
-/**
- * Instances of the class `LabelScope` represent a scope in which a single label is defined.
- */
-class LabelScope {
/**
- * The label scope enclosing this label scope.
+ * The name of this init state.
*/
- final LabelScope _outerScope;
+ final String name;
/**
- * The label defined in this scope.
+ * The ordinal value of the init state.
*/
- final String _label;
+ final int ordinal;
- /**
- * The element to which the label resolves.
- */
- final LabelElement element;
+ const INIT_STATE(this.name, this.ordinal);
- /**
- * The AST node to which the label resolves.
- */
- final AstNode node;
+ @override
+ int get hashCode => ordinal;
- /**
- * Initialize a newly created scope to represent the label [_label].
- * [_outerScope] is the scope enclosing the new label scope. [node] is the
- * AST node the label resolves to. [element] is the element the label
- * resolves to.
- */
- LabelScope(this._outerScope, this._label, this.node, this.element);
+ @override
+ int compareTo(INIT_STATE other) => ordinal - other.ordinal;
- /**
- * Return the LabelScope which defines [targetLabel], or `null` if it is not
- * defined in this scope.
- */
- LabelScope lookup(String targetLabel) {
- if (_label == targetLabel) {
- return this;
- } else if (_outerScope != null) {
- return _outerScope.lookup(targetLabel);
- } else {
- return null;
- }
- }
+ @override
+ String toString() => name;
}
/**
- * Instances of the class `Library` represent the data about a single library during the
- * resolution of some (possibly different) library. They are not intended to be used except during
- * the resolution process.
+ * An AST visitor that is used to re-resolve the initializers of instance
+ * fields. Although this class is an AST visitor, clients are expected to use
+ * the method [resolveCompilationUnit] to run it over a compilation unit.
*/
-class Library {
+class InstanceFieldResolverVisitor extends ResolverVisitor {
/**
- * An empty list that can be used to initialize lists of libraries.
- */
- static const List<Library> _EMPTY_ARRAY = const <Library>[];
-
- /**
- * The prefix of a URI using the dart-ext scheme to reference a native code library.
- */
- static String _DART_EXT_SCHEME = "dart-ext:";
-
- /**
- * The analysis context in which this library is being analyzed.
- */
- final InternalAnalysisContext _analysisContext;
-
- /**
- * The inheritance manager which is used for this member lookups in this library.
- */
- InheritanceManager _inheritanceManager;
-
- /**
- * The listener to which analysis errors will be reported.
- */
- final AnalysisErrorListener errorListener;
-
- /**
- * The source specifying the defining compilation unit of this library.
- */
- final Source librarySource;
-
- /**
- * The library element representing this library.
- */
- LibraryElementImpl _libraryElement;
-
- /**
- * A list containing all of the libraries that are imported into this library.
+ * Initialize a newly created visitor to resolve the nodes in an AST node.
+ *
+ * The [definingLibrary] is the element for the library containing the node
+ * being visited. The [source] is the source representing the compilation unit
+ * containing the node being visited. The [typeProvider] is the object used to
+ * access the types from the core library. The [errorListener] is the error
+ * listener that will be informed of any errors that are found during
+ * resolution. The [nameScope] is the scope used to resolve identifiers in the
+ * node that will first be visited. If `null` or unspecified, a new
+ * [LibraryScope] will be created based on the [definingLibrary].
*/
- List<Library> _importedLibraries = _EMPTY_ARRAY;
+ InstanceFieldResolverVisitor(LibraryElement definingLibrary, Source source,
+ TypeProvider typeProvider, AnalysisErrorListener errorListener,
+ {Scope nameScope})
+ : super(definingLibrary, source, typeProvider, errorListener,
+ nameScope: nameScope);
/**
- * A table mapping URI-based directive to the actual URI value.
+ * Resolve the instance fields in the given compilation unit [node].
*/
- HashMap<UriBasedDirective, String> _directiveUris =
- new HashMap<UriBasedDirective, String>();
+ void resolveCompilationUnit(CompilationUnit node) {
+ _overrideManager.enterScope();
+ try {
+ NodeList<CompilationUnitMember> declarations = node.declarations;
+ int declarationCount = declarations.length;
+ for (int i = 0; i < declarationCount; i++) {
+ CompilationUnitMember declaration = declarations[i];
+ if (declaration is ClassDeclaration) {
+ _resolveClassDeclaration(declaration);
+ }
+ }
+ } finally {
+ _overrideManager.exitScope();
+ }
+ }
/**
- * A flag indicating whether this library explicitly imports core.
+ * Resolve the instance fields in the given class declaration [node].
*/
- bool explicitlyImportsCore = false;
+ void _resolveClassDeclaration(ClassDeclaration node) {
+ _enclosingClassDeclaration = node;
+ ClassElement outerType = enclosingClass;
+ Scope outerScope = nameScope;
+ try {
+ enclosingClass = node.element;
+ typeAnalyzer.thisType = enclosingClass?.type;
+ if (enclosingClass == null) {
+ AnalysisEngine.instance.logger.logInformation(
+ "Missing element for class declaration ${node.name.name} in ${definingLibrary.source.fullName}",
+ new CaughtException(new AnalysisException(), null));
+ // Don't try to re-resolve the initializers if we cannot set up the
+ // right name scope for resolution.
+ } else {
+ nameScope = new ClassScope(nameScope, enclosingClass);
+ NodeList<ClassMember> members = node.members;
+ int length = members.length;
+ for (int i = 0; i < length; i++) {
+ ClassMember member = members[i];
+ if (member is FieldDeclaration) {
+ _resolveFieldDeclaration(member);
+ }
+ }
+ }
+ } finally {
+ nameScope = outerScope;
+ typeAnalyzer.thisType = outerType?.type;
+ enclosingClass = outerType;
+ _enclosingClassDeclaration = null;
+ }
+ }
/**
- * A list containing all of the libraries that are exported from this library.
+ * Resolve the instance fields in the given field declaration [node].
*/
- List<Library> _exportedLibraries = _EMPTY_ARRAY;
+ void _resolveFieldDeclaration(FieldDeclaration node) {
+ if (!node.isStatic) {
+ for (VariableDeclaration field in node.fields.variables) {
+ if (field.initializer != null) {
+ field.initializer.accept(this);
+ FieldElement fieldElement = field.name.staticElement;
+ if (fieldElement.initializer != null) {
+ (fieldElement.initializer as ExecutableElementImpl).returnType =
+ field.initializer.staticType;
+ }
+ }
+ }
+ }
+ }
+}
+/**
+ * Instances of the class `OverrideVerifier` visit all of the declarations in a compilation
+ * unit to verify that if they have an override annotation it is being used correctly.
+ */
+class OverrideVerifier extends RecursiveAstVisitor {
/**
- * A table mapping the sources for the compilation units in this library to their corresponding
- * AST structures.
+ * The error reporter used to report errors.
*/
- HashMap<Source, CompilationUnit> _astMap =
- new HashMap<Source, CompilationUnit>();
+ final ErrorReporter _errorReporter;
/**
- * The library scope used when resolving elements within this library's compilation units.
+ * The inheritance manager used to find overridden methods.
*/
- LibraryScope _libraryScope;
+ final InheritanceManager _manager;
/**
- * Initialize a newly created data holder that can maintain the data associated with a library.
+ * Initialize a newly created verifier to look for inappropriate uses of the override annotation.
*
- * @param analysisContext the analysis context in which this library is being analyzed
- * @param errorListener the listener to which analysis errors will be reported
- * @param librarySource the source specifying the defining compilation unit of this library
+ * @param errorReporter the error reporter used to report errors
+ * @param manager the inheritance manager used to find overridden methods
*/
- Library(this._analysisContext, this.errorListener, this.librarySource) {
- this._libraryElement =
- _analysisContext.getLibraryElement(librarySource) as LibraryElementImpl;
- }
+ OverrideVerifier(this._errorReporter, this._manager);
- /**
- * Return an array of the [CompilationUnit]s that make up the library. The first unit is
- * always the defining unit.
- *
- * @return an array of the [CompilationUnit]s that make up the library. The first unit is
- * always the defining unit
- */
- List<CompilationUnit> get compilationUnits {
- List<CompilationUnit> unitArrayList = new List<CompilationUnit>();
- unitArrayList.add(definingCompilationUnit);
- for (Source source in _astMap.keys.toSet()) {
- if (librarySource != source) {
- unitArrayList.add(getAST(source));
+ @override
+ visitFieldDeclaration(FieldDeclaration node) {
+ for (VariableDeclaration field in node.fields.variables) {
+ VariableElement fieldElement = field.element;
+ if (fieldElement is FieldElement && _isOverride(fieldElement)) {
+ PropertyAccessorElement getter = fieldElement.getter;
+ PropertyAccessorElement setter = fieldElement.setter;
+ if (!(getter != null && _getOverriddenMember(getter) != null ||
+ setter != null && _getOverriddenMember(setter) != null)) {
+ _errorReporter.reportErrorForNode(
+ HintCode.OVERRIDE_ON_NON_OVERRIDING_FIELD, field.name);
+ }
}
}
- return unitArrayList;
}
- /**
- * Return a collection containing the sources for the compilation units in this library, including
- * the defining compilation unit.
- *
- * @return the sources for the compilation units in this library
- */
- Set<Source> get compilationUnitSources => _astMap.keys.toSet();
-
- /**
- * Return the AST structure associated with the defining compilation unit for this library.
- *
- * @return the AST structure associated with the defining compilation unit for this library
- * @throws AnalysisException if an AST structure could not be created for the defining compilation
- * unit
- */
- CompilationUnit get definingCompilationUnit => getAST(librarySource);
+ @override
+ visitMethodDeclaration(MethodDeclaration node) {
+ ExecutableElement element = node.element;
+ if (_isOverride(element)) {
+ if (_getOverriddenMember(element) == null) {
+ if (element is MethodElement) {
+ _errorReporter.reportErrorForNode(
+ HintCode.OVERRIDE_ON_NON_OVERRIDING_METHOD, node.name);
+ } else if (element is PropertyAccessorElement) {
+ if (element.isGetter) {
+ _errorReporter.reportErrorForNode(
+ HintCode.OVERRIDE_ON_NON_OVERRIDING_GETTER, node.name);
+ } else {
+ _errorReporter.reportErrorForNode(
+ HintCode.OVERRIDE_ON_NON_OVERRIDING_SETTER, node.name);
+ }
+ }
+ }
+ }
+ }
/**
- * Set the libraries that are exported by this library to be those in the given array.
+ * Return the member that overrides the given member.
*
- * @param exportedLibraries the libraries that are exported by this library
+ * @param member the member that overrides the returned member
+ * @return the member that overrides the given member
*/
- void set exportedLibraries(List<Library> exportedLibraries) {
- this._exportedLibraries = exportedLibraries;
+ ExecutableElement _getOverriddenMember(ExecutableElement member) {
+ LibraryElement library = member.library;
+ if (library == null) {
+ return null;
+ }
+ ClassElement classElement =
+ member.getAncestor((element) => element is ClassElement);
+ if (classElement == null) {
+ return null;
+ }
+ return _manager.lookupInheritance(classElement, member.name);
}
/**
- * Return an array containing the libraries that are exported from this library.
+ * Return `true` if the given element has an override annotation associated with it.
*
- * @return an array containing the libraries that are exported from this library
+ * @param element the element being tested
+ * @return `true` if the element has an override annotation associated with it
*/
- List<Library> get exports => _exportedLibraries;
+ bool _isOverride(Element element) => element != null && element.isOverride;
+}
+/**
+ * An AST visitor that is used to resolve the some of the nodes within a single
+ * compilation unit. The nodes that are skipped are those that are within
+ * function bodies.
+ */
+class PartialResolverVisitor extends ResolverVisitor {
/**
- * Set the libraries that are imported into this library to be those in the given array.
- *
- * @param importedLibraries the libraries that are imported into this library
+ * The static variables and fields that have an initializer. These are the
+ * variables that need to be re-resolved after static variables have their
+ * types inferred. A subset of these variables are those whose types should
+ * be inferred.
*/
- void set importedLibraries(List<Library> importedLibraries) {
- this._importedLibraries = importedLibraries;
- }
+ final List<VariableElement> staticVariables = <VariableElement>[];
/**
- * Return an array containing the libraries that are imported into this library.
+ * Initialize a newly created visitor to resolve the nodes in an AST node.
*
- * @return an array containing the libraries that are imported into this library
+ * The [definingLibrary] is the element for the library containing the node
+ * being visited. The [source] is the source representing the compilation unit
+ * containing the node being visited. The [typeProvider] is the object used to
+ * access the types from the core library. The [errorListener] is the error
+ * listener that will be informed of any errors that are found during
+ * resolution. The [nameScope] is the scope used to resolve identifiers in the
+ * node that will first be visited. If `null` or unspecified, a new
+ * [LibraryScope] will be created based on [definingLibrary] and
+ * [typeProvider]. The [inheritanceManager] is used to perform inheritance
+ * lookups. If `null` or unspecified, a new [InheritanceManager] will be
+ * created based on [definingLibrary]. The [typeAnalyzerFactory] is used to
+ * create the type analyzer. If `null` or unspecified, a type analyzer of
+ * type [StaticTypeAnalyzer] will be created.
*/
- List<Library> get imports => _importedLibraries;
+ PartialResolverVisitor(LibraryElement definingLibrary, Source source,
+ TypeProvider typeProvider, AnalysisErrorListener errorListener,
+ {Scope nameScope})
+ : super(definingLibrary, source, typeProvider, errorListener,
+ nameScope: nameScope);
- /**
- * Return an array containing the libraries that are either imported or exported from this
- * library.
- *
- * @return the libraries that are either imported or exported from this library
- */
- List<Library> get importsAndExports {
- HashSet<Library> libraries = new HashSet<Library>();
- for (Library library in _importedLibraries) {
- libraries.add(library);
- }
- for (Library library in _exportedLibraries) {
- libraries.add(library);
+ @override
+ Object visitBlockFunctionBody(BlockFunctionBody node) {
+ if (_shouldBeSkipped(node)) {
+ return null;
}
- return new List.from(libraries);
+ return super.visitBlockFunctionBody(node);
}
- /**
- * Return the inheritance manager for this library.
- *
- * @return the inheritance manager for this library
- */
- InheritanceManager get inheritanceManager {
- if (_inheritanceManager == null) {
- return _inheritanceManager = new InheritanceManager(_libraryElement);
+ @override
+ Object visitExpressionFunctionBody(ExpressionFunctionBody node) {
+ if (_shouldBeSkipped(node)) {
+ return null;
}
- return _inheritanceManager;
+ return super.visitExpressionFunctionBody(node);
}
- /**
- * Return the library element representing this library, creating it if necessary.
- *
- * @return the library element representing this library
- */
- LibraryElementImpl get libraryElement {
- if (_libraryElement == null) {
- try {
- _libraryElement = _analysisContext.computeLibraryElement(librarySource)
- as LibraryElementImpl;
- } on AnalysisException catch (exception, stackTrace) {
- AnalysisEngine.instance.logger.logError(
- "Could not compute library element for ${librarySource.fullName}",
- new CaughtException(exception, stackTrace));
- }
+ @override
+ Object visitFieldDeclaration(FieldDeclaration node) {
+ if (node.isStatic) {
+ _addStaticVariables(node.fields.variables);
}
- return _libraryElement;
+ return super.visitFieldDeclaration(node);
}
- /**
- * Set the library element representing this library to the given library element.
- *
- * @param libraryElement the library element representing this library
- */
- void set libraryElement(LibraryElementImpl libraryElement) {
- this._libraryElement = libraryElement;
- if (_inheritanceManager != null) {
- _inheritanceManager.libraryElement = libraryElement;
- }
+ @override
+ Object visitNode(AstNode node) {
+ return super.visitNode(node);
}
- /**
- * Return the library scope used when resolving elements within this library's compilation units.
- *
- * @return the library scope used when resolving elements within this library's compilation units
- */
- LibraryScope get libraryScope {
- if (_libraryScope == null) {
- _libraryScope = new LibraryScope(_libraryElement, errorListener);
- }
- return _libraryScope;
+ @override
+ Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+ _addStaticVariables(node.variables.variables);
+ return super.visitTopLevelVariableDeclaration(node);
}
/**
- * Return the AST structure associated with the given source.
- *
- * @param source the source representing the compilation unit whose AST is to be returned
- * @return the AST structure associated with the given source
- * @throws AnalysisException if an AST structure could not be created for the compilation unit
+ * Add all of the [variables] with initializers to the list of variables whose
+ * type can be inferred. Technically, we only infer the types of variables
+ * that do not have a static type, but all variables with initializers
+ * potentially need to be re-resolved after inference because they might
+ * refer to a field whose type was inferred.
*/
- CompilationUnit getAST(Source source) {
- CompilationUnit unit = _astMap[source];
- if (unit == null) {
- unit = _analysisContext.computeResolvableCompilationUnit(source);
- _astMap[source] = unit;
+ void _addStaticVariables(List<VariableDeclaration> variables) {
+ int length = variables.length;
+ for (int i = 0; i < length; i++) {
+ VariableDeclaration variable = variables[i];
+ if (variable.name.name.isNotEmpty && variable.initializer != null) {
+ staticVariables.add(variable.element);
+ }
}
- return unit;
}
/**
- * Return the result of resolving the URI of the given URI-based directive against the URI of the
- * library, or `null` if the URI is not valid. If the URI is not valid, report the error.
- *
- * @param directive the directive which URI should be resolved
- * @return the result of resolving the URI against the URI of the library
+ * Return `true` if the given function body should be skipped because it is
+ * the body of a top-level function, method or constructor.
*/
- Source getSource(UriBasedDirective directive) {
- StringLiteral uriLiteral = directive.uri;
- if (uriLiteral is StringInterpolation) {
- errorListener.onError(new AnalysisError(librarySource, uriLiteral.offset,
- uriLiteral.length, CompileTimeErrorCode.URI_WITH_INTERPOLATION));
- return null;
+ bool _shouldBeSkipped(FunctionBody body) {
+ AstNode parent = body.parent;
+ if (parent is MethodDeclaration) {
+ return parent.body == body;
}
- String uriContent = uriLiteral.stringValue.trim();
- _directiveUris[directive] = uriContent;
- uriContent = Uri.encodeFull(uriContent);
- if (directive is ImportDirective &&
- uriContent.startsWith(_DART_EXT_SCHEME)) {
- _libraryElement.hasExtUri = true;
- return null;
+ if (parent is ConstructorDeclaration) {
+ return parent.body == body;
}
- try {
- parseUriWithException(uriContent);
- Source source =
- _analysisContext.sourceFactory.resolveUri(librarySource, uriContent);
- if (!_analysisContext.exists(source)) {
- errorListener.onError(new AnalysisError(
- librarySource,
- uriLiteral.offset,
- uriLiteral.length,
- CompileTimeErrorCode.URI_DOES_NOT_EXIST,
- [uriContent]));
- }
- return source;
- } on URISyntaxException {
- errorListener.onError(new AnalysisError(librarySource, uriLiteral.offset,
- uriLiteral.length, CompileTimeErrorCode.INVALID_URI, [uriContent]));
+ if (parent is FunctionExpression) {
+ AstNode parent2 = parent.parent;
+ if (parent2 is FunctionDeclaration &&
+ parent2.parent is! FunctionDeclarationStatement) {
+ return parent.body == body;
+ }
}
- return null;
- }
-
- /**
- * Returns the URI value of the given directive.
- */
- String getUri(UriBasedDirective directive) => _directiveUris[directive];
-
- /**
- * Set the AST structure associated with the defining compilation unit for this library to the
- * given AST structure.
- *
- * @param unit the AST structure associated with the defining compilation unit for this library
- */
- void setDefiningCompilationUnit(CompilationUnit unit) {
- _astMap[librarySource] = unit;
+ return false;
}
-
- @override
- String toString() => librarySource.shortName;
}
/**
- * Instances of the class `LibraryElementBuilder` build an element model for a single library.
+ * Instances of the class `PubVerifier` traverse an AST structure looking for deviations from
+ * pub best practices.
*/
-class LibraryElementBuilder {
- /**
- * The analysis context in which the element model will be built.
- */
- final InternalAnalysisContext _analysisContext;
+class PubVerifier extends RecursiveAstVisitor<Object> {
+// static String _PUBSPEC_YAML = "pubspec.yaml";
/**
- * The listener to which errors will be reported.
+ * The analysis context containing the sources to be analyzed
*/
- final AnalysisErrorListener _errorListener;
+ final AnalysisContext _context;
/**
- * Initialize a newly created library element builder.
- *
- * @param analysisContext the analysis context in which the element model will be built
- * @param errorListener the listener to which errors will be reported
+ * The error reporter by which errors will be reported.
*/
- LibraryElementBuilder(this._analysisContext, this._errorListener);
-
- /**
- * Build the library element for the given library.
- *
- * @param library the library for which an element model is to be built
- * @return the library element that was built
- * @throws AnalysisException if the analysis could not be performed
- */
- LibraryElementImpl buildLibrary(Library library) {
- CompilationUnitBuilder builder = new CompilationUnitBuilder();
- Source librarySource = library.librarySource;
- CompilationUnit definingCompilationUnit = library.definingCompilationUnit;
- CompilationUnitElementImpl definingCompilationUnitElement = builder
- .buildCompilationUnit(
- librarySource, definingCompilationUnit, librarySource);
- NodeList<Directive> directives = definingCompilationUnit.directives;
- LibraryDirective libraryDirective = null;
- LibraryIdentifier libraryNameNode = null;
- bool hasPartDirective = false;
- FunctionElement entryPoint =
- _findEntryPoint(definingCompilationUnitElement);
- List<Directive> directivesToResolve = new List<Directive>();
- List<CompilationUnitElementImpl> sourcedCompilationUnits =
- new List<CompilationUnitElementImpl>();
- for (Directive directive in directives) {
- //
- // We do not build the elements representing the import and export
- // directives at this point. That is not done until we get to
- // LibraryResolver.buildDirectiveModels() because we need the
- // LibraryElements for the referenced libraries, which might not exist at
- // this point (due to the possibility of circular references).
- //
- if (directive is LibraryDirective) {
- if (libraryNameNode == null) {
- libraryDirective = directive;
- libraryNameNode = directive.name;
- directivesToResolve.add(directive);
- }
- } else if (directive is PartDirective) {
- PartDirective partDirective = directive;
- StringLiteral partUri = partDirective.uri;
- Source partSource = partDirective.source;
- if (_analysisContext.exists(partSource)) {
- hasPartDirective = true;
- CompilationUnit partUnit = library.getAST(partSource);
- CompilationUnitElementImpl part =
- builder.buildCompilationUnit(partSource, partUnit, librarySource);
- part.uriOffset = partUri.offset;
- part.uriEnd = partUri.end;
- part.uri = partDirective.uriContent;
- //
- // Validate that the part contains a part-of directive with the same
- // name as the library.
- //
- String partLibraryName =
- _getPartLibraryName(partSource, partUnit, directivesToResolve);
- if (partLibraryName == null) {
- _errorListener.onError(new AnalysisError(
- librarySource,
- partUri.offset,
- partUri.length,
- CompileTimeErrorCode.PART_OF_NON_PART,
- [partUri.toSource()]));
- } else if (libraryNameNode == null) {
- // TODO(brianwilkerson) Collect the names declared by the part.
- // If they are all the same then we can use that name as the
- // inferred name of the library and present it in a quick-fix.
- // partLibraryNames.add(partLibraryName);
- } else if (libraryNameNode.name != partLibraryName) {
- _errorListener.onError(new AnalysisError(
- librarySource,
- partUri.offset,
- partUri.length,
- StaticWarningCode.PART_OF_DIFFERENT_LIBRARY,
- [libraryNameNode.name, partLibraryName]));
- }
- if (entryPoint == null) {
- entryPoint = _findEntryPoint(part);
- }
- directive.element = part;
- sourcedCompilationUnits.add(part);
- }
- }
- }
- if (hasPartDirective && libraryNameNode == null) {
- _errorListener.onError(new AnalysisError(librarySource, 0, 0,
- ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART));
- }
- //
- // Create and populate the library element.
- //
- LibraryElementImpl libraryElement = new LibraryElementImpl.forNode(
- _analysisContext.getContextFor(librarySource), libraryNameNode);
- _setDocRange(libraryElement, libraryDirective);
- libraryElement.definingCompilationUnit = definingCompilationUnitElement;
- if (entryPoint != null) {
- libraryElement.entryPoint = entryPoint;
- }
- int sourcedUnitCount = sourcedCompilationUnits.length;
- libraryElement.parts = sourcedCompilationUnits;
- for (Directive directive in directivesToResolve) {
- directive.element = libraryElement;
- }
- library.libraryElement = libraryElement;
- if (sourcedUnitCount > 0) {
- _patchTopLevelAccessors(libraryElement);
- }
- return libraryElement;
- }
-
- /**
- * Build the library element for the given library. The resulting element is
- * stored in the [ResolvableLibrary] structure.
- *
- * @param library the library for which an element model is to be built
- * @throws AnalysisException if the analysis could not be performed
- */
- void buildLibrary2(ResolvableLibrary library) {
- CompilationUnitBuilder builder = new CompilationUnitBuilder();
- Source librarySource = library.librarySource;
- CompilationUnit definingCompilationUnit = library.definingCompilationUnit;
- CompilationUnitElementImpl definingCompilationUnitElement = builder
- .buildCompilationUnit(
- librarySource, definingCompilationUnit, librarySource);
- NodeList<Directive> directives = definingCompilationUnit.directives;
- LibraryDirective libraryDirective = null;
- LibraryIdentifier libraryNameNode = null;
- bool hasPartDirective = false;
- FunctionElement entryPoint =
- _findEntryPoint(definingCompilationUnitElement);
- List<Directive> directivesToResolve = new List<Directive>();
- List<CompilationUnitElementImpl> sourcedCompilationUnits =
- new List<CompilationUnitElementImpl>();
- for (Directive directive in directives) {
- //
- // We do not build the elements representing the import and export
- // directives at this point. That is not done until we get to
- // LibraryResolver.buildDirectiveModels() because we need the
- // LibraryElements for the referenced libraries, which might not exist at
- // this point (due to the possibility of circular references).
- //
- if (directive is LibraryDirective) {
- if (libraryNameNode == null) {
- libraryDirective = directive;
- libraryNameNode = directive.name;
- directivesToResolve.add(directive);
- }
- } else if (directive is PartDirective) {
- PartDirective partDirective = directive;
- StringLiteral partUri = partDirective.uri;
- Source partSource = partDirective.source;
- if (_analysisContext.exists(partSource)) {
- hasPartDirective = true;
- CompilationUnit partUnit = library.getAST(partSource);
- if (partUnit != null) {
- CompilationUnitElementImpl part = builder.buildCompilationUnit(
- partSource, partUnit, librarySource);
- part.uriOffset = partUri.offset;
- part.uriEnd = partUri.end;
- part.uri = partDirective.uriContent;
- //
- // Validate that the part contains a part-of directive with the same
- // name as the library.
- //
- String partLibraryName =
- _getPartLibraryName(partSource, partUnit, directivesToResolve);
- if (partLibraryName == null) {
- _errorListener.onError(new AnalysisError(
- librarySource,
- partUri.offset,
- partUri.length,
- CompileTimeErrorCode.PART_OF_NON_PART,
- [partUri.toSource()]));
- } else if (libraryNameNode == null) {
- // TODO(brianwilkerson) Collect the names declared by the part.
- // If they are all the same then we can use that name as the
- // inferred name of the library and present it in a quick-fix.
- // partLibraryNames.add(partLibraryName);
- } else if (libraryNameNode.name != partLibraryName) {
- _errorListener.onError(new AnalysisError(
- librarySource,
- partUri.offset,
- partUri.length,
- StaticWarningCode.PART_OF_DIFFERENT_LIBRARY,
- [libraryNameNode.name, partLibraryName]));
- }
- if (entryPoint == null) {
- entryPoint = _findEntryPoint(part);
- }
- directive.element = part;
- sourcedCompilationUnits.add(part);
- }
- }
- }
- }
- if (hasPartDirective && libraryNameNode == null) {
- _errorListener.onError(new AnalysisError(librarySource, 0, 0,
- ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART));
- }
- //
- // Create and populate the library element.
- //
- LibraryElementImpl libraryElement = new LibraryElementImpl.forNode(
- _analysisContext.getContextFor(librarySource), libraryNameNode);
- _setDocRange(libraryElement, libraryDirective);
- libraryElement.definingCompilationUnit = definingCompilationUnitElement;
- if (entryPoint != null) {
- libraryElement.entryPoint = entryPoint;
- }
- int sourcedUnitCount = sourcedCompilationUnits.length;
- libraryElement.parts = sourcedCompilationUnits;
- for (Directive directive in directivesToResolve) {
- directive.element = libraryElement;
- }
- library.libraryElement = libraryElement;
- if (sourcedUnitCount > 0) {
- _patchTopLevelAccessors(libraryElement);
- }
- }
-
- /**
- * Add all of the non-synthetic getters and setters defined in the given compilation unit that
- * have no corresponding accessor to one of the given collections.
- *
- * @param getters the map to which getters are to be added
- * @param setters the list to which setters are to be added
- * @param unit the compilation unit defining the accessors that are potentially being added
- */
- void _collectAccessors(HashMap<String, PropertyAccessorElement> getters,
- List<PropertyAccessorElement> setters, CompilationUnitElement unit) {
- for (PropertyAccessorElement accessor in unit.accessors) {
- if (accessor.isGetter) {
- if (!accessor.isSynthetic && accessor.correspondingSetter == null) {
- getters[accessor.displayName] = accessor;
- }
- } else {
- if (!accessor.isSynthetic && accessor.correspondingGetter == null) {
- setters.add(accessor);
- }
- }
- }
- }
-
- /**
- * Search the top-level functions defined in the given compilation unit for the entry point.
- *
- * @param element the compilation unit to be searched
- * @return the entry point that was found, or `null` if the compilation unit does not define
- * an entry point
- */
- FunctionElement _findEntryPoint(CompilationUnitElementImpl element) {
- for (FunctionElement function in element.functions) {
- if (function.isEntryPoint) {
- return function;
- }
- }
- return null;
- }
-
- /**
- * Return the name of the library that the given part is declared to be a part of, or `null`
- * if the part does not contain a part-of directive.
- *
- * @param partSource the source representing the part
- * @param partUnit the AST structure of the part
- * @param directivesToResolve a list of directives that should be resolved to the library being
- * built
- * @return the name of the library that the given part is declared to be a part of
- */
- String _getPartLibraryName(Source partSource, CompilationUnit partUnit,
- List<Directive> directivesToResolve) {
- for (Directive directive in partUnit.directives) {
- if (directive is PartOfDirective) {
- directivesToResolve.add(directive);
- LibraryIdentifier libraryName = directive.libraryName;
- if (libraryName != null) {
- return libraryName.name;
- }
- }
- }
- return null;
- }
-
- /**
- * Look through all of the compilation units defined for the given library, looking for getters
- * and setters that are defined in different compilation units but that have the same names. If
- * any are found, make sure that they have the same variable element.
- *
- * @param libraryElement the library defining the compilation units to be processed
- */
- void _patchTopLevelAccessors(LibraryElementImpl libraryElement) {
- HashMap<String, PropertyAccessorElement> getters =
- new HashMap<String, PropertyAccessorElement>();
- List<PropertyAccessorElement> setters = new List<PropertyAccessorElement>();
- _collectAccessors(getters, setters, libraryElement.definingCompilationUnit);
- for (CompilationUnitElement unit in libraryElement.parts) {
- _collectAccessors(getters, setters, unit);
- }
- for (PropertyAccessorElement setter in setters) {
- PropertyAccessorElement getter = getters[setter.displayName];
- if (getter != null) {
- PropertyInducingElementImpl variable =
- getter.variable as PropertyInducingElementImpl;
- variable.setter = setter;
- (setter as PropertyAccessorElementImpl).variable = variable;
- }
- }
- }
-
- /**
- * If the given [node] has a documentation comment, remember its range
- * into the given [element].
- */
- void _setDocRange(ElementImpl element, LibraryDirective node) {
- if (node != null) {
- Comment comment = node.documentationComment;
- if (comment != null && comment.isDocumentation) {
- element.setDocRange(comment.offset, comment.length);
- }
- }
- }
-}
-
-/**
- * Instances of the class `LibraryImportScope` represent the scope containing all of the names
- * available from imported libraries.
- */
-class LibraryImportScope extends Scope {
- /**
- * The element representing the library in which this scope is enclosed.
- */
- final LibraryElement _definingLibrary;
-
- /**
- * The listener that is to be informed when an error is encountered.
- */
- final AnalysisErrorListener errorListener;
-
- /**
- * A list of the namespaces representing the names that are available in this scope from imported
- * libraries.
- */
- List<Namespace> _importedNamespaces;
-
- /**
- * Initialize a newly created scope representing the names imported into the given library.
- *
- * @param definingLibrary the element representing the library that imports the names defined in
- * this scope
- * @param errorListener the listener that is to be informed when an error is encountered
- */
- LibraryImportScope(this._definingLibrary, this.errorListener) {
- _createImportedNamespaces();
- }
-
- @override
- void define(Element element) {
- if (!Scope.isPrivateName(element.displayName)) {
- super.define(element);
- }
- }
-
- @override
- Source getSource(AstNode node) {
- Source source = super.getSource(node);
- if (source == null) {
- source = _definingLibrary.definingCompilationUnit.source;
- }
- return source;
- }
-
- @override
- Element internalLookup(
- Identifier identifier, String name, LibraryElement referencingLibrary) {
- Element foundElement = localLookup(name, referencingLibrary);
- if (foundElement != null) {
- return foundElement;
- }
- for (int i = 0; i < _importedNamespaces.length; i++) {
- Namespace nameSpace = _importedNamespaces[i];
- Element element = nameSpace.get(name);
- if (element != null) {
- if (foundElement == null) {
- foundElement = element;
- } else if (!identical(foundElement, element)) {
- foundElement = MultiplyDefinedElementImpl.fromElements(
- _definingLibrary.context, foundElement, element);
- }
- }
- }
- if (foundElement is MultiplyDefinedElementImpl) {
- foundElement = _removeSdkElements(
- identifier, name, foundElement as MultiplyDefinedElementImpl);
- }
- if (foundElement is MultiplyDefinedElementImpl) {
- String foundEltName = foundElement.displayName;
- List<Element> conflictingMembers = foundElement.conflictingElements;
- int count = conflictingMembers.length;
- List<String> libraryNames = new List<String>(count);
- for (int i = 0; i < count; i++) {
- libraryNames[i] = _getLibraryName(conflictingMembers[i]);
- }
- libraryNames.sort();
- errorListener.onError(new AnalysisError(
- getSource(identifier),
- identifier.offset,
- identifier.length,
- StaticWarningCode.AMBIGUOUS_IMPORT, [
- foundEltName,
- StringUtilities.printListOfQuotedNames(libraryNames)
- ]));
- return foundElement;
- }
- if (foundElement != null) {
- defineNameWithoutChecking(name, foundElement);
- }
- return foundElement;
- }
-
- /**
- * Create all of the namespaces associated with the libraries imported into this library. The
- * names are not added to this scope, but are stored for later reference.
- *
- * @param definingLibrary the element representing the library that imports the libraries for
- * which namespaces will be created
- */
- void _createImportedNamespaces() {
- NamespaceBuilder builder = new NamespaceBuilder();
- List<ImportElement> imports = _definingLibrary.imports;
- int count = imports.length;
- _importedNamespaces = new List<Namespace>(count);
- for (int i = 0; i < count; i++) {
- _importedNamespaces[i] =
- builder.createImportNamespaceForDirective(imports[i]);
- }
- }
-
- /**
- * Returns the name of the library that defines given element.
- *
- * @param element the element to get library name
- * @return the name of the library that defines given element
- */
- String _getLibraryName(Element element) {
- if (element == null) {
- return StringUtilities.EMPTY;
- }
- LibraryElement library = element.library;
- if (library == null) {
- return StringUtilities.EMPTY;
- }
- List<ImportElement> imports = _definingLibrary.imports;
- int count = imports.length;
- for (int i = 0; i < count; i++) {
- if (identical(imports[i].importedLibrary, library)) {
- return library.definingCompilationUnit.displayName;
- }
- }
- List<String> indirectSources = new List<String>();
- for (int i = 0; i < count; i++) {
- LibraryElement importedLibrary = imports[i].importedLibrary;
- if (importedLibrary != null) {
- for (LibraryElement exportedLibrary
- in importedLibrary.exportedLibraries) {
- if (identical(exportedLibrary, library)) {
- indirectSources
- .add(importedLibrary.definingCompilationUnit.displayName);
- }
- }
- }
- }
- int indirectCount = indirectSources.length;
- StringBuffer buffer = new StringBuffer();
- buffer.write(library.definingCompilationUnit.displayName);
- if (indirectCount > 0) {
- buffer.write(" (via ");
- if (indirectCount > 1) {
- indirectSources.sort();
- buffer.write(StringUtilities.printListOfQuotedNames(indirectSources));
- } else {
- buffer.write(indirectSources[0]);
- }
- buffer.write(")");
- }
- return buffer.toString();
- }
-
- /**
- * Given a collection of elements (captured by the [foundElement]) that the
- * [identifier] (with the given [name]) resolved to, remove from the list all
- * of the names defined in the SDK and return the element(s) that remain.
- */
- Element _removeSdkElements(Identifier identifier, String name,
- MultiplyDefinedElementImpl foundElement) {
- List<Element> conflictingElements = foundElement.conflictingElements;
- List<Element> nonSdkElements = new List<Element>();
- Element sdkElement = null;
- for (Element member in conflictingElements) {
- if (member.library.isInSdk) {
- sdkElement = member;
- } else {
- nonSdkElements.add(member);
- }
- }
- if (sdkElement != null && nonSdkElements.length > 0) {
- String sdkLibName = _getLibraryName(sdkElement);
- String otherLibName = _getLibraryName(nonSdkElements[0]);
- errorListener.onError(new AnalysisError(
- getSource(identifier),
- identifier.offset,
- identifier.length,
- StaticWarningCode.CONFLICTING_DART_IMPORT,
- [name, sdkLibName, otherLibName]));
- }
- if (nonSdkElements.length == conflictingElements.length) {
- // None of the members were removed
- return foundElement;
- } else if (nonSdkElements.length == 1) {
- // All but one member was removed
- return nonSdkElements[0];
- } else if (nonSdkElements.length == 0) {
- // All members were removed
- AnalysisEngine.instance.logger
- .logInformation("Multiply defined SDK element: $foundElement");
- return foundElement;
- }
- return new MultiplyDefinedElementImpl(
- _definingLibrary.context, nonSdkElements);
- }
-}
-
-/**
- * Instances of the class `LibraryResolver` are used to resolve one or more mutually dependent
- * libraries within a single context.
- */
-class LibraryResolver {
- /**
- * The analysis context in which the libraries are being analyzed.
- */
- final InternalAnalysisContext analysisContext;
-
- /**
- * The listener to which analysis errors will be reported, this error listener is either
- * references [recordingErrorListener], or it unions the passed
- * [AnalysisErrorListener] with the [recordingErrorListener].
- */
- RecordingErrorListener _errorListener;
-
- /**
- * A source object representing the core library (dart:core).
- */
- Source _coreLibrarySource;
-
- /**
- * A Source object representing the async library (dart:async).
- */
- Source _asyncLibrarySource;
-
- /**
- * The object representing the core library.
- */
- Library _coreLibrary;
-
- /**
- * The object representing the async library.
- */
- Library _asyncLibrary;
-
- /**
- * The object used to access the types from the core library.
- */
- TypeProvider _typeProvider;
-
- /**
- * The type system in use for the library
- */
- TypeSystem _typeSystem;
-
- /**
- * A table mapping library sources to the information being maintained for those libraries.
- */
- HashMap<Source, Library> _libraryMap = new HashMap<Source, Library>();
-
- /**
- * A collection containing the libraries that are being resolved together.
- */
- Set<Library> _librariesInCycles;
-
- /**
- * Initialize a newly created library resolver to resolve libraries within the given context.
- *
- * @param analysisContext the analysis context in which the library is being analyzed
- */
- LibraryResolver(this.analysisContext) {
- this._errorListener = new RecordingErrorListener();
- _coreLibrarySource =
- analysisContext.sourceFactory.forUri(DartSdk.DART_CORE);
- _asyncLibrarySource =
- analysisContext.sourceFactory.forUri(DartSdk.DART_ASYNC);
- }
-
- /**
- * Return the listener to which analysis errors will be reported.
- *
- * @return the listener to which analysis errors will be reported
- */
- RecordingErrorListener get errorListener => _errorListener;
-
- /**
- * Return an array containing information about all of the libraries that were resolved.
- *
- * @return an array containing the libraries that were resolved
- */
- Set<Library> get resolvedLibraries => _librariesInCycles;
-
- /**
- * The object used to access the types from the core library.
- */
- TypeProvider get typeProvider => _typeProvider;
-
- /**
- * The type system in use.
- */
- TypeSystem get typeSystem => _typeSystem;
-
- /**
- * Create an object to represent the information about the library defined by the compilation unit
- * with the given source.
- *
- * @param librarySource the source of the library's defining compilation unit
- * @return the library object that was created
- * @throws AnalysisException if the library source is not valid
- */
- Library createLibrary(Source librarySource) {
- Library library =
- new Library(analysisContext, _errorListener, librarySource);
- _libraryMap[librarySource] = library;
- return library;
- }
-
- /**
- * Resolve the library specified by the given source in the given context. The library is assumed
- * to be embedded in the given source.
- *
- * @param librarySource the source specifying the defining compilation unit of the library to be
- * resolved
- * @param unit the compilation unit representing the embedded library
- * @param fullAnalysis `true` if a full analysis should be performed
- * @return the element representing the resolved library
- * @throws AnalysisException if the library could not be resolved for some reason
- */
- LibraryElement resolveEmbeddedLibrary(
- Source librarySource, CompilationUnit unit, bool fullAnalysis) {
- //
- // Create the objects representing the library being resolved and the core
- // library.
- //
- Library targetLibrary = _createLibraryWithUnit(librarySource, unit);
- _coreLibrary = _libraryMap[_coreLibrarySource];
- if (_coreLibrary == null) {
- // This will only happen if the library being analyzed is the core
- // library.
- _coreLibrary = createLibrary(_coreLibrarySource);
- if (_coreLibrary == null) {
- LibraryResolver2.missingCoreLibrary(
- analysisContext, _coreLibrarySource);
- }
- }
- _asyncLibrary = _libraryMap[_asyncLibrarySource];
- if (_asyncLibrary == null) {
- // This will only happen if the library being analyzed is the async
- // library.
- _asyncLibrary = createLibrary(_asyncLibrarySource);
- if (_asyncLibrary == null) {
- LibraryResolver2.missingAsyncLibrary(
- analysisContext, _asyncLibrarySource);
- }
- }
- //
- // Compute the set of libraries that need to be resolved together.
- //
- _computeEmbeddedLibraryDependencies(targetLibrary, unit);
- _librariesInCycles = _computeLibrariesInCycles(targetLibrary);
- //
- // Build the element models representing the libraries being resolved.
- // This is done in three steps:
- //
- // 1. Build the basic element models without making any connections
- // between elements other than the basic parent/child relationships.
- // This includes building the elements representing the libraries.
- // 2. Build the elements for the import and export directives. This
- // requires that we have the elements built for the referenced
- // libraries, but because of the possibility of circular references
- // needs to happen after all of the library elements have been created.
- // 3. Build the rest of the type model by connecting superclasses, mixins,
- // and interfaces. This requires that we be able to compute the names
- // visible in the libraries being resolved, which in turn requires that
- // we have resolved the import directives.
- //
- _buildElementModels();
- LibraryElement coreElement = _coreLibrary.libraryElement;
- if (coreElement == null) {
- throw new AnalysisException("Could not resolve dart:core");
- }
- LibraryElement asyncElement = _asyncLibrary.libraryElement;
- if (asyncElement == null) {
- throw new AnalysisException("Could not resolve dart:async");
- }
- _buildDirectiveModels();
- _typeProvider = new TypeProviderImpl(coreElement, asyncElement);
- _typeSystem = TypeSystem.create(analysisContext);
- _buildTypeHierarchies();
- //
- // Perform resolution and type analysis.
- //
- // TODO(brianwilkerson) Decide whether we want to resolve all of the
- // libraries or whether we want to only resolve the target library.
- // The advantage to resolving everything is that we have already done part
- // of the work so we'll avoid duplicated effort. The disadvantage of
- // resolving everything is that we might do extra work that we don't
- // really care about. Another possibility is to add a parameter to this
- // method and punt the decision to the clients.
- //
- //if (analyzeAll) {
- resolveReferencesAndTypes();
- //} else {
- // resolveReferencesAndTypes(targetLibrary);
- //}
- _performConstantEvaluation();
- return targetLibrary.libraryElement;
- }
-
- /**
- * Resolve the library specified by the given source in the given context.
- *
- * Note that because Dart allows circular imports between libraries, it is possible that more than
- * one library will need to be resolved. In such cases the error listener can receive errors from
- * multiple libraries.
- *
- * @param librarySource the source specifying the defining compilation unit of the library to be
- * resolved
- * @param fullAnalysis `true` if a full analysis should be performed
- * @return the element representing the resolved library
- * @throws AnalysisException if the library could not be resolved for some reason
- */
- LibraryElement resolveLibrary(Source librarySource, bool fullAnalysis) {
- //
- // Create the object representing the library being resolved and compute
- // the dependency relationship. Note that all libraries depend implicitly
- // on core, and we inject an ersatz dependency on async, so once this is
- // done the core and async library elements will have been created.
- //
- Library targetLibrary = createLibrary(librarySource);
- _computeLibraryDependencies(targetLibrary);
- _coreLibrary = _libraryMap[_coreLibrarySource];
- _asyncLibrary = _libraryMap[_asyncLibrarySource];
- //
- // Compute the set of libraries that need to be resolved together.
- //
- _librariesInCycles = _computeLibrariesInCycles(targetLibrary);
- //
- // Build the element models representing the libraries being resolved.
- // This is done in three steps:
- //
- // 1. Build the basic element models without making any connections
- // between elements other than the basic parent/child relationships.
- // This includes building the elements representing the libraries, but
- // excludes members defined in enums.
- // 2. Build the elements for the import and export directives. This
- // requires that we have the elements built for the referenced
- // libraries, but because of the possibility of circular references
- // needs to happen after all of the library elements have been created.
- // 3. Build the members in enum declarations.
- // 4. Build the rest of the type model by connecting superclasses, mixins,
- // and interfaces. This requires that we be able to compute the names
- // visible in the libraries being resolved, which in turn requires that
- // we have resolved the import directives.
- //
- _buildElementModels();
- LibraryElement coreElement = _coreLibrary.libraryElement;
- if (coreElement == null) {
- throw new AnalysisException("Could not resolve dart:core");
- }
- LibraryElement asyncElement = _asyncLibrary.libraryElement;
- if (asyncElement == null) {
- throw new AnalysisException("Could not resolve dart:async");
- }
- _buildDirectiveModels();
- _typeProvider = new TypeProviderImpl(coreElement, asyncElement);
- _typeSystem = TypeSystem.create(analysisContext);
- _buildEnumMembers();
- _buildTypeHierarchies();
- //
- // Perform resolution and type analysis.
- //
- // TODO(brianwilkerson) Decide whether we want to resolve all of the
- // libraries or whether we want to only resolve the target library. The
- // advantage to resolving everything is that we have already done part of
- // the work so we'll avoid duplicated effort. The disadvantage of
- // resolving everything is that we might do extra work that we don't
- // really care about. Another possibility is to add a parameter to this
- // method and punt the decision to the clients.
- //
- //if (analyzeAll) {
- resolveReferencesAndTypes();
- //} else {
- // resolveReferencesAndTypes(targetLibrary);
- //}
- _performConstantEvaluation();
- return targetLibrary.libraryElement;
- }
-
- /**
- * Resolve the identifiers and perform type analysis in the libraries in the current cycle.
- *
- * @throws AnalysisException if any of the identifiers could not be resolved or if any of the
- * libraries could not have their types analyzed
- */
- void resolveReferencesAndTypes() {
- for (Library library in _librariesInCycles) {
- _resolveReferencesAndTypesInLibrary(library);
- }
- }
-
- /**
- * Add a dependency to the given map from the referencing library to the referenced library.
- *
- * @param dependencyMap the map to which the dependency is to be added
- * @param referencingLibrary the library that references the referenced library
- * @param referencedLibrary the library referenced by the referencing library
- */
- void _addDependencyToMap(HashMap<Library, List<Library>> dependencyMap,
- Library referencingLibrary, Library referencedLibrary) {
- List<Library> dependentLibraries = dependencyMap[referencedLibrary];
- if (dependentLibraries == null) {
- dependentLibraries = new List<Library>();
- dependencyMap[referencedLibrary] = dependentLibraries;
- }
- dependentLibraries.add(referencingLibrary);
- }
-
- /**
- * Given a library that is part of a cycle that includes the root library, add to the given set of
- * libraries all of the libraries reachable from the root library that are also included in the
- * cycle.
- *
- * @param library the library to be added to the collection of libraries in cycles
- * @param librariesInCycle a collection of the libraries that are in the cycle
- * @param dependencyMap a table mapping libraries to the collection of libraries from which those
- * libraries are referenced
- */
- void _addLibrariesInCycle(Library library, Set<Library> librariesInCycle,
- HashMap<Library, List<Library>> dependencyMap) {
- if (librariesInCycle.add(library)) {
- List<Library> dependentLibraries = dependencyMap[library];
- if (dependentLibraries != null) {
- for (Library dependentLibrary in dependentLibraries) {
- _addLibrariesInCycle(
- dependentLibrary, librariesInCycle, dependencyMap);
- }
- }
- }
- }
-
- /**
- * Add the given library, and all libraries reachable from it that have not already been visited,
- * to the given dependency map.
- *
- * @param library the library currently being added to the dependency map
- * @param dependencyMap the dependency map being computed
- * @param visitedLibraries the libraries that have already been visited, used to prevent infinite
- * recursion
- */
- void _addToDependencyMap(
- Library library,
- HashMap<Library, List<Library>> dependencyMap,
- Set<Library> visitedLibraries) {
- if (visitedLibraries.add(library)) {
- bool asyncFound = false;
- for (Library referencedLibrary in library.importsAndExports) {
- _addDependencyToMap(dependencyMap, library, referencedLibrary);
- _addToDependencyMap(referencedLibrary, dependencyMap, visitedLibraries);
- if (identical(referencedLibrary, _asyncLibrary)) {
- asyncFound = true;
- }
- }
- if (!library.explicitlyImportsCore && !identical(library, _coreLibrary)) {
- _addDependencyToMap(dependencyMap, library, _coreLibrary);
- }
- if (!asyncFound && !identical(library, _asyncLibrary)) {
- _addDependencyToMap(dependencyMap, library, _asyncLibrary);
- _addToDependencyMap(_asyncLibrary, dependencyMap, visitedLibraries);
- }
- }
- }
-
- /**
- * Build the element model representing the combinators declared by the given directive.
- *
- * @param directive the directive that declares the combinators
- * @return an array containing the import combinators that were built
- */
- List<NamespaceCombinator> _buildCombinators(NamespaceDirective directive) {
- List<NamespaceCombinator> combinators = new List<NamespaceCombinator>();
- for (Combinator combinator in directive.combinators) {
- if (combinator is HideCombinator) {
- HideElementCombinatorImpl hide = new HideElementCombinatorImpl();
- hide.hiddenNames = _getIdentifiers(combinator.hiddenNames);
- combinators.add(hide);
- } else {
- ShowElementCombinatorImpl show = new ShowElementCombinatorImpl();
- show.offset = combinator.offset;
- show.end = combinator.end;
- show.shownNames =
- _getIdentifiers((combinator as ShowCombinator).shownNames);
- combinators.add(show);
- }
- }
- return combinators;
- }
-
- /**
- * Every library now has a corresponding [LibraryElement], so it is now possible to resolve
- * the import and export directives.
- *
- * @throws AnalysisException if the defining compilation unit for any of the libraries could not
- * be accessed
- */
- void _buildDirectiveModels() {
- for (Library library in _librariesInCycles) {
- HashMap<String, PrefixElementImpl> nameToPrefixMap =
- new HashMap<String, PrefixElementImpl>();
- List<ImportElement> imports = new List<ImportElement>();
- List<ExportElement> exports = new List<ExportElement>();
- for (Directive directive in library.definingCompilationUnit.directives) {
- if (directive is ImportDirective) {
- ImportDirective importDirective = directive;
- String uriContent = importDirective.uriContent;
- if (DartUriResolver.isDartExtUri(uriContent)) {
- library.libraryElement.hasExtUri = true;
- }
- Source importedSource = importDirective.source;
- if (importedSource != null) {
- // The imported source will be null if the URI in the import
- // directive was invalid.
- Library importedLibrary = _libraryMap[importedSource];
- if (importedLibrary != null) {
- ImportElementImpl importElement =
- new ImportElementImpl(directive.offset);
- StringLiteral uriLiteral = importDirective.uri;
- importElement.uriOffset = uriLiteral.offset;
- importElement.uriEnd = uriLiteral.end;
- importElement.uri = uriContent;
- importElement.deferred = importDirective.deferredKeyword != null;
- importElement.combinators = _buildCombinators(importDirective);
- LibraryElement importedLibraryElement =
- importedLibrary.libraryElement;
- if (importedLibraryElement != null) {
- importElement.importedLibrary = importedLibraryElement;
- }
- SimpleIdentifier prefixNode = directive.prefix;
- if (prefixNode != null) {
- importElement.prefixOffset = prefixNode.offset;
- String prefixName = prefixNode.name;
- PrefixElementImpl prefix = nameToPrefixMap[prefixName];
- if (prefix == null) {
- prefix = new PrefixElementImpl.forNode(prefixNode);
- nameToPrefixMap[prefixName] = prefix;
- }
- importElement.prefix = prefix;
- prefixNode.staticElement = prefix;
- }
- directive.element = importElement;
- imports.add(importElement);
- if (analysisContext.computeKindOf(importedSource) !=
- SourceKind.LIBRARY) {
- ErrorCode errorCode = (importElement.isDeferred
- ? StaticWarningCode.IMPORT_OF_NON_LIBRARY
- : CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY);
- _errorListener.onError(new AnalysisError(
- library.librarySource,
- uriLiteral.offset,
- uriLiteral.length,
- errorCode,
- [uriLiteral.toSource()]));
- }
- }
- }
- } else if (directive is ExportDirective) {
- ExportDirective exportDirective = directive;
- Source exportedSource = exportDirective.source;
- if (exportedSource != null) {
- // The exported source will be null if the URI in the export
- // directive was invalid.
- Library exportedLibrary = _libraryMap[exportedSource];
- if (exportedLibrary != null) {
- ExportElementImpl exportElement =
- new ExportElementImpl(directive.offset);
- StringLiteral uriLiteral = exportDirective.uri;
- exportElement.uriOffset = uriLiteral.offset;
- exportElement.uriEnd = uriLiteral.end;
- exportElement.uri = exportDirective.uriContent;
- exportElement.combinators = _buildCombinators(exportDirective);
- LibraryElement exportedLibraryElement =
- exportedLibrary.libraryElement;
- if (exportedLibraryElement != null) {
- exportElement.exportedLibrary = exportedLibraryElement;
- }
- directive.element = exportElement;
- exports.add(exportElement);
- if (analysisContext.computeKindOf(exportedSource) !=
- SourceKind.LIBRARY) {
- _errorListener.onError(new AnalysisError(
- library.librarySource,
- uriLiteral.offset,
- uriLiteral.length,
- CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY,
- [uriLiteral.toSource()]));
- }
- }
- }
- }
- }
- Source librarySource = library.librarySource;
- if (!library.explicitlyImportsCore &&
- _coreLibrarySource != librarySource) {
- ImportElementImpl importElement = new ImportElementImpl(-1);
- importElement.importedLibrary = _coreLibrary.libraryElement;
- importElement.synthetic = true;
- imports.add(importElement);
- }
- LibraryElementImpl libraryElement = library.libraryElement;
- libraryElement.imports = imports;
- libraryElement.exports = exports;
- if (libraryElement.entryPoint == null) {
- Namespace namespace = new NamespaceBuilder()
- .createExportNamespaceForLibrary(libraryElement);
- Element element = namespace.get(FunctionElement.MAIN_FUNCTION_NAME);
- if (element is FunctionElement) {
- libraryElement.entryPoint = element;
- }
- }
- }
- }
-
- /**
- * Build element models for all of the libraries in the current cycle.
- *
- * @throws AnalysisException if any of the element models cannot be built
- */
- void _buildElementModels() {
- for (Library library in _librariesInCycles) {
- LibraryElementBuilder builder =
- new LibraryElementBuilder(analysisContext, errorListener);
- LibraryElementImpl libraryElement = builder.buildLibrary(library);
- library.libraryElement = libraryElement;
- }
- }
-
- /**
- * Build the members in enum declarations. This cannot be done while building the rest of the
- * element model because it depends on being able to access core types, which cannot happen until
- * the rest of the element model has been built (when resolving the core library).
- *
- * @throws AnalysisException if any of the enum members could not be built
- */
- void _buildEnumMembers() {
- PerformanceStatistics.resolve.makeCurrentWhile(() {
- for (Library library in _librariesInCycles) {
- for (Source source in library.compilationUnitSources) {
- EnumMemberBuilder builder = new EnumMemberBuilder(_typeProvider);
- library.getAST(source).accept(builder);
- }
- }
- });
- }
-
- /**
- * Resolve the type hierarchy across all of the types declared in the libraries in the current
- * cycle.
- *
- * @throws AnalysisException if any of the type hierarchies could not be resolved
- */
- void _buildTypeHierarchies() {
- PerformanceStatistics.resolve.makeCurrentWhile(() {
- for (Library library in _librariesInCycles) {
- for (Source source in library.compilationUnitSources) {
- TypeResolverVisitorFactory typeResolverVisitorFactory =
- analysisContext.typeResolverVisitorFactory;
- TypeResolverVisitor visitor = (typeResolverVisitorFactory == null)
- ? new TypeResolverVisitor(library.libraryElement, source,
- _typeProvider, library.errorListener,
- nameScope: library.libraryScope)
- : typeResolverVisitorFactory(library, source, _typeProvider);
- library.getAST(source).accept(visitor);
- }
- }
- });
- }
-
- /**
- * Compute a dependency map of libraries reachable from the given library. A dependency map is a
- * table that maps individual libraries to a list of the libraries that either import or export
- * those libraries.
- *
- * This map is used to compute all of the libraries involved in a cycle that include the root
- * library. Given that we only add libraries that are reachable from the root library, when we
- * work backward we are guaranteed to only get libraries in the cycle.
- *
- * @param library the library currently being added to the dependency map
- */
- HashMap<Library, List<Library>> _computeDependencyMap(Library library) {
- HashMap<Library, List<Library>> dependencyMap =
- new HashMap<Library, List<Library>>();
- _addToDependencyMap(library, dependencyMap, new HashSet<Library>());
- return dependencyMap;
- }
-
- /**
- * Recursively traverse the libraries reachable from the given library, creating instances of the
- * class [Library] to represent them, and record the references in the library objects.
- *
- * @param library the library to be processed to find libraries that have not yet been traversed
- * @throws AnalysisException if some portion of the library graph could not be traversed
- */
- void _computeEmbeddedLibraryDependencies(
- Library library, CompilationUnit unit) {
- Source librarySource = library.librarySource;
- HashSet<Source> exportedSources = new HashSet<Source>();
- HashSet<Source> importedSources = new HashSet<Source>();
- for (Directive directive in unit.directives) {
- if (directive is ExportDirective) {
- Source exportSource = _resolveSource(librarySource, directive);
- if (exportSource != null) {
- exportedSources.add(exportSource);
- }
- } else if (directive is ImportDirective) {
- Source importSource = _resolveSource(librarySource, directive);
- if (importSource != null) {
- importedSources.add(importSource);
- }
- }
- }
- _computeLibraryDependenciesFromDirectives(library,
- new List.from(importedSources), new List.from(exportedSources));
- }
-
- /**
- * Return a collection containing all of the libraries reachable from the given library that are
- * contained in a cycle that includes the given library.
- *
- * @param library the library that must be included in any cycles whose members are to be returned
- * @return all of the libraries referenced by the given library that have a circular reference
- * back to the given library
- */
- Set<Library> _computeLibrariesInCycles(Library library) {
- HashMap<Library, List<Library>> dependencyMap =
- _computeDependencyMap(library);
- Set<Library> librariesInCycle = new HashSet<Library>();
- _addLibrariesInCycle(library, librariesInCycle, dependencyMap);
- return librariesInCycle;
- }
-
- /**
- * Recursively traverse the libraries reachable from the given library, creating instances of the
- * class [Library] to represent them, and record the references in the library objects.
- *
- * @param library the library to be processed to find libraries that have not yet been traversed
- * @throws AnalysisException if some portion of the library graph could not be traversed
- */
- void _computeLibraryDependencies(Library library) {
- Source librarySource = library.librarySource;
- _computeLibraryDependenciesFromDirectives(
- library,
- analysisContext.computeImportedLibraries(librarySource),
- analysisContext.computeExportedLibraries(librarySource));
- }
-
- /**
- * Recursively traverse the libraries reachable from the given library, creating instances of the
- * class [Library] to represent them, and record the references in the library objects.
- *
- * @param library the library to be processed to find libraries that have not yet been traversed
- * @param importedSources an array containing the sources that are imported into the given library
- * @param exportedSources an array containing the sources that are exported from the given library
- * @throws AnalysisException if some portion of the library graph could not be traversed
- */
- void _computeLibraryDependenciesFromDirectives(Library library,
- List<Source> importedSources, List<Source> exportedSources) {
- List<Library> importedLibraries = new List<Library>();
- bool explicitlyImportsCore = false;
- bool importsAsync = false;
- for (Source importedSource in importedSources) {
- if (importedSource == _coreLibrarySource) {
- explicitlyImportsCore = true;
- }
- if (importedSource == _asyncLibrarySource) {
- importsAsync = true;
- }
- Library importedLibrary = _libraryMap[importedSource];
- if (importedLibrary == null) {
- importedLibrary = _createLibraryOrNull(importedSource);
- if (importedLibrary != null) {
- _computeLibraryDependencies(importedLibrary);
- }
- }
- if (importedLibrary != null) {
- importedLibraries.add(importedLibrary);
- }
- }
- library.importedLibraries = importedLibraries;
- List<Library> exportedLibraries = new List<Library>();
- for (Source exportedSource in exportedSources) {
- Library exportedLibrary = _libraryMap[exportedSource];
- if (exportedLibrary == null) {
- exportedLibrary = _createLibraryOrNull(exportedSource);
- if (exportedLibrary != null) {
- _computeLibraryDependencies(exportedLibrary);
- }
- }
- if (exportedLibrary != null) {
- exportedLibraries.add(exportedLibrary);
- }
- }
- library.exportedLibraries = exportedLibraries;
- library.explicitlyImportsCore = explicitlyImportsCore;
- if (!explicitlyImportsCore && _coreLibrarySource != library.librarySource) {
- Library importedLibrary = _libraryMap[_coreLibrarySource];
- if (importedLibrary == null) {
- importedLibrary = _createLibraryOrNull(_coreLibrarySource);
- if (importedLibrary != null) {
- _computeLibraryDependencies(importedLibrary);
- }
- }
- }
- if (!importsAsync && _asyncLibrarySource != library.librarySource) {
- Library importedLibrary = _libraryMap[_asyncLibrarySource];
- if (importedLibrary == null) {
- importedLibrary = _createLibraryOrNull(_asyncLibrarySource);
- if (importedLibrary != null) {
- _computeLibraryDependencies(importedLibrary);
- }
- }
- }
- }
-
- /**
- * Create an object to represent the information about the library defined by the compilation unit
- * with the given source. Return the library object that was created, or `null` if the
- * source is not valid.
- *
- * @param librarySource the source of the library's defining compilation unit
- * @return the library object that was created
- */
- Library _createLibraryOrNull(Source librarySource) {
- if (!analysisContext.exists(librarySource)) {
- return null;
- }
- Library library =
- new Library(analysisContext, _errorListener, librarySource);
- _libraryMap[librarySource] = library;
- return library;
- }
-
- /**
- * Create an object to represent the information about the library defined by the compilation unit
- * with the given source.
- *
- * @param librarySource the source of the library's defining compilation unit
- * @param unit the compilation unit that defines the library
- * @return the library object that was created
- * @throws AnalysisException if the library source is not valid
- */
- Library _createLibraryWithUnit(Source librarySource, CompilationUnit unit) {
- Library library =
- new Library(analysisContext, _errorListener, librarySource);
- library.setDefiningCompilationUnit(unit);
- _libraryMap[librarySource] = library;
- return library;
- }
-
- /**
- * Return an array containing the lexical identifiers associated with the nodes in the given list.
- *
- * @param names the AST nodes representing the identifiers
- * @return the lexical identifiers associated with the nodes in the list
- */
- List<String> _getIdentifiers(NodeList<SimpleIdentifier> names) {
- int count = names.length;
- List<String> identifiers = new List<String>(count);
- for (int i = 0; i < count; i++) {
- identifiers[i] = names[i].name;
- }
- return identifiers;
- }
-
- /**
- * Compute a value for all of the constants in the libraries being analyzed.
- */
- void _performConstantEvaluation() {
- PerformanceStatistics.resolve.makeCurrentWhile(() {
- ConstantValueComputer computer = new ConstantValueComputer(
- analysisContext,
- _typeProvider,
- analysisContext.declaredVariables,
- null,
- _typeSystem);
- for (Library library in _librariesInCycles) {
- for (Source source in library.compilationUnitSources) {
- try {
- CompilationUnit unit = library.getAST(source);
- if (unit != null) {
- computer.add(unit, source, library.librarySource);
- }
- } on AnalysisException catch (exception, stackTrace) {
- AnalysisEngine.instance.logger.logError(
- "Internal Error: Could not access AST for ${source.fullName} during constant evaluation",
- new CaughtException(exception, stackTrace));
- }
- }
- }
- computer.computeValues();
- // As a temporary workaround for issue 21572, run ConstantVerifier now.
- // TODO(paulberry): remove this workaround once issue 21572 is fixed.
- for (Library library in _librariesInCycles) {
- for (Source source in library.compilationUnitSources) {
- try {
- CompilationUnit unit = library.getAST(source);
- ErrorReporter errorReporter =
- new ErrorReporter(_errorListener, source);
- ConstantVerifier constantVerifier = new ConstantVerifier(
- errorReporter,
- library.libraryElement,
- _typeProvider,
- analysisContext.declaredVariables);
- unit.accept(constantVerifier);
- } on AnalysisException catch (exception, stackTrace) {
- AnalysisEngine.instance.logger.logError(
- "Internal Error: Could not access AST for ${source.fullName} "
- "during constant verification",
- new CaughtException(exception, stackTrace));
- }
- }
- }
- });
- }
-
- /**
- * Resolve the identifiers and perform type analysis in the given library.
- *
- * @param library the library to be resolved
- * @throws AnalysisException if any of the identifiers could not be resolved or if the types in
- * the library cannot be analyzed
- */
- void _resolveReferencesAndTypesInLibrary(Library library) {
- PerformanceStatistics.resolve.makeCurrentWhile(() {
- for (Source source in library.compilationUnitSources) {
- CompilationUnit ast = library.getAST(source);
- ast.accept(new VariableResolverVisitor(library.libraryElement, source,
- _typeProvider, library.errorListener,
- nameScope: library.libraryScope));
- ResolverVisitorFactory visitorFactory =
- analysisContext.resolverVisitorFactory;
- ResolverVisitor visitor = visitorFactory != null
- ? visitorFactory(library, source, _typeProvider)
- : new ResolverVisitor(library.libraryElement, source, _typeProvider,
- library.errorListener,
- nameScope: library.libraryScope,
- inheritanceManager: library.inheritanceManager);
- ast.accept(visitor);
- }
- });
- }
-
- /**
- * Return the result of resolving the URI of the given URI-based directive against the URI of the
- * given library, or `null` if the URI is not valid.
- *
- * @param librarySource the source representing the library containing the directive
- * @param directive the directive which URI should be resolved
- * @return the result of resolving the URI against the URI of the library
- */
- Source _resolveSource(Source librarySource, UriBasedDirective directive) {
- StringLiteral uriLiteral = directive.uri;
- if (uriLiteral is StringInterpolation) {
- return null;
- }
- String uriContent = uriLiteral.stringValue.trim();
- if (uriContent == null || uriContent.isEmpty) {
- return null;
- }
- uriContent = Uri.encodeFull(uriContent);
- return analysisContext.sourceFactory.resolveUri(librarySource, uriContent);
- }
-}
-
-/**
- * Instances of the class `LibraryResolver` are used to resolve one or more mutually dependent
- * libraries within a single context.
- */
-class LibraryResolver2 {
- /**
- * The analysis context in which the libraries are being analyzed.
- */
- final InternalAnalysisContext analysisContext;
-
- /**
- * The listener to which analysis errors will be reported, this error listener is either
- * references [recordingErrorListener], or it unions the passed
- * [AnalysisErrorListener] with the [recordingErrorListener].
- */
- RecordingErrorListener _errorListener;
-
- /**
- * A source object representing the core library (dart:core).
- */
- Source _coreLibrarySource;
-
- /**
- * A source object representing the async library (dart:async).
- */
- Source _asyncLibrarySource;
-
- /**
- * The object representing the core library.
- */
- ResolvableLibrary _coreLibrary;
-
- /**
- * The object representing the async library.
- */
- ResolvableLibrary _asyncLibrary;
-
- /**
- * The object used to access the types from the core library.
- */
- TypeProvider _typeProvider;
-
- /**
- * The type system in use for the library
- */
- TypeSystem _typeSystem;
-
- /**
- * A table mapping library sources to the information being maintained for those libraries.
- */
- HashMap<Source, ResolvableLibrary> _libraryMap =
- new HashMap<Source, ResolvableLibrary>();
-
- /**
- * A collection containing the libraries that are being resolved together.
- */
- List<ResolvableLibrary> _librariesInCycle;
-
- /**
- * Initialize a newly created library resolver to resolve libraries within the given context.
- *
- * @param analysisContext the analysis context in which the library is being analyzed
- */
- LibraryResolver2(this.analysisContext) {
- this._errorListener = new RecordingErrorListener();
- _coreLibrarySource =
- analysisContext.sourceFactory.forUri(DartSdk.DART_CORE);
- _asyncLibrarySource =
- analysisContext.sourceFactory.forUri(DartSdk.DART_ASYNC);
- }
-
- /**
- * Return the listener to which analysis errors will be reported.
- *
- * @return the listener to which analysis errors will be reported
- */
- RecordingErrorListener get errorListener => _errorListener;
-
- /**
- * Return an array containing information about all of the libraries that were resolved.
- *
- * @return an array containing the libraries that were resolved
- */
- List<ResolvableLibrary> get resolvedLibraries => _librariesInCycle;
-
- /**
- * Resolve the library specified by the given source in the given context.
- *
- * Note that because Dart allows circular imports between libraries, it is possible that more than
- * one library will need to be resolved. In such cases the error listener can receive errors from
- * multiple libraries.
- *
- * @param librarySource the source specifying the defining compilation unit of the library to be
- * resolved
- * @param fullAnalysis `true` if a full analysis should be performed
- * @return the element representing the resolved library
- * @throws AnalysisException if the library could not be resolved for some reason
- */
- LibraryElement resolveLibrary(
- Source librarySource, List<ResolvableLibrary> librariesInCycle) {
- //
- // Build the map of libraries that are known.
- //
- this._librariesInCycle = librariesInCycle;
- _libraryMap = _buildLibraryMap();
- ResolvableLibrary targetLibrary = _libraryMap[librarySource];
- _coreLibrary = _libraryMap[_coreLibrarySource];
- _asyncLibrary = _libraryMap[_asyncLibrarySource];
- //
- // Build the element models representing the libraries being resolved.
- // This is done in three steps:
- //
- // 1. Build the basic element models without making any connections
- // between elements other than the basic parent/child relationships.
- // This includes building the elements representing the libraries, but
- // excludes members defined in enums.
- // 2. Build the elements for the import and export directives. This
- // requires that we have the elements built for the referenced
- // libraries, but because of the possibility of circular references
- // needs to happen after all of the library elements have been created.
- // 3. Build the members in enum declarations.
- // 4. Build the rest of the type model by connecting superclasses, mixins,
- // and interfaces. This requires that we be able to compute the names
- // visible in the libraries being resolved, which in turn requires that
- // we have resolved the import directives.
- //
- _buildElementModels();
- LibraryElement coreElement = _coreLibrary.libraryElement;
- if (coreElement == null) {
- missingCoreLibrary(analysisContext, _coreLibrarySource);
- }
- LibraryElement asyncElement = _asyncLibrary.libraryElement;
- if (asyncElement == null) {
- missingAsyncLibrary(analysisContext, _asyncLibrarySource);
- }
- _buildDirectiveModels();
- _typeProvider = new TypeProviderImpl(coreElement, asyncElement);
- _typeSystem = TypeSystem.create(analysisContext);
- _buildEnumMembers();
- _buildTypeHierarchies();
- //
- // Perform resolution and type analysis.
- //
- // TODO(brianwilkerson) Decide whether we want to resolve all of the
- // libraries or whether we want to only resolve the target library. The
- // advantage to resolving everything is that we have already done part of
- // the work so we'll avoid duplicated effort. The disadvantage of
- // resolving everything is that we might do extra work that we don't
- // really care about. Another possibility is to add a parameter to this
- // method and punt the decision to the clients.
- //
- //if (analyzeAll) {
- _resolveReferencesAndTypes();
- //} else {
- // resolveReferencesAndTypes(targetLibrary);
- //}
- _performConstantEvaluation();
- return targetLibrary.libraryElement;
- }
-
- /**
- * Build the element model representing the combinators declared by the given directive.
- *
- * @param directive the directive that declares the combinators
- * @return an array containing the import combinators that were built
- */
- List<NamespaceCombinator> _buildCombinators(NamespaceDirective directive) {
- List<NamespaceCombinator> combinators = new List<NamespaceCombinator>();
- for (Combinator combinator in directive.combinators) {
- if (combinator is HideCombinator) {
- HideElementCombinatorImpl hide = new HideElementCombinatorImpl();
- hide.hiddenNames = _getIdentifiers(combinator.hiddenNames);
- combinators.add(hide);
- } else {
- ShowElementCombinatorImpl show = new ShowElementCombinatorImpl();
- show.offset = combinator.offset;
- show.end = combinator.end;
- show.shownNames =
- _getIdentifiers((combinator as ShowCombinator).shownNames);
- combinators.add(show);
- }
- }
- return combinators;
- }
-
- /**
- * Every library now has a corresponding [LibraryElement], so it is now possible to resolve
- * the import and export directives.
- *
- * @throws AnalysisException if the defining compilation unit for any of the libraries could not
- * be accessed
- */
- void _buildDirectiveModels() {
- for (ResolvableLibrary library in _librariesInCycle) {
- HashMap<String, PrefixElementImpl> nameToPrefixMap =
- new HashMap<String, PrefixElementImpl>();
- List<ImportElement> imports = new List<ImportElement>();
- List<ExportElement> exports = new List<ExportElement>();
- for (Directive directive in library.definingCompilationUnit.directives) {
- if (directive is ImportDirective) {
- ImportDirective importDirective = directive;
- String uriContent = importDirective.uriContent;
- if (DartUriResolver.isDartExtUri(uriContent)) {
- library.libraryElement.hasExtUri = true;
- }
- Source importedSource = importDirective.source;
- if (importedSource != null &&
- analysisContext.exists(importedSource)) {
- // The imported source will be null if the URI in the import
- // directive was invalid.
- ResolvableLibrary importedLibrary = _libraryMap[importedSource];
- if (importedLibrary != null) {
- ImportElementImpl importElement =
- new ImportElementImpl(directive.offset);
- StringLiteral uriLiteral = importDirective.uri;
- if (uriLiteral != null) {
- importElement.uriOffset = uriLiteral.offset;
- importElement.uriEnd = uriLiteral.end;
- }
- importElement.uri = uriContent;
- importElement.deferred = importDirective.deferredKeyword != null;
- importElement.combinators = _buildCombinators(importDirective);
- LibraryElement importedLibraryElement =
- importedLibrary.libraryElement;
- if (importedLibraryElement != null) {
- importElement.importedLibrary = importedLibraryElement;
- }
- SimpleIdentifier prefixNode = directive.prefix;
- if (prefixNode != null) {
- importElement.prefixOffset = prefixNode.offset;
- String prefixName = prefixNode.name;
- PrefixElementImpl prefix = nameToPrefixMap[prefixName];
- if (prefix == null) {
- prefix = new PrefixElementImpl.forNode(prefixNode);
- nameToPrefixMap[prefixName] = prefix;
- }
- importElement.prefix = prefix;
- prefixNode.staticElement = prefix;
- }
- directive.element = importElement;
- imports.add(importElement);
- if (analysisContext.computeKindOf(importedSource) !=
- SourceKind.LIBRARY) {
- ErrorCode errorCode = (importElement.isDeferred
- ? StaticWarningCode.IMPORT_OF_NON_LIBRARY
- : CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY);
- _errorListener.onError(new AnalysisError(
- library.librarySource,
- uriLiteral.offset,
- uriLiteral.length,
- errorCode,
- [uriLiteral.toSource()]));
- }
- }
- }
- } else if (directive is ExportDirective) {
- ExportDirective exportDirective = directive;
- Source exportedSource = exportDirective.source;
- if (exportedSource != null &&
- analysisContext.exists(exportedSource)) {
- // The exported source will be null if the URI in the export
- // directive was invalid.
- ResolvableLibrary exportedLibrary = _libraryMap[exportedSource];
- if (exportedLibrary != null) {
- ExportElementImpl exportElement =
- new ExportElementImpl(directive.offset);
- StringLiteral uriLiteral = exportDirective.uri;
- if (uriLiteral != null) {
- exportElement.uriOffset = uriLiteral.offset;
- exportElement.uriEnd = uriLiteral.end;
- }
- exportElement.uri = exportDirective.uriContent;
- exportElement.combinators = _buildCombinators(exportDirective);
- LibraryElement exportedLibraryElement =
- exportedLibrary.libraryElement;
- if (exportedLibraryElement != null) {
- exportElement.exportedLibrary = exportedLibraryElement;
- }
- directive.element = exportElement;
- exports.add(exportElement);
- if (analysisContext.computeKindOf(exportedSource) !=
- SourceKind.LIBRARY) {
- _errorListener.onError(new AnalysisError(
- library.librarySource,
- uriLiteral.offset,
- uriLiteral.length,
- CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY,
- [uriLiteral.toSource()]));
- }
- }
- }
- }
- }
- Source librarySource = library.librarySource;
- if (!library.explicitlyImportsCore &&
- _coreLibrarySource != librarySource) {
- ImportElementImpl importElement = new ImportElementImpl(-1);
- importElement.importedLibrary = _coreLibrary.libraryElement;
- importElement.synthetic = true;
- imports.add(importElement);
- }
- LibraryElementImpl libraryElement = library.libraryElement;
- libraryElement.imports = imports;
- libraryElement.exports = exports;
- if (libraryElement.entryPoint == null) {
- Namespace namespace = new NamespaceBuilder()
- .createExportNamespaceForLibrary(libraryElement);
- Element element = namespace.get(FunctionElement.MAIN_FUNCTION_NAME);
- if (element is FunctionElement) {
- libraryElement.entryPoint = element;
- }
- }
- }
- }
-
- /**
- * Build element models for all of the libraries in the current cycle.
- *
- * @throws AnalysisException if any of the element models cannot be built
- */
- void _buildElementModels() {
- for (ResolvableLibrary library in _librariesInCycle) {
- LibraryElementBuilder builder =
- new LibraryElementBuilder(analysisContext, errorListener);
- builder.buildLibrary2(library);
- }
- }
-
- /**
- * Build the members in enum declarations. This cannot be done while building the rest of the
- * element model because it depends on being able to access core types, which cannot happen until
- * the rest of the element model has been built (when resolving the core library).
- *
- * @throws AnalysisException if any of the enum members could not be built
- */
- void _buildEnumMembers() {
- PerformanceStatistics.resolve.makeCurrentWhile(() {
- for (ResolvableLibrary library in _librariesInCycle) {
- for (Source source in library.compilationUnitSources) {
- EnumMemberBuilder builder = new EnumMemberBuilder(_typeProvider);
- library.getAST(source).accept(builder);
- }
- }
- });
- }
-
- HashMap<Source, ResolvableLibrary> _buildLibraryMap() {
- HashMap<Source, ResolvableLibrary> libraryMap =
- new HashMap<Source, ResolvableLibrary>();
- int libraryCount = _librariesInCycle.length;
- for (int i = 0; i < libraryCount; i++) {
- ResolvableLibrary library = _librariesInCycle[i];
- library.errorListener = _errorListener;
- libraryMap[library.librarySource] = library;
- List<ResolvableLibrary> dependencies = library.importsAndExports;
- int dependencyCount = dependencies.length;
- for (int j = 0; j < dependencyCount; j++) {
- ResolvableLibrary dependency = dependencies[j];
- //dependency.setErrorListener(errorListener);
- libraryMap[dependency.librarySource] = dependency;
- }
- }
- return libraryMap;
- }
-
- /**
- * Resolve the type hierarchy across all of the types declared in the libraries in the current
- * cycle.
- *
- * @throws AnalysisException if any of the type hierarchies could not be resolved
- */
- void _buildTypeHierarchies() {
- PerformanceStatistics.resolve.makeCurrentWhile(() {
- for (ResolvableLibrary library in _librariesInCycle) {
- for (ResolvableCompilationUnit unit
- in library.resolvableCompilationUnits) {
- Source source = unit.source;
- CompilationUnit ast = unit.compilationUnit;
- TypeResolverVisitor visitor = new TypeResolverVisitor(
- library.libraryElement,
- source,
- _typeProvider,
- library.libraryScope.errorListener,
- nameScope: library.libraryScope);
- ast.accept(visitor);
- }
- }
- });
- }
-
- /**
- * Return an array containing the lexical identifiers associated with the nodes in the given list.
- *
- * @param names the AST nodes representing the identifiers
- * @return the lexical identifiers associated with the nodes in the list
- */
- List<String> _getIdentifiers(NodeList<SimpleIdentifier> names) {
- int count = names.length;
- List<String> identifiers = new List<String>(count);
- for (int i = 0; i < count; i++) {
- identifiers[i] = names[i].name;
- }
- return identifiers;
- }
-
- /**
- * Compute a value for all of the constants in the libraries being analyzed.
- */
- void _performConstantEvaluation() {
- PerformanceStatistics.resolve.makeCurrentWhile(() {
- ConstantValueComputer computer = new ConstantValueComputer(
- analysisContext,
- _typeProvider,
- analysisContext.declaredVariables,
- null,
- _typeSystem);
- for (ResolvableLibrary library in _librariesInCycle) {
- for (ResolvableCompilationUnit unit
- in library.resolvableCompilationUnits) {
- CompilationUnit ast = unit.compilationUnit;
- if (ast != null) {
- computer.add(ast, unit.source, library.librarySource);
- }
- }
- }
- computer.computeValues();
- // As a temporary workaround for issue 21572, run ConstantVerifier now.
- // TODO(paulberry): remove this workaround once issue 21572 is fixed.
- for (ResolvableLibrary library in _librariesInCycle) {
- for (ResolvableCompilationUnit unit
- in library.resolvableCompilationUnits) {
- CompilationUnit ast = unit.compilationUnit;
- ErrorReporter errorReporter =
- new ErrorReporter(_errorListener, unit.source);
- ConstantVerifier constantVerifier = new ConstantVerifier(
- errorReporter,
- library.libraryElement,
- _typeProvider,
- analysisContext.declaredVariables);
- ast.accept(constantVerifier);
- }
- }
- });
- }
-
- /**
- * Resolve the identifiers and perform type analysis in the libraries in the current cycle.
- *
- * @throws AnalysisException if any of the identifiers could not be resolved or if any of the
- * libraries could not have their types analyzed
- */
- void _resolveReferencesAndTypes() {
- for (ResolvableLibrary library in _librariesInCycle) {
- _resolveReferencesAndTypesInLibrary(library);
- }
- }
-
- /**
- * Resolve the identifiers and perform type analysis in the given library.
- *
- * @param library the library to be resolved
- * @throws AnalysisException if any of the identifiers could not be resolved or if the types in
- * the library cannot be analyzed
- */
- void _resolveReferencesAndTypesInLibrary(ResolvableLibrary library) {
- PerformanceStatistics.resolve.makeCurrentWhile(() {
- for (ResolvableCompilationUnit unit
- in library.resolvableCompilationUnits) {
- Source source = unit.source;
- CompilationUnit ast = unit.compilationUnit;
- ast.accept(new VariableResolverVisitor(library.libraryElement, source,
- _typeProvider, library.libraryScope.errorListener,
- nameScope: library.libraryScope));
- ResolverVisitor visitor = new ResolverVisitor(library.libraryElement,
- source, _typeProvider, library._libraryScope.errorListener,
- nameScope: library._libraryScope,
- inheritanceManager: library.inheritanceManager);
- ast.accept(visitor);
- }
- });
- }
-
- /**
- * Report that the async library could not be resolved in the given
- * [analysisContext] and throw an exception. [asyncLibrarySource] is the source
- * representing the async library.
- */
- static void missingAsyncLibrary(
- AnalysisContext analysisContext, Source asyncLibrarySource) {
- throw new AnalysisException("Could not resolve dart:async");
- }
-
- /**
- * Report that the core library could not be resolved in the given analysis context and throw an
- * exception.
- *
- * @param analysisContext the analysis context in which the failure occurred
- * @param coreLibrarySource the source representing the core library
- * @throws AnalysisException always
- */
- static void missingCoreLibrary(
- AnalysisContext analysisContext, Source coreLibrarySource) {
- throw new AnalysisException("Could not resolve dart:core");
- }
-}
-
-/**
- * Instances of the class `LibraryScope` implement a scope containing all of the names defined
- * in a given library.
- */
-class LibraryScope extends EnclosedScope {
- /**
- * Initialize a newly created scope representing the names defined in the given library.
- *
- * @param definingLibrary the element representing the library represented by this scope
- * @param errorListener the listener that is to be informed when an error is encountered
- */
- LibraryScope(
- LibraryElement definingLibrary, AnalysisErrorListener errorListener)
- : super(new LibraryImportScope(definingLibrary, errorListener)) {
- _defineTopLevelNames(definingLibrary);
- }
-
- @override
- AnalysisError getErrorForDuplicate(Element existing, Element duplicate) {
- if (existing is PrefixElement) {
- // TODO(scheglov) consider providing actual 'nameOffset' from the
- // synthetic accessor
- int offset = duplicate.nameOffset;
- if (duplicate is PropertyAccessorElement) {
- PropertyAccessorElement accessor = duplicate;
- if (accessor.isSynthetic) {
- offset = accessor.variable.nameOffset;
- }
- }
- return new AnalysisError(
- duplicate.source,
- offset,
- duplicate.nameLength,
- CompileTimeErrorCode.PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER,
- [existing.displayName]);
- }
- return super.getErrorForDuplicate(existing, duplicate);
- }
-
- /**
- * Add to this scope all of the public top-level names that are defined in the given compilation
- * unit.
- *
- * @param compilationUnit the compilation unit defining the top-level names to be added to this
- * scope
- */
- void _defineLocalNames(CompilationUnitElement compilationUnit) {
- for (PropertyAccessorElement element in compilationUnit.accessors) {
- define(element);
- }
- for (ClassElement element in compilationUnit.enums) {
- define(element);
- }
- for (FunctionElement element in compilationUnit.functions) {
- define(element);
- }
- for (FunctionTypeAliasElement element
- in compilationUnit.functionTypeAliases) {
- define(element);
- }
- for (ClassElement element in compilationUnit.types) {
- define(element);
- }
- }
-
- /**
- * Add to this scope all of the names that are explicitly defined in the given library.
- *
- * @param definingLibrary the element representing the library that defines the names in this
- * scope
- */
- void _defineTopLevelNames(LibraryElement definingLibrary) {
- for (PrefixElement prefix in definingLibrary.prefixes) {
- define(prefix);
- }
- _defineLocalNames(definingLibrary.definingCompilationUnit);
- for (CompilationUnitElement compilationUnit in definingLibrary.parts) {
- _defineLocalNames(compilationUnit);
- }
- }
-}
-
-/**
- * This class is used to replace uses of `HashMap<String, ExecutableElement>`
- * which are not as performant as this class.
- */
-class MemberMap {
- /**
- * The current size of this map.
- */
- int _size = 0;
-
- /**
- * The array of keys.
- */
- List<String> _keys;
-
- /**
- * The array of ExecutableElement values.
- */
- List<ExecutableElement> _values;
-
- /**
- * Initialize a newly created member map to have the given [initialCapacity].
- * The map will grow if needed.
- */
- MemberMap([int initialCapacity = 10]) {
- _initArrays(initialCapacity);
- }
-
- /**
- * This constructor takes an initial capacity of the map.
- *
- * @param initialCapacity the initial capacity
- */
- @deprecated // Use new MemberMap(initialCapacity)
- MemberMap.con1(int initialCapacity) {
- _initArrays(initialCapacity);
- }
-
- /**
- * Copy constructor.
- */
- @deprecated // Use new MemberMap.from(memberMap)
- MemberMap.con2(MemberMap memberMap) {
- _initArrays(memberMap._size + 5);
- for (int i = 0; i < memberMap._size; i++) {
- _keys[i] = memberMap._keys[i];
- _values[i] = memberMap._values[i];
- }
- _size = memberMap._size;
- }
-
- /**
- * Initialize a newly created member map to contain the same members as the
- * given [memberMap].
- */
- MemberMap.from(MemberMap memberMap) {
- _initArrays(memberMap._size + 5);
- for (int i = 0; i < memberMap._size; i++) {
- _keys[i] = memberMap._keys[i];
- _values[i] = memberMap._values[i];
- }
- _size = memberMap._size;
- }
-
- /**
- * The size of the map.
- *
- * @return the size of the map.
- */
- int get size => _size;
-
- /**
- * Given some key, return the ExecutableElement value from the map, if the key does not exist in
- * the map, `null` is returned.
- *
- * @param key some key to look up in the map
- * @return the associated ExecutableElement value from the map, if the key does not exist in the
- * map, `null` is returned
- */
- ExecutableElement get(String key) {
- for (int i = 0; i < _size; i++) {
- if (_keys[i] != null && _keys[i] == key) {
- return _values[i];
- }
- }
- return null;
- }
-
- /**
- * Get and return the key at the specified location. If the key/value pair has been removed from
- * the set, then `null` is returned.
- *
- * @param i some non-zero value less than size
- * @return the key at the passed index
- * @throw ArrayIndexOutOfBoundsException this exception is thrown if the passed index is less than
- * zero or greater than or equal to the capacity of the arrays
- */
- String getKey(int i) => _keys[i];
-
- /**
- * Get and return the ExecutableElement at the specified location. If the key/value pair has been
- * removed from the set, then then `null` is returned.
- *
- * @param i some non-zero value less than size
- * @return the key at the passed index
- * @throw ArrayIndexOutOfBoundsException this exception is thrown if the passed index is less than
- * zero or greater than or equal to the capacity of the arrays
- */
- ExecutableElement getValue(int i) => _values[i];
-
- /**
- * Given some key/value pair, store the pair in the map. If the key exists already, then the new
- * value overrides the old value.
- *
- * @param key the key to store in the map
- * @param value the ExecutableElement value to store in the map
- */
- void put(String key, ExecutableElement value) {
- // If we already have a value with this key, override the value
- for (int i = 0; i < _size; i++) {
- if (_keys[i] != null && _keys[i] == key) {
- _values[i] = value;
- return;
- }
- }
- // If needed, double the size of our arrays and copy values over in both
- // arrays
- if (_size == _keys.length) {
- int newArrayLength = _size * 2;
- List<String> keys_new_array = new List<String>(newArrayLength);
- List<ExecutableElement> values_new_array =
- new List<ExecutableElement>(newArrayLength);
- for (int i = 0; i < _size; i++) {
- keys_new_array[i] = _keys[i];
- }
- for (int i = 0; i < _size; i++) {
- values_new_array[i] = _values[i];
- }
- _keys = keys_new_array;
- _values = values_new_array;
- }
- // Put new value at end of array
- _keys[_size] = key;
- _values[_size] = value;
- _size++;
- }
-
- /**
- * Given some [String] key, this method replaces the associated key and value pair with
- * `null`. The size is not decremented with this call, instead it is expected that the users
- * check for `null`.
- *
- * @param key the key of the key/value pair to remove from the map
- */
- void remove(String key) {
- for (int i = 0; i < _size; i++) {
- if (_keys[i] == key) {
- _keys[i] = null;
- _values[i] = null;
- return;
- }
- }
- }
-
- /**
- * Sets the ExecutableElement at the specified location.
- *
- * @param i some non-zero value less than size
- * @param value the ExecutableElement value to store in the map
- */
- void setValue(int i, ExecutableElement value) {
- _values[i] = value;
- }
-
- /**
- * Initializes [keys] and [values].
- */
- void _initArrays(int initialCapacity) {
- _keys = new List<String>(initialCapacity);
- _values = new List<ExecutableElement>(initialCapacity);
- }
-}
-
-/**
- * Instances of the class `Namespace` implement a mapping of identifiers to the elements
- * represented by those identifiers. Namespaces are the building blocks for scopes.
- */
-class Namespace {
- /**
- * An empty namespace.
- */
- static Namespace EMPTY = new Namespace(new HashMap<String, Element>());
-
- /**
- * A table mapping names that are defined in this namespace to the element representing the thing
- * declared with that name.
- */
- final HashMap<String, Element> _definedNames;
-
- /**
- * Initialize a newly created namespace to have the given defined names.
- *
- * @param definedNames the mapping from names that are defined in this namespace to the
- * corresponding elements
- */
- Namespace(this._definedNames);
-
- /**
- * Return a table containing the same mappings as those defined by this namespace.
- *
- * @return a table containing the same mappings as those defined by this namespace
- */
- Map<String, Element> get definedNames => _definedNames;
-
- /**
- * Return the element in this namespace that is available to the containing scope using the given
- * name.
- *
- * @param name the name used to reference the
- * @return the element represented by the given identifier
- */
- Element get(String name) => _definedNames[name];
-}
-
-/**
- * Instances of the class `NamespaceBuilder` are used to build a `Namespace`. Namespace
- * builders are thread-safe and re-usable.
- */
-class NamespaceBuilder {
- /**
- * Create a namespace representing the export namespace of the given [ExportElement].
- *
- * @param element the export element whose export namespace is to be created
- * @return the export namespace that was created
- */
- Namespace createExportNamespaceForDirective(ExportElement element) {
- LibraryElement exportedLibrary = element.exportedLibrary;
- if (exportedLibrary == null) {
- //
- // The exported library will be null if the URI does not reference a valid
- // library.
- //
- return Namespace.EMPTY;
- }
- HashMap<String, Element> definedNames =
- _createExportMapping(exportedLibrary, new HashSet<LibraryElement>());
- definedNames = _applyCombinators(definedNames, element.combinators);
- return new Namespace(definedNames);
- }
-
- /**
- * Create a namespace representing the export namespace of the given library.
- *
- * @param library the library whose export namespace is to be created
- * @return the export namespace that was created
- */
- Namespace createExportNamespaceForLibrary(LibraryElement library) =>
- new Namespace(
- _createExportMapping(library, new HashSet<LibraryElement>()));
-
- /**
- * Create a namespace representing the import namespace of the given library.
- *
- * @param library the library whose import namespace is to be created
- * @return the import namespace that was created
- */
- Namespace createImportNamespaceForDirective(ImportElement element) {
- LibraryElement importedLibrary = element.importedLibrary;
- if (importedLibrary == null) {
- //
- // The imported library will be null if the URI does not reference a valid
- // library.
- //
- return Namespace.EMPTY;
- }
- HashMap<String, Element> definedNames =
- _createExportMapping(importedLibrary, new HashSet<LibraryElement>());
- definedNames = _applyCombinators(definedNames, element.combinators);
- definedNames = _applyPrefix(definedNames, element.prefix);
- return new Namespace(definedNames);
- }
-
- /**
- * Create a namespace representing the public namespace of the given library.
- *
- * @param library the library whose public namespace is to be created
- * @return the public namespace that was created
- */
- Namespace createPublicNamespaceForLibrary(LibraryElement library) {
- HashMap<String, Element> definedNames = new HashMap<String, Element>();
- _addPublicNames(definedNames, library.definingCompilationUnit);
- for (CompilationUnitElement compilationUnit in library.parts) {
- _addPublicNames(definedNames, compilationUnit);
- }
- return new Namespace(definedNames);
- }
-
- /**
- * Add all of the names in the given namespace to the given mapping table.
- *
- * @param definedNames the mapping table to which the names in the given namespace are to be added
- * @param namespace the namespace containing the names to be added to this namespace
- */
- void _addAllFromNamespace(
- Map<String, Element> definedNames, Namespace namespace) {
- if (namespace != null) {
- definedNames.addAll(namespace.definedNames);
- }
- }
-
- /**
- * Add the given element to the given mapping table if it has a publicly visible name.
- *
- * @param definedNames the mapping table to which the public name is to be added
- * @param element the element to be added
- */
- void _addIfPublic(Map<String, Element> definedNames, Element element) {
- String name = element.name;
- if (name != null && !Scope.isPrivateName(name)) {
- definedNames[name] = element;
- }
- }
-
- /**
- * Add to the given mapping table all of the public top-level names that are defined in the given
- * compilation unit.
- *
- * @param definedNames the mapping table to which the public names are to be added
- * @param compilationUnit the compilation unit defining the top-level names to be added to this
- * namespace
- */
- void _addPublicNames(Map<String, Element> definedNames,
- CompilationUnitElement compilationUnit) {
- for (PropertyAccessorElement element in compilationUnit.accessors) {
- _addIfPublic(definedNames, element);
- }
- for (ClassElement element in compilationUnit.enums) {
- _addIfPublic(definedNames, element);
- }
- for (FunctionElement element in compilationUnit.functions) {
- _addIfPublic(definedNames, element);
- }
- for (FunctionTypeAliasElement element
- in compilationUnit.functionTypeAliases) {
- _addIfPublic(definedNames, element);
- }
- for (ClassElement element in compilationUnit.types) {
- _addIfPublic(definedNames, element);
- }
- }
-
- /**
- * Apply the given combinators to all of the names in the given mapping table.
- *
- * @param definedNames the mapping table to which the namespace operations are to be applied
- * @param combinators the combinators to be applied
- */
- HashMap<String, Element> _applyCombinators(
- HashMap<String, Element> definedNames,
- List<NamespaceCombinator> combinators) {
- for (NamespaceCombinator combinator in combinators) {
- if (combinator is HideElementCombinator) {
- definedNames = _hide(definedNames, combinator.hiddenNames);
- } else if (combinator is ShowElementCombinator) {
- definedNames = _show(definedNames, combinator.shownNames);
- } else {
- // Internal error.
- AnalysisEngine.instance.logger
- .logError("Unknown type of combinator: ${combinator.runtimeType}");
- }
- }
- return definedNames;
- }
-
- /**
- * Apply the given prefix to all of the names in the table of defined names.
- *
- * @param definedNames the names that were defined before this operation
- * @param prefixElement the element defining the prefix to be added to the names
- */
- HashMap<String, Element> _applyPrefix(
- HashMap<String, Element> definedNames, PrefixElement prefixElement) {
- if (prefixElement != null) {
- String prefix = prefixElement.name;
- HashMap<String, Element> newNames = new HashMap<String, Element>();
- definedNames.forEach((String name, Element element) {
- newNames["$prefix.$name"] = element;
- });
- return newNames;
- } else {
- return definedNames;
- }
- }
-
- /**
- * Create a mapping table representing the export namespace of the given library.
- *
- * @param library the library whose public namespace is to be created
- * @param visitedElements a set of libraries that do not need to be visited when processing the
- * export directives of the given library because all of the names defined by them will
- * be added by another library
- * @return the mapping table that was created
- */
- HashMap<String, Element> _createExportMapping(
- LibraryElement library, HashSet<LibraryElement> visitedElements) {
- // Check if the export namespace has been already computed.
- {
- Namespace exportNamespace = library.exportNamespace;
- if (exportNamespace != null) {
- return exportNamespace.definedNames;
- }
- }
- // TODO(scheglov) Remove this after switching to the new task model.
- visitedElements.add(library);
- try {
- HashMap<String, Element> definedNames = new HashMap<String, Element>();
- for (ExportElement element in library.exports) {
- LibraryElement exportedLibrary = element.exportedLibrary;
- if (exportedLibrary != null &&
- !visitedElements.contains(exportedLibrary)) {
- //
- // The exported library will be null if the URI does not reference a
- // valid library.
- //
- HashMap<String, Element> exportedNames =
- _createExportMapping(exportedLibrary, visitedElements);
- exportedNames = _applyCombinators(exportedNames, element.combinators);
- definedNames.addAll(exportedNames);
- }
- }
- _addAllFromNamespace(
- definedNames,
- (library.context as InternalAnalysisContext)
- .getPublicNamespace(library));
- return definedNames;
- } finally {
- visitedElements.remove(library);
- }
- }
-
- /**
- * Return a new map of names which has all the names from [definedNames]
- * with exception of [hiddenNames].
- */
- Map<String, Element> _hide(
- HashMap<String, Element> definedNames, List<String> hiddenNames) {
- HashMap<String, Element> newNames =
- new HashMap<String, Element>.from(definedNames);
- for (String name in hiddenNames) {
- newNames.remove(name);
- newNames.remove("$name=");
- }
- return newNames;
- }
-
- /**
- * Return a new map of names which has only [shownNames] from [definedNames].
- */
- HashMap<String, Element> _show(
- HashMap<String, Element> definedNames, List<String> shownNames) {
- HashMap<String, Element> newNames = new HashMap<String, Element>();
- for (String name in shownNames) {
- Element element = definedNames[name];
- if (element != null) {
- newNames[name] = element;
- }
- String setterName = "$name=";
- element = definedNames[setterName];
- if (element != null) {
- newNames[setterName] = element;
- }
- }
- return newNames;
- }
-}
-
-/**
- * Instances of the class `OverrideVerifier` visit all of the declarations in a compilation
- * unit to verify that if they have an override annotation it is being used correctly.
- */
-class OverrideVerifier extends RecursiveAstVisitor<Object> {
- /**
- * The error reporter used to report errors.
- */
- final ErrorReporter _errorReporter;
-
- /**
- * The inheritance manager used to find overridden methods.
- */
- final InheritanceManager _manager;
-
- /**
- * Initialize a newly created verifier to look for inappropriate uses of the override annotation.
- *
- * @param errorReporter the error reporter used to report errors
- * @param manager the inheritance manager used to find overridden methods
- */
- OverrideVerifier(this._errorReporter, this._manager);
-
- @override
- Object visitMethodDeclaration(MethodDeclaration node) {
- ExecutableElement element = node.element;
- if (_isOverride(element)) {
- if (_getOverriddenMember(element) == null) {
- if (element is MethodElement) {
- _errorReporter.reportErrorForNode(
- HintCode.OVERRIDE_ON_NON_OVERRIDING_METHOD, node.name);
- } else if (element is PropertyAccessorElement) {
- if (element.isGetter) {
- _errorReporter.reportErrorForNode(
- HintCode.OVERRIDE_ON_NON_OVERRIDING_GETTER, node.name);
- } else {
- _errorReporter.reportErrorForNode(
- HintCode.OVERRIDE_ON_NON_OVERRIDING_SETTER, node.name);
- }
- }
- }
- }
- return super.visitMethodDeclaration(node);
- }
-
- /**
- * Return the member that overrides the given member.
- *
- * @param member the member that overrides the returned member
- * @return the member that overrides the given member
- */
- ExecutableElement _getOverriddenMember(ExecutableElement member) {
- LibraryElement library = member.library;
- if (library == null) {
- return null;
- }
- ClassElement classElement =
- member.getAncestor((element) => element is ClassElement);
- if (classElement == null) {
- return null;
- }
- return _manager.lookupInheritance(classElement, member.name);
- }
-
- /**
- * Return `true` if the given element has an override annotation associated with it.
- *
- * @param element the element being tested
- * @return `true` if the element has an override annotation associated with it
- */
- bool _isOverride(Element element) => element != null && element.isOverride;
-}
-
-/**
- * An AST visitor that is used to resolve the some of the nodes within a single
- * compilation unit. The nodes that are skipped are those that are within
- * function bodies.
- */
-class PartialResolverVisitor extends ResolverVisitor {
- /**
- * A flag indicating whether the resolver is being run in strong mode.
- */
- final bool strongMode;
-
- /**
- * The static variables and fields that have an initializer. These are the
- * variables that need to be re-resolved after static variables have their
- * types inferred. A subset of these variables are those whose types should
- * be inferred. The list will be empty unless the resolver is being run in
- * strong mode.
- */
- final List<VariableElement> variablesAndFields = <VariableElement>[];
-
- /**
- * A flag indicating whether we should discard errors while resolving the
- * initializer for variable declarations. We do this for top-level variables
- * and fields because their initializer will be re-resolved at a later time.
- */
- bool discardErrorsInInitializer = false;
-
- /**
- * Initialize a newly created visitor to resolve the nodes in an AST node.
- *
- * The [definingLibrary] is the element for the library containing the node
- * being visited. The [source] is the source representing the compilation unit
- * containing the node being visited. The [typeProvider] is the object used to
- * access the types from the core library. The [errorListener] is the error
- * listener that will be informed of any errors that are found during
- * resolution. The [nameScope] is the scope used to resolve identifiers in the
- * node that will first be visited. If `null` or unspecified, a new
- * [LibraryScope] will be created based on [definingLibrary] and
- * [typeProvider]. The [inheritanceManager] is used to perform inheritance
- * lookups. If `null` or unspecified, a new [InheritanceManager] will be
- * created based on [definingLibrary]. The [typeAnalyzerFactory] is used to
- * create the type analyzer. If `null` or unspecified, a type analyzer of
- * type [StaticTypeAnalyzer] will be created.
- */
- PartialResolverVisitor(LibraryElement definingLibrary, Source source,
- TypeProvider typeProvider, AnalysisErrorListener errorListener,
- {Scope nameScope,
- InheritanceManager inheritanceManager,
- StaticTypeAnalyzerFactory typeAnalyzerFactory})
- : strongMode = definingLibrary.context.analysisOptions.strongMode,
- super(definingLibrary, source, typeProvider,
- new DisablableErrorListener(errorListener));
-
- @override
- Object visitBlockFunctionBody(BlockFunctionBody node) {
- if (_shouldBeSkipped(node)) {
- return null;
- }
- return super.visitBlockFunctionBody(node);
- }
-
- @override
- Object visitExpressionFunctionBody(ExpressionFunctionBody node) {
- if (_shouldBeSkipped(node)) {
- return null;
- }
- return super.visitExpressionFunctionBody(node);
- }
-
- @override
- Object visitFieldDeclaration(FieldDeclaration node) {
- if (strongMode && node.isStatic) {
- _addVariables(node.fields.variables);
- bool wasDiscarding = discardErrorsInInitializer;
- discardErrorsInInitializer = true;
- try {
- return super.visitFieldDeclaration(node);
- } finally {
- discardErrorsInInitializer = wasDiscarding;
- }
- }
- return super.visitFieldDeclaration(node);
- }
-
- @override
- Object visitNode(AstNode node) {
- if (discardErrorsInInitializer) {
- AstNode parent = node.parent;
- if (parent is VariableDeclaration && parent.initializer == node) {
- DisablableErrorListener listener = errorListener;
- return listener.disableWhile(() => super.visitNode(node));
- }
- }
- return super.visitNode(node);
- }
-
- @override
- Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
- if (strongMode) {
- _addVariables(node.variables.variables);
- bool wasDiscarding = discardErrorsInInitializer;
- discardErrorsInInitializer = true;
- try {
- return super.visitTopLevelVariableDeclaration(node);
- } finally {
- discardErrorsInInitializer = wasDiscarding;
- }
- }
- return super.visitTopLevelVariableDeclaration(node);
- }
-
- /**
- * Add all of the [variables] with initializers to the list of variables whose
- * type can be inferred. Technically, we only infer the types of variables
- * that do not have a static type, but all variables with initializers
- * potentially need to be re-resolved after inference because they might
- * refer to a field whose type was inferred.
- */
- void _addVariables(NodeList<VariableDeclaration> variables) {
- for (VariableDeclaration variable in variables) {
- if (variable.initializer != null) {
- variablesAndFields.add(variable.element);
- }
- }
- }
-
- /**
- * Return `true` if the given function body should be skipped because it is
- * the body of a top-level function, method or constructor.
- */
- bool _shouldBeSkipped(FunctionBody body) {
- AstNode parent = body.parent;
- if (parent is MethodDeclaration) {
- return parent.body == body;
- }
- if (parent is ConstructorDeclaration) {
- return parent.body == body;
- }
- if (parent is FunctionExpression) {
- AstNode parent2 = parent.parent;
- if (parent2 is FunctionDeclaration &&
- parent2.parent is! FunctionDeclarationStatement) {
- return parent.body == body;
- }
- }
- return false;
- }
-}
-
-/**
- * Instances of the class `PubVerifier` traverse an AST structure looking for deviations from
- * pub best practices.
- */
-class PubVerifier extends RecursiveAstVisitor<Object> {
-// static String _PUBSPEC_YAML = "pubspec.yaml";
-
- /**
- * The analysis context containing the sources to be analyzed
- */
- final AnalysisContext _context;
-
- /**
- * The error reporter by which errors will be reported.
- */
- final ErrorReporter _errorReporter;
+ final ErrorReporter _errorReporter;
PubVerifier(this._context, this._errorReporter);
@@ -9875,363 +5503,115 @@ class PubVerifier extends RecursiveAstVisitor<Object> {
// Source source = _getSource(uriLiteral);
// String relativePubspecPath = path.substring(0, pathIndex) + _PUBSPEC_YAML;
// Source pubspecSource =
-// _context.sourceFactory.resolveUri(source, relativePubspecPath);
-// if (!_context.exists(pubspecSource)) {
-// return false;
-// }
-// String fullName = _getSourceFullName(source);
-// if (fullName != null) {
-// if (StringUtilities.indexOf5(fullName, 0, 0x2F, 0x6C, 0x69, 0x62, 0x2F) <
-// 0) {
-// // Files outside the lib directory hierarchy should not reference files
-// // inside ... use package: url instead
-// _errorReporter.reportErrorForNode(
-// HintCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE,
-// uriLiteral);
-// return true;
-// }
-// }
-// return false;
-// }
-
-// /**
-// * This verifies that the passed package import directive does not contain ".."
-// *
-// * @param uriLiteral the import URL (not `null`)
-// * @param path the path to be validated (not `null`)
-// * @return `true` if and only if an error code is generated on the passed node
-// * See [PubSuggestionCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT].
-// */
-// bool _checkForPackageImportContainsDotDot(StringLiteral uriLiteral,
-// String path) {
-// if (StringUtilities.startsWith3(path, 0, 0x2E, 0x2E, 0x2F) ||
-// StringUtilities.indexOf4(path, 0, 0x2F, 0x2E, 0x2E, 0x2F) >= 0) {
-// // Package import should not to contain ".."
-// _errorReporter.reportErrorForNode(
-// HintCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT,
-// uriLiteral);
-// return true;
-// }
-// return false;
-// }
-
-// /**
-// * Answer the source associated with the compilation unit containing the given AST node.
-// *
-// * @param node the node (not `null`)
-// * @return the source or `null` if it could not be determined
-// */
-// Source _getSource(AstNode node) {
-// Source source = null;
-// CompilationUnit unit = node.getAncestor((node) => node is CompilationUnit);
-// if (unit != null) {
-// CompilationUnitElement element = unit.element;
-// if (element != null) {
-// source = element.source;
-// }
-// }
-// return source;
-// }
-
-// /**
-// * Answer the full name of the given source. The returned value will have all
-// * [File.separatorChar] replace by '/'.
-// *
-// * @param source the source
-// * @return the full name or `null` if it could not be determined
-// */
-// String _getSourceFullName(Source source) {
-// if (source != null) {
-// String fullName = source.fullName;
-// if (fullName != null) {
-// return fullName.replaceAll(r'\', '/');
-// }
-// }
-// return null;
-// }
-}
-
-/**
- * Kind of the redirecting constructor.
- */
-class RedirectingConstructorKind extends Enum<RedirectingConstructorKind> {
- static const RedirectingConstructorKind CONST =
- const RedirectingConstructorKind('CONST', 0);
-
- static const RedirectingConstructorKind NORMAL =
- const RedirectingConstructorKind('NORMAL', 1);
-
- static const List<RedirectingConstructorKind> values = const [CONST, NORMAL];
-
- const RedirectingConstructorKind(String name, int ordinal)
- : super(name, ordinal);
-}
-
-/**
- * A `ResolvableLibrary` represents a single library during the resolution of
- * some (possibly different) library. They are not intended to be used except
- * during the resolution process.
- */
-class ResolvableLibrary {
- /**
- * An empty array that can be used to initialize lists of libraries.
- */
- static List<ResolvableLibrary> _EMPTY_ARRAY = new List<ResolvableLibrary>(0);
-
- /**
- * The next artificial hash code.
- */
- static int _NEXT_HASH_CODE = 0;
-
- /**
- * The artifitial hash code for this object.
- */
- final int _hashCode = _nextHashCode();
-
- /**
- * The source specifying the defining compilation unit of this library.
- */
- final Source librarySource;
-
- /**
- * A list containing all of the libraries that are imported into this library.
- */
- List<ResolvableLibrary> _importedLibraries = _EMPTY_ARRAY;
-
- /**
- * A flag indicating whether this library explicitly imports core.
- */
- bool explicitlyImportsCore = false;
-
- /**
- * An array containing all of the libraries that are exported from this library.
- */
- List<ResolvableLibrary> _exportedLibraries = _EMPTY_ARRAY;
-
- /**
- * An array containing the compilation units that comprise this library. The
- * defining compilation unit is always first.
- */
- List<ResolvableCompilationUnit> _compilationUnits;
-
- /**
- * The library element representing this library.
- */
- LibraryElementImpl _libraryElement;
-
- /**
- * The listener to which analysis errors will be reported.
- */
- AnalysisErrorListener _errorListener;
-
- /**
- * The inheritance manager which is used for member lookups in this library.
- */
- InheritanceManager _inheritanceManager;
-
- /**
- * The library scope used when resolving elements within this library's compilation units.
- */
- LibraryScope _libraryScope;
-
- /**
- * Initialize a newly created data holder that can maintain the data associated with a library.
- *
- * @param librarySource the source specifying the defining compilation unit of this library
- * @param errorListener the listener to which analysis errors will be reported
- */
- ResolvableLibrary(this.librarySource);
-
- /**
- * Return an array of the [CompilationUnit]s that make up the library. The first unit is
- * always the defining unit.
- *
- * @return an array of the [CompilationUnit]s that make up the library. The first unit is
- * always the defining unit
- */
- List<CompilationUnit> get compilationUnits {
- int count = _compilationUnits.length;
- List<CompilationUnit> units = new List<CompilationUnit>(count);
- for (int i = 0; i < count; i++) {
- units[i] = _compilationUnits[i].compilationUnit;
- }
- return units;
- }
-
- /**
- * Return an array containing the sources for the compilation units in this library, including the
- * defining compilation unit.
- *
- * @return the sources for the compilation units in this library
- */
- List<Source> get compilationUnitSources {
- int count = _compilationUnits.length;
- List<Source> sources = new List<Source>(count);
- for (int i = 0; i < count; i++) {
- sources[i] = _compilationUnits[i].source;
- }
- return sources;
- }
-
- /**
- * Return the AST structure associated with the defining compilation unit for this library.
- *
- * @return the AST structure associated with the defining compilation unit for this library
- * @throws AnalysisException if an AST structure could not be created for the defining compilation
- * unit
- */
- CompilationUnit get definingCompilationUnit =>
- _compilationUnits[0].compilationUnit;
-
- /**
- * Set the listener to which analysis errors will be reported to be the given listener.
- *
- * @param errorListener the listener to which analysis errors will be reported
- */
- void set errorListener(AnalysisErrorListener errorListener) {
- this._errorListener = errorListener;
- }
-
- /**
- * Set the libraries that are exported by this library to be those in the given array.
- *
- * @param exportedLibraries the libraries that are exported by this library
- */
- void set exportedLibraries(List<ResolvableLibrary> exportedLibraries) {
- this._exportedLibraries = exportedLibraries;
- }
-
- /**
- * Return an array containing the libraries that are exported from this library.
- *
- * @return an array containing the libraries that are exported from this library
- */
- List<ResolvableLibrary> get exports => _exportedLibraries;
-
- @override
- int get hashCode => _hashCode;
+// _context.sourceFactory.resolveUri(source, relativePubspecPath);
+// if (!_context.exists(pubspecSource)) {
+// return false;
+// }
+// String fullName = _getSourceFullName(source);
+// if (fullName != null) {
+// if (StringUtilities.indexOf5(fullName, 0, 0x2F, 0x6C, 0x69, 0x62, 0x2F) <
+// 0) {
+// // Files outside the lib directory hierarchy should not reference files
+// // inside ... use package: url instead
+// _errorReporter.reportErrorForNode(
+// HintCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE,
+// uriLiteral);
+// return true;
+// }
+// }
+// return false;
+// }
- /**
- * Set the libraries that are imported into this library to be those in the given array.
- *
- * @param importedLibraries the libraries that are imported into this library
- */
- void set importedLibraries(List<ResolvableLibrary> importedLibraries) {
- this._importedLibraries = importedLibraries;
- }
+// /**
+// * This verifies that the passed package import directive does not contain ".."
+// *
+// * @param uriLiteral the import URL (not `null`)
+// * @param path the path to be validated (not `null`)
+// * @return `true` if and only if an error code is generated on the passed node
+// * See [PubSuggestionCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT].
+// */
+// bool _checkForPackageImportContainsDotDot(StringLiteral uriLiteral,
+// String path) {
+// if (StringUtilities.startsWith3(path, 0, 0x2E, 0x2E, 0x2F) ||
+// StringUtilities.indexOf4(path, 0, 0x2F, 0x2E, 0x2E, 0x2F) >= 0) {
+// // Package import should not to contain ".."
+// _errorReporter.reportErrorForNode(
+// HintCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT,
+// uriLiteral);
+// return true;
+// }
+// return false;
+// }
- /**
- * Return an array containing the libraries that are imported into this library.
- *
- * @return an array containing the libraries that are imported into this library
- */
- List<ResolvableLibrary> get imports => _importedLibraries;
+// /**
+// * Answer the source associated with the compilation unit containing the given AST node.
+// *
+// * @param node the node (not `null`)
+// * @return the source or `null` if it could not be determined
+// */
+// Source _getSource(AstNode node) {
+// Source source = null;
+// CompilationUnit unit = node.getAncestor((node) => node is CompilationUnit);
+// if (unit != null) {
+// CompilationUnitElement element = unit.element;
+// if (element != null) {
+// source = element.source;
+// }
+// }
+// return source;
+// }
- /**
- * Return an array containing the libraries that are either imported or exported from this
- * library.
- *
- * @return the libraries that are either imported or exported from this library
- */
- List<ResolvableLibrary> get importsAndExports {
- HashSet<ResolvableLibrary> libraries = new HashSet<ResolvableLibrary>();
- for (ResolvableLibrary library in _importedLibraries) {
- libraries.add(library);
- }
- for (ResolvableLibrary library in _exportedLibraries) {
- libraries.add(library);
- }
- return new List.from(libraries);
- }
+// /**
+// * Answer the full name of the given source. The returned value will have all
+// * [File.separatorChar] replace by '/'.
+// *
+// * @param source the source
+// * @return the full name or `null` if it could not be determined
+// */
+// String _getSourceFullName(Source source) {
+// if (source != null) {
+// String fullName = source.fullName;
+// if (fullName != null) {
+// return fullName.replaceAll(r'\', '/');
+// }
+// }
+// return null;
+// }
+}
- /**
- * Return the inheritance manager for this library.
- *
- * @return the inheritance manager for this library
- */
- InheritanceManager get inheritanceManager {
- if (_inheritanceManager == null) {
- return _inheritanceManager = new InheritanceManager(_libraryElement);
- }
- return _inheritanceManager;
- }
+/**
+ * Kind of the redirecting constructor.
+ */
+class RedirectingConstructorKind
+ implements Comparable<RedirectingConstructorKind> {
+ static const RedirectingConstructorKind CONST =
+ const RedirectingConstructorKind('CONST', 0);
- /**
- * Return the library element representing this library, creating it if necessary.
- *
- * @return the library element representing this library
- */
- LibraryElementImpl get libraryElement => _libraryElement;
+ static const RedirectingConstructorKind NORMAL =
+ const RedirectingConstructorKind('NORMAL', 1);
- /**
- * Set the library element representing this library to the given library element.
- *
- * @param libraryElement the library element representing this library
- */
- void set libraryElement(LibraryElementImpl libraryElement) {
- this._libraryElement = libraryElement;
- if (_inheritanceManager != null) {
- _inheritanceManager.libraryElement = libraryElement;
- }
- }
+ static const List<RedirectingConstructorKind> values = const [CONST, NORMAL];
/**
- * Return the library scope used when resolving elements within this library's compilation units.
- *
- * @return the library scope used when resolving elements within this library's compilation units
+ * The name of this redirecting constructor kind.
*/
- LibraryScope get libraryScope {
- if (_libraryScope == null) {
- _libraryScope = new LibraryScope(_libraryElement, _errorListener);
- }
- return _libraryScope;
- }
+ final String name;
/**
- * Return an array containing the compilation units that comprise this library. The defining
- * compilation unit is always first.
- *
- * @return the compilation units that comprise this library
+ * The ordinal value of the redirecting constructor kind.
*/
- List<ResolvableCompilationUnit> get resolvableCompilationUnits =>
- _compilationUnits;
+ final int ordinal;
- /**
- * Set the compilation unit in this library to the given compilation units. The defining
- * compilation unit must be the first element of the array.
- *
- * @param units the compilation units in this library
- */
- void set resolvableCompilationUnits(List<ResolvableCompilationUnit> units) {
- _compilationUnits = units;
- }
+ const RedirectingConstructorKind(this.name, this.ordinal);
- /**
- * Return the AST structure associated with the given source, or `null` if the source does
- * not represent a compilation unit that is included in this library.
- *
- * @param source the source representing the compilation unit whose AST is to be returned
- * @return the AST structure associated with the given source
- * @throws AnalysisException if an AST structure could not be created for the compilation unit
- */
- CompilationUnit getAST(Source source) {
- int count = _compilationUnits.length;
- for (int i = 0; i < count; i++) {
- if (_compilationUnits[i].source == source) {
- return _compilationUnits[i].compilationUnit;
- }
- }
- return null;
- }
+ @override
+ int get hashCode => ordinal;
@override
- String toString() => librarySource.shortName;
+ int compareTo(RedirectingConstructorKind other) => ordinal - other.ordinal;
- static int _nextHashCode() {
- int next = (_NEXT_HASH_CODE + 1) & 0xFFFFFF;
- _NEXT_HASH_CODE = next;
- return next;
- }
+ @override
+ String toString() => name;
}
/**
@@ -10275,11 +5655,6 @@ class ResolverErrorCode extends ErrorCode {
* compilation unit.
*/
class ResolverVisitor extends ScopedVisitor {
- /**
- * The manager for the inheritance mappings.
- */
- InheritanceManager _inheritanceManager;
-
/**
* The object used to resolve the element associated with the current node.
*/
@@ -10290,16 +5665,10 @@ class ResolverVisitor extends ScopedVisitor {
*/
StaticTypeAnalyzer typeAnalyzer;
- /*
- * The type system in use during resolution.
- */
- TypeSystem typeSystem;
-
/**
- * The class element representing the class containing the current node,
- * or `null` if the current node is not contained in a class.
+ * The type system in use during resolution.
*/
- ClassElement enclosingClass = null;
+ TypeSystem typeSystem;
/**
* The class declaration representing the class containing the current node, or `null` if
@@ -10319,11 +5688,7 @@ class ResolverVisitor extends ScopedVisitor {
*/
ExecutableElement _enclosingFunction = null;
- /**
- * The [Comment] before a [FunctionDeclaration] or a [MethodDeclaration] that
- * cannot be resolved where we visited it, because it should be resolved in the scope of the body.
- */
- Comment _commentBeforeFunction = null;
+ InferenceContext inferenceContext = null;
/**
* The object keeping track of which elements have had their types overridden.
@@ -10345,6 +5710,16 @@ class ResolverVisitor extends ScopedVisitor {
*/
bool resolveOnlyCommentInFunctionBody = false;
+ /**
+ * Body of the function currently being analyzed, if any.
+ */
+ FunctionBody _currentFunctionBody;
+
+ /**
+ * Are we running in strong mode or not.
+ */
+ bool strongMode;
+
/**
* Initialize a newly created visitor to resolve the nodes in an AST node.
*
@@ -10364,44 +5739,22 @@ class ResolverVisitor extends ScopedVisitor {
*/
ResolverVisitor(LibraryElement definingLibrary, Source source,
TypeProvider typeProvider, AnalysisErrorListener errorListener,
- {Scope nameScope,
- InheritanceManager inheritanceManager,
- StaticTypeAnalyzerFactory typeAnalyzerFactory})
+ {Scope nameScope})
: super(definingLibrary, source, typeProvider, errorListener,
nameScope: nameScope) {
- if (inheritanceManager == null) {
- this._inheritanceManager = new InheritanceManager(definingLibrary);
- } else {
- this._inheritanceManager = inheritanceManager;
- }
+ AnalysisOptions options = definingLibrary.context.analysisOptions;
+ this.strongMode = options.strongMode;
this.elementResolver = new ElementResolver(this);
this.typeSystem = definingLibrary.context.typeSystem;
- if (typeAnalyzerFactory == null) {
- this.typeAnalyzer = new StaticTypeAnalyzer(this);
- } else {
- this.typeAnalyzer = typeAnalyzerFactory(this);
+ bool strongModeHints = false;
+ if (options is AnalysisOptionsImpl) {
+ strongModeHints = options.strongModeHints;
}
+ this.inferenceContext = new InferenceContext._(
+ errorReporter, typeProvider, typeSystem, strongModeHints);
+ this.typeAnalyzer = new StaticTypeAnalyzer(this);
}
- /**
- * Initialize a newly created visitor to resolve the nodes in a compilation unit.
- *
- * @param library the library containing the compilation unit being resolved
- * @param source the source representing the compilation unit being visited
- * @param typeProvider the object used to access the types from the core library
- *
- * Deprecated. Please use unnamed constructor instead.
- */
- @deprecated
- ResolverVisitor.con1(
- Library library, Source source, TypeProvider typeProvider,
- {StaticTypeAnalyzerFactory typeAnalyzerFactory})
- : this(
- library.libraryElement, source, typeProvider, library.errorListener,
- nameScope: library.libraryScope,
- inheritanceManager: library.inheritanceManager,
- typeAnalyzerFactory: typeAnalyzerFactory);
-
/**
* Return the element representing the function containing the current node, or `null` if
* the current node is not contained in a function.
@@ -10469,30 +5822,21 @@ class ResolverVisitor extends ScopedVisitor {
}
/**
- * Return the static element associated with the given expression whose type can be promoted, or
- * `null` if there is no element whose type can be promoted.
- *
- * @param expression the expression with which the element is associated
- * @return the element associated with the given expression
+ * Return the static element associated with the given expression whose type
+ * can be promoted, or `null` if there is no element whose type can be
+ * promoted.
*/
VariableElement getPromotionStaticElement(Expression expression) {
- while (expression is ParenthesizedExpression) {
- expression = (expression as ParenthesizedExpression).expression;
- }
- if (expression is! SimpleIdentifier) {
- return null;
- }
- SimpleIdentifier identifier = expression as SimpleIdentifier;
- Element element = identifier.staticElement;
- if (element is! VariableElement) {
- return null;
- }
- ElementKind kind = element.kind;
- if (kind == ElementKind.LOCAL_VARIABLE) {
- return element as VariableElement;
- }
- if (kind == ElementKind.PARAMETER) {
- return element as VariableElement;
+ expression = expression?.unParenthesized;
+ if (expression is SimpleIdentifier) {
+ Element element = expression.staticElement;
+ if (element is VariableElement) {
+ ElementKind kind = element.kind;
+ if (kind == ElementKind.LOCAL_VARIABLE ||
+ kind == ElementKind.PARAMETER) {
+ return element;
+ }
+ }
}
return null;
}
@@ -10500,15 +5844,74 @@ class ResolverVisitor extends ScopedVisitor {
/**
* Prepares this [ResolverVisitor] to using it for incremental resolution.
*/
- void initForIncrementalResolution([Declaration declaration = null]) {
- if (declaration != null) {
- Element element = declaration.element;
- if (element is ExecutableElement) {
- _enclosingFunction = element;
+ void initForIncrementalResolution() {
+ _overrideManager.enterScope();
+ }
+
+ /**
+ * Returns true if this method is `Future.then` or an override thereof.
+ *
+ * If so we will apply special typing rules in strong mode, to handle the
+ * implicit union of `S | Future<S>`
+ */
+ bool isFutureThen(Element element) {
+ // If we are a method named then
+ if (element is MethodElement && element.name == 'then') {
+ DartType type = element.enclosingElement.type;
+ // On Future or a subtype, then we're good.
+ return (type.isDartAsyncFuture || isSubtypeOfFuture(type));
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if this type is any subtype of the built in Future type.
+ */
+ bool isSubtypeOfFuture(DartType type) =>
+ typeSystem.isSubtypeOf(type, typeProvider.futureDynamicType);
+
+ /**
+ * Given a downward inference type [fnType], and the declared
+ * [typeParameterList] for a function expression, determines if we can enable
+ * downward inference and if so, returns the function type to use for
+ * inference.
+ *
+ * This will return null if inference is not possible. This happens when
+ * there is no way we can find a subtype of the function type, given the
+ * provided type parameter list.
+ */
+ FunctionType matchFunctionTypeParameters(
+ TypeParameterList typeParameterList, FunctionType fnType) {
+ if (typeParameterList == null) {
+ if (fnType.typeFormals.isEmpty) {
+ return fnType;
}
- _commentBeforeFunction = declaration.documentationComment;
+
+ // A non-generic function cannot be a subtype of a generic one.
+ return null;
}
- _overrideManager.enterScope();
+
+ NodeList<TypeParameter> typeParameters = typeParameterList.typeParameters;
+ if (fnType.typeFormals.isEmpty) {
+ // TODO(jmesserly): this is a legal subtype. We don't currently infer
+ // here, but we could. This is similar to
+ // StrongTypeSystemImpl.inferFunctionTypeInstantiation, but we don't
+ // have the FunctionType yet for the current node, so it's not quite
+ // straightforward to apply.
+ return null;
+ }
+
+ if (fnType.typeFormals.length != typeParameters.length) {
+ // A subtype cannot have different number of type formals.
+ return null;
+ }
+
+ // Same number of type formals. Instantiate the function type so its
+ // parameter and return type are in terms of the surrounding context.
+ return fnType.instantiate(typeParameters
+ .map((TypeParameter t) =>
+ (t.name.staticElement as TypeParameterElement).type)
+ .toList());
}
/**
@@ -10603,7 +6006,7 @@ class ResolverVisitor extends ScopedVisitor {
void prepareToResolveMembersInClass(ClassDeclaration node) {
_enclosingClassDeclaration = node;
enclosingClass = node.element;
- typeAnalyzer.thisType = enclosingClass == null ? null : enclosingClass.type;
+ typeAnalyzer.thisType = enclosingClass?.type;
}
/**
@@ -10617,7 +6020,7 @@ class ResolverVisitor extends ScopedVisitor {
void recordPropagatedTypeIfBetter(Expression expression, DartType type,
[bool hasOldPropagatedType = false]) {
// Ensure that propagated type invalid.
- if (type == null || type.isDynamic || type.isBottom) {
+ if (strongMode || type == null || type.isDynamic || type.isBottom) {
if (!hasOldPropagatedType) {
expression.propagatedType = null;
}
@@ -10641,6 +6044,15 @@ class ResolverVisitor extends ScopedVisitor {
expression.propagatedType = type;
}
+ /**
+ * Visit the given [comment] if it is not `null`.
+ */
+ void safelyVisitComment(Comment comment) {
+ if (comment != null) {
+ super.visitComment(comment);
+ }
+ }
+
@override
Object visitAnnotation(Annotation node) {
AstNode parent = node.parent;
@@ -10648,7 +6060,68 @@ class ResolverVisitor extends ScopedVisitor {
identical(parent, _enclosingFunctionTypeAlias)) {
return null;
}
- return super.visitAnnotation(node);
+ node.name?.accept(this);
+ node.constructorName?.accept(this);
+ Element element = node.element;
+ if (element is ExecutableElement) {
+ InferenceContext.setType(node.arguments, element.type);
+ }
+ node.arguments?.accept(this);
+ node.accept(elementResolver);
+ node.accept(typeAnalyzer);
+ ElementAnnotationImpl elementAnnotationImpl = node.elementAnnotation;
+ if (elementAnnotationImpl == null) {
+ // Analyzer ignores annotations on "part of" directives.
+ assert(parent is PartOfDirective);
+ } else {
+ elementAnnotationImpl.annotationAst =
+ new ConstantAstCloner().cloneNode(node);
+ }
+ return null;
+ }
+
+ @override
+ Object visitArgumentList(ArgumentList node) {
+ DartType callerType = InferenceContext.getType(node);
+ if (callerType is FunctionType) {
+ Map<String, DartType> namedParameterTypes =
+ callerType.namedParameterTypes;
+ List<DartType> normalParameterTypes = callerType.normalParameterTypes;
+ List<DartType> optionalParameterTypes = callerType.optionalParameterTypes;
+ int normalCount = normalParameterTypes.length;
+ int optionalCount = optionalParameterTypes.length;
+
+ NodeList<Expression> arguments = node.arguments;
+ Iterable<Expression> positional =
+ arguments.takeWhile((l) => l is! NamedExpression);
+ Iterable<Expression> required = positional.take(normalCount);
+ Iterable<Expression> optional =
+ positional.skip(normalCount).take(optionalCount);
+ Iterable<Expression> named =
+ arguments.skipWhile((l) => l is! NamedExpression);
+
+ //TODO(leafp): Consider using the parameter elements here instead.
+ //TODO(leafp): Make sure that the parameter elements are getting
+ // setup correctly with inference.
+ int index = 0;
+ for (Expression argument in required) {
+ InferenceContext.setType(argument, normalParameterTypes[index++]);
+ }
+ index = 0;
+ for (Expression argument in optional) {
+ InferenceContext.setType(argument, optionalParameterTypes[index++]);
+ }
+
+ for (Expression argument in named) {
+ if (argument is NamedExpression) {
+ DartType type = namedParameterTypes[argument.name.label.name];
+ if (type != null) {
+ InferenceContext.setType(argument, type);
+ }
+ }
+ }
+ }
+ return super.visitArgumentList(node);
}
@override
@@ -10668,13 +6141,39 @@ class ResolverVisitor extends ScopedVisitor {
return null;
}
+ @override
+ Object visitAssignmentExpression(AssignmentExpression node) {
+ node.leftHandSide?.accept(this);
+ TokenType operator = node.operator.type;
+ if (operator == TokenType.EQ ||
+ operator == TokenType.QUESTION_QUESTION_EQ) {
+ InferenceContext.setType(
+ node.rightHandSide, node.leftHandSide.staticType);
+ }
+ node.rightHandSide?.accept(this);
+ node.accept(elementResolver);
+ node.accept(typeAnalyzer);
+ return null;
+ }
+
+ @override
+ Object visitAwaitExpression(AwaitExpression node) {
+ DartType contextType = InferenceContext.getContext(node);
+ if (contextType != null) {
+ var futureUnion =
+ FutureUnionType.from(contextType, typeProvider, typeSystem);
+ InferenceContext.setType(node.expression, futureUnion);
+ }
+ return super.visitAwaitExpression(node);
+ }
+
@override
Object visitBinaryExpression(BinaryExpression node) {
- sc.TokenType operatorType = node.operator.type;
+ TokenType operatorType = node.operator.type;
Expression leftOperand = node.leftOperand;
Expression rightOperand = node.rightOperand;
- if (operatorType == sc.TokenType.AMPERSAND_AMPERSAND) {
- safelyVisit(leftOperand);
+ if (operatorType == TokenType.AMPERSAND_AMPERSAND) {
+ leftOperand?.accept(this);
if (rightOperand != null) {
_overrideManager.enterScope();
try {
@@ -10696,8 +6195,8 @@ class ResolverVisitor extends ScopedVisitor {
_overrideManager.exitScope();
}
}
- } else if (operatorType == sc.TokenType.BAR_BAR) {
- safelyVisit(leftOperand);
+ } else if (operatorType == TokenType.BAR_BAR) {
+ leftOperand?.accept(this);
if (rightOperand != null) {
_overrideManager.enterScope();
try {
@@ -10708,8 +6207,23 @@ class ResolverVisitor extends ScopedVisitor {
}
}
} else {
- safelyVisit(leftOperand);
- safelyVisit(rightOperand);
+ // TODO(leafp): Do downwards inference using the declared type
+ // of the binary operator for other cases.
+ if (operatorType == TokenType.QUESTION_QUESTION) {
+ InferenceContext.setTypeFromNode(leftOperand, node);
+ }
+ leftOperand?.accept(this);
+ if (operatorType == TokenType.QUESTION_QUESTION) {
+ // Set the right side, either from the context, or using the information
+ // from the left side if it is more precise.
+ DartType contextType = InferenceContext.getContext(node);
+ DartType leftType = leftOperand?.staticType;
+ if (contextType == null || contextType.isDynamic) {
+ contextType = leftType;
+ }
+ InferenceContext.setType(rightOperand, contextType);
+ }
+ rightOperand?.accept(this);
}
node.accept(elementResolver);
node.accept(typeAnalyzer);
@@ -10718,12 +6232,13 @@ class ResolverVisitor extends ScopedVisitor {
@override
Object visitBlockFunctionBody(BlockFunctionBody node) {
- safelyVisit(_commentBeforeFunction);
_overrideManager.enterScope();
try {
+ inferenceContext.pushReturnContext(node);
super.visitBlockFunctionBody(node);
} finally {
_overrideManager.exitScope();
+ inferenceContext.popReturnContext(node);
}
return null;
}
@@ -10739,14 +6254,18 @@ class ResolverVisitor extends ScopedVisitor {
return null;
}
+ @override
+ Object visitCascadeExpression(CascadeExpression node) {
+ InferenceContext.setTypeFromNode(node.target, node);
+ return super.visitCascadeExpression(node);
+ }
+
@override
Object visitClassDeclaration(ClassDeclaration node) {
//
// Resolve the metadata in the library scope.
//
- if (node.metadata != null) {
- node.metadata.accept(this);
- }
+ node.metadata?.accept(this);
_enclosingClassDeclaration = node;
//
// Continue the class resolution.
@@ -10754,13 +6273,12 @@ class ResolverVisitor extends ScopedVisitor {
ClassElement outerType = enclosingClass;
try {
enclosingClass = node.element;
- typeAnalyzer.thisType =
- enclosingClass == null ? null : enclosingClass.type;
+ typeAnalyzer.thisType = enclosingClass?.type;
super.visitClassDeclaration(node);
node.accept(elementResolver);
node.accept(typeAnalyzer);
} finally {
- typeAnalyzer.thisType = outerType == null ? null : outerType.type;
+ typeAnalyzer.thisType = outerType?.type;
enclosingClass = outerType;
_enclosingClassDeclaration = null;
}
@@ -10775,31 +6293,27 @@ class ResolverVisitor extends ScopedVisitor {
//
// Resolve the metadata in the library scope.
//
- if (node.metadata != null) {
- node.metadata.accept(this);
- }
+ node.metadata?.accept(this);
_enclosingClassDeclaration = node;
//
// Continue the class resolution.
//
enclosingClass = node.element;
- typeAnalyzer.thisType = enclosingClass == null ? null : enclosingClass.type;
+ typeAnalyzer.thisType = enclosingClass?.type;
node.accept(elementResolver);
node.accept(typeAnalyzer);
}
@override
Object visitComment(Comment node) {
- if (node.parent is FunctionDeclaration ||
- node.parent is ConstructorDeclaration ||
- node.parent is MethodDeclaration) {
- if (!identical(node, _commentBeforeFunction)) {
- _commentBeforeFunction = node;
- return null;
- }
+ AstNode parent = node.parent;
+ if (parent is FunctionDeclaration ||
+ parent is FunctionTypeAlias ||
+ parent is ConstructorDeclaration ||
+ parent is MethodDeclaration) {
+ return null;
}
super.visitComment(node);
- _commentBeforeFunction = null;
return null;
}
@@ -10816,16 +6330,6 @@ class ResolverVisitor extends ScopedVisitor {
@override
Object visitCompilationUnit(CompilationUnit node) {
- //
- // TODO(brianwilkerson) The goal of the code below is to visit the
- // declarations in such an order that we can infer type information for
- // top-level variables before we visit references to them. This is better
- // than making no effort, but still doesn't completely satisfy that goal
- // (consider for example "final var a = b; final var b = 0;"; we'll infer a
- // type of 'int' for 'b', but not for 'a' because of the order of the
- // visits). Ideally we would create a dependency graph, but that would
- // require references to be resolved, which they are not.
- //
_overrideManager.enterScope();
try {
NodeList<Directive> directives = node.directives;
@@ -10836,16 +6340,7 @@ class ResolverVisitor extends ScopedVisitor {
NodeList<CompilationUnitMember> declarations = node.declarations;
int declarationCount = declarations.length;
for (int i = 0; i < declarationCount; i++) {
- CompilationUnitMember declaration = declarations[i];
- if (declaration is! ClassDeclaration) {
- declaration.accept(this);
- }
- }
- for (int i = 0; i < declarationCount; i++) {
- CompilationUnitMember declaration = declarations[i];
- if (declaration is ClassDeclaration) {
- declaration.accept(this);
- }
+ declarations[i].accept(this);
}
} finally {
_overrideManager.exitScope();
@@ -10858,7 +6353,7 @@ class ResolverVisitor extends ScopedVisitor {
@override
Object visitConditionalExpression(ConditionalExpression node) {
Expression condition = node.condition;
- safelyVisit(condition);
+ condition?.accept(this);
Expression thenExpression = node.thenExpression;
if (thenExpression != null) {
_overrideManager.enterScope();
@@ -10872,6 +6367,7 @@ class ResolverVisitor extends ScopedVisitor {
_clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated(
thenExpression);
// Visit "then" expression.
+ InferenceContext.setTypeFromNode(thenExpression, node);
thenExpression.accept(this);
} finally {
_promoteManager.exitScope();
@@ -10885,6 +6381,7 @@ class ResolverVisitor extends ScopedVisitor {
_overrideManager.enterScope();
try {
_propagateFalseState(condition);
+ InferenceContext.setTypeFromNode(elseExpression, node);
elseExpression.accept(this);
} finally {
_overrideManager.exitScope();
@@ -10907,10 +6404,15 @@ class ResolverVisitor extends ScopedVisitor {
@override
Object visitConstructorDeclaration(ConstructorDeclaration node) {
ExecutableElement outerFunction = _enclosingFunction;
+ FunctionBody outerFunctionBody = _currentFunctionBody;
try {
+ _currentFunctionBody = node.body;
_enclosingFunction = node.element;
+ FunctionType type = _enclosingFunction.type;
+ InferenceContext.setType(node.body, type.returnType);
super.visitConstructorDeclaration(node);
} finally {
+ _currentFunctionBody = outerFunctionBody;
_enclosingFunction = outerFunction;
}
ConstructorElementImpl constructor = node.element;
@@ -10919,13 +6421,27 @@ class ResolverVisitor extends ScopedVisitor {
return null;
}
+ @override
+ void visitConstructorDeclarationInScope(ConstructorDeclaration node) {
+ super.visitConstructorDeclarationInScope(node);
+ // Because of needing a different scope for the initializer list, the
+ // overridden implementation of this method cannot cause the visitNode
+ // method to be invoked. As a result, we have to hard-code using the
+ // element resolver and type analyzer to visit the constructor declaration.
+ node.accept(elementResolver);
+ node.accept(typeAnalyzer);
+ safelyVisitComment(node.documentationComment);
+ }
+
@override
Object visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
//
// We visit the expression, but do not visit the field name because it needs
// to be visited in the context of the constructor field initializer node.
//
- safelyVisit(node.expression);
+ FieldElement fieldElement = enclosingClass.getField(node.fieldName.name);
+ InferenceContext.setType(node.expression, fieldElement?.type);
+ node.expression?.accept(this);
node.accept(elementResolver);
node.accept(typeAnalyzer);
return null;
@@ -10956,19 +6472,17 @@ class ResolverVisitor extends ScopedVisitor {
@override
Object visitDefaultFormalParameter(DefaultFormalParameter node) {
+ InferenceContext.setType(node.defaultValue, node.parameter.element?.type);
super.visitDefaultFormalParameter(node);
ParameterElement element = node.element;
if (element.initializer != null && node.defaultValue != null) {
(element.initializer as FunctionElementImpl).returnType =
node.defaultValue.staticType;
}
- FormalParameterList parent = node.parent;
- AstNode grandparent = parent.parent;
- if (grandparent is ConstructorDeclaration &&
- grandparent.constKeyword != null) {
- // For const constructors, we need to clone the ASTs for default formal
- // parameters, so that we can use them during constant evaluation.
- ParameterElement element = node.element;
+ // Clone the ASTs for default formal parameters, so that we can use them
+ // during constant evaluation.
+ if (!LibraryElementImpl.hasResolutionCapability(
+ definingLibrary, LibraryResolutionCapability.constantExpressions)) {
(element as ConstVariableElement).constantInitializer =
new ConstantAstCloner().cloneNode(node.defaultValue);
}
@@ -10990,7 +6504,6 @@ class ResolverVisitor extends ScopedVisitor {
@override
Object visitEmptyFunctionBody(EmptyFunctionBody node) {
- safelyVisit(_commentBeforeFunction);
if (resolveOnlyCommentInFunctionBody) {
return null;
}
@@ -11005,7 +6518,7 @@ class ResolverVisitor extends ScopedVisitor {
//
if (node.metadata != null) {
node.metadata.accept(this);
- ElementResolver.setMetadata(node.element, node);
+ ElementResolver.resolveMetadata(node);
}
//
// Continue the enum resolution.
@@ -11013,13 +6526,12 @@ class ResolverVisitor extends ScopedVisitor {
ClassElement outerType = enclosingClass;
try {
enclosingClass = node.element;
- typeAnalyzer.thisType =
- enclosingClass == null ? null : enclosingClass.type;
+ typeAnalyzer.thisType = enclosingClass?.type;
super.visitEnumDeclaration(node);
node.accept(elementResolver);
node.accept(typeAnalyzer);
} finally {
- typeAnalyzer.thisType = outerType == null ? null : outerType.type;
+ typeAnalyzer.thisType = outerType?.type;
enclosingClass = outerType;
_enclosingClassDeclaration = null;
}
@@ -11028,12 +6540,12 @@ class ResolverVisitor extends ScopedVisitor {
@override
Object visitExpressionFunctionBody(ExpressionFunctionBody node) {
- safelyVisit(_commentBeforeFunction);
if (resolveOnlyCommentInFunctionBody) {
return null;
}
_overrideManager.enterScope();
try {
+ InferenceContext.setTypeFromNode(node.expression, node);
super.visitExpressionFunctionBody(node);
} finally {
_overrideManager.exitScope();
@@ -11073,11 +6585,18 @@ class ResolverVisitor extends ScopedVisitor {
// cannot be in scope while visiting the iterator.
//
Expression iterable = node.iterable;
- safelyVisit(iterable);
DeclaredIdentifier loopVariable = node.loopVariable;
SimpleIdentifier identifier = node.identifier;
- safelyVisit(loopVariable);
- safelyVisit(identifier);
+ if (loopVariable?.type?.type != null) {
+ InterfaceType targetType = (node.awaitKeyword == null)
+ ? typeProvider.iterableType
+ : typeProvider.streamType;
+ InferenceContext.setType(
+ iterable, targetType.instantiate([loopVariable.type.type]));
+ }
+ iterable?.accept(this);
+ loopVariable?.accept(this);
+ identifier?.accept(this);
Statement body = node.body;
if (body != null) {
_overrideManager.enterScope();
@@ -11127,9 +6646,9 @@ class ResolverVisitor extends ScopedVisitor {
@override
void visitForStatementInScope(ForStatement node) {
- safelyVisit(node.variables);
- safelyVisit(node.initialization);
- safelyVisit(node.condition);
+ node.variables?.accept(this);
+ node.initialization?.accept(this);
+ node.condition?.accept(this);
_overrideManager.enterScope();
try {
_propagateTrueState(node.condition);
@@ -11145,28 +6664,71 @@ class ResolverVisitor extends ScopedVisitor {
@override
Object visitFunctionDeclaration(FunctionDeclaration node) {
ExecutableElement outerFunction = _enclosingFunction;
+ FunctionBody outerFunctionBody = _currentFunctionBody;
try {
SimpleIdentifier functionName = node.name;
+ _currentFunctionBody = node.functionExpression.body;
_enclosingFunction = functionName.staticElement as ExecutableElement;
+ InferenceContext.setType(
+ node.functionExpression, _enclosingFunction.type);
super.visitFunctionDeclaration(node);
} finally {
+ _currentFunctionBody = outerFunctionBody;
_enclosingFunction = outerFunction;
}
return null;
}
+ @override
+ void visitFunctionDeclarationInScope(FunctionDeclaration node) {
+ super.visitFunctionDeclarationInScope(node);
+ safelyVisitComment(node.documentationComment);
+ }
+
@override
Object visitFunctionExpression(FunctionExpression node) {
ExecutableElement outerFunction = _enclosingFunction;
+ FunctionBody outerFunctionBody = _currentFunctionBody;
try {
+ _currentFunctionBody = node.body;
_enclosingFunction = node.element;
_overrideManager.enterScope();
try {
+ DartType functionType = InferenceContext.getType(node);
+ if (functionType is FunctionType) {
+ functionType =
+ matchFunctionTypeParameters(node.typeParameters, functionType);
+ if (functionType is FunctionType) {
+ _inferFormalParameterList(node.parameters, functionType);
+
+ DartType returnType;
+ if (isFutureThen(node.staticParameterElement?.enclosingElement)) {
+ var futureThenType =
+ InferenceContext.getContext(node.parent) as FunctionType;
+
+ // Pretend the return type of Future<T>.then<S> first parameter is
+ //
+ // T -> (S | Future<S>)
+ //
+ // We can't represent this in Dart so we populate it here during
+ // inference.
+ var typeParamS =
+ futureThenType.returnType.flattenFutures(typeSystem);
+ returnType =
+ FutureUnionType.from(typeParamS, typeProvider, typeSystem);
+ } else {
+ returnType = _computeReturnOrYieldType(functionType.returnType);
+ }
+
+ InferenceContext.setType(node.body, returnType);
+ }
+ }
super.visitFunctionExpression(node);
} finally {
_overrideManager.exitScope();
}
} finally {
+ _currentFunctionBody = outerFunctionBody;
_enclosingFunction = outerFunction;
}
return null;
@@ -11174,10 +6736,10 @@ class ResolverVisitor extends ScopedVisitor {
@override
Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
- safelyVisit(node.function);
+ node.function?.accept(this);
node.accept(elementResolver);
- _inferFunctionExpressionsParametersTypes(node.argumentList);
- safelyVisit(node.argumentList);
+ _inferArgumentTypesFromContext(node);
+ node.argumentList?.accept(this);
node.accept(typeAnalyzer);
return null;
}
@@ -11198,15 +6760,21 @@ class ResolverVisitor extends ScopedVisitor {
return null;
}
+ @override
+ void visitFunctionTypeAliasInScope(FunctionTypeAlias node) {
+ super.visitFunctionTypeAliasInScope(node);
+ safelyVisitComment(node.documentationComment);
+ }
+
@override
Object visitHideCombinator(HideCombinator node) => null;
@override
Object visitIfStatement(IfStatement node) {
Expression condition = node.condition;
- safelyVisit(condition);
+ condition?.accept(this);
Map<VariableElement, DartType> thenOverrides =
- new HashMap<VariableElement, DartType>();
+ const <VariableElement, DartType>{};
Statement thenStatement = node.thenStatement;
if (thenStatement != null) {
_overrideManager.enterScope();
@@ -11230,7 +6798,7 @@ class ResolverVisitor extends ScopedVisitor {
}
}
Map<VariableElement, DartType> elseOverrides =
- new HashMap<VariableElement, DartType>();
+ const <VariableElement, DartType>{};
Statement elseStatement = node.elseStatement;
if (elseStatement != null) {
_overrideManager.enterScope();
@@ -11255,7 +6823,7 @@ class ResolverVisitor extends ScopedVisitor {
_overrideManager.applyOverrides(elseOverrides);
} else if (!thenIsAbrupt && !elseIsAbrupt) {
List<Map<VariableElement, DartType>> perBranchOverrides =
- new List<Map<VariableElement, DartType>>();
+ <Map<VariableElement, DartType>>[];
perBranchOverrides.add(thenOverrides);
perBranchOverrides.add(elseOverrides);
_overrideManager.mergeOverrides(perBranchOverrides);
@@ -11263,38 +6831,164 @@ class ResolverVisitor extends ScopedVisitor {
return null;
}
+ @override
+ Object visitInstanceCreationExpression(InstanceCreationExpression node) {
+ TypeName classTypeName = node.constructorName.type;
+ // TODO(leafp): Currently, we may re-infer types here, since we
+ // sometimes resolve multiple times. We should really check that we
+ // have not already inferred something. However, the obvious ways to
+ // check this don't work, since we may have been instantiated
+ // to bounds in an earlier phase, and we *do* want to do inference
+ // in that case.
+ if (classTypeName.typeArguments == null) {
+ // Given a union of context types ` T0 | T1 | ... | Tn`, find the first
+ // valid instantiation `new C<Ti>`, if it exists.
+ // TODO(jmesserly): if we support union types for real, `new C<Ti | Tj>`
+ // will become a valid possibility. Right now the only allowed union is
+ // `T | Future<T>` so we can take a simple approach.
+ for (var contextType in InferenceContext.getTypes(node)) {
+ if (contextType is InterfaceType &&
+ contextType.typeArguments != null &&
+ contextType.typeArguments.isNotEmpty) {
+ // TODO(jmesserly): for generic methods we use the
+ // StrongTypeSystemImpl.inferGenericFunctionCall, which appears to
+ // be a tad more powerful than matchTypes.
+ //
+ // For example it can infer this case:
+ //
+ // class E<S, T> extends A<C<S>, T> { ... }
+ // A<C<int>, String> a0 = /*infer<int, String>*/new E("hello");
+ //
+ // See _inferArgumentTypesFromContext in this file for use of it.
+ List<DartType> targs =
+ inferenceContext.matchTypes(classTypeName.type, contextType);
+ if (targs != null && targs.any((t) => !t.isDynamic)) {
+ ClassElement classElement = classTypeName.type.element;
+ InterfaceType rawType = classElement.type;
+ InterfaceType fullType =
+ rawType.substitute2(targs, rawType.typeArguments);
+ // The element resolver uses the type on the constructor name, so
+ // infer it first
+ typeAnalyzer.inferConstructorName(node.constructorName, fullType);
+ break;
+ }
+ }
+ }
+ }
+ node.constructorName?.accept(this);
+ FunctionType constructorType = node.constructorName.staticElement?.type;
+ if (constructorType != null) {
+ InferenceContext.setType(node.argumentList, constructorType);
+ }
+ node.argumentList?.accept(this);
+ node.accept(elementResolver);
+ node.accept(typeAnalyzer);
+ return null;
+ }
+
@override
Object visitLabel(Label node) => null;
@override
Object visitLibraryIdentifier(LibraryIdentifier node) => null;
+ @override
+ Object visitListLiteral(ListLiteral node) {
+ DartType contextType = InferenceContext.getType(node);
+ List<DartType> targs = null;
+ if (node.typeArguments != null) {
+ targs = node.typeArguments.arguments.map((t) => t.type).toList();
+ } else if (contextType is InterfaceType) {
+ InterfaceType listD =
+ typeProvider.listType.instantiate([typeProvider.dynamicType]);
+ targs = inferenceContext.matchTypes(listD, contextType);
+ }
+ if (targs != null && targs.length == 1 && !targs[0].isDynamic) {
+ DartType eType = targs[0];
+ InterfaceType listT = typeProvider.listType.instantiate([eType]);
+ for (Expression child in node.elements) {
+ InferenceContext.setType(child, eType);
+ }
+ InferenceContext.setType(node, listT);
+ } else {
+ InferenceContext.clearType(node);
+ }
+ super.visitListLiteral(node);
+ return null;
+ }
+
+ @override
+ Object visitMapLiteral(MapLiteral node) {
+ DartType contextType = InferenceContext.getType(node);
+ List<DartType> targs = null;
+ if (node.typeArguments != null) {
+ targs = node.typeArguments.arguments.map((t) => t.type).toList();
+ } else if (contextType is InterfaceType) {
+ InterfaceType mapD = typeProvider.mapType
+ .instantiate([typeProvider.dynamicType, typeProvider.dynamicType]);
+ targs = inferenceContext.matchTypes(mapD, contextType);
+ }
+ if (targs != null && targs.length == 2 && targs.any((t) => !t.isDynamic)) {
+ DartType kType = targs[0];
+ DartType vType = targs[1];
+ InterfaceType mapT = typeProvider.mapType.instantiate([kType, vType]);
+ for (MapLiteralEntry entry in node.entries) {
+ InferenceContext.setType(entry.key, kType);
+ InferenceContext.setType(entry.value, vType);
+ }
+ InferenceContext.setType(node, mapT);
+ } else {
+ InferenceContext.clearType(node);
+ }
+ super.visitMapLiteral(node);
+ return null;
+ }
+
@override
Object visitMethodDeclaration(MethodDeclaration node) {
ExecutableElement outerFunction = _enclosingFunction;
+ FunctionBody outerFunctionBody = _currentFunctionBody;
try {
+ _currentFunctionBody = node.body;
_enclosingFunction = node.element;
+ DartType returnType =
+ _computeReturnOrYieldType(_enclosingFunction.type?.returnType);
+ InferenceContext.setType(node.body, returnType);
super.visitMethodDeclaration(node);
} finally {
+ _currentFunctionBody = outerFunctionBody;
_enclosingFunction = outerFunction;
}
return null;
}
+ @override
+ void visitMethodDeclarationInScope(MethodDeclaration node) {
+ super.visitMethodDeclarationInScope(node);
+ safelyVisitComment(node.documentationComment);
+ }
+
@override
Object visitMethodInvocation(MethodInvocation node) {
//
// We visit the target and argument list, but do not visit the method name
// because it needs to be visited in the context of the invocation.
//
- safelyVisit(node.target);
+ node.target?.accept(this);
+ node.typeArguments?.accept(this);
node.accept(elementResolver);
- _inferFunctionExpressionsParametersTypes(node.argumentList);
- safelyVisit(node.argumentList);
+ _inferArgumentTypesFromContext(node);
+ node.argumentList?.accept(this);
node.accept(typeAnalyzer);
return null;
}
+ @override
+ Object visitNamedExpression(NamedExpression node) {
+ InferenceContext.setTypeFromNode(node.expression, node);
+ return super.visitNamedExpression(node);
+ }
+
@override
Object visitNode(AstNode node) {
node.visitChildren(this);
@@ -11303,13 +6997,19 @@ class ResolverVisitor extends ScopedVisitor {
return null;
}
+ @override
+ Object visitParenthesizedExpression(ParenthesizedExpression node) {
+ InferenceContext.setTypeFromNode(node.expression, node);
+ return super.visitParenthesizedExpression(node);
+ }
+
@override
Object visitPrefixedIdentifier(PrefixedIdentifier node) {
//
// We visit the prefix, but do not visit the identifier because it needs to
// be visited in the context of the prefix.
//
- safelyVisit(node.prefix);
+ node.prefix?.accept(this);
node.accept(elementResolver);
node.accept(typeAnalyzer);
return null;
@@ -11321,7 +7021,7 @@ class ResolverVisitor extends ScopedVisitor {
// We visit the target, but do not visit the property name because it needs
// to be visited in the context of the property access node.
//
- safelyVisit(node.target);
+ node.target?.accept(this);
node.accept(elementResolver);
node.accept(typeAnalyzer);
return null;
@@ -11335,12 +7035,30 @@ class ResolverVisitor extends ScopedVisitor {
// because it needs to be visited in the context of the constructor
// invocation.
//
- safelyVisit(node.argumentList);
+ InferenceContext.setType(node.argumentList, node.staticElement?.type);
+ node.argumentList?.accept(this);
node.accept(elementResolver);
node.accept(typeAnalyzer);
return null;
}
+ @override
+ Object visitReturnStatement(ReturnStatement node) {
+ Expression e = node.expression;
+ InferenceContext.setType(e, inferenceContext.returnContext);
+ super.visitReturnStatement(node);
+ DartType type = e?.staticType;
+ // Generators cannot return values, so don't try to do any inference if
+ // we're processing erroneous code.
+ if (type != null && _enclosingFunction?.isGenerator == false) {
+ if (_enclosingFunction.isAsynchronous) {
+ type = type.flattenFutures(typeSystem);
+ }
+ inferenceContext.addReturnOrYieldType(type);
+ }
+ return null;
+ }
+
@override
Object visitShowCombinator(ShowCombinator node) => null;
@@ -11351,7 +7069,8 @@ class ResolverVisitor extends ScopedVisitor {
// because it needs to be visited in the context of the constructor
// invocation.
//
- safelyVisit(node.argumentList);
+ InferenceContext.setType(node.argumentList, node.staticElement?.type);
+ node.argumentList?.accept(this);
node.accept(elementResolver);
node.accept(typeAnalyzer);
return null;
@@ -11398,6 +7117,7 @@ class ResolverVisitor extends ScopedVisitor {
@override
Object visitVariableDeclaration(VariableDeclaration node) {
+ InferenceContext.setTypeFromNode(node.initializer, node);
super.visitVariableDeclaration(node);
VariableElement element = node.element;
if (element.initializer != null && node.initializer != null) {
@@ -11419,6 +7139,14 @@ class ResolverVisitor extends ScopedVisitor {
return null;
}
+ @override
+ visitVariableDeclarationList(VariableDeclarationList node) {
+ for (VariableDeclaration decl in node.variables) {
+ InferenceContext.setType(decl, decl.element?.type);
+ }
+ super.visitVariableDeclarationList(node);
+ }
+
@override
Object visitWhileStatement(WhileStatement node) {
// Note: since we don't call the base class, we have to maintain
@@ -11427,7 +7155,7 @@ class ResolverVisitor extends ScopedVisitor {
try {
_implicitLabelScope = _implicitLabelScope.nest(node);
Expression condition = node.condition;
- safelyVisit(condition);
+ condition?.accept(this);
Statement body = node.body;
if (body != null) {
_overrideManager.enterScope();
@@ -11448,6 +7176,46 @@ class ResolverVisitor extends ScopedVisitor {
return null;
}
+ @override
+ Object visitYieldStatement(YieldStatement node) {
+ Expression e = node.expression;
+ DartType returnType = inferenceContext.returnContext;
+ bool isGenerator = _enclosingFunction?.isGenerator ?? false;
+ if (returnType != null && isGenerator) {
+ // If we're not in a generator ([a]sync*, then we shouldn't have a yield.
+ // so don't infer
+
+ // If this just a yield, then we just pass on the element type
+ DartType type = returnType;
+ if (node.star != null) {
+ // If this is a yield*, then we wrap the element return type
+ // If it's synchronous, we expect Iterable<T>, otherwise Stream<T>
+ InterfaceType wrapperType = _enclosingFunction.isSynchronous
+ ? typeProvider.iterableType
+ : typeProvider.streamType;
+ type = wrapperType.instantiate(<DartType>[type]);
+ }
+ InferenceContext.setType(e, type);
+ }
+ super.visitYieldStatement(node);
+ DartType type = e?.staticType;
+ if (type != null && isGenerator) {
+ // If this just a yield, then we just pass on the element type
+ if (node.star != null) {
+ // If this is a yield*, then we unwrap the element return type
+ // If it's synchronous, we expect Iterable<T>, otherwise Stream<T>
+ InterfaceType wrapperType = _enclosingFunction.isSynchronous
+ ? typeProvider.iterableType
+ : typeProvider.streamType;
+ type = typeSystem.mostSpecificTypeArgument(type, wrapperType);
+ }
+ if (type != null) {
+ inferenceContext.addReturnOrYieldType(type);
+ }
+ }
+ return null;
+ }
+
/**
* Checks each promoted variable in the current scope for compliance with the following
* specification statement:
@@ -11458,7 +7226,7 @@ class ResolverVisitor extends ScopedVisitor {
void _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated(
AstNode target) {
for (Element element in _promoteManager.promotedElements) {
- if ((element as VariableElementImpl).isPotentiallyMutatedInScope) {
+ if (_currentFunctionBody.isPotentiallyMutatedInScope(element)) {
if (_isVariableAccessedInClosure(element, target)) {
_promoteManager.setType(element, null);
}
@@ -11480,6 +7248,38 @@ class ResolverVisitor extends ScopedVisitor {
}
}
+ /**
+ * Given the declared return type of a function, compute the type of the
+ * values which should be returned or yielded as appropriate. If a type
+ * cannot be computed from the declared return type, return null.
+ */
+ DartType _computeReturnOrYieldType(DartType declaredType) {
+ bool isGenerator = _enclosingFunction.isGenerator;
+ bool isAsynchronous = _enclosingFunction.isAsynchronous;
+
+ // Ordinary functions just return their declared types.
+ if (!isGenerator && !isAsynchronous) {
+ return declaredType;
+ }
+ if (declaredType is InterfaceType) {
+ if (isGenerator) {
+ // If it's sync* we expect Iterable<T>
+ // If it's async* we expect Stream<T>
+ InterfaceType rawType = isAsynchronous
+ ? typeProvider.streamDynamicType
+ : typeProvider.iterableDynamicType;
+ // Match the types to instantiate the type arguments if possible
+ List<DartType> typeArgs =
+ inferenceContext.matchTypes(rawType, declaredType);
+ return (typeArgs?.length == 1) ? typeArgs[0] : null;
+ }
+ // async functions expect `Future<T> | T`
+ var futureTypeParam = declaredType.flattenFutures(typeSystem);
+ return FutureUnionType.from(futureTypeParam, typeProvider, typeSystem);
+ }
+ return declaredType;
+ }
+
/**
* The given expression is the expression used to compute the iterator for a
* for-each statement. Attempt to compute the type of objects that will be
@@ -11490,18 +7290,16 @@ class ResolverVisitor extends ScopedVisitor {
DartType _getIteratorElementType(Expression iteratorExpression) {
DartType expressionType = iteratorExpression.bestType;
if (expressionType is InterfaceType) {
- InterfaceType interfaceType = expressionType;
- FunctionType iteratorFunction =
- _inheritanceManager.lookupMemberType(interfaceType, "iterator");
+ PropertyAccessorElement iteratorFunction =
+ expressionType.lookUpInheritedGetter("iterator");
if (iteratorFunction == null) {
// TODO(brianwilkerson) Should we report this error?
return null;
}
DartType iteratorType = iteratorFunction.returnType;
if (iteratorType is InterfaceType) {
- InterfaceType iteratorInterfaceType = iteratorType;
- FunctionType currentFunction = _inheritanceManager.lookupMemberType(
- iteratorInterfaceType, "current");
+ PropertyAccessorElement currentFunction =
+ iteratorType.lookUpInheritedGetter("current");
if (currentFunction == null) {
// TODO(brianwilkerson) Should we report this error?
return null;
@@ -11514,16 +7312,15 @@ class ResolverVisitor extends ScopedVisitor {
/**
* The given expression is the expression used to compute the stream for an
- * asyncronous for-each statement. Attempt to compute the type of objects that
- * will be assigned to the loop variable and return that type. Return `null`
- * if the type could not be determined. The [streamExpression] is the
- * expression that will return the stream being iterated over.
+ * asynchronous for-each statement. Attempt to compute the type of objects
+ * that will be assigned to the loop variable and return that type.
+ * Return `null` if the type could not be determined. The [streamExpression]
+ * is the expression that will return the stream being iterated over.
*/
DartType _getStreamElementType(Expression streamExpression) {
DartType streamType = streamExpression.bestType;
if (streamType is InterfaceType) {
- FunctionType listenFunction =
- _inheritanceManager.lookupMemberType(streamType, "listen");
+ MethodElement listenFunction = streamType.lookUpInheritedMethod("listen");
if (listenFunction == null) {
return null;
}
@@ -11534,22 +7331,67 @@ class ResolverVisitor extends ScopedVisitor {
DartType onDataType = listenParameters[0].type;
if (onDataType is FunctionType) {
List<ParameterElement> onDataParameters = onDataType.parameters;
- if (onDataParameters == null || onDataParameters.length < 1) {
+ if (onDataParameters == null || onDataParameters.isEmpty) {
return null;
}
- DartType eventType = onDataParameters[0].type;
- // TODO(paulberry): checking that typeParameters.isNotEmpty is a
- // band-aid fix for dartbug.com/24191. Figure out what the correct
- // logic should be.
- if (streamType.typeParameters.isNotEmpty &&
- eventType.element == streamType.typeParameters[0]) {
- return streamType.typeArguments[0];
- }
+ return onDataParameters[0].type;
}
}
return null;
}
+ void _inferArgumentTypesFromContext(InvocationExpression node) {
+ if (!strongMode) {
+ // Use propagated type inference for lambdas if not in strong mode.
+ _inferFunctionExpressionsParametersTypes(node.argumentList);
+ return;
+ }
+
+ DartType contextType = node.staticInvokeType;
+ if (contextType is FunctionType) {
+ DartType originalType = node.function.staticType;
+ DartType returnContextType = InferenceContext.getContext(node);
+ TypeSystem ts = typeSystem;
+ if (returnContextType != null &&
+ node.typeArguments == null &&
+ originalType is FunctionType &&
+ originalType.typeFormals.isNotEmpty &&
+ ts is StrongTypeSystemImpl) {
+ contextType = ts.inferGenericFunctionCall(
+ typeProvider,
+ originalType,
+ DartType.EMPTY_LIST,
+ DartType.EMPTY_LIST,
+ originalType.returnType,
+ returnContextType);
+ }
+
+ InferenceContext.setType(node.argumentList, contextType);
+ }
+ }
+
+ void _inferFormalParameterList(FormalParameterList node, DartType type) {
+ if (typeAnalyzer.inferFormalParameterList(node, type)) {
+ // TODO(leafp): This gets dropped on the floor if we're in the field
+ // inference task. We should probably keep these infos.
+ //
+ // TODO(jmesserly): this is reporting the context type, and therefore not
+ // necessarily the correct inferred type for the lambda.
+ //
+ // For example, `([x]) {}` could be passed to `int -> void` but its type
+ // will really be `([int]) -> void`. Similar issue for named arguments.
+ // It can also happen if the return type is inferred later on to be
+ // more precise.
+ //
+ // This reporting bug defeats the deduplication of error messages and
+ // results in the same inference message being reported twice.
+ //
+ // To get this right, we'd have to delay reporting until we have the
+ // complete type including return type.
+ inferenceContext.recordInference(node.parent, type);
+ }
+ }
+
/**
* If given "mayBeClosure" is [FunctionExpression] without explicit parameters types and its
* required type is [FunctionType], then infer parameters types from [FunctionType].
@@ -11568,14 +7410,21 @@ class ResolverVisitor extends ScopedVisitor {
FunctionType expectedClosureType = mayByFunctionType as FunctionType;
// If the expectedClosureType is not more specific than the static type,
// return.
- DartType staticClosureType =
- closure.element != null ? closure.element.type : null;
+ DartType staticClosureType = closure.element?.type;
if (staticClosureType != null &&
- !expectedClosureType.isMoreSpecificThan(staticClosureType)) {
+ !FunctionTypeImpl.relate(
+ expectedClosureType,
+ staticClosureType,
+ (DartType t, DartType s, _, __) =>
+ (t as TypeImpl).isMoreSpecificThan(s),
+ new TypeSystemImpl().instantiateToBounds,
+ returnRelation: (s, t) => true)) {
return;
}
// set propagated type for the closure
- closure.propagatedType = expectedClosureType;
+ if (!strongMode) {
+ closure.propagatedType = expectedClosureType;
+ }
// set inferred types for parameters
NodeList<FormalParameter> parameters = closure.parameters.parameters;
List<ParameterElement> expectedParameters = expectedClosureType.parameters;
@@ -11597,7 +7446,10 @@ class ResolverVisitor extends ScopedVisitor {
* Try to infer types of parameters of the [FunctionExpression] arguments.
*/
void _inferFunctionExpressionsParametersTypes(ArgumentList argumentList) {
- for (Expression argument in argumentList.arguments) {
+ NodeList<Expression> arguments = argumentList.arguments;
+ int length = arguments.length;
+ for (int i = 0; i < length; i++) {
+ Expression argument = arguments[i];
ParameterElement parameter = argument.propagatedParameterElement;
if (parameter == null) {
parameter = argument.staticParameterElement;
@@ -11620,9 +7472,7 @@ class ResolverVisitor extends ScopedVisitor {
// would eventually turn this into a method on Expression that returns a
// termination indication (normal, abrupt with no exception, abrupt with an
// exception).
- while (expression is ParenthesizedExpression) {
- expression = (expression as ParenthesizedExpression).expression;
- }
+ expression = expression?.unParenthesized;
return expression is ThrowExpression || expression is RethrowExpression;
}
@@ -11723,28 +7573,21 @@ class ResolverVisitor extends ScopedVisitor {
VariableElement element = getPromotionStaticElement(expression);
if (element != null) {
// may be mutated somewhere in closure
- if (element.isPotentiallyMutatedInClosure) {
+ if (_currentFunctionBody.isPotentiallyMutatedInClosure(element)) {
return;
}
// prepare current variable type
- DartType type = _promoteManager.getType(element);
- if (type == null) {
- type = expression.staticType;
- }
- // Declared type should not be "dynamic".
- if (type == null || type.isDynamic) {
- return;
- }
- // Promoted type should not be "dynamic".
- if (potentialType == null || potentialType.isDynamic) {
- return;
- }
- // Promoted type should be more specific than declared.
- if (!potentialType.isMoreSpecificThan(type)) {
- return;
+ DartType type = _promoteManager.getType(element) ??
+ expression.staticType ??
+ DynamicTypeImpl.instance;
+
+ potentialType ??= DynamicTypeImpl.instance;
+
+ // Check if we can promote to potentialType from type.
+ if (typeSystem.canPromoteToType(potentialType, type)) {
+ // Do promote type of variable.
+ _promoteManager.setType(element, potentialType);
}
- // Do promote type of variable.
- _promoteManager.setType(element, potentialType);
}
}
@@ -11753,18 +7596,16 @@ class ResolverVisitor extends ScopedVisitor {
*/
void _promoteTypes(Expression condition) {
if (condition is BinaryExpression) {
- BinaryExpression binary = condition;
- if (binary.operator.type == sc.TokenType.AMPERSAND_AMPERSAND) {
- Expression left = binary.leftOperand;
- Expression right = binary.rightOperand;
+ if (condition.operator.type == TokenType.AMPERSAND_AMPERSAND) {
+ Expression left = condition.leftOperand;
+ Expression right = condition.rightOperand;
_promoteTypes(left);
_promoteTypes(right);
_clearTypePromotionsIfPotentiallyMutatedIn(right);
}
} else if (condition is IsExpression) {
- IsExpression is2 = condition;
- if (is2.notOperator == null) {
- _promote(is2.expression, is2.type.type);
+ if (condition.notOperator == null) {
+ _promote(condition.expression, condition.type.type);
}
} else if (condition is ParenthesizedExpression) {
_promoteTypes(condition.expression);
@@ -11779,23 +7620,21 @@ class ResolverVisitor extends ScopedVisitor {
*/
void _propagateFalseState(Expression condition) {
if (condition is BinaryExpression) {
- BinaryExpression binary = condition;
- if (binary.operator.type == sc.TokenType.BAR_BAR) {
- _propagateFalseState(binary.leftOperand);
- _propagateFalseState(binary.rightOperand);
+ if (condition.operator.type == TokenType.BAR_BAR) {
+ _propagateFalseState(condition.leftOperand);
+ _propagateFalseState(condition.rightOperand);
}
} else if (condition is IsExpression) {
- IsExpression is2 = condition;
- if (is2.notOperator != null) {
+ if (condition.notOperator != null) {
// Since an is-statement doesn't actually change the type, we don't
// let it affect the propagated type when it would result in a loss
// of precision.
- overrideExpression(is2.expression, is2.type.type, false, false);
+ overrideExpression(
+ condition.expression, condition.type.type, false, false);
}
} else if (condition is PrefixExpression) {
- PrefixExpression prefix = condition;
- if (prefix.operator.type == sc.TokenType.BANG) {
- _propagateTrueState(prefix.operand);
+ if (condition.operator.type == TokenType.BANG) {
+ _propagateTrueState(condition.operand);
}
} else if (condition is ParenthesizedExpression) {
_propagateFalseState(condition.expression);
@@ -11820,224 +7659,138 @@ class ResolverVisitor extends ScopedVisitor {
*/
void _propagateTrueState(Expression condition) {
if (condition is BinaryExpression) {
- BinaryExpression binary = condition;
- if (binary.operator.type == sc.TokenType.AMPERSAND_AMPERSAND) {
- _propagateTrueState(binary.leftOperand);
- _propagateTrueState(binary.rightOperand);
+ if (condition.operator.type == TokenType.AMPERSAND_AMPERSAND) {
+ _propagateTrueState(condition.leftOperand);
+ _propagateTrueState(condition.rightOperand);
}
} else if (condition is IsExpression) {
- IsExpression is2 = condition;
- if (is2.notOperator == null) {
+ if (condition.notOperator == null) {
// Since an is-statement doesn't actually change the type, we don't
// let it affect the propagated type when it would result in a loss
// of precision.
- overrideExpression(is2.expression, is2.type.type, false, false);
+ overrideExpression(
+ condition.expression, condition.type.type, false, false);
}
} else if (condition is PrefixExpression) {
- PrefixExpression prefix = condition;
- if (prefix.operator.type == sc.TokenType.BANG) {
- _propagateFalseState(prefix.operand);
+ if (condition.operator.type == TokenType.BANG) {
+ _propagateFalseState(condition.operand);
}
} else if (condition is ParenthesizedExpression) {
_propagateTrueState(condition.expression);
}
}
-}
-
-/**
- * The abstract class `Scope` defines the behavior common to name scopes used by the resolver
- * to determine which names are visible at any given point in the code.
- */
-abstract class Scope {
- /**
- * The prefix used to mark an identifier as being private to its library.
- */
- static int PRIVATE_NAME_PREFIX = 0x5F;
-
- /**
- * The suffix added to the declared name of a setter when looking up the setter. Used to
- * disambiguate between a getter and a setter that have the same name.
- */
- static String SETTER_SUFFIX = "=";
-
- /**
- * The name used to look up the method used to implement the unary minus operator. Used to
- * disambiguate between the unary and binary operators.
- */
- static String UNARY_MINUS = "unary-";
-
- /**
- * A table mapping names that are defined in this scope to the element representing the thing
- * declared with that name.
- */
- HashMap<String, Element> _definedNames = new HashMap<String, Element>();
-
- /**
- * A flag indicating whether there are any names defined in this scope.
- */
- bool _hasName = false;
-
- /**
- * Return the scope in which this scope is lexically enclosed.
- *
- * @return the scope in which this scope is lexically enclosed
- */
- Scope get enclosingScope => null;
/**
- * Return the listener that is to be informed when an error is encountered.
- *
- * @return the listener that is to be informed when an error is encountered
- */
- AnalysisErrorListener get errorListener;
-
- /**
- * Add the given element to this scope. If there is already an element with the given name defined
- * in this scope, then an error will be generated and the original element will continue to be
- * mapped to the name. If there is an element with the given name in an enclosing scope, then a
- * warning will be generated but the given element will hide the inherited element.
- *
- * @param element the element to be added to this scope
- */
- void define(Element element) {
- String name = _getName(element);
- if (name != null && !name.isEmpty) {
- if (_definedNames.containsKey(name)) {
- errorListener
- .onError(getErrorForDuplicate(_definedNames[name], element));
+ * 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 to [onError] if any of the arguments cannot be
+ * matched to a parameter. onError can be null to ignore the error.
+ *
+ * The flag [reportAsError] should be `true` if a compile-time error should be
+ * reported; or `false` if a compile-time warning should be reported.
+ *
+ * Returns the parameters that correspond to the arguments. If no parameter
+ * matched an argument, that position will be `null` in the list.
+ */
+ static List<ParameterElement> resolveArgumentsToParameters(
+ ArgumentList argumentList,
+ List<ParameterElement> parameters,
+ void onError(ErrorCode errorCode, AstNode node, [List<Object> arguments]),
+ {bool reportAsError: false}) {
+ if (parameters.isEmpty && argumentList.arguments.isEmpty) {
+ return const <ParameterElement>[];
+ }
+ int requiredParameterCount = 0;
+ int unnamedParameterCount = 0;
+ List<ParameterElement> unnamedParameters = new List<ParameterElement>();
+ HashMap<String, ParameterElement> namedParameters = null;
+ int length = parameters.length;
+ for (int i = 0; i < length; i++) {
+ ParameterElement parameter = parameters[i];
+ ParameterKind kind = parameter.parameterKind;
+ if (kind == ParameterKind.REQUIRED) {
+ unnamedParameters.add(parameter);
+ unnamedParameterCount++;
+ requiredParameterCount++;
+ } else if (kind == ParameterKind.POSITIONAL) {
+ unnamedParameters.add(parameter);
+ unnamedParameterCount++;
+ } else {
+ namedParameters ??= new HashMap<String, ParameterElement>();
+ namedParameters[parameter.name] = parameter;
+ }
+ }
+ int unnamedIndex = 0;
+ NodeList<Expression> arguments = argumentList.arguments;
+ int argumentCount = arguments.length;
+ List<ParameterElement> resolvedParameters =
+ new List<ParameterElement>(argumentCount);
+ int positionalArgumentCount = 0;
+ HashSet<String> usedNames = null;
+ bool noBlankArguments = true;
+ for (int i = 0; i < argumentCount; i++) {
+ Expression argument = arguments[i];
+ if (argument is NamedExpression) {
+ SimpleIdentifier nameNode = argument.name.label;
+ String name = nameNode.name;
+ ParameterElement element =
+ namedParameters != null ? namedParameters[name] : null;
+ if (element == null) {
+ ErrorCode errorCode = (reportAsError
+ ? CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER
+ : StaticWarningCode.UNDEFINED_NAMED_PARAMETER);
+ if (onError != null) {
+ onError(errorCode, nameNode, [name]);
+ }
+ } else {
+ resolvedParameters[i] = element;
+ nameNode.staticElement = element;
+ }
+ usedNames ??= new HashSet<String>();
+ if (!usedNames.add(name)) {
+ if (onError != null) {
+ onError(CompileTimeErrorCode.DUPLICATE_NAMED_ARGUMENT, nameNode,
+ [name]);
+ }
+ }
} else {
- _definedNames[name] = element;
- _hasName = true;
+ if (argument is SimpleIdentifier && argument.name.isEmpty) {
+ noBlankArguments = false;
+ }
+ positionalArgumentCount++;
+ if (unnamedIndex < unnamedParameterCount) {
+ resolvedParameters[i] = unnamedParameters[unnamedIndex++];
+ }
}
}
- }
-
- /**
- * Add the given element to this scope without checking for duplication or hiding.
- *
- * @param name the name of the element to be added
- * @param element the element to be added to this scope
- */
- void defineNameWithoutChecking(String name, Element element) {
- _definedNames[name] = element;
- _hasName = true;
- }
-
- /**
- * Add the given element to this scope without checking for duplication or hiding.
- *
- * @param element the element to be added to this scope
- */
- void defineWithoutChecking(Element element) {
- _definedNames[_getName(element)] = element;
- _hasName = true;
- }
-
- /**
- * Return the error code to be used when reporting that a name being defined locally conflicts
- * with another element of the same name in the local scope.
- *
- * @param existing the first element to be declared with the conflicting name
- * @param duplicate another element declared with the conflicting name
- * @return the error code used to report duplicate names within a scope
- */
- AnalysisError getErrorForDuplicate(Element existing, Element duplicate) {
- // TODO(brianwilkerson) Customize the error message based on the types of
- // elements that share the same name.
- // TODO(jwren) There are 4 error codes for duplicate, but only 1 is being
- // generated.
- Source source = duplicate.source;
- return new AnalysisError(source, duplicate.nameOffset, duplicate.nameLength,
- CompileTimeErrorCode.DUPLICATE_DEFINITION, [existing.displayName]);
- }
-
- /**
- * Return the source that contains the given identifier, or the source associated with this scope
- * if the source containing the identifier could not be determined.
- *
- * @param identifier the identifier whose source is to be returned
- * @return the source that contains the given identifier
- */
- Source getSource(AstNode node) {
- CompilationUnit unit = node.getAncestor((node) => node is CompilationUnit);
- if (unit != null) {
- CompilationUnitElement unitElement = unit.element;
- if (unitElement != null) {
- return unitElement.source;
+ if (positionalArgumentCount < requiredParameterCount && noBlankArguments) {
+ ErrorCode errorCode = (reportAsError
+ ? CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS
+ : StaticWarningCode.NOT_ENOUGH_REQUIRED_ARGUMENTS);
+ if (onError != null) {
+ onError(errorCode, argumentList,
+ [requiredParameterCount, positionalArgumentCount]);
}
- }
- return null;
- }
-
- /**
- * Return the element with which the given name is associated, or `null` if the name is not
- * defined within this scope.
- *
- * @param identifier the identifier node to lookup element for, used to report correct kind of a
- * problem and associate problem with
- * @param name the name associated with the element to be returned
- * @param referencingLibrary the library that contains the reference to the name, used to
- * implement library-level privacy
- * @return the element with which the given name is associated
- */
- Element internalLookup(
- Identifier identifier, String name, LibraryElement referencingLibrary);
-
- /**
- * Return the element with which the given name is associated, or `null` if the name is not
- * defined within this scope. This method only returns elements that are directly defined within
- * this scope, not elements that are defined in an enclosing scope.
- *
- * @param name the name associated with the element to be returned
- * @param referencingLibrary the library that contains the reference to the name, used to
- * implement library-level privacy
- * @return the element with which the given name is associated
- */
- Element localLookup(String name, LibraryElement referencingLibrary) {
- if (_hasName) {
- return _definedNames[name];
- }
- return null;
- }
-
- /**
- * Return the element with which the given identifier is associated, or `null` if the name
- * is not defined within this scope.
- *
- * @param identifier the identifier associated with the element to be returned
- * @param referencingLibrary the library that contains the reference to the name, used to
- * implement library-level privacy
- * @return the element with which the given identifier is associated
- */
- Element lookup(Identifier identifier, LibraryElement referencingLibrary) =>
- internalLookup(identifier, identifier.name, referencingLibrary);
-
- /**
- * Return the name that will be used to look up the given element.
- *
- * @param element the element whose look-up name is to be returned
- * @return the name that will be used to look up the given element
- */
- String _getName(Element element) {
- if (element is MethodElement) {
- MethodElement method = element;
- if (method.name == "-" && method.parameters.length == 0) {
- return UNARY_MINUS;
+ } else if (positionalArgumentCount > unnamedParameterCount &&
+ noBlankArguments) {
+ ErrorCode errorCode = (reportAsError
+ ? CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS
+ : StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS);
+ if (onError != null) {
+ onError(errorCode, argumentList,
+ [unnamedParameterCount, positionalArgumentCount]);
}
}
- return element.name;
+ return resolvedParameters;
}
-
- /**
- * Return `true` if the given name is a library-private name.
- *
- * @param name the name being tested
- * @return `true` if the given name is a library-private name
- */
- static bool isPrivateName(String name) =>
- name != null && StringUtilities.startsWithChar(name, PRIVATE_NAME_PREFIX);
}
+/**
+ * The abstract class `ScopedVisitor` maintains name and label scopes as an AST structure is
+ * being visited.
+ */
/**
* The abstract class `ScopedVisitor` maintains name and label scopes as an AST structure is
* being visited.
@@ -12054,19 +7807,20 @@ abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
final Source source;
/**
- * The error listener that will be informed of any errors that are found during resolution.
+ * The object used to access the types from the core library.
*/
- final AnalysisErrorListener errorListener;
+ final TypeProvider typeProvider;
/**
- * The scope used to resolve identifiers.
+ * The error reporter that will be informed of any errors that are found
+ * during resolution.
*/
- Scope nameScope;
+ final ErrorReporter errorReporter;
/**
- * The object used to access the types from the core library.
+ * The scope used to resolve identifiers.
*/
- final TypeProvider typeProvider;
+ Scope nameScope;
/**
* The scope used to resolve unlabeled `break` and `continue` statements.
@@ -12079,6 +7833,12 @@ abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
*/
LabelScope labelScope;
+ /**
+ * A flag indicating whether to enable support for allowing access to field
+ * formal parameters in a constructor's initializer list.
+ */
+ bool enableInitializingFormalAccess = false;
+
/**
* The class containing the AST nodes being visited,
* or `null` if we are not in the scope of a class.
@@ -12100,14 +7860,18 @@ abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
* first be visited. If `null` or unspecified, a new [LibraryScope] will be
* created based on [definingLibrary] and [typeProvider].
*/
- ScopedVisitor(
- this.definingLibrary, this.source, this.typeProvider, this.errorListener,
- {Scope nameScope}) {
+ ScopedVisitor(this.definingLibrary, Source source, this.typeProvider,
+ AnalysisErrorListener errorListener,
+ {Scope nameScope})
+ : source = source,
+ errorReporter = new ErrorReporter(errorListener, source) {
if (nameScope == null) {
- this.nameScope = new LibraryScope(definingLibrary, errorListener);
+ this.nameScope = new LibraryScope(definingLibrary);
} else {
this.nameScope = nameScope;
}
+ enableInitializingFormalAccess =
+ definingLibrary.context.analysisOptions.enableInitializingFormalAccess;
}
/**
@@ -12137,63 +7901,11 @@ abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
return nameScope;
}
- /**
- * Report an error with the given error code and arguments.
- *
- * @param errorCode the error code of the error to be reported
- * @param node the node specifying the location of the error
- * @param arguments the arguments to the error, used to compose the error message
- */
- void reportErrorForNode(ErrorCode errorCode, AstNode node,
- [List<Object> arguments]) {
- errorListener.onError(new AnalysisError(
- source, node.offset, node.length, errorCode, arguments));
- }
-
- /**
- * Report an error with the given error code and arguments.
- *
- * @param errorCode the error code of the error to be reported
- * @param offset the offset of the location of the error
- * @param length the length of the location of the error
- * @param arguments the arguments to the error, used to compose the error message
- */
- void reportErrorForOffset(ErrorCode errorCode, int offset, int length,
- [List<Object> arguments]) {
- errorListener.onError(
- new AnalysisError(source, offset, length, errorCode, arguments));
- }
-
- /**
- * Report an error with the given error code and arguments.
- *
- * @param errorCode the error code of the error to be reported
- * @param token the token specifying the location of the error
- * @param arguments the arguments to the error, used to compose the error message
- */
- void reportErrorForToken(ErrorCode errorCode, sc.Token token,
- [List<Object> arguments]) {
- errorListener.onError(new AnalysisError(
- source, token.offset, token.length, errorCode, arguments));
- }
-
- /**
- * Visit the given AST node if it is not null.
- *
- * @param node the node to be visited
- */
- void safelyVisit(AstNode node) {
- if (node != null) {
- node.accept(this);
- }
- }
-
@override
Object visitBlock(Block node) {
Scope outerScope = nameScope;
try {
- EnclosedScope enclosedScope = new EnclosedScope(nameScope);
- _hideNamesDefinedInBlock(enclosedScope, node);
+ EnclosedScope enclosedScope = new BlockScope(nameScope, node);
nameScope = enclosedScope;
super.visitBlock(node);
} finally {
@@ -12265,16 +7977,16 @@ abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
}
void visitClassDeclarationInScope(ClassDeclaration node) {
- safelyVisit(node.name);
- safelyVisit(node.typeParameters);
- safelyVisit(node.extendsClause);
- safelyVisit(node.withClause);
- safelyVisit(node.implementsClause);
- safelyVisit(node.nativeClause);
+ node.name?.accept(this);
+ node.typeParameters?.accept(this);
+ node.extendsClause?.accept(this);
+ node.withClause?.accept(this);
+ node.implementsClause?.accept(this);
+ node.nativeClause?.accept(this);
}
void visitClassMembersInScope(ClassDeclaration node) {
- safelyVisit(node.documentationComment);
+ node.documentationComment?.accept(this);
node.metadata.accept(this);
node.members.accept(this);
}
@@ -12296,30 +8008,51 @@ abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
@override
Object visitConstructorDeclaration(ConstructorDeclaration node) {
ConstructorElement constructorElement = node.element;
+ if (constructorElement == null) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.write("Missing element for constructor ");
+ buffer.write(node.returnType.name);
+ if (node.name != null) {
+ buffer.write(".");
+ buffer.write(node.name.name);
+ }
+ buffer.write(" in ");
+ buffer.write(definingLibrary.source.fullName);
+ AnalysisEngine.instance.logger.logInformation(buffer.toString(),
+ new CaughtException(new AnalysisException(), null));
+ }
Scope outerScope = nameScope;
try {
- if (constructorElement == null) {
- StringBuffer buffer = new StringBuffer();
- buffer.write("Missing element for constructor ");
- buffer.write(node.returnType.name);
- if (node.name != null) {
- buffer.write(".");
- buffer.write(node.name.name);
- }
- buffer.write(" in ");
- buffer.write(definingLibrary.source.fullName);
- AnalysisEngine.instance.logger.logInformation(buffer.toString(),
- new CaughtException(new AnalysisException(), null));
- } else {
+ if (constructorElement != null) {
nameScope = new FunctionScope(nameScope, constructorElement);
}
- super.visitConstructorDeclaration(node);
+ node.documentationComment?.accept(this);
+ node.metadata.accept(this);
+ node.returnType?.accept(this);
+ node.name?.accept(this);
+ node.parameters?.accept(this);
+ Scope functionScope = nameScope;
+ try {
+ if (constructorElement != null && enableInitializingFormalAccess) {
+ nameScope =
+ new ConstructorInitializerScope(nameScope, constructorElement);
+ }
+ node.initializers.accept(this);
+ } finally {
+ nameScope = functionScope;
+ }
+ node.redirectedConstructor?.accept(this);
+ visitConstructorDeclarationInScope(node);
} finally {
nameScope = outerScope;
}
return null;
}
+ void visitConstructorDeclarationInScope(ConstructorDeclaration node) {
+ node.body?.accept(this);
+ }
+
@override
Object visitDeclaredIdentifier(DeclaredIdentifier node) {
VariableElement element = node.element;
@@ -12336,7 +8069,7 @@ abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
try {
_implicitLabelScope = _implicitLabelScope.nest(node);
visitStatementInScope(node.body);
- safelyVisit(node.condition);
+ node.condition?.accept(this);
} finally {
_implicitLabelScope = outerImplicitScope;
}
@@ -12370,7 +8103,7 @@ abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
}
void visitEnumMembersInScope(EnumDeclaration node) {
- safelyVisit(node.documentationComment);
+ node.documentationComment?.accept(this);
node.metadata.accept(this);
node.constants.accept(this);
}
@@ -12402,9 +8135,9 @@ abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
// We visit the iterator before the loop variable because the loop variable
// cannot be in scope while visiting the iterator.
//
- safelyVisit(node.identifier);
- safelyVisit(node.iterable);
- safelyVisit(node.loopVariable);
+ node.identifier?.accept(this);
+ node.iterable?.accept(this);
+ node.loopVariable?.accept(this);
visitStatementInScope(node.body);
}
@@ -12448,9 +8181,9 @@ abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
* @param node the statement to be visited
*/
void visitForStatementInScope(ForStatement node) {
- safelyVisit(node.variables);
- safelyVisit(node.initialization);
- safelyVisit(node.condition);
+ node.variables?.accept(this);
+ node.initialization?.accept(this);
+ node.condition?.accept(this);
node.updaters.accept(this);
visitStatementInScope(node.body);
}
@@ -12471,13 +8204,17 @@ abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
} else {
nameScope = new FunctionScope(nameScope, functionElement);
}
- super.visitFunctionDeclaration(node);
+ visitFunctionDeclarationInScope(node);
} finally {
nameScope = outerScope;
}
return null;
}
+ void visitFunctionDeclarationInScope(FunctionDeclaration node) {
+ super.visitFunctionDeclaration(node);
+ }
+
@override
Object visitFunctionExpression(FunctionExpression node) {
if (node.parent is FunctionDeclaration) {
@@ -12520,13 +8257,17 @@ abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
Scope outerScope = nameScope;
try {
nameScope = new FunctionTypeScope(nameScope, node.element);
- super.visitFunctionTypeAlias(node);
+ visitFunctionTypeAliasInScope(node);
} finally {
nameScope = outerScope;
}
return null;
}
+ void visitFunctionTypeAliasInScope(FunctionTypeAlias node) {
+ super.visitFunctionTypeAlias(node);
+ }
+
@override
Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
Scope outerScope = nameScope;
@@ -12538,9 +8279,11 @@ abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
new CaughtException(new AnalysisException(), null));
} else {
nameScope = new EnclosedScope(nameScope);
- for (TypeParameterElement typeParameter
- in parameterElement.typeParameters) {
- nameScope.define(typeParameter);
+ List<TypeParameterElement> typeParameters =
+ parameterElement.typeParameters;
+ int length = typeParameters.length;
+ for (int i = 0; i < length; i++) {
+ nameScope.define(typeParameters[i]);
}
}
super.visitFunctionTypedFormalParameter(node);
@@ -12552,7 +8295,7 @@ abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
@override
Object visitIfStatement(IfStatement node) {
- safelyVisit(node.condition);
+ node.condition?.accept(this);
visitStatementInScope(node.thenStatement);
visitStatementInScope(node.elseStatement);
return null;
@@ -12566,687 +8309,888 @@ abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
} finally {
labelScope = outerScope;
}
- return null;
+ return null;
+ }
+
+ @override
+ Object visitMethodDeclaration(MethodDeclaration node) {
+ Scope outerScope = nameScope;
+ try {
+ ExecutableElement methodElement = node.element;
+ if (methodElement == null) {
+ AnalysisEngine.instance.logger.logInformation(
+ "Missing element for method ${node.name.name} in ${definingLibrary.source.fullName}",
+ new CaughtException(new AnalysisException(), null));
+ } else {
+ nameScope = new FunctionScope(nameScope, methodElement);
+ }
+ visitMethodDeclarationInScope(node);
+ } finally {
+ nameScope = outerScope;
+ }
+ return null;
+ }
+
+ void visitMethodDeclarationInScope(MethodDeclaration node) {
+ super.visitMethodDeclaration(node);
+ }
+
+ /**
+ * Visit the given statement after it's scope has been created. This is used by ResolverVisitor to
+ * correctly visit the 'then' and 'else' statements of an 'if' statement.
+ *
+ * @param node the statement to be visited
+ */
+ void visitStatementInScope(Statement node) {
+ if (node is Block) {
+ // Don't create a scope around a block because the block will create it's
+ // own scope.
+ visitBlock(node);
+ } else if (node != null) {
+ Scope outerNameScope = nameScope;
+ try {
+ nameScope = new EnclosedScope(nameScope);
+ node.accept(this);
+ } finally {
+ nameScope = outerNameScope;
+ }
+ }
+ }
+
+ @override
+ Object visitSwitchCase(SwitchCase node) {
+ node.expression.accept(this);
+ Scope outerNameScope = nameScope;
+ try {
+ nameScope = new EnclosedScope(nameScope);
+ node.statements.accept(this);
+ } finally {
+ nameScope = outerNameScope;
+ }
+ return null;
+ }
+
+ @override
+ Object visitSwitchDefault(SwitchDefault node) {
+ Scope outerNameScope = nameScope;
+ try {
+ nameScope = new EnclosedScope(nameScope);
+ node.statements.accept(this);
+ } finally {
+ nameScope = outerNameScope;
+ }
+ return null;
+ }
+
+ @override
+ Object visitSwitchStatement(SwitchStatement node) {
+ LabelScope outerScope = labelScope;
+ ImplicitLabelScope outerImplicitScope = _implicitLabelScope;
+ try {
+ _implicitLabelScope = _implicitLabelScope.nest(node);
+ for (SwitchMember member in node.members) {
+ for (Label label in member.labels) {
+ SimpleIdentifier labelName = label.label;
+ LabelElement labelElement = labelName.staticElement as LabelElement;
+ labelScope =
+ new LabelScope(labelScope, labelName.name, member, labelElement);
+ }
+ }
+ super.visitSwitchStatement(node);
+ } finally {
+ labelScope = outerScope;
+ _implicitLabelScope = outerImplicitScope;
+ }
+ return null;
+ }
+
+ @override
+ Object visitVariableDeclaration(VariableDeclaration node) {
+ super.visitVariableDeclaration(node);
+ if (node.parent.parent is! TopLevelVariableDeclaration &&
+ node.parent.parent is! FieldDeclaration) {
+ VariableElement element = node.element;
+ if (element != null) {
+ nameScope.define(element);
+ }
+ }
+ return null;
+ }
+
+ @override
+ Object visitWhileStatement(WhileStatement node) {
+ node.condition?.accept(this);
+ ImplicitLabelScope outerImplicitScope = _implicitLabelScope;
+ try {
+ _implicitLabelScope = _implicitLabelScope.nest(node);
+ visitStatementInScope(node.body);
+ } finally {
+ _implicitLabelScope = outerImplicitScope;
+ }
+ return null;
+ }
+
+ /**
+ * Add scopes for each of the given labels.
+ *
+ * @param labels the labels for which new scopes are to be added
+ * @return the scope that was in effect before the new scopes were added
+ */
+ LabelScope _addScopesFor(NodeList<Label> labels, AstNode node) {
+ LabelScope outerScope = labelScope;
+ for (Label label in labels) {
+ SimpleIdentifier labelNameNode = label.label;
+ String labelName = labelNameNode.name;
+ LabelElement labelElement = labelNameNode.staticElement as LabelElement;
+ labelScope = new LabelScope(labelScope, labelName, node, labelElement);
+ }
+ return outerScope;
+ }
+}
+
+/**
+ * Instances of this class manage the knowledge of what the set of subtypes are for a given type.
+ */
+class SubtypeManager {
+ /**
+ * A map between [ClassElement]s and a set of [ClassElement]s that are subtypes of the
+ * key.
+ */
+ HashMap<ClassElement, HashSet<ClassElement>> _subtypeMap =
+ new HashMap<ClassElement, HashSet<ClassElement>>();
+
+ /**
+ * The set of all [LibraryElement]s that have been visited by the manager. This is used both
+ * to prevent infinite loops in the recursive methods, and also as a marker for the scope of the
+ * libraries visited by this manager.
+ */
+ HashSet<LibraryElement> _visitedLibraries = new HashSet<LibraryElement>();
+
+ /**
+ * Given some [ClassElement], return the set of all subtypes, and subtypes of subtypes.
+ *
+ * @param classElement the class to recursively return the set of subtypes of
+ */
+ HashSet<ClassElement> computeAllSubtypes(ClassElement classElement) {
+ // Ensure that we have generated the subtype map for the library
+ _computeSubtypesInLibrary(classElement.library);
+ // use the subtypeMap to compute the set of all subtypes and subtype's
+ // subtypes
+ HashSet<ClassElement> allSubtypes = new HashSet<ClassElement>();
+ _safelyComputeAllSubtypes(
+ classElement, new HashSet<ClassElement>(), allSubtypes);
+ return allSubtypes;
+ }
+
+ /**
+ * Given some [LibraryElement], visit all of the types in the library, the passed library,
+ * and any imported libraries, will be in the [visitedLibraries] set.
+ *
+ * @param libraryElement the library to visit, it it hasn't been visited already
+ */
+ void ensureLibraryVisited(LibraryElement libraryElement) {
+ _computeSubtypesInLibrary(libraryElement);
+ }
+
+ /**
+ * Given some [ClassElement], this method adds all of the pairs combinations of itself and
+ * all of its supertypes to the [subtypeMap] map.
+ *
+ * @param classElement the class element
+ */
+ void _computeSubtypesInClass(ClassElement classElement) {
+ InterfaceType supertypeType = classElement.supertype;
+ if (supertypeType != null) {
+ ClassElement supertypeElement = supertypeType.element;
+ if (supertypeElement != null) {
+ _putInSubtypeMap(supertypeElement, classElement);
+ }
+ }
+ List<InterfaceType> interfaceTypes = classElement.interfaces;
+ int interfaceLength = interfaceTypes.length;
+ for (int i = 0; i < interfaceLength; i++) {
+ InterfaceType interfaceType = interfaceTypes[i];
+ ClassElement interfaceElement = interfaceType.element;
+ if (interfaceElement != null) {
+ _putInSubtypeMap(interfaceElement, classElement);
+ }
+ }
+ List<InterfaceType> mixinTypes = classElement.mixins;
+ int mixinLength = mixinTypes.length;
+ for (int i = 0; i < mixinLength; i++) {
+ InterfaceType mixinType = mixinTypes[i];
+ ClassElement mixinElement = mixinType.element;
+ if (mixinElement != null) {
+ _putInSubtypeMap(mixinElement, classElement);
+ }
+ }
+ }
+
+ /**
+ * Given some [CompilationUnitElement], this method calls
+ * [computeAllSubtypes] on all of the [ClassElement]s in the
+ * compilation unit.
+ *
+ * @param unitElement the compilation unit element
+ */
+ void _computeSubtypesInCompilationUnit(CompilationUnitElement unitElement) {
+ List<ClassElement> classElements = unitElement.types;
+ int length = classElements.length;
+ for (int i = 0; i < length; i++) {
+ ClassElement classElement = classElements[i];
+ _computeSubtypesInClass(classElement);
+ }
+ }
+
+ /**
+ * Given some [LibraryElement], this method calls
+ * [computeAllSubtypes] on all of the [ClassElement]s in the
+ * compilation unit, and itself for all imported and exported libraries. All visited libraries are
+ * added to the [visitedLibraries] set.
+ *
+ * @param libraryElement the library element
+ */
+ void _computeSubtypesInLibrary(LibraryElement libraryElement) {
+ if (libraryElement == null || _visitedLibraries.contains(libraryElement)) {
+ return;
+ }
+ _visitedLibraries.add(libraryElement);
+ _computeSubtypesInCompilationUnit(libraryElement.definingCompilationUnit);
+ List<CompilationUnitElement> parts = libraryElement.parts;
+ int partLength = parts.length;
+ for (int i = 0; i < partLength; i++) {
+ CompilationUnitElement part = parts[i];
+ _computeSubtypesInCompilationUnit(part);
+ }
+ List<LibraryElement> imports = libraryElement.importedLibraries;
+ int importLength = imports.length;
+ for (int i = 0; i < importLength; i++) {
+ LibraryElement importElt = imports[i];
+ _computeSubtypesInLibrary(importElt.library);
+ }
+ List<LibraryElement> exports = libraryElement.exportedLibraries;
+ int exportLength = exports.length;
+ for (int i = 0; i < exportLength; i++) {
+ LibraryElement exportElt = exports[i];
+ _computeSubtypesInLibrary(exportElt.library);
+ }
}
- @override
- Object visitMethodDeclaration(MethodDeclaration node) {
- Scope outerScope = nameScope;
- try {
- ExecutableElement methodElement = node.element;
- if (methodElement == null) {
- AnalysisEngine.instance.logger.logInformation(
- "Missing element for method ${node.name.name} in ${definingLibrary.source.fullName}",
- new CaughtException(new AnalysisException(), null));
- } else {
- nameScope = new FunctionScope(nameScope, methodElement);
- }
- super.visitMethodDeclaration(node);
- } finally {
- nameScope = outerScope;
+ /**
+ * Add some key/ value pair into the [subtypeMap] map.
+ *
+ * @param supertypeElement the key for the [subtypeMap] map
+ * @param subtypeElement the value for the [subtypeMap] map
+ */
+ void _putInSubtypeMap(
+ ClassElement supertypeElement, ClassElement subtypeElement) {
+ HashSet<ClassElement> subtypes = _subtypeMap[supertypeElement];
+ if (subtypes == null) {
+ subtypes = new HashSet<ClassElement>();
+ _subtypeMap[supertypeElement] = subtypes;
}
- return null;
+ subtypes.add(subtypeElement);
}
/**
- * Visit the given statement after it's scope has been created. This is used by ResolverVisitor to
- * correctly visit the 'then' and 'else' statements of an 'if' statement.
+ * Given some [ClassElement] and a [HashSet<ClassElement>], this method recursively
+ * adds all of the subtypes of the [ClassElement] to the passed array.
*
- * @param node the statement to be visited
+ * @param classElement the type to compute the set of subtypes of
+ * @param visitedClasses the set of class elements that this method has already recursively seen
+ * @param allSubtypes the computed set of subtypes of the passed class element
*/
- void visitStatementInScope(Statement node) {
- if (node is Block) {
- // Don't create a scope around a block because the block will create it's
- // own scope.
- visitBlock(node);
- } else if (node != null) {
- Scope outerNameScope = nameScope;
- try {
- nameScope = new EnclosedScope(nameScope);
- node.accept(this);
- } finally {
- nameScope = outerNameScope;
- }
+ void _safelyComputeAllSubtypes(ClassElement classElement,
+ HashSet<ClassElement> visitedClasses, HashSet<ClassElement> allSubtypes) {
+ if (!visitedClasses.add(classElement)) {
+ // if this class has already been called on this class element
+ return;
}
- }
-
- @override
- Object visitSwitchCase(SwitchCase node) {
- node.expression.accept(this);
- Scope outerNameScope = nameScope;
- try {
- nameScope = new EnclosedScope(nameScope);
- node.statements.accept(this);
- } finally {
- nameScope = outerNameScope;
+ HashSet<ClassElement> subtypes = _subtypeMap[classElement];
+ if (subtypes == null) {
+ return;
}
- return null;
+ for (ClassElement subtype in subtypes) {
+ _safelyComputeAllSubtypes(subtype, visitedClasses, allSubtypes);
+ }
+ allSubtypes.addAll(subtypes);
}
+}
- @override
- Object visitSwitchDefault(SwitchDefault node) {
- Scope outerNameScope = nameScope;
- try {
- nameScope = new EnclosedScope(nameScope);
- node.statements.accept(this);
- } finally {
- nameScope = outerNameScope;
- }
- return null;
+/**
+ * Instances of the class `ToDoFinder` find to-do comments in Dart code.
+ */
+class ToDoFinder {
+ /**
+ * The error reporter by which to-do comments will be reported.
+ */
+ final ErrorReporter _errorReporter;
+
+ /**
+ * Initialize a newly created to-do finder to report to-do comments to the given reporter.
+ *
+ * @param errorReporter the error reporter by which to-do comments will be reported
+ */
+ ToDoFinder(this._errorReporter);
+
+ /**
+ * Search the comments in the given compilation unit for to-do comments and report an error for
+ * each.
+ *
+ * @param unit the compilation unit containing the to-do comments
+ */
+ void findIn(CompilationUnit unit) {
+ _gatherTodoComments(unit.beginToken);
}
- @override
- Object visitSwitchStatement(SwitchStatement node) {
- LabelScope outerScope = labelScope;
- ImplicitLabelScope outerImplicitScope = _implicitLabelScope;
- try {
- _implicitLabelScope = _implicitLabelScope.nest(node);
- for (SwitchMember member in node.members) {
- for (Label label in member.labels) {
- SimpleIdentifier labelName = label.label;
- LabelElement labelElement = labelName.staticElement as LabelElement;
- labelScope =
- new LabelScope(labelScope, labelName.name, member, labelElement);
+ /**
+ * Search the comment tokens reachable from the given token and create errors for each to-do
+ * comment.
+ *
+ * @param token the head of the list of tokens being searched
+ */
+ void _gatherTodoComments(Token token) {
+ while (token != null && token.type != TokenType.EOF) {
+ Token commentToken = token.precedingComments;
+ while (commentToken != null) {
+ if (commentToken.type == TokenType.SINGLE_LINE_COMMENT ||
+ commentToken.type == TokenType.MULTI_LINE_COMMENT) {
+ _scrapeTodoComment(commentToken);
}
+ commentToken = commentToken.next;
}
- super.visitSwitchStatement(node);
- } finally {
- labelScope = outerScope;
- _implicitLabelScope = outerImplicitScope;
+ token = token.next;
}
- return null;
}
- @override
- Object visitVariableDeclaration(VariableDeclaration node) {
- super.visitVariableDeclaration(node);
- if (node.parent.parent is! TopLevelVariableDeclaration &&
- node.parent.parent is! FieldDeclaration) {
- VariableElement element = node.element;
- if (element != null) {
- nameScope.define(element);
- }
+ /**
+ * Look for user defined tasks in comments and convert them into info level analysis issues.
+ *
+ * @param commentToken the comment token to analyze
+ */
+ void _scrapeTodoComment(Token commentToken) {
+ Iterable<Match> matches =
+ TodoCode.TODO_REGEX.allMatches(commentToken.lexeme);
+ for (Match match in matches) {
+ int offset = commentToken.offset + match.start + match.group(1).length;
+ int length = match.group(2).length;
+ _errorReporter.reportErrorForOffset(
+ TodoCode.TODO, offset, length, [match.group(2)]);
}
- return null;
}
+}
- @override
- Object visitWhileStatement(WhileStatement node) {
- safelyVisit(node.condition);
- ImplicitLabelScope outerImplicitScope = _implicitLabelScope;
- try {
- _implicitLabelScope = _implicitLabelScope.nest(node);
- visitStatementInScope(node.body);
- } finally {
- _implicitLabelScope = outerImplicitScope;
- }
- return null;
- }
+/**
+ * Helper for resolving [TypeName]s.
+ *
+ * The client must set [nameScope] before calling [resolveTypeName].
+ */
+class TypeNameResolver {
+ final TypeSystem typeSystem;
+ final DartType dynamicType;
+ final DartType undefinedType;
+ final LibraryElement definingLibrary;
+ final Source source;
+ final AnalysisErrorListener errorListener;
+
+ Scope nameScope;
+
+ TypeNameResolver(this.typeSystem, TypeProvider typeProvider,
+ this.definingLibrary, this.source, this.errorListener)
+ : dynamicType = typeProvider.dynamicType,
+ undefinedType = typeProvider.undefinedType;
/**
- * Add scopes for each of the given labels.
+ * Report an error with the given error code and arguments.
*
- * @param labels the labels for which new scopes are to be added
- * @return the scope that was in effect before the new scopes were added
+ * @param errorCode the error code of the error to be reported
+ * @param node the node specifying the location of the error
+ * @param arguments the arguments to the error, used to compose the error message
*/
- LabelScope _addScopesFor(NodeList<Label> labels, AstNode node) {
- LabelScope outerScope = labelScope;
- for (Label label in labels) {
- SimpleIdentifier labelNameNode = label.label;
- String labelName = labelNameNode.name;
- LabelElement labelElement = labelNameNode.staticElement as LabelElement;
- labelScope = new LabelScope(labelScope, labelName, node, labelElement);
- }
- return outerScope;
+ void reportErrorForNode(ErrorCode errorCode, AstNode node,
+ [List<Object> arguments]) {
+ errorListener.onError(new AnalysisError(
+ source, node.offset, node.length, errorCode, arguments));
}
/**
- * Marks the local declarations of the given [Block] hidden in the enclosing scope.
- * According to the scoping rules name is hidden if block defines it, but name is defined after
- * its declaration statement.
+ * Resolve the given [TypeName] - set its element and static type. Only the
+ * given [node] is resolved, all its children must be already resolved.
+ *
+ * The client must set [nameScope] before calling [resolveTypeName].
*/
- void _hideNamesDefinedInBlock(EnclosedScope scope, Block block) {
- NodeList<Statement> statements = block.statements;
- int statementCount = statements.length;
- for (int i = 0; i < statementCount; i++) {
- Statement statement = statements[i];
- if (statement is VariableDeclarationStatement) {
- VariableDeclarationStatement vds = statement;
- NodeList<VariableDeclaration> variables = vds.variables.variables;
- int variableCount = variables.length;
- for (int j = 0; j < variableCount; j++) {
- scope.hide(variables[j].element);
+ void resolveTypeName(TypeName node) {
+ Identifier typeName = node.name;
+ _setElement(typeName, null); // Clear old Elements from previous run.
+ TypeArgumentList argumentList = node.typeArguments;
+ Element element = nameScope.lookup(typeName, definingLibrary);
+ if (element == null) {
+ //
+ // Check to see whether the type name is either 'dynamic' or 'void',
+ // neither of which are in the name scope and hence will not be found by
+ // normal means.
+ //
+ if (typeName.name == dynamicType.name) {
+ _setElement(typeName, dynamicType.element);
+// if (argumentList != null) {
+// // TODO(brianwilkerson) Report this error
+// reporter.reportError(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, node, dynamicType.getName(), 0, argumentList.getArguments().size());
+// }
+ typeName.staticType = dynamicType;
+ node.type = dynamicType;
+ return;
+ }
+ VoidTypeImpl voidType = VoidTypeImpl.instance;
+ if (typeName.name == voidType.name) {
+ // There is no element for 'void'.
+// if (argumentList != null) {
+// // TODO(brianwilkerson) Report this error
+// reporter.reportError(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, node, voidType.getName(), 0, argumentList.getArguments().size());
+// }
+ typeName.staticType = voidType;
+ node.type = voidType;
+ return;
+ }
+ if (nameScope.shouldIgnoreUndefined(typeName)) {
+ typeName.staticType = undefinedType;
+ node.type = undefinedType;
+ return;
+ }
+ //
+ // If not, the look to see whether we might have created the wrong AST
+ // structure for a constructor name. If so, fix the AST structure and then
+ // proceed.
+ //
+ AstNode parent = node.parent;
+ if (typeName is PrefixedIdentifier &&
+ parent is ConstructorName &&
+ argumentList == null) {
+ ConstructorName name = parent;
+ if (name.name == null) {
+ PrefixedIdentifier prefixedIdentifier =
+ typeName as PrefixedIdentifier;
+ SimpleIdentifier prefix = prefixedIdentifier.prefix;
+ element = nameScope.lookup(prefix, definingLibrary);
+ if (element is PrefixElement) {
+ if (nameScope.shouldIgnoreUndefined(typeName)) {
+ typeName.staticType = undefinedType;
+ node.type = undefinedType;
+ return;
+ }
+ AstNode grandParent = parent.parent;
+ if (grandParent is InstanceCreationExpression &&
+ grandParent.isConst) {
+ // If, if this is a const expression, then generate a
+ // CompileTimeErrorCode.CONST_WITH_NON_TYPE error.
+ reportErrorForNode(
+ CompileTimeErrorCode.CONST_WITH_NON_TYPE,
+ prefixedIdentifier.identifier,
+ [prefixedIdentifier.identifier.name]);
+ } else {
+ // Else, if this expression is a new expression, report a
+ // NEW_WITH_NON_TYPE warning.
+ reportErrorForNode(
+ StaticWarningCode.NEW_WITH_NON_TYPE,
+ prefixedIdentifier.identifier,
+ [prefixedIdentifier.identifier.name]);
+ }
+ _setElement(prefix, element);
+ return;
+ } else if (element != null) {
+ //
+ // Rewrite the constructor name. The parser, when it sees a
+ // constructor named "a.b", cannot tell whether "a" is a prefix and
+ // "b" is a class name, or whether "a" is a class name and "b" is a
+ // constructor name. It arbitrarily chooses the former, but in this
+ // case was wrong.
+ //
+ name.name = prefixedIdentifier.identifier;
+ name.period = prefixedIdentifier.period;
+ node.name = prefix;
+ typeName = prefix;
+ }
}
- } else if (statement is FunctionDeclarationStatement) {
- FunctionDeclarationStatement fds = statement;
- scope.hide(fds.functionDeclaration.element);
- }
- }
- }
-}
-
-/**
- * Implementation of [TypeSystem] using the strong mode rules.
- * https://github.com/dart-lang/dev_compiler/blob/master/STRONG_MODE.md
- */
-class StrongTypeSystemImpl implements TypeSystem {
- final _specTypeSystem = new TypeSystemImpl();
-
- StrongTypeSystemImpl();
-
- @override
- DartType getLeastUpperBound(
- TypeProvider typeProvider, DartType type1, DartType type2) {
- // TODO(leafp): Implement a strong mode version of this.
- return _specTypeSystem.getLeastUpperBound(typeProvider, type1, type2);
- }
-
- // TODO(leafp): Document the rules in play here
- @override
- bool isAssignableTo(DartType fromType, DartType toType) {
- // An actual subtype
- if (isSubtypeOf(fromType, toType)) {
- return true;
- }
-
- // Don't allow implicit downcasts between function types
- // and call method objects, as these will almost always fail.
- if ((fromType is FunctionType && _getCallMethodType(toType) != null) ||
- (toType is FunctionType && _getCallMethodType(fromType) != null)) {
- return false;
- }
-
- // If the subtype relation goes the other way, allow the implicit downcast.
- // TODO(leafp): Emit warnings and hints for these in some way.
- // TODO(leafp): Consider adding a flag to disable these? Or just rely on
- // --warnings-as-errors?
- if (isSubtypeOf(toType, fromType) ||
- _specTypeSystem.isAssignableTo(toType, fromType)) {
- // TODO(leafp): error if type is known to be exact (literal,
- // instance creation).
- // TODO(leafp): Warn on composite downcast.
- // TODO(leafp): hint on object/dynamic downcast.
- // TODO(leafp): Consider allowing assignment casts.
- return true;
- }
-
- return false;
- }
-
- @override
- bool isSubtypeOf(DartType leftType, DartType rightType) {
- return _isSubtypeOf(leftType, rightType, null);
- }
-
- FunctionType _getCallMethodType(DartType t) {
- if (t is InterfaceType) {
- ClassElement element = t.element;
- InheritanceManager manager = new InheritanceManager(element.library);
- FunctionType callType = manager.lookupMemberType(t, "call");
- return callType;
- }
- return null;
- }
-
- // Given a type t, if t is an interface type with a call method
- // defined, return the function type for the call method, otherwise
- // return null.
- _GuardedSubtypeChecker<DartType> _guard(
- _GuardedSubtypeChecker<DartType> check) {
- return (DartType t1, DartType t2, Set<Element> visited) {
- Element element = t1.element;
- if (visited == null) {
- visited = new HashSet<Element>();
- }
- if (element == null || !visited.add(element)) {
- return false;
}
- try {
- return check(t1, t2, visited);
- } finally {
- visited.remove(element);
+ if (nameScope.shouldIgnoreUndefined(typeName)) {
+ typeName.staticType = undefinedType;
+ node.type = undefinedType;
+ return;
}
- };
- }
-
- bool _isBottom(DartType t, {bool dynamicIsBottom: false}) {
- return (t.isDynamic && dynamicIsBottom) || t.isBottom;
- }
-
- // Guard against loops in the class hierarchy
- /**
- * Check that [f1] is a subtype of [f2].
- * [fuzzyArrows] indicates whether or not the f1 and f2 should be
- * treated as fuzzy arrow types (and hence dynamic parameters to f2 treated
- * as bottom).
- */
- bool _isFunctionSubtypeOf(FunctionType f1, FunctionType f2,
- {bool fuzzyArrows: true}) {
- final r1s = f1.normalParameterTypes;
- final o1s = f1.optionalParameterTypes;
- final n1s = f1.namedParameterTypes;
- final r2s = f2.normalParameterTypes;
- final o2s = f2.optionalParameterTypes;
- final n2s = f2.namedParameterTypes;
- final ret1 = f1.returnType;
- final ret2 = f2.returnType;
-
- // A -> B <: C -> D if C <: A and
- // either D is void or B <: D
- if (!ret2.isVoid && !isSubtypeOf(ret1, ret2)) {
- return false;
- }
-
- // Reject if one has named and the other has optional
- if (n1s.length > 0 && o2s.length > 0) {
- return false;
- }
- if (n2s.length > 0 && o1s.length > 0) {
- return false;
}
-
- // Rebind _isSubtypeOf for convenience
- _SubtypeChecker<DartType> parameterSubtype = (DartType t1, DartType t2) =>
- _isSubtypeOf(t1, t2, null, dynamicIsBottom: fuzzyArrows);
-
- // f2 has named parameters
- if (n2s.length > 0) {
- // Check that every named parameter in f2 has a match in f1
- for (String k2 in n2s.keys) {
- if (!n1s.containsKey(k2)) {
- return false;
+ // check element
+ bool elementValid = element is! MultiplyDefinedElement;
+ if (elementValid &&
+ element is! ClassElement &&
+ _isTypeNameInInstanceCreationExpression(node)) {
+ SimpleIdentifier typeNameSimple = _getTypeSimpleIdentifier(typeName);
+ InstanceCreationExpression creation =
+ node.parent.parent as InstanceCreationExpression;
+ if (creation.isConst) {
+ if (element == null) {
+ reportErrorForNode(
+ CompileTimeErrorCode.UNDEFINED_CLASS, typeNameSimple, [typeName]);
+ } else {
+ reportErrorForNode(CompileTimeErrorCode.CONST_WITH_NON_TYPE,
+ typeNameSimple, [typeName]);
}
- if (!parameterSubtype(n2s[k2], n1s[k2])) {
- return false;
+ elementValid = false;
+ } else {
+ if (element != null) {
+ reportErrorForNode(
+ StaticWarningCode.NEW_WITH_NON_TYPE, typeNameSimple, [typeName]);
+ elementValid = false;
}
}
}
- // If we get here, we either have no named parameters,
- // or else the named parameters match and we have no optional
- // parameters
-
- // If f1 has more required parameters, reject
- if (r1s.length > r2s.length) {
- return false;
- }
-
- // If f2 has more required + optional parameters, reject
- if (r2s.length + o2s.length > r1s.length + o1s.length) {
- return false;
- }
-
- // The parameter lists must look like the following at this point
- // where rrr is a region of required, and ooo is a region of optionals.
- // f1: rrr ooo ooo ooo
- // f2: rrr rrr ooo
- int rr = r1s.length; // required in both
- int or = r2s.length - r1s.length; // optional in f1, required in f2
- int oo = o2s.length; // optional in both
-
- for (int i = 0; i < rr; ++i) {
- if (!parameterSubtype(r2s[i], r1s[i])) {
- return false;
+ if (elementValid && element == null) {
+ // We couldn't resolve the type name.
+ // TODO(jwren) Consider moving the check for
+ // CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE from the
+ // ErrorVerifier, so that we don't have two errors on a built in
+ // identifier being used as a class name.
+ // See CompileTimeErrorCodeTest.test_builtInIdentifierAsType().
+ SimpleIdentifier typeNameSimple = _getTypeSimpleIdentifier(typeName);
+ RedirectingConstructorKind redirectingConstructorKind;
+ if (_isBuiltInIdentifier(node) && _isTypeAnnotation(node)) {
+ reportErrorForNode(CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE,
+ typeName, [typeName.name]);
+ } else if (typeNameSimple.name == "boolean") {
+ reportErrorForNode(
+ StaticWarningCode.UNDEFINED_CLASS_BOOLEAN, typeNameSimple, []);
+ } else if (_isTypeNameInCatchClause(node)) {
+ reportErrorForNode(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, typeName,
+ [typeName.name]);
+ } else if (_isTypeNameInAsExpression(node)) {
+ reportErrorForNode(
+ StaticWarningCode.CAST_TO_NON_TYPE, typeName, [typeName.name]);
+ } else if (_isTypeNameInIsExpression(node)) {
+ reportErrorForNode(StaticWarningCode.TYPE_TEST_WITH_UNDEFINED_NAME,
+ typeName, [typeName.name]);
+ } else if ((redirectingConstructorKind =
+ _getRedirectingConstructorKind(node)) !=
+ null) {
+ ErrorCode errorCode =
+ (redirectingConstructorKind == RedirectingConstructorKind.CONST
+ ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS
+ : StaticWarningCode.REDIRECT_TO_NON_CLASS);
+ reportErrorForNode(errorCode, typeName, [typeName.name]);
+ } else if (_isTypeNameInTypeArgumentList(node)) {
+ reportErrorForNode(StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT,
+ typeName, [typeName.name]);
+ } else {
+ reportErrorForNode(
+ StaticWarningCode.UNDEFINED_CLASS, typeName, [typeName.name]);
}
+ elementValid = false;
}
- for (int i = 0, j = rr; i < or; ++i, ++j) {
- if (!parameterSubtype(r2s[j], o1s[i])) {
- return false;
+ if (!elementValid) {
+ if (element is MultiplyDefinedElement) {
+ _setElement(typeName, element);
}
+ typeName.staticType = undefinedType;
+ node.type = undefinedType;
+ return;
}
- for (int i = or, j = 0; i < oo; ++i, ++j) {
- if (!parameterSubtype(o2s[j], o1s[i])) {
- return false;
+ DartType type = null;
+ if (element is ClassElement) {
+ _setElement(typeName, element);
+ type = element.type;
+ } else if (element is FunctionTypeAliasElement) {
+ _setElement(typeName, element);
+ type = element.type;
+ } else if (element is TypeParameterElement) {
+ _setElement(typeName, element);
+ type = element.type;
+// if (argumentList != null) {
+// // Type parameters cannot have type arguments.
+// // TODO(brianwilkerson) Report this error.
+// // resolver.reportError(ResolverErrorCode.?, keyType);
+// }
+ } else if (element is MultiplyDefinedElement) {
+ List<Element> elements = element.conflictingElements;
+ type = _getTypeWhenMultiplyDefined(elements);
+ if (type != null) {
+ node.type = type;
}
- }
- return true;
- }
-
- bool _isInterfaceSubtypeOf(
- InterfaceType i1, InterfaceType i2, Set<Element> visited) {
- // Guard recursive calls
- _GuardedSubtypeChecker<InterfaceType> guardedInterfaceSubtype =
- _guard(_isInterfaceSubtypeOf);
-
- if (i1 == i2) {
- return true;
- }
-
- if (i1.element == i2.element) {
- List<DartType> tArgs1 = i1.typeArguments;
- List<DartType> tArgs2 = i2.typeArguments;
-
- assert(tArgs1.length == tArgs2.length);
-
- for (int i = 0; i < tArgs1.length; i++) {
- DartType t1 = tArgs1[i];
- DartType t2 = tArgs2[i];
- if (!isSubtypeOf(t1, t2)) {
- return false;
+ } else {
+ // The name does not represent a type.
+ RedirectingConstructorKind redirectingConstructorKind;
+ if (_isTypeNameInCatchClause(node)) {
+ reportErrorForNode(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, typeName,
+ [typeName.name]);
+ } else if (_isTypeNameInAsExpression(node)) {
+ reportErrorForNode(
+ StaticWarningCode.CAST_TO_NON_TYPE, typeName, [typeName.name]);
+ } else if (_isTypeNameInIsExpression(node)) {
+ reportErrorForNode(StaticWarningCode.TYPE_TEST_WITH_NON_TYPE, typeName,
+ [typeName.name]);
+ } else if ((redirectingConstructorKind =
+ _getRedirectingConstructorKind(node)) !=
+ null) {
+ ErrorCode errorCode =
+ (redirectingConstructorKind == RedirectingConstructorKind.CONST
+ ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS
+ : StaticWarningCode.REDIRECT_TO_NON_CLASS);
+ reportErrorForNode(errorCode, typeName, [typeName.name]);
+ } else if (_isTypeNameInTypeArgumentList(node)) {
+ reportErrorForNode(StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT,
+ typeName, [typeName.name]);
+ } else {
+ AstNode parent = typeName.parent;
+ while (parent is TypeName) {
+ parent = parent.parent;
+ }
+ if (parent is ExtendsClause ||
+ parent is ImplementsClause ||
+ parent is WithClause ||
+ parent is ClassTypeAlias) {
+ // Ignored. The error will be reported elsewhere.
+ } else if (element is LocalVariableElement ||
+ (element is FunctionElement &&
+ element.enclosingElement is ExecutableElement)) {
+ reportErrorForNode(CompileTimeErrorCode.REFERENCED_BEFORE_DECLARATION,
+ typeName, [typeName.name]);
+ } else {
+ reportErrorForNode(
+ StaticWarningCode.NOT_A_TYPE, typeName, [typeName.name]);
}
}
- return true;
- }
-
- if (i2.isDartCoreFunction && i1.element.getMethod("call") != null) {
- return true;
- }
-
- if (i1.isObject) {
- return false;
- }
-
- if (guardedInterfaceSubtype(i1.superclass, i2, visited)) {
- return true;
- }
-
- for (final parent in i1.interfaces) {
- if (guardedInterfaceSubtype(parent, i2, visited)) {
- return true;
- }
- }
-
- for (final parent in i1.mixins) {
- if (guardedInterfaceSubtype(parent, i2, visited)) {
- return true;
- }
- }
-
- return false;
- }
-
- bool _isSubtypeOf(DartType t1, DartType t2, Set<Element> visited,
- {bool dynamicIsBottom: false}) {
- // Guard recursive calls
- _GuardedSubtypeChecker<DartType> guardedSubtype = _guard(_isSubtypeOf);
-
- if (t1 == t2) {
- return true;
- }
-
- // The types are void, dynamic, bottom, interface types, function types
- // and type parameters. We proceed by eliminating these different classes
- // from consideration.
-
- // Trivially true.
- if (_isTop(t2, dynamicIsBottom: dynamicIsBottom) ||
- _isBottom(t1, dynamicIsBottom: dynamicIsBottom)) {
- return true;
- }
-
- // Trivially false.
- if (_isTop(t1, dynamicIsBottom: dynamicIsBottom) ||
- _isBottom(t2, dynamicIsBottom: dynamicIsBottom)) {
- return false;
- }
-
- // S <: T where S is a type variable
- // T is not dynamic or object (handled above)
- // S != T (handled above)
- // So only true if bound of S is S' and
- // S' <: T
- if (t1 is TypeParameterType) {
- DartType bound = t1.element.bound;
- if (bound == null) return false;
- return guardedSubtype(bound, t2, visited);
- }
-
- if (t2 is TypeParameterType) {
- return false;
- }
-
- if (t1.isVoid || t2.isVoid) {
- return false;
- }
-
- // We've eliminated void, dynamic, bottom, and type parameters. The only
- // cases are the combinations of interface type and function type.
-
- // A function type can only subtype an interface type if
- // the interface type is Function
- if (t1 is FunctionType && t2 is InterfaceType) {
- return t2.isDartCoreFunction;
- }
-
- // An interface type can only subtype a function type if
- // the interface type declares a call method with a type
- // which is a super type of the function type.
- if (t1 is InterfaceType && t2 is FunctionType) {
- var callType = _getCallMethodType(t1);
- return (callType != null) && _isFunctionSubtypeOf(callType, t2);
+ typeName.staticType = dynamicType;
+ node.type = dynamicType;
+ return;
}
-
- // Two interface types
- if (t1 is InterfaceType && t2 is InterfaceType) {
- return _isInterfaceSubtypeOf(t1, t2, visited);
+ if (argumentList != null) {
+ NodeList<TypeName> arguments = argumentList.arguments;
+ int argumentCount = arguments.length;
+ List<DartType> parameters = typeSystem.typeFormalsAsTypes(type);
+ int parameterCount = parameters.length;
+ List<DartType> typeArguments = new List<DartType>(parameterCount);
+ if (argumentCount == parameterCount) {
+ for (int i = 0; i < parameterCount; i++) {
+ TypeName argumentTypeName = arguments[i];
+ DartType argumentType = _getType(argumentTypeName);
+ if (argumentType == null) {
+ argumentType = dynamicType;
+ }
+ typeArguments[i] = argumentType;
+ }
+ } else {
+ reportErrorForNode(_getInvalidTypeParametersErrorCode(node), node,
+ [typeName.name, parameterCount, argumentCount]);
+ for (int i = 0; i < parameterCount; i++) {
+ typeArguments[i] = dynamicType;
+ }
+ }
+ type = typeSystem.instantiateType(type, typeArguments);
+ } else {
+ type = typeSystem.instantiateToBounds(type);
}
-
- return _isFunctionSubtypeOf(t1 as FunctionType, t2 as FunctionType);
- }
-
- // TODO(leafp): Document the rules in play here
- bool _isTop(DartType t, {bool dynamicIsBottom: false}) {
- return (t.isDynamic && !dynamicIsBottom) || t.isObject;
+ typeName.staticType = type;
+ node.type = type;
}
-}
-/**
- * Instances of this class manage the knowledge of what the set of subtypes are for a given type.
- */
-class SubtypeManager {
/**
- * A map between [ClassElement]s and a set of [ClassElement]s that are subtypes of the
- * key.
+ * The number of type arguments in the given type name does not match the number of parameters in
+ * the corresponding class element. Return the error code that should be used to report this
+ * error.
+ *
+ * @param node the type name with the wrong number of type arguments
+ * @return the error code that should be used to report that the wrong number of type arguments
+ * were provided
*/
- HashMap<ClassElement, HashSet<ClassElement>> _subtypeMap =
- new HashMap<ClassElement, HashSet<ClassElement>>();
+ ErrorCode _getInvalidTypeParametersErrorCode(TypeName node) {
+ AstNode parent = node.parent;
+ if (parent is ConstructorName) {
+ parent = parent.parent;
+ if (parent is InstanceCreationExpression) {
+ if (parent.isConst) {
+ return CompileTimeErrorCode.CONST_WITH_INVALID_TYPE_PARAMETERS;
+ } else {
+ return StaticWarningCode.NEW_WITH_INVALID_TYPE_PARAMETERS;
+ }
+ }
+ }
+ return StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS;
+ }
/**
- * The set of all [LibraryElement]s that have been visited by the manager. This is used both
- * to prevent infinite loops in the recursive methods, and also as a marker for the scope of the
- * libraries visited by this manager.
+ * Checks if the given type name is the target in a redirected constructor.
+ *
+ * @param typeName the type name to analyze
+ * @return some [RedirectingConstructorKind] if the given type name is used as the type in a
+ * redirected constructor, or `null` otherwise
*/
- HashSet<LibraryElement> _visitedLibraries = new HashSet<LibraryElement>();
+ RedirectingConstructorKind _getRedirectingConstructorKind(TypeName typeName) {
+ AstNode parent = typeName.parent;
+ if (parent is ConstructorName) {
+ AstNode grandParent = parent.parent;
+ if (grandParent is ConstructorDeclaration) {
+ if (identical(grandParent.redirectedConstructor, parent)) {
+ if (grandParent.constKeyword != null) {
+ return RedirectingConstructorKind.CONST;
+ }
+ return RedirectingConstructorKind.NORMAL;
+ }
+ }
+ }
+ return null;
+ }
/**
- * Given some [ClassElement], return the set of all subtypes, and subtypes of subtypes.
+ * Return the type represented by the given type name.
*
- * @param classElement the class to recursively return the set of subtypes of
+ * @param typeName the type name representing the type to be returned
+ * @return the type represented by the type name
*/
- HashSet<ClassElement> computeAllSubtypes(ClassElement classElement) {
- // Ensure that we have generated the subtype map for the library
- _computeSubtypesInLibrary(classElement.library);
- // use the subtypeMap to compute the set of all subtypes and subtype's
- // subtypes
- HashSet<ClassElement> allSubtypes = new HashSet<ClassElement>();
- _safelyComputeAllSubtypes(
- classElement, new HashSet<ClassElement>(), allSubtypes);
- return allSubtypes;
+ DartType _getType(TypeName typeName) {
+ DartType type = typeName.type;
+ if (type == null) {
+ return undefinedType;
+ }
+ return type;
}
/**
- * Given some [LibraryElement], visit all of the types in the library, the passed library,
- * and any imported libraries, will be in the [visitedLibraries] set.
+ * Returns the simple identifier of the given (may be qualified) type name.
*
- * @param libraryElement the library to visit, it it hasn't been visited already
+ * @param typeName the (may be qualified) qualified type name
+ * @return the simple identifier of the given (may be qualified) type name.
*/
- void ensureLibraryVisited(LibraryElement libraryElement) {
- _computeSubtypesInLibrary(libraryElement);
+ SimpleIdentifier _getTypeSimpleIdentifier(Identifier typeName) {
+ if (typeName is SimpleIdentifier) {
+ return typeName;
+ } else {
+ return (typeName as PrefixedIdentifier).identifier;
+ }
}
/**
- * Given some [ClassElement], this method adds all of the pairs combinations of itself and
- * all of its supertypes to the [subtypeMap] map.
+ * Given the multiple elements to which a single name could potentially be resolved, return the
+ * single interface type that should be used, or `null` if there is no clear choice.
*
- * @param classElement the class element
+ * @param elements the elements to which a single name could potentially be resolved
+ * @return the single interface type that should be used for the type name
*/
- void _computeSubtypesInClass(ClassElement classElement) {
- InterfaceType supertypeType = classElement.supertype;
- if (supertypeType != null) {
- ClassElement supertypeElement = supertypeType.element;
- if (supertypeElement != null) {
- _putInSubtypeMap(supertypeElement, classElement);
- }
- }
- List<InterfaceType> interfaceTypes = classElement.interfaces;
- for (InterfaceType interfaceType in interfaceTypes) {
- ClassElement interfaceElement = interfaceType.element;
- if (interfaceElement != null) {
- _putInSubtypeMap(interfaceElement, classElement);
- }
- }
- List<InterfaceType> mixinTypes = classElement.mixins;
- for (InterfaceType mixinType in mixinTypes) {
- ClassElement mixinElement = mixinType.element;
- if (mixinElement != null) {
- _putInSubtypeMap(mixinElement, classElement);
+ InterfaceType _getTypeWhenMultiplyDefined(List<Element> elements) {
+ InterfaceType type = null;
+ int length = elements.length;
+ for (int i = 0; i < length; i++) {
+ Element element = elements[i];
+ if (element is ClassElement) {
+ if (type != null) {
+ return null;
+ }
+ type = element.type;
}
}
+ return type;
}
/**
- * Given some [CompilationUnitElement], this method calls
- * [computeAllSubtypes] on all of the [ClassElement]s in the
- * compilation unit.
+ * Checks if the given type name is used as the type in an as expression.
*
- * @param unitElement the compilation unit element
+ * @param typeName the type name to analyzer
+ * @return `true` if the given type name is used as the type in an as expression
*/
- void _computeSubtypesInCompilationUnit(CompilationUnitElement unitElement) {
- List<ClassElement> classElements = unitElement.types;
- for (ClassElement classElement in classElements) {
- _computeSubtypesInClass(classElement);
+ bool _isTypeNameInAsExpression(TypeName typeName) {
+ AstNode parent = typeName.parent;
+ if (parent is AsExpression) {
+ return identical(parent.type, typeName);
}
+ return false;
}
/**
- * Given some [LibraryElement], this method calls
- * [computeAllSubtypes] on all of the [ClassElement]s in the
- * compilation unit, and itself for all imported and exported libraries. All visited libraries are
- * added to the [visitedLibraries] set.
+ * Checks if the given type name is used as the exception type in a catch clause.
*
- * @param libraryElement the library element
+ * @param typeName the type name to analyzer
+ * @return `true` if the given type name is used as the exception type in a catch clause
*/
- void _computeSubtypesInLibrary(LibraryElement libraryElement) {
- if (libraryElement == null || _visitedLibraries.contains(libraryElement)) {
- return;
- }
- _visitedLibraries.add(libraryElement);
- _computeSubtypesInCompilationUnit(libraryElement.definingCompilationUnit);
- List<CompilationUnitElement> parts = libraryElement.parts;
- for (CompilationUnitElement part in parts) {
- _computeSubtypesInCompilationUnit(part);
- }
- List<LibraryElement> imports = libraryElement.importedLibraries;
- for (LibraryElement importElt in imports) {
- _computeSubtypesInLibrary(importElt.library);
- }
- List<LibraryElement> exports = libraryElement.exportedLibraries;
- for (LibraryElement exportElt in exports) {
- _computeSubtypesInLibrary(exportElt.library);
+ bool _isTypeNameInCatchClause(TypeName typeName) {
+ AstNode parent = typeName.parent;
+ if (parent is CatchClause) {
+ return identical(parent.exceptionType, typeName);
}
+ return false;
}
/**
- * Add some key/ value pair into the [subtypeMap] map.
+ * Checks if the given type name is used as the type in an instance creation expression.
*
- * @param supertypeElement the key for the [subtypeMap] map
- * @param subtypeElement the value for the [subtypeMap] map
+ * @param typeName the type name to analyzer
+ * @return `true` if the given type name is used as the type in an instance creation
+ * expression
*/
- void _putInSubtypeMap(
- ClassElement supertypeElement, ClassElement subtypeElement) {
- HashSet<ClassElement> subtypes = _subtypeMap[supertypeElement];
- if (subtypes == null) {
- subtypes = new HashSet<ClassElement>();
- _subtypeMap[supertypeElement] = subtypes;
+ bool _isTypeNameInInstanceCreationExpression(TypeName typeName) {
+ AstNode parent = typeName.parent;
+ if (parent is ConstructorName &&
+ parent.parent is InstanceCreationExpression) {
+ return parent != null && identical(parent.type, typeName);
}
- subtypes.add(subtypeElement);
+ return false;
}
/**
- * Given some [ClassElement] and a [HashSet<ClassElement>], this method recursively
- * adds all of the subtypes of the [ClassElement] to the passed array.
+ * Checks if the given type name is used as the type in an is expression.
*
- * @param classElement the type to compute the set of subtypes of
- * @param visitedClasses the set of class elements that this method has already recursively seen
- * @param allSubtypes the computed set of subtypes of the passed class element
+ * @param typeName the type name to analyzer
+ * @return `true` if the given type name is used as the type in an is expression
*/
- void _safelyComputeAllSubtypes(ClassElement classElement,
- HashSet<ClassElement> visitedClasses, HashSet<ClassElement> allSubtypes) {
- if (!visitedClasses.add(classElement)) {
- // if this class has already been called on this class element
- return;
- }
- HashSet<ClassElement> subtypes = _subtypeMap[classElement];
- if (subtypes == null) {
- return;
- }
- for (ClassElement subtype in subtypes) {
- _safelyComputeAllSubtypes(subtype, visitedClasses, allSubtypes);
+ bool _isTypeNameInIsExpression(TypeName typeName) {
+ AstNode parent = typeName.parent;
+ if (parent is IsExpression) {
+ return identical(parent.type, typeName);
}
- allSubtypes.addAll(subtypes);
+ return false;
}
-}
-
-/**
- * Instances of the class `ToDoFinder` find to-do comments in Dart code.
- */
-class ToDoFinder {
- /**
- * The error reporter by which to-do comments will be reported.
- */
- final ErrorReporter _errorReporter;
/**
- * Initialize a newly created to-do finder to report to-do comments to the given reporter.
+ * Checks if the given type name used in a type argument list.
*
- * @param errorReporter the error reporter by which to-do comments will be reported
+ * @param typeName the type name to analyzer
+ * @return `true` if the given type name is in a type argument list
*/
- ToDoFinder(this._errorReporter);
+ bool _isTypeNameInTypeArgumentList(TypeName typeName) =>
+ typeName.parent is TypeArgumentList;
/**
- * Search the comments in the given compilation unit for to-do comments and report an error for
- * each.
+ * Records the new Element for a TypeName's Identifier.
*
- * @param unit the compilation unit containing the to-do comments
+ * A null may be passed in to indicate that the element can't be resolved.
+ * (During a re-run of a task, it's important to clear any previous value
+ * of the element.)
*/
- void findIn(CompilationUnit unit) {
- _gatherTodoComments(unit.beginToken);
+ void _setElement(Identifier typeName, Element element) {
+ if (typeName is SimpleIdentifier) {
+ typeName.staticElement = element;
+ } else if (typeName is PrefixedIdentifier) {
+ typeName.identifier.staticElement = element;
+ SimpleIdentifier prefix = typeName.prefix;
+ prefix.staticElement = nameScope.lookup(prefix, definingLibrary);
+ }
}
/**
- * Search the comment tokens reachable from the given token and create errors for each to-do
- * comment.
- *
- * @param token the head of the list of tokens being searched
+ * @return `true` if the name of the given [TypeName] is an built-in identifier.
*/
- void _gatherTodoComments(sc.Token token) {
- while (token != null && token.type != sc.TokenType.EOF) {
- sc.Token commentToken = token.precedingComments;
- while (commentToken != null) {
- if (commentToken.type == sc.TokenType.SINGLE_LINE_COMMENT ||
- commentToken.type == sc.TokenType.MULTI_LINE_COMMENT) {
- _scrapeTodoComment(commentToken);
- }
- commentToken = commentToken.next;
- }
- token = token.next;
- }
+ static bool _isBuiltInIdentifier(TypeName node) {
+ Token token = node.name.beginToken;
+ return token.type == TokenType.KEYWORD;
}
/**
- * Look for user defined tasks in comments and convert them into info level analysis issues.
- *
- * @param commentToken the comment token to analyze
+ * @return `true` if given [TypeName] is used as a type annotation.
*/
- void _scrapeTodoComment(sc.Token commentToken) {
- JavaPatternMatcher matcher =
- new JavaPatternMatcher(TodoCode.TODO_REGEX, commentToken.lexeme);
- if (matcher.find()) {
- int offset =
- commentToken.offset + matcher.start() + matcher.group(1).length;
- int length = matcher.group(2).length;
- _errorReporter.reportErrorForOffset(
- TodoCode.TODO, offset, length, [matcher.group(2)]);
+ static bool _isTypeAnnotation(TypeName node) {
+ AstNode parent = node.parent;
+ if (parent is VariableDeclarationList) {
+ return identical(parent.type, node);
+ } else if (parent is FieldFormalParameter) {
+ return identical(parent.type, node);
+ } else if (parent is SimpleFormalParameter) {
+ return identical(parent.type, node);
}
+ return false;
}
}
@@ -13267,7 +9211,7 @@ class TypeOverrideManager {
*/
void applyOverrides(Map<VariableElement, DartType> overrides) {
if (currentScope == null) {
- throw new IllegalStateException("Cannot apply overrides without a scope");
+ throw new StateError("Cannot apply overrides without a scope");
}
currentScope.applyOverrides(overrides);
}
@@ -13280,8 +9224,7 @@ class TypeOverrideManager {
*/
Map<VariableElement, DartType> captureLocalOverrides() {
if (currentScope == null) {
- throw new IllegalStateException(
- "Cannot capture local overrides without a scope");
+ throw new StateError("Cannot capture local overrides without a scope");
}
return currentScope.captureLocalOverrides();
}
@@ -13296,8 +9239,7 @@ class TypeOverrideManager {
Map<VariableElement, DartType> captureOverrides(
VariableDeclarationList variableList) {
if (currentScope == null) {
- throw new IllegalStateException(
- "Cannot capture overrides without a scope");
+ throw new StateError("Cannot capture overrides without a scope");
}
return currentScope.captureOverrides(variableList);
}
@@ -13314,7 +9256,7 @@ class TypeOverrideManager {
*/
void exitScope() {
if (currentScope == null) {
- throw new IllegalStateException("No scope to exit");
+ throw new StateError("No scope to exit");
}
currentScope = currentScope._outerScope;
}
@@ -13328,7 +9270,7 @@ class TypeOverrideManager {
*/
DartType getBestType(VariableElement element) {
DartType bestType = getType(element);
- return bestType == null ? element.type : bestType;
+ return bestType ?? element.type;
}
/**
@@ -13353,7 +9295,9 @@ class TypeOverrideManager {
* the branching, then its propagated type is reset to `null`.
*/
void mergeOverrides(List<Map<VariableElement, DartType>> perBranchOverrides) {
- for (Map<VariableElement, DartType> branch in perBranchOverrides) {
+ int length = perBranchOverrides.length;
+ for (int i = 0; i < length; i++) {
+ Map<VariableElement, DartType> branch = perBranchOverrides[i];
branch.forEach((VariableElement variable, DartType branchType) {
DartType currentType = currentScope.getType(variable);
if (currentType != branchType) {
@@ -13371,7 +9315,7 @@ class TypeOverrideManager {
*/
void setType(VariableElement element, DartType type) {
if (currentScope == null) {
- throw new IllegalStateException("Cannot override without a scope");
+ throw new StateError("Cannot override without a scope");
}
currentScope.setType(element, type);
}
@@ -13450,19 +9394,13 @@ class TypeOverrideManager_TypeOverrideScope {
* @return the overridden type of the given element
*/
DartType getType(Element element) {
- if (element is PropertyAccessorElement) {
- element = (element as PropertyAccessorElement).variable;
- }
- DartType type = _overridenTypes[element];
- if (_overridenTypes.containsKey(element)) {
- return type;
- }
- if (type != null) {
+ Element nonAccessor =
+ element is PropertyAccessorElement ? element.variable : element;
+ DartType type = _overridenTypes[nonAccessor];
+ if (_overridenTypes.containsKey(nonAccessor)) {
return type;
- } else if (_outerScope != null) {
- return _outerScope.getType(element);
}
- return null;
+ return type ?? _outerScope?.getType(element);
}
/**
@@ -13484,32 +9422,70 @@ class TypeOverrideManager_TypeOverrideScope {
}
/**
- * Instances of the class `TypeParameterScope` implement the scope defined by the type
- * parameters in a class.
+ * This class resolves bounds of type parameters of classes, class and function
+ * type aliases.
*/
-class TypeParameterScope extends EnclosedScope {
+class TypeParameterBoundsResolver {
+ final TypeProvider typeProvider;
+ final LibraryElement library;
+ final Source source;
+ final AnalysisErrorListener errorListener;
+
+ Scope libraryScope = null;
+ TypeNameResolver typeNameResolver = null;
+
+ TypeParameterBoundsResolver(
+ this.typeProvider, this.library, this.source, this.errorListener);
+
/**
- * Initialize a newly created scope enclosed within another scope.
- *
- * @param enclosingScope the scope in which this scope is lexically enclosed
- * @param typeElement the element representing the type represented by this scope
+ * Resolve bounds of type parameters of classes, class and function type
+ * aliases.
*/
- TypeParameterScope(Scope enclosingScope, ClassElement typeElement)
- : super(enclosingScope) {
- if (typeElement == null) {
- throw new IllegalArgumentException("class element cannot be null");
+ void resolveTypeBounds(CompilationUnit unit) {
+ for (CompilationUnitMember unitMember in unit.declarations) {
+ if (unitMember is ClassDeclaration) {
+ _resolveTypeParameters(unitMember.typeParameters,
+ () => new TypeParameterScope(libraryScope, unitMember.element));
+ } else if (unitMember is ClassTypeAlias) {
+ _resolveTypeParameters(unitMember.typeParameters,
+ () => new TypeParameterScope(libraryScope, unitMember.element));
+ } else if (unitMember is FunctionTypeAlias) {
+ _resolveTypeParameters(unitMember.typeParameters,
+ () => new FunctionTypeScope(libraryScope, unitMember.element));
+ }
}
- _defineTypeParameters(typeElement);
}
- /**
- * Define the type parameters for the class.
- *
- * @param typeElement the element representing the type represented by this scope
- */
- void _defineTypeParameters(ClassElement typeElement) {
- for (TypeParameterElement typeParameter in typeElement.typeParameters) {
- define(typeParameter);
+ void _resolveTypeName(TypeName typeName) {
+ typeName.typeArguments?.arguments?.forEach(_resolveTypeName);
+ typeNameResolver.resolveTypeName(typeName);
+ // TODO(scheglov) report error when don't apply type bounds for type bounds
+ }
+
+ void _resolveTypeParameters(
+ TypeParameterList typeParameters, Scope createTypeParametersScope()) {
+ if (typeParameters != null) {
+ Scope typeParametersScope = null;
+ for (TypeParameter typeParameter in typeParameters.typeParameters) {
+ TypeName bound = typeParameter.bound;
+ if (bound != null) {
+ Element typeParameterElement = typeParameter.name.staticElement;
+ if (typeParameterElement is TypeParameterElementImpl) {
+ if (LibraryElementImpl.hasResolutionCapability(
+ library, LibraryResolutionCapability.resolvedTypeNames)) {
+ bound.type = typeParameterElement.bound;
+ } else {
+ libraryScope ??= new LibraryScope(library);
+ typeParametersScope ??= createTypeParametersScope();
+ typeNameResolver ??= new TypeNameResolver(new TypeSystemImpl(),
+ typeProvider, library, source, errorListener);
+ typeNameResolver.nameScope = typeParametersScope;
+ _resolveTypeName(bound);
+ typeParameterElement.bound = bound.type;
+ }
+ }
+ }
+ }
}
}
}
@@ -13541,37 +9517,22 @@ class TypePromotionManager {
*/
void exitScope() {
if (currentScope == null) {
- throw new IllegalStateException("No scope to exit");
+ throw new StateError("No scope to exit");
}
currentScope = currentScope._outerScope;
}
/**
- * Returns static type of the given variable - declared or promoted.
- *
- * @return the static type of the given variable - declared or promoted
+ * Return the static type of the given [variable] - declared or promoted.
*/
- DartType getStaticType(VariableElement variable) {
- DartType staticType = getType(variable);
- if (staticType == null) {
- staticType = variable.type;
- }
- return staticType;
- }
+ DartType getStaticType(VariableElement variable) =>
+ getType(variable) ?? variable.type;
/**
- * Return the promoted type of the given element, or `null` if the type of the element has
- * not been promoted.
- *
- * @param element the element whose type might have been promoted
- * @return the promoted type of the given element
+ * Return the promoted type of the given [element], or `null` if the type of
+ * the element has not been promoted.
*/
- DartType getType(Element element) {
- if (currentScope == null) {
- return null;
- }
- return currentScope.getType(element);
- }
+ DartType getType(Element element) => currentScope?.getType(element);
/**
* Set the promoted type of the given element to the given type.
@@ -13581,7 +9542,7 @@ class TypePromotionManager {
*/
void setType(Element element, DartType type) {
if (currentScope == null) {
- throw new IllegalStateException("Cannot promote without a scope");
+ throw new StateError("Cannot promote without a scope");
}
currentScope.setType(element, type);
}
@@ -13780,13 +9741,64 @@ abstract class TypeProvider {
* Return the type representing typenames that can't be resolved.
*/
DartType get undefinedType;
+
+ /**
+ * Return 'true' if [id] is the name of a getter on
+ * the Object type.
+ */
+ bool isObjectGetter(String id);
+
+ /**
+ * Return 'true' if [id] is the name of a method or getter on
+ * the Object type.
+ */
+ bool isObjectMember(String id);
+
+ /**
+ * Return 'true' if [id] is the name of a method on
+ * the Object type.
+ */
+ bool isObjectMethod(String id);
+}
+
+/**
+ * Provide common functionality shared by the various TypeProvider
+ * implementations.
+ */
+abstract class TypeProviderBase implements TypeProvider {
+ @override
+ List<InterfaceType> get nonSubtypableTypes => <InterfaceType>[
+ nullType,
+ numType,
+ intType,
+ doubleType,
+ boolType,
+ stringType
+ ];
+
+ @override
+ bool isObjectGetter(String id) {
+ PropertyAccessorElement element = objectType.element.getGetter(id);
+ return (element != null && !element.isStatic);
+ }
+
+ @override
+ bool isObjectMember(String id) {
+ return isObjectGetter(id) || isObjectMethod(id);
+ }
+
+ @override
+ bool isObjectMethod(String id) {
+ MethodElement element = objectType.element.getMethod(id);
+ return (element != null && !element.isStatic);
+ }
}
/**
* Instances of the class `TypeProviderImpl` provide access to types defined by the language
* by looking for those types in the element model for the core library.
*/
-class TypeProviderImpl implements TypeProvider {
+class TypeProviderImpl extends TypeProviderBase {
/**
* The type representing the built-in type 'bool'.
*/
@@ -13901,189 +9913,11 @@ class TypeProviderImpl implements TypeProvider {
* The type representing the built-in type 'Symbol'.
*/
InterfaceType _symbolType;
-
- /**
- * The type representing the built-in type 'Type'.
- */
- InterfaceType _typeType;
-
- /**
- * The type representing typenames that can't be resolved.
- */
- DartType _undefinedType;
-
- /**
- * Initialize a newly created type provider to provide the types defined in
- * the given [coreLibrary] and [asyncLibrary].
- */
- TypeProviderImpl(LibraryElement coreLibrary, LibraryElement asyncLibrary) {
- Namespace coreNamespace =
- new NamespaceBuilder().createPublicNamespaceForLibrary(coreLibrary);
- Namespace asyncNamespace =
- new NamespaceBuilder().createPublicNamespaceForLibrary(asyncLibrary);
- _initializeFrom(coreNamespace, asyncNamespace);
- }
-
- /**
- * Initialize a newly created type provider to provide the types defined in
- * the given [Namespace]s.
- */
- TypeProviderImpl.forNamespaces(
- Namespace coreNamespace, Namespace asyncNamespace) {
- _initializeFrom(coreNamespace, asyncNamespace);
- }
-
- @override
- InterfaceType get boolType => _boolType;
-
- @override
- DartType get bottomType => _bottomType;
-
- @override
- InterfaceType get deprecatedType => _deprecatedType;
-
- @override
- InterfaceType get doubleType => _doubleType;
-
- @override
- DartType get dynamicType => _dynamicType;
-
- @override
- InterfaceType get functionType => _functionType;
-
- @override
- InterfaceType get futureDynamicType => _futureDynamicType;
-
- @override
- InterfaceType get futureNullType => _futureNullType;
-
- @override
- InterfaceType get futureType => _futureType;
-
- @override
- InterfaceType get intType => _intType;
-
- @override
- InterfaceType get iterableDynamicType => _iterableDynamicType;
-
- @override
- InterfaceType get iterableType => _iterableType;
-
- @override
- InterfaceType get listType => _listType;
-
- @override
- InterfaceType get mapType => _mapType;
-
- @override
- List<InterfaceType> get nonSubtypableTypes => <InterfaceType>[
- nullType,
- numType,
- intType,
- doubleType,
- boolType,
- stringType
- ];
-
- @override
- DartObjectImpl get nullObject {
- if (_nullObject == null) {
- _nullObject = new DartObjectImpl(nullType, NullState.NULL_STATE);
- }
- return _nullObject;
- }
-
- @override
- InterfaceType get nullType => _nullType;
-
- @override
- InterfaceType get numType => _numType;
-
- @override
- InterfaceType get objectType => _objectType;
-
- @override
- InterfaceType get stackTraceType => _stackTraceType;
-
- @override
- InterfaceType get streamDynamicType => _streamDynamicType;
-
- @override
- InterfaceType get streamType => _streamType;
-
- @override
- InterfaceType get stringType => _stringType;
-
- @override
- InterfaceType get symbolType => _symbolType;
-
- @override
- InterfaceType get typeType => _typeType;
-
- @override
- DartType get undefinedType => _undefinedType;
-
- /**
- * Return the type with the given name from the given namespace, or `null` if there is no
- * class with the given name.
- *
- * @param namespace the namespace in which to search for the given name
- * @param typeName the name of the type being searched for
- * @return the type that was found
- */
- InterfaceType _getType(Namespace namespace, String typeName) {
- Element element = namespace.get(typeName);
- if (element == null) {
- AnalysisEngine.instance.logger
- .logInformation("No definition of type $typeName");
- return null;
- }
- return (element as ClassElement).type;
- }
-
- /**
- * Initialize the types provided by this type provider from the given
- * [Namespace]s.
- */
- void _initializeFrom(Namespace coreNamespace, Namespace asyncNamespace) {
- _boolType = _getType(coreNamespace, "bool");
- _bottomType = BottomTypeImpl.instance;
- _deprecatedType = _getType(coreNamespace, "Deprecated");
- _doubleType = _getType(coreNamespace, "double");
- _dynamicType = DynamicTypeImpl.instance;
- _functionType = _getType(coreNamespace, "Function");
- _futureType = _getType(asyncNamespace, "Future");
- _intType = _getType(coreNamespace, "int");
- _iterableType = _getType(coreNamespace, "Iterable");
- _listType = _getType(coreNamespace, "List");
- _mapType = _getType(coreNamespace, "Map");
- _nullType = _getType(coreNamespace, "Null");
- _numType = _getType(coreNamespace, "num");
- _objectType = _getType(coreNamespace, "Object");
- _stackTraceType = _getType(coreNamespace, "StackTrace");
- _streamType = _getType(asyncNamespace, "Stream");
- _stringType = _getType(coreNamespace, "String");
- _symbolType = _getType(coreNamespace, "Symbol");
- _typeType = _getType(coreNamespace, "Type");
- _undefinedType = UndefinedTypeImpl.instance;
- _futureDynamicType = _futureType.substitute4(<DartType>[_dynamicType]);
- _futureNullType = _futureType.substitute4(<DartType>[_nullType]);
- _iterableDynamicType = _iterableType.substitute4(<DartType>[_dynamicType]);
- _streamDynamicType = _streamType.substitute4(<DartType>[_dynamicType]);
- }
-}
-
-/**
- * Instances of the class `TypeResolverVisitor` are used to resolve the types associated with
- * the elements in the element model. This includes the types of superclasses, mixins, interfaces,
- * fields, methods, parameters, and local variables. As a side-effect, this also finishes building
- * the type hierarchy.
- */
-class TypeResolverVisitor extends ScopedVisitor {
+
/**
- * The type representing the type 'dynamic'.
+ * The type representing the built-in type 'Type'.
*/
- DartType _dynamicType;
+ InterfaceType _typeType;
/**
* The type representing typenames that can't be resolved.
@@ -14091,949 +9925,663 @@ class TypeResolverVisitor extends ScopedVisitor {
DartType _undefinedType;
/**
- * The flag specifying if currently visited class references 'super' expression.
+ * Initialize a newly created type provider to provide the types defined in
+ * the given [coreLibrary] and [asyncLibrary].
*/
- bool _hasReferenceToSuper = false;
+ TypeProviderImpl(LibraryElement coreLibrary, LibraryElement asyncLibrary) {
+ Namespace coreNamespace =
+ new NamespaceBuilder().createPublicNamespaceForLibrary(coreLibrary);
+ Namespace asyncNamespace =
+ new NamespaceBuilder().createPublicNamespaceForLibrary(asyncLibrary);
+ _initializeFrom(coreNamespace, asyncNamespace);
+ }
/**
- * Initialize a newly created visitor to resolve the nodes in an AST node.
- *
- * [definingLibrary] is the element for the library containing the node being
- * visited.
- * [source] is the source representing the compilation unit containing the
- * node being visited.
- * [typeProvider] is the object used to access the types from the core
- * library.
- * [errorListener] is the error listener that will be informed of any errors
- * that are found during resolution.
- * [nameScope] is the scope used to resolve identifiers in the node that will
- * first be visited. If `null` or unspecified, a new [LibraryScope] will be
- * created based on [definingLibrary] and [typeProvider].
+ * Initialize a newly created type provider to provide the types defined in
+ * the given [Namespace]s.
*/
- TypeResolverVisitor(LibraryElement definingLibrary, Source source,
- TypeProvider typeProvider, AnalysisErrorListener errorListener,
- {Scope nameScope})
- : super(definingLibrary, source, typeProvider, errorListener,
- nameScope: nameScope) {
- _dynamicType = typeProvider.dynamicType;
- _undefinedType = typeProvider.undefinedType;
+ TypeProviderImpl.forNamespaces(
+ Namespace coreNamespace, Namespace asyncNamespace) {
+ _initializeFrom(coreNamespace, asyncNamespace);
}
@override
- Object visitAnnotation(Annotation node) {
- //
- // Visit annotations, if the annotation is @proxy, on a class, and "proxy"
- // resolves to the proxy annotation in dart.core, then create create the
- // ElementAnnotationImpl and set it as the metadata on the enclosing class.
- //
- // Element resolution is done in the ElementResolver, and this work will be
- // done in the general case for all annotations in the ElementResolver.
- // The reason we resolve this particular element early is so that
- // ClassElement.isProxy() returns the correct information during all
- // phases of the ElementResolver.
- //
- super.visitAnnotation(node);
- Identifier identifier = node.name;
- if (identifier.name.endsWith(ElementAnnotationImpl.PROXY_VARIABLE_NAME) &&
- node.parent is ClassDeclaration) {
- Element element = nameScope.lookup(identifier, definingLibrary);
- if (element != null &&
- element.library.isDartCore &&
- element is PropertyAccessorElement) {
- // This is the @proxy from dart.core
- ClassDeclaration classDeclaration = node.parent as ClassDeclaration;
- ElementAnnotationImpl elementAnnotation =
- new ElementAnnotationImpl(element);
- node.elementAnnotation = elementAnnotation;
- (classDeclaration.element as ClassElementImpl).metadata =
- <ElementAnnotationImpl>[elementAnnotation];
- }
- }
- return null;
- }
+ InterfaceType get boolType => _boolType;
@override
- Object visitCatchClause(CatchClause node) {
- super.visitCatchClause(node);
- SimpleIdentifier exception = node.exceptionParameter;
- if (exception != null) {
- // If an 'on' clause is provided the type of the exception parameter is
- // the type in the 'on' clause. Otherwise, the type of the exception
- // parameter is 'Object'.
- TypeName exceptionTypeName = node.exceptionType;
- DartType exceptionType;
- if (exceptionTypeName == null) {
- exceptionType = typeProvider.dynamicType;
- } else {
- exceptionType = _getType(exceptionTypeName);
- }
- _recordType(exception, exceptionType);
- Element element = exception.staticElement;
- if (element is VariableElementImpl) {
- element.type = exceptionType;
- } else {
- // TODO(brianwilkerson) Report the internal error
- }
- }
- SimpleIdentifier stackTrace = node.stackTraceParameter;
- if (stackTrace != null) {
- _recordType(stackTrace, typeProvider.stackTraceType);
- Element element = stackTrace.staticElement;
- if (element is VariableElementImpl) {
- element.type = typeProvider.stackTraceType;
- } else {
- // TODO(brianwilkerson) Report the internal error
- }
- }
- return null;
- }
+ DartType get bottomType => _bottomType;
@override
- Object visitClassDeclaration(ClassDeclaration node) {
- _hasReferenceToSuper = false;
- super.visitClassDeclaration(node);
- ClassElementImpl classElement = _getClassElement(node.name);
- if (classElement != null) {
- classElement.hasReferenceToSuper = _hasReferenceToSuper;
- }
- return null;
- }
+ InterfaceType get deprecatedType => _deprecatedType;
@override
- void visitClassDeclarationInScope(ClassDeclaration node) {
- super.visitClassDeclarationInScope(node);
- ExtendsClause extendsClause = node.extendsClause;
- WithClause withClause = node.withClause;
- ImplementsClause implementsClause = node.implementsClause;
- ClassElementImpl classElement = _getClassElement(node.name);
- InterfaceType superclassType = null;
- if (extendsClause != null) {
- ErrorCode errorCode = (withClause == null
- ? CompileTimeErrorCode.EXTENDS_NON_CLASS
- : CompileTimeErrorCode.MIXIN_WITH_NON_CLASS_SUPERCLASS);
- superclassType = _resolveType(extendsClause.superclass, errorCode,
- CompileTimeErrorCode.EXTENDS_ENUM, errorCode);
- if (!identical(superclassType, typeProvider.objectType)) {
- classElement.validMixin = false;
- }
- }
- if (classElement != null) {
- if (superclassType == null) {
- InterfaceType objectType = typeProvider.objectType;
- if (!identical(classElement.type, objectType)) {
- superclassType = objectType;
- }
- }
- classElement.supertype = superclassType;
- }
- _resolve(classElement, withClause, implementsClause);
- return null;
- }
+ InterfaceType get doubleType => _doubleType;
@override
- void visitClassMembersInScope(ClassDeclaration node) {
- //
- // Process field declarations before constructors and methods so that the
- // types of field formal parameters can be correctly resolved.
- //
- List<ClassMember> nonFields = new List<ClassMember>();
- node.visitChildren(
- new _TypeResolverVisitor_visitClassMembersInScope(this, nonFields));
- int count = nonFields.length;
- for (int i = 0; i < count; i++) {
- nonFields[i].accept(this);
- }
- }
+ DartType get dynamicType => _dynamicType;
@override
- Object visitClassTypeAlias(ClassTypeAlias node) {
- super.visitClassTypeAlias(node);
- ErrorCode errorCode = CompileTimeErrorCode.MIXIN_WITH_NON_CLASS_SUPERCLASS;
- InterfaceType superclassType = _resolveType(node.superclass, errorCode,
- CompileTimeErrorCode.EXTENDS_ENUM, errorCode);
- if (superclassType == null) {
- superclassType = typeProvider.objectType;
- }
- ClassElementImpl classElement = _getClassElement(node.name);
- if (classElement != null) {
- classElement.supertype = superclassType;
- }
- _resolve(classElement, node.withClause, node.implementsClause);
- return null;
- }
+ InterfaceType get functionType => _functionType;
@override
- Object visitConstructorDeclaration(ConstructorDeclaration node) {
- super.visitConstructorDeclaration(node);
- ExecutableElementImpl element = node.element as ExecutableElementImpl;
- if (element == null) {
- ClassDeclaration classNode =
- node.getAncestor((node) => node is ClassDeclaration);
- StringBuffer buffer = new StringBuffer();
- buffer.write("The element for the constructor ");
- buffer.write(node.name == null ? "<unnamed>" : node.name.name);
- buffer.write(" in ");
- if (classNode == null) {
- buffer.write("<unknown class>");
- } else {
- buffer.write(classNode.name.name);
- }
- buffer.write(" in ");
- buffer.write(source.fullName);
- buffer.write(" was not set while trying to resolve types.");
- AnalysisEngine.instance.logger.logError(buffer.toString(),
- new CaughtException(new AnalysisException(), null));
- } else {
- ClassElement definingClass = element.enclosingElement as ClassElement;
- element.returnType = definingClass.type;
- FunctionTypeImpl type = new FunctionTypeImpl(element);
- type.typeArguments = definingClass.type.typeArguments;
- element.type = type;
- }
- return null;
- }
+ InterfaceType get futureDynamicType => _futureDynamicType;
@override
- Object visitDeclaredIdentifier(DeclaredIdentifier node) {
- super.visitDeclaredIdentifier(node);
- DartType declaredType;
- TypeName typeName = node.type;
- if (typeName == null) {
- declaredType = _dynamicType;
- } else {
- declaredType = _getType(typeName);
- }
- LocalVariableElementImpl element = node.element as LocalVariableElementImpl;
- element.type = declaredType;
- return null;
- }
+ InterfaceType get futureNullType => _futureNullType;
@override
- Object visitFieldFormalParameter(FieldFormalParameter node) {
- super.visitFieldFormalParameter(node);
- Element element = node.identifier.staticElement;
- if (element is ParameterElementImpl) {
- ParameterElementImpl parameter = element;
- FormalParameterList parameterList = node.parameters;
- if (parameterList == null) {
- DartType type;
- TypeName typeName = node.type;
- if (typeName == null) {
- element.hasImplicitType = true;
- type = _dynamicType;
- if (parameter is FieldFormalParameterElement) {
- FieldElement fieldElement =
- (parameter as FieldFormalParameterElement).field;
- if (fieldElement != null) {
- type = fieldElement.type;
- }
- }
- } else {
- type = _getType(typeName);
- }
- parameter.type = type;
- } else {
- _setFunctionTypedParameterType(parameter, node.type, node.parameters);
- }
- } else {
- // TODO(brianwilkerson) Report this internal error
- }
- return null;
- }
+ InterfaceType get futureType => _futureType;
@override
- Object visitFunctionDeclaration(FunctionDeclaration node) {
- super.visitFunctionDeclaration(node);
- ExecutableElementImpl element = node.element as ExecutableElementImpl;
- if (element == null) {
- StringBuffer buffer = new StringBuffer();
- buffer.write("The element for the top-level function ");
- buffer.write(node.name);
- buffer.write(" in ");
- buffer.write(source.fullName);
- buffer.write(" was not set while trying to resolve types.");
- AnalysisEngine.instance.logger.logError(buffer.toString(),
- new CaughtException(new AnalysisException(), null));
- }
- element.returnType = _computeReturnType(node.returnType);
- FunctionTypeImpl type = new FunctionTypeImpl(element);
- ClassElement definingClass =
- element.getAncestor((element) => element is ClassElement);
- if (definingClass != null) {
- type.typeArguments = definingClass.type.typeArguments;
+ InterfaceType get intType => _intType;
+
+ @override
+ InterfaceType get iterableDynamicType => _iterableDynamicType;
+
+ @override
+ InterfaceType get iterableType => _iterableType;
+
+ @override
+ InterfaceType get listType => _listType;
+
+ @override
+ InterfaceType get mapType => _mapType;
+
+ @override
+ DartObjectImpl get nullObject {
+ if (_nullObject == null) {
+ _nullObject = new DartObjectImpl(nullType, NullState.NULL_STATE);
}
- element.type = type;
- return null;
+ return _nullObject;
}
@override
- Object visitFunctionTypeAlias(FunctionTypeAlias node) {
- FunctionTypeAliasElementImpl element =
- node.element as FunctionTypeAliasElementImpl;
- super.visitFunctionTypeAlias(node);
- element.returnType = _computeReturnType(node.returnType);
- return null;
- }
+ InterfaceType get nullType => _nullType;
@override
- Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
- super.visitFunctionTypedFormalParameter(node);
- Element element = node.identifier.staticElement;
- if (element is ParameterElementImpl) {
- _setFunctionTypedParameterType(element, node.returnType, node.parameters);
- } else {
- // TODO(brianwilkerson) Report this internal error
- }
- return null;
- }
+ InterfaceType get numType => _numType;
@override
- Object visitMethodDeclaration(MethodDeclaration node) {
- super.visitMethodDeclaration(node);
- ExecutableElementImpl element = node.element as ExecutableElementImpl;
- if (element == null) {
- ClassDeclaration classNode =
- node.getAncestor((node) => node is ClassDeclaration);
- StringBuffer buffer = new StringBuffer();
- buffer.write("The element for the method ");
- buffer.write(node.name.name);
- buffer.write(" in ");
- if (classNode == null) {
- buffer.write("<unknown class>");
- } else {
- buffer.write(classNode.name.name);
- }
- buffer.write(" in ");
- buffer.write(source.fullName);
- buffer.write(" was not set while trying to resolve types.");
- AnalysisEngine.instance.logger.logError(buffer.toString(),
- new CaughtException(new AnalysisException(), null));
- }
- element.returnType = _computeReturnType(node.returnType);
- FunctionTypeImpl type = new FunctionTypeImpl(element);
- ClassElement definingClass =
- element.getAncestor((element) => element is ClassElement);
- if (definingClass != null) {
- type.typeArguments = definingClass.type.typeArguments;
- }
- element.type = type;
- if (element is PropertyAccessorElement) {
- PropertyAccessorElement accessor = element as PropertyAccessorElement;
- PropertyInducingElementImpl variable =
- accessor.variable as PropertyInducingElementImpl;
- if (accessor.isGetter) {
- variable.type = type.baseReturnType;
- } else if (variable.type == null) {
- List<ParameterElement> parameters = type.baseParameters;
- if (parameters != null && parameters.length > 0) {
- variable.type = parameters[0].type;
- }
- }
- }
- return null;
- }
+ InterfaceType get objectType => _objectType;
@override
- Object visitSimpleFormalParameter(SimpleFormalParameter node) {
- super.visitSimpleFormalParameter(node);
- DartType declaredType;
- TypeName typeName = node.type;
- if (typeName == null) {
- declaredType = _dynamicType;
- } else {
- declaredType = _getType(typeName);
- }
- Element element = node.identifier.staticElement;
- if (element is ParameterElement) {
- (element as ParameterElementImpl).type = declaredType;
- } else {
- // TODO(brianwilkerson) Report the internal error.
- }
- return null;
- }
+ InterfaceType get stackTraceType => _stackTraceType;
@override
- Object visitSuperExpression(SuperExpression node) {
- _hasReferenceToSuper = true;
- return super.visitSuperExpression(node);
- }
+ InterfaceType get streamDynamicType => _streamDynamicType;
@override
- Object visitTypeName(TypeName node) {
- super.visitTypeName(node);
- Identifier typeName = node.name;
- TypeArgumentList argumentList = node.typeArguments;
- Element element = nameScope.lookup(typeName, definingLibrary);
+ InterfaceType get streamType => _streamType;
+
+ @override
+ InterfaceType get stringType => _stringType;
+
+ @override
+ InterfaceType get symbolType => _symbolType;
+
+ @override
+ InterfaceType get typeType => _typeType;
+
+ @override
+ DartType get undefinedType => _undefinedType;
+
+ /**
+ * Return the type with the given name from the given namespace, or `null` if there is no
+ * class with the given name.
+ *
+ * @param namespace the namespace in which to search for the given name
+ * @param typeName the name of the type being searched for
+ * @return the type that was found
+ */
+ InterfaceType _getType(Namespace namespace, String typeName) {
+ Element element = namespace.get(typeName);
if (element == null) {
- //
- // Check to see whether the type name is either 'dynamic' or 'void',
- // neither of which are in the name scope and hence will not be found by
- // normal means.
- //
- if (typeName.name == _dynamicType.name) {
- _setElement(typeName, _dynamicType.element);
- if (argumentList != null) {
- // TODO(brianwilkerson) Report this error
-// reporter.reportError(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, node, dynamicType.getName(), 0, argumentList.getArguments().size());
- }
- typeName.staticType = _dynamicType;
- node.type = _dynamicType;
- return null;
- }
- VoidTypeImpl voidType = VoidTypeImpl.instance;
- if (typeName.name == voidType.name) {
- // There is no element for 'void'.
- if (argumentList != null) {
- // TODO(brianwilkerson) Report this error
-// reporter.reportError(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, node, voidType.getName(), 0, argumentList.getArguments().size());
- }
- typeName.staticType = voidType;
- node.type = voidType;
- return null;
- }
- //
- // If not, the look to see whether we might have created the wrong AST
- // structure for a constructor name. If so, fix the AST structure and then
- // proceed.
- //
- AstNode parent = node.parent;
- if (typeName is PrefixedIdentifier &&
- parent is ConstructorName &&
- argumentList == null) {
- ConstructorName name = parent;
- if (name.name == null) {
- PrefixedIdentifier prefixedIdentifier =
- typeName as PrefixedIdentifier;
- SimpleIdentifier prefix = prefixedIdentifier.prefix;
- element = nameScope.lookup(prefix, definingLibrary);
- if (element is PrefixElement) {
- if (parent.parent is InstanceCreationExpression &&
- (parent.parent as InstanceCreationExpression).isConst) {
- // If, if this is a const expression, then generate a
- // CompileTimeErrorCode.CONST_WITH_NON_TYPE error.
- reportErrorForNode(
- CompileTimeErrorCode.CONST_WITH_NON_TYPE,
- prefixedIdentifier.identifier,
- [prefixedIdentifier.identifier.name]);
- } else {
- // Else, if this expression is a new expression, report a
- // NEW_WITH_NON_TYPE warning.
- reportErrorForNode(
- StaticWarningCode.NEW_WITH_NON_TYPE,
- prefixedIdentifier.identifier,
- [prefixedIdentifier.identifier.name]);
- }
- _setElement(prefix, element);
- return null;
- } else if (element != null) {
- //
- // Rewrite the constructor name. The parser, when it sees a
- // constructor named "a.b", cannot tell whether "a" is a prefix and
- // "b" is a class name, or whether "a" is a class name and "b" is a
- // constructor name. It arbitrarily chooses the former, but in this
- // case was wrong.
- //
- name.name = prefixedIdentifier.identifier;
- name.period = prefixedIdentifier.period;
- node.name = prefix;
- typeName = prefix;
- }
- }
+ AnalysisEngine.instance.logger
+ .logInformation("No definition of type $typeName");
+ return null;
+ }
+ return (element as ClassElement).type;
+ }
+
+ /**
+ * Initialize the types provided by this type provider from the given
+ * [Namespace]s.
+ */
+ void _initializeFrom(Namespace coreNamespace, Namespace asyncNamespace) {
+ _boolType = _getType(coreNamespace, "bool");
+ _bottomType = BottomTypeImpl.instance;
+ _deprecatedType = _getType(coreNamespace, "Deprecated");
+ _doubleType = _getType(coreNamespace, "double");
+ _dynamicType = DynamicTypeImpl.instance;
+ _functionType = _getType(coreNamespace, "Function");
+ _futureType = _getType(asyncNamespace, "Future");
+ _intType = _getType(coreNamespace, "int");
+ _iterableType = _getType(coreNamespace, "Iterable");
+ _listType = _getType(coreNamespace, "List");
+ _mapType = _getType(coreNamespace, "Map");
+ _nullType = _getType(coreNamespace, "Null");
+ _numType = _getType(coreNamespace, "num");
+ _objectType = _getType(coreNamespace, "Object");
+ _stackTraceType = _getType(coreNamespace, "StackTrace");
+ _streamType = _getType(asyncNamespace, "Stream");
+ _stringType = _getType(coreNamespace, "String");
+ _symbolType = _getType(coreNamespace, "Symbol");
+ _typeType = _getType(coreNamespace, "Type");
+ _undefinedType = UndefinedTypeImpl.instance;
+ _futureDynamicType = _futureType.instantiate(<DartType>[_dynamicType]);
+ _futureNullType = _futureType.instantiate(<DartType>[_nullType]);
+ _iterableDynamicType = _iterableType.instantiate(<DartType>[_dynamicType]);
+ _streamDynamicType = _streamType.instantiate(<DartType>[_dynamicType]);
+ }
+}
+
+/**
+ * Instances of the class `TypeResolverVisitor` are used to resolve the types associated with
+ * the elements in the element model. This includes the types of superclasses, mixins, interfaces,
+ * fields, methods, parameters, and local variables. As a side-effect, this also finishes building
+ * the type hierarchy.
+ */
+class TypeResolverVisitor extends ScopedVisitor {
+ /**
+ * The type representing the type 'dynamic'.
+ */
+ DartType _dynamicType;
+
+ /**
+ * The type representing typenames that can't be resolved.
+ */
+ DartType _undefinedType;
+
+ /**
+ * The flag specifying if currently visited class references 'super' expression.
+ */
+ bool _hasReferenceToSuper = false;
+
+ /**
+ * True if we're analyzing in strong mode.
+ */
+ bool _strongMode;
+
+ /**
+ * Type type system in use for this resolver pass.
+ */
+ TypeSystem _typeSystem;
+
+ /**
+ * The helper to resolve [TypeName]s.
+ */
+ TypeNameResolver _typeNameResolver;
+
+ /**
+ * Initialize a newly created visitor to resolve the nodes in an AST node.
+ *
+ * [definingLibrary] is the element for the library containing the node being
+ * visited.
+ * [source] is the source representing the compilation unit containing the
+ * node being visited.
+ * [typeProvider] is the object used to access the types from the core
+ * library.
+ * [errorListener] is the error listener that will be informed of any errors
+ * that are found during resolution.
+ * [nameScope] is the scope used to resolve identifiers in the node that will
+ * first be visited. If `null` or unspecified, a new [LibraryScope] will be
+ * created based on [definingLibrary] and [typeProvider].
+ */
+ TypeResolverVisitor(LibraryElement definingLibrary, Source source,
+ TypeProvider typeProvider, AnalysisErrorListener errorListener,
+ {Scope nameScope})
+ : super(definingLibrary, source, typeProvider, errorListener,
+ nameScope: nameScope) {
+ _dynamicType = typeProvider.dynamicType;
+ _undefinedType = typeProvider.undefinedType;
+ _strongMode = definingLibrary.context.analysisOptions.strongMode;
+ _typeSystem = TypeSystem.create(definingLibrary.context);
+ _typeNameResolver = new TypeNameResolver(
+ _typeSystem, typeProvider, definingLibrary, source, errorListener);
+ }
+
+ @override
+ Object visitAnnotation(Annotation node) {
+ //
+ // Visit annotations, if the annotation is @proxy, on a class, and "proxy"
+ // resolves to the proxy annotation in dart.core, then resolve the
+ // ElementAnnotation.
+ //
+ // Element resolution is done in the ElementResolver, and this work will be
+ // done in the general case for all annotations in the ElementResolver.
+ // The reason we resolve this particular element early is so that
+ // ClassElement.isProxy() returns the correct information during all
+ // phases of the ElementResolver.
+ //
+ super.visitAnnotation(node);
+ Identifier identifier = node.name;
+ if (identifier.name.endsWith(ElementAnnotationImpl.PROXY_VARIABLE_NAME) &&
+ node.parent is ClassDeclaration) {
+ Element element = nameScope.lookup(identifier, definingLibrary);
+ if (element != null &&
+ element.library.isDartCore &&
+ element is PropertyAccessorElement) {
+ // This is the @proxy from dart.core
+ ElementAnnotationImpl elementAnnotation = node.elementAnnotation;
+ elementAnnotation.element = element;
}
}
- // check element
- bool elementValid = element is! MultiplyDefinedElement;
- if (elementValid &&
- element is! ClassElement &&
- _isTypeNameInInstanceCreationExpression(node)) {
- SimpleIdentifier typeNameSimple = _getTypeSimpleIdentifier(typeName);
- InstanceCreationExpression creation =
- node.parent.parent as InstanceCreationExpression;
- if (creation.isConst) {
- if (element == null) {
- reportErrorForNode(
- CompileTimeErrorCode.UNDEFINED_CLASS, typeNameSimple, [typeName]);
- } else {
- reportErrorForNode(CompileTimeErrorCode.CONST_WITH_NON_TYPE,
- typeNameSimple, [typeName]);
- }
- elementValid = false;
+ return null;
+ }
+
+ @override
+ Object visitCatchClause(CatchClause node) {
+ super.visitCatchClause(node);
+ SimpleIdentifier exception = node.exceptionParameter;
+ if (exception != null) {
+ // If an 'on' clause is provided the type of the exception parameter is
+ // the type in the 'on' clause. Otherwise, the type of the exception
+ // parameter is 'Object'.
+ TypeName exceptionTypeName = node.exceptionType;
+ DartType exceptionType;
+ if (exceptionTypeName == null) {
+ exceptionType = typeProvider.dynamicType;
} else {
- if (element != null) {
- reportErrorForNode(
- StaticWarningCode.NEW_WITH_NON_TYPE, typeNameSimple, [typeName]);
- elementValid = false;
- }
+ exceptionType = _typeNameResolver._getType(exceptionTypeName);
}
- }
- if (elementValid && element == null) {
- // We couldn't resolve the type name.
- // TODO(jwren) Consider moving the check for
- // CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE from the
- // ErrorVerifier, so that we don't have two errors on a built in
- // identifier being used as a class name.
- // See CompileTimeErrorCodeTest.test_builtInIdentifierAsType().
- SimpleIdentifier typeNameSimple = _getTypeSimpleIdentifier(typeName);
- RedirectingConstructorKind redirectingConstructorKind;
- if (_isBuiltInIdentifier(node) && _isTypeAnnotation(node)) {
- reportErrorForNode(CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE,
- typeName, [typeName.name]);
- } else if (typeNameSimple.name == "boolean") {
- reportErrorForNode(
- StaticWarningCode.UNDEFINED_CLASS_BOOLEAN, typeNameSimple, []);
- } else if (_isTypeNameInCatchClause(node)) {
- reportErrorForNode(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, typeName,
- [typeName.name]);
- } else if (_isTypeNameInAsExpression(node)) {
- reportErrorForNode(
- StaticWarningCode.CAST_TO_NON_TYPE, typeName, [typeName.name]);
- } else if (_isTypeNameInIsExpression(node)) {
- reportErrorForNode(StaticWarningCode.TYPE_TEST_WITH_UNDEFINED_NAME,
- typeName, [typeName.name]);
- } else if ((redirectingConstructorKind =
- _getRedirectingConstructorKind(node)) !=
- null) {
- ErrorCode errorCode = (redirectingConstructorKind ==
- RedirectingConstructorKind.CONST
- ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS
- : StaticWarningCode.REDIRECT_TO_NON_CLASS);
- reportErrorForNode(errorCode, typeName, [typeName.name]);
- } else if (_isTypeNameInTypeArgumentList(node)) {
- reportErrorForNode(StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT,
- typeName, [typeName.name]);
+ _recordType(exception, exceptionType);
+ Element element = exception.staticElement;
+ if (element is VariableElementImpl) {
+ element.type = exceptionType;
} else {
- reportErrorForNode(
- StaticWarningCode.UNDEFINED_CLASS, typeName, [typeName.name]);
+ // TODO(brianwilkerson) Report the internal error
}
- elementValid = false;
}
- if (!elementValid) {
- if (element is MultiplyDefinedElement) {
- _setElement(typeName, element);
+ SimpleIdentifier stackTrace = node.stackTraceParameter;
+ if (stackTrace != null) {
+ _recordType(stackTrace, typeProvider.stackTraceType);
+ Element element = stackTrace.staticElement;
+ if (element is VariableElementImpl) {
+ element.type = typeProvider.stackTraceType;
} else {
- _setElement(typeName, _dynamicType.element);
+ // TODO(brianwilkerson) Report the internal error
}
- typeName.staticType = _undefinedType;
- node.type = _undefinedType;
- return null;
}
- DartType type = null;
- if (element is ClassElement) {
- _setElement(typeName, element);
- type = element.type;
- } else if (element is FunctionTypeAliasElement) {
- _setElement(typeName, element);
- type = element.type;
- } else if (element is TypeParameterElement) {
- _setElement(typeName, element);
- type = element.type;
- if (argumentList != null) {
- // Type parameters cannot have type arguments.
- // TODO(brianwilkerson) Report this error.
- // resolver.reportError(ResolverErrorCode.?, keyType);
- }
- } else if (element is MultiplyDefinedElement) {
- List<Element> elements = element.conflictingElements;
- type = _getTypeWhenMultiplyDefined(elements);
- if (type != null) {
- node.type = type;
- }
- } else {
- // The name does not represent a type.
- RedirectingConstructorKind redirectingConstructorKind;
- if (_isTypeNameInCatchClause(node)) {
- reportErrorForNode(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, typeName,
- [typeName.name]);
- } else if (_isTypeNameInAsExpression(node)) {
- reportErrorForNode(
- StaticWarningCode.CAST_TO_NON_TYPE, typeName, [typeName.name]);
- } else if (_isTypeNameInIsExpression(node)) {
- reportErrorForNode(StaticWarningCode.TYPE_TEST_WITH_NON_TYPE, typeName,
- [typeName.name]);
- } else if ((redirectingConstructorKind =
- _getRedirectingConstructorKind(node)) !=
- null) {
- ErrorCode errorCode = (redirectingConstructorKind ==
- RedirectingConstructorKind.CONST
- ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS
- : StaticWarningCode.REDIRECT_TO_NON_CLASS);
- reportErrorForNode(errorCode, typeName, [typeName.name]);
- } else if (_isTypeNameInTypeArgumentList(node)) {
- reportErrorForNode(StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT,
- typeName, [typeName.name]);
- } else {
- AstNode parent = typeName.parent;
- while (parent is TypeName) {
- parent = parent.parent;
- }
- if (parent is ExtendsClause ||
- parent is ImplementsClause ||
- parent is WithClause ||
- parent is ClassTypeAlias) {
- // Ignored. The error will be reported elsewhere.
- } else {
- reportErrorForNode(
- StaticWarningCode.NOT_A_TYPE, typeName, [typeName.name]);
- }
- }
- _setElement(typeName, _dynamicType.element);
- typeName.staticType = _dynamicType;
- node.type = _dynamicType;
- return null;
+ return null;
+ }
+
+ @override
+ Object visitClassDeclaration(ClassDeclaration node) {
+ _hasReferenceToSuper = false;
+ super.visitClassDeclaration(node);
+ ClassElementImpl classElement = _getClassElement(node.name);
+ if (classElement != null) {
+ // Clear this flag, as we just invalidated any inferred member types.
+ classElement.hasBeenInferred = false;
+ classElement.hasReferenceToSuper = _hasReferenceToSuper;
}
- if (argumentList != null) {
- NodeList<TypeName> arguments = argumentList.arguments;
- int argumentCount = arguments.length;
- List<DartType> parameters = _getTypeArguments(type);
- int parameterCount = parameters.length;
- List<DartType> typeArguments = new List<DartType>(parameterCount);
- if (argumentCount == parameterCount) {
- for (int i = 0; i < parameterCount; i++) {
- TypeName argumentTypeName = arguments[i];
- DartType argumentType = _getType(argumentTypeName);
- if (argumentType == null) {
- argumentType = _dynamicType;
- }
- typeArguments[i] = argumentType;
- }
- } else {
- reportErrorForNode(_getInvalidTypeParametersErrorCode(node), node,
- [typeName.name, parameterCount, argumentCount]);
- for (int i = 0; i < parameterCount; i++) {
- typeArguments[i] = _dynamicType;
+ return null;
+ }
+
+ @override
+ void visitClassDeclarationInScope(ClassDeclaration node) {
+ super.visitClassDeclarationInScope(node);
+ ExtendsClause extendsClause = node.extendsClause;
+ WithClause withClause = node.withClause;
+ ImplementsClause implementsClause = node.implementsClause;
+ ClassElementImpl classElement = _getClassElement(node.name);
+ InterfaceType superclassType = null;
+ if (extendsClause != null) {
+ ErrorCode errorCode = (withClause == null
+ ? CompileTimeErrorCode.EXTENDS_NON_CLASS
+ : CompileTimeErrorCode.MIXIN_WITH_NON_CLASS_SUPERCLASS);
+ superclassType = _resolveType(extendsClause.superclass, errorCode,
+ CompileTimeErrorCode.EXTENDS_ENUM, errorCode);
+ }
+ if (classElement != null) {
+ if (superclassType == null) {
+ InterfaceType objectType = typeProvider.objectType;
+ if (!identical(classElement.type, objectType)) {
+ superclassType = objectType;
}
}
- if (type is InterfaceTypeImpl) {
- InterfaceTypeImpl interfaceType = type as InterfaceTypeImpl;
- type = interfaceType.substitute4(typeArguments);
- } else if (type is FunctionTypeImpl) {
- FunctionTypeImpl functionType = type as FunctionTypeImpl;
- type = functionType.substitute3(typeArguments);
+ classElement.supertype = superclassType;
+ }
+ _resolve(classElement, withClause, implementsClause);
+ return null;
+ }
+
+ @override
+ void visitClassMembersInScope(ClassDeclaration node) {
+ node.documentationComment?.accept(this);
+ node.metadata.accept(this);
+ //
+ // Process field declarations before constructors and methods so that the
+ // types of field formal parameters can be correctly resolved.
+ //
+ List<ClassMember> nonFields = new List<ClassMember>();
+ NodeList<ClassMember> members = node.members;
+ int length = members.length;
+ for (int i = 0; i < length; i++) {
+ ClassMember member = members[i];
+ if (member is ConstructorDeclaration) {
+ nonFields.add(member);
} else {
- // TODO(brianwilkerson) Report this internal error.
- }
- } else {
- //
- // Check for the case where there are no type arguments given for a
- // parameterized type.
- //
- List<DartType> parameters = _getTypeArguments(type);
- int parameterCount = parameters.length;
- if (parameterCount > 0) {
- DynamicTypeImpl dynamicType = DynamicTypeImpl.instance;
- List<DartType> arguments = new List<DartType>(parameterCount);
- for (int i = 0; i < parameterCount; i++) {
- arguments[i] = dynamicType;
- }
- type = type.substitute2(arguments, parameters);
+ member.accept(this);
}
}
- typeName.staticType = type;
- node.type = type;
+ int count = nonFields.length;
+ for (int i = 0; i < count; i++) {
+ nonFields[i].accept(this);
+ }
+ }
+
+ @override
+ Object visitClassTypeAlias(ClassTypeAlias node) {
+ super.visitClassTypeAlias(node);
+ ErrorCode errorCode = CompileTimeErrorCode.MIXIN_WITH_NON_CLASS_SUPERCLASS;
+ InterfaceType superclassType = _resolveType(node.superclass, errorCode,
+ CompileTimeErrorCode.EXTENDS_ENUM, errorCode);
+ if (superclassType == null) {
+ superclassType = typeProvider.objectType;
+ }
+ ClassElementImpl classElement = _getClassElement(node.name);
+ if (classElement != null) {
+ classElement.supertype = superclassType;
+ }
+ _resolve(classElement, node.withClause, node.implementsClause);
return null;
}
@override
- Object visitTypeParameter(TypeParameter node) {
- super.visitTypeParameter(node);
- TypeName bound = node.bound;
- if (bound != null) {
- TypeParameterElementImpl typeParameter =
- node.name.staticElement as TypeParameterElementImpl;
- if (typeParameter != null) {
- typeParameter.bound = bound.type;
+ Object visitConstructorDeclaration(ConstructorDeclaration node) {
+ super.visitConstructorDeclaration(node);
+ if (node.element == null) {
+ ClassDeclaration classNode =
+ node.getAncestor((node) => node is ClassDeclaration);
+ StringBuffer buffer = new StringBuffer();
+ buffer.write("The element for the constructor ");
+ buffer.write(node.name == null ? "<unnamed>" : node.name.name);
+ buffer.write(" in ");
+ if (classNode == null) {
+ buffer.write("<unknown class>");
+ } else {
+ buffer.write(classNode.name.name);
}
+ buffer.write(" in ");
+ buffer.write(source.fullName);
+ buffer.write(" was not set while trying to resolve types.");
+ AnalysisEngine.instance.logger.logError(buffer.toString(),
+ new CaughtException(new AnalysisException(), null));
}
return null;
}
@override
- Object visitVariableDeclaration(VariableDeclaration node) {
- super.visitVariableDeclaration(node);
+ Object visitDeclaredIdentifier(DeclaredIdentifier node) {
+ super.visitDeclaredIdentifier(node);
DartType declaredType;
- TypeName typeName = (node.parent as VariableDeclarationList).type;
+ TypeName typeName = node.type;
if (typeName == null) {
declaredType = _dynamicType;
} else {
- declaredType = _getType(typeName);
+ declaredType = _typeNameResolver._getType(typeName);
}
- Element element = node.name.staticElement;
- if (element is VariableElement) {
- (element as VariableElementImpl).type = declaredType;
- if (element is PropertyInducingElement) {
- PropertyInducingElement variableElement = element;
- PropertyAccessorElementImpl getter =
- variableElement.getter as PropertyAccessorElementImpl;
- getter.returnType = declaredType;
- FunctionTypeImpl getterType = new FunctionTypeImpl(getter);
- ClassElement definingClass =
- element.getAncestor((element) => element is ClassElement);
- if (definingClass != null) {
- getterType.typeArguments = definingClass.type.typeArguments;
- }
- getter.type = getterType;
- PropertyAccessorElementImpl setter =
- variableElement.setter as PropertyAccessorElementImpl;
- if (setter != null) {
- List<ParameterElement> parameters = setter.parameters;
- if (parameters.length > 0) {
- (parameters[0] as ParameterElementImpl).type = declaredType;
- }
- setter.returnType = VoidTypeImpl.instance;
- FunctionTypeImpl setterType = new FunctionTypeImpl(setter);
- if (definingClass != null) {
- setterType.typeArguments = definingClass.type.typeArguments;
+ LocalVariableElementImpl element = node.element as LocalVariableElementImpl;
+ element.type = declaredType;
+ return null;
+ }
+
+ @override
+ Object visitFieldFormalParameter(FieldFormalParameter node) {
+ super.visitFieldFormalParameter(node);
+ Element element = node.identifier.staticElement;
+ if (element is ParameterElementImpl) {
+ FormalParameterList parameterList = node.parameters;
+ if (parameterList == null) {
+ DartType type;
+ TypeName typeName = node.type;
+ if (typeName == null) {
+ element.hasImplicitType = true;
+ if (element is FieldFormalParameterElement) {
+ FieldElement fieldElement =
+ (element as FieldFormalParameterElement).field;
+ type = fieldElement?.type;
}
- setter.type = setterType;
+ } else {
+ type = _typeNameResolver._getType(typeName);
}
+ element.type = type ?? _dynamicType;
+ } else {
+ _setFunctionTypedParameterType(element, node.type, node.parameters);
}
} else {
- // TODO(brianwilkerson) Report the internal error.
+ // TODO(brianwilkerson) Report this internal error
}
return null;
}
- /**
- * Given a type name representing the return type of a function, compute the return type of the
- * function.
- *
- * @param returnType the type name representing the return type of the function
- * @return the return type that was computed
- */
- DartType _computeReturnType(TypeName returnType) {
- if (returnType == null) {
- return _dynamicType;
- } else {
- return returnType.type;
+ @override
+ Object visitFunctionDeclaration(FunctionDeclaration node) {
+ super.visitFunctionDeclaration(node);
+ ExecutableElementImpl element = node.element as ExecutableElementImpl;
+ if (element == null) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.write("The element for the top-level function ");
+ buffer.write(node.name);
+ buffer.write(" in ");
+ buffer.write(source.fullName);
+ buffer.write(" was not set while trying to resolve types.");
+ AnalysisEngine.instance.logger.logError(buffer.toString(),
+ new CaughtException(new AnalysisException(), null));
}
+ element.returnType = _computeReturnType(node.returnType);
+ element.type = new FunctionTypeImpl(element);
+ _inferSetterReturnType(element);
+ return null;
}
- /**
- * Return the class element that represents the class whose name was provided.
- *
- * @param identifier the name from the declaration of a class
- * @return the class element that represents the class
- */
- ClassElementImpl _getClassElement(SimpleIdentifier identifier) {
- // TODO(brianwilkerson) Seems like we should be using
- // ClassDeclaration.getElement().
- if (identifier == null) {
- // TODO(brianwilkerson) Report this
- // Internal error: We should never build a class declaration without a
- // name.
- return null;
- }
- Element element = identifier.staticElement;
- if (element is! ClassElementImpl) {
- // TODO(brianwilkerson) Report this
- // Internal error: Failed to create an element for a class declaration.
- return null;
- }
- return element as ClassElementImpl;
+ @override
+ Object visitFunctionTypeAlias(FunctionTypeAlias node) {
+ FunctionTypeAliasElementImpl element =
+ node.element as FunctionTypeAliasElementImpl;
+ super.visitFunctionTypeAlias(node);
+ element.returnType = _computeReturnType(node.returnType);
+ return null;
}
- /**
- * Return an array containing all of the elements associated with the parameters in the given
- * list.
- *
- * @param parameterList the list of parameters whose elements are to be returned
- * @return the elements associated with the parameters
- */
- List<ParameterElement> _getElements(FormalParameterList parameterList) {
- List<ParameterElement> elements = new List<ParameterElement>();
- for (FormalParameter parameter in parameterList.parameters) {
- ParameterElement element =
- parameter.identifier.staticElement as ParameterElement;
- // TODO(brianwilkerson) Understand why the element would be null.
- if (element != null) {
- elements.add(element);
- }
+ @override
+ Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+ super.visitFunctionTypedFormalParameter(node);
+ Element element = node.identifier.staticElement;
+ if (element is ParameterElementImpl) {
+ _setFunctionTypedParameterType(element, node.returnType, node.parameters);
+ } else {
+ // TODO(brianwilkerson) Report this internal error
}
- return elements;
+ return null;
}
- /**
- * The number of type arguments in the given type name does not match the number of parameters in
- * the corresponding class element. Return the error code that should be used to report this
- * error.
- *
- * @param node the type name with the wrong number of type arguments
- * @return the error code that should be used to report that the wrong number of type arguments
- * were provided
- */
- ErrorCode _getInvalidTypeParametersErrorCode(TypeName node) {
- AstNode parent = node.parent;
- if (parent is ConstructorName) {
- parent = parent.parent;
- if (parent is InstanceCreationExpression) {
- if (parent.isConst) {
- return CompileTimeErrorCode.CONST_WITH_INVALID_TYPE_PARAMETERS;
- } else {
- return StaticWarningCode.NEW_WITH_INVALID_TYPE_PARAMETERS;
- }
+ @override
+ Object visitMethodDeclaration(MethodDeclaration node) {
+ super.visitMethodDeclaration(node);
+ ExecutableElementImpl element = node.element as ExecutableElementImpl;
+ if (element == null) {
+ ClassDeclaration classNode =
+ node.getAncestor((node) => node is ClassDeclaration);
+ StringBuffer buffer = new StringBuffer();
+ buffer.write("The element for the method ");
+ buffer.write(node.name.name);
+ buffer.write(" in ");
+ if (classNode == null) {
+ buffer.write("<unknown class>");
+ } else {
+ buffer.write(classNode.name.name);
}
+ buffer.write(" in ");
+ buffer.write(source.fullName);
+ buffer.write(" was not set while trying to resolve types.");
+ AnalysisEngine.instance.logger.logError(buffer.toString(),
+ new CaughtException(new AnalysisException(), null));
}
- return StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS;
- }
-
- /**
- * Checks if the given type name is the target in a redirected constructor.
- *
- * @param typeName the type name to analyze
- * @return some [RedirectingConstructorKind] if the given type name is used as the type in a
- * redirected constructor, or `null` otherwise
- */
- RedirectingConstructorKind _getRedirectingConstructorKind(TypeName typeName) {
- AstNode parent = typeName.parent;
- if (parent is ConstructorName) {
- ConstructorName constructorName = parent as ConstructorName;
- parent = constructorName.parent;
- if (parent is ConstructorDeclaration) {
- if (identical(parent.redirectedConstructor, constructorName)) {
- if (parent.constKeyword != null) {
- return RedirectingConstructorKind.CONST;
- }
- return RedirectingConstructorKind.NORMAL;
+ element.returnType = _computeReturnType(node.returnType);
+ element.type = new FunctionTypeImpl(element);
+ _inferSetterReturnType(element);
+ if (element is PropertyAccessorElement) {
+ PropertyAccessorElement accessor = element as PropertyAccessorElement;
+ PropertyInducingElementImpl variable =
+ accessor.variable as PropertyInducingElementImpl;
+ if (accessor.isGetter) {
+ variable.type = element.returnType;
+ } else if (variable.type == null) {
+ List<ParameterElement> parameters = element.parameters;
+ if (parameters != null && parameters.length > 0) {
+ variable.type = parameters[0].type;
}
}
}
return null;
}
- /**
- * Return the type represented by the given type name.
- *
- * @param typeName the type name representing the type to be returned
- * @return the type represented by the type name
- */
- DartType _getType(TypeName typeName) {
- DartType type = typeName.type;
- if (type == null) {
- return _undefinedType;
+ @override
+ Object visitSimpleFormalParameter(SimpleFormalParameter node) {
+ super.visitSimpleFormalParameter(node);
+ DartType declaredType;
+ TypeName typeName = node.type;
+ if (typeName == null) {
+ declaredType = _dynamicType;
+ } else {
+ declaredType = _typeNameResolver._getType(typeName);
}
- return type;
- }
-
- /**
- * Return the type arguments associated with the given type.
- *
- * @param type the type whole type arguments are to be returned
- * @return the type arguments associated with the given type
- */
- List<DartType> _getTypeArguments(DartType type) {
- if (type is InterfaceType) {
- return type.typeArguments;
- } else if (type is FunctionType) {
- return type.typeArguments;
+ Element element = node.identifier.staticElement;
+ if (element is ParameterElementImpl) {
+ element.type = declaredType;
+ } else {
+ // TODO(brianwilkerson) Report the internal error.
}
- return DartType.EMPTY_LIST;
+ return null;
}
- /**
- * Returns the simple identifier of the given (may be qualified) type name.
- *
- * @param typeName the (may be qualified) qualified type name
- * @return the simple identifier of the given (may be qualified) type name.
- */
- SimpleIdentifier _getTypeSimpleIdentifier(Identifier typeName) {
- if (typeName is SimpleIdentifier) {
- return typeName;
- } else {
- return (typeName as PrefixedIdentifier).identifier;
- }
+ @override
+ Object visitSuperExpression(SuperExpression node) {
+ _hasReferenceToSuper = true;
+ return super.visitSuperExpression(node);
}
- /**
- * Given the multiple elements to which a single name could potentially be resolved, return the
- * single interface type that should be used, or `null` if there is no clear choice.
- *
- * @param elements the elements to which a single name could potentially be resolved
- * @return the single interface type that should be used for the type name
- */
- InterfaceType _getTypeWhenMultiplyDefined(List<Element> elements) {
- InterfaceType type = null;
- for (Element element in elements) {
- if (element is ClassElement) {
- if (type != null) {
- return null;
+ @override
+ Object visitTypeName(TypeName node) {
+ super.visitTypeName(node);
+ _typeNameResolver.nameScope = this.nameScope;
+ _typeNameResolver.resolveTypeName(node);
+ return null;
+ }
+
+ @override
+ Object visitTypeParameter(TypeParameter node) {
+ super.visitTypeParameter(node);
+ AstNode parent2 = node.parent?.parent;
+ if (parent2 is ClassDeclaration ||
+ parent2 is ClassTypeAlias ||
+ parent2 is FunctionTypeAlias) {
+ // Bounds of parameters of classes and function type aliases are
+ // already resolved.
+ } else {
+ TypeName bound = node.bound;
+ if (bound != null) {
+ TypeParameterElementImpl typeParameter =
+ node.name.staticElement as TypeParameterElementImpl;
+ if (typeParameter != null) {
+ typeParameter.bound = bound.type;
}
- type = element.type;
}
}
- return type;
+ return null;
}
- /**
- * Checks if the given type name is used as the type in an as expression.
- *
- * @param typeName the type name to analyzer
- * @return `true` if the given type name is used as the type in an as expression
- */
- bool _isTypeNameInAsExpression(TypeName typeName) {
- AstNode parent = typeName.parent;
- if (parent is AsExpression) {
- AsExpression asExpression = parent;
- return identical(asExpression.type, typeName);
+ @override
+ Object visitVariableDeclaration(VariableDeclaration node) {
+ super.visitVariableDeclaration(node);
+ DartType declaredType;
+ TypeName typeName = (node.parent as VariableDeclarationList).type;
+ if (typeName == null) {
+ declaredType = _dynamicType;
+ } else {
+ declaredType = _typeNameResolver._getType(typeName);
}
- return false;
+ Element element = node.name.staticElement;
+ if (element is VariableElementImpl) {
+ element.type = declaredType;
+ }
+ return null;
}
/**
- * Checks if the given type name is used as the exception type in a catch clause.
+ * Given a type name representing the return type of a function, compute the return type of the
+ * function.
*
- * @param typeName the type name to analyzer
- * @return `true` if the given type name is used as the exception type in a catch clause
+ * @param returnType the type name representing the return type of the function
+ * @return the return type that was computed
*/
- bool _isTypeNameInCatchClause(TypeName typeName) {
- AstNode parent = typeName.parent;
- if (parent is CatchClause) {
- CatchClause catchClause = parent;
- return identical(catchClause.exceptionType, typeName);
+ DartType _computeReturnType(TypeName returnType) {
+ if (returnType == null) {
+ return _dynamicType;
+ } else {
+ return returnType.type;
}
- return false;
}
/**
- * Checks if the given type name is used as the type in an instance creation expression.
+ * Return the class element that represents the class whose name was provided.
*
- * @param typeName the type name to analyzer
- * @return `true` if the given type name is used as the type in an instance creation
- * expression
+ * @param identifier the name from the declaration of a class
+ * @return the class element that represents the class
*/
- bool _isTypeNameInInstanceCreationExpression(TypeName typeName) {
- AstNode parent = typeName.parent;
- if (parent is ConstructorName &&
- parent.parent is InstanceCreationExpression) {
- ConstructorName constructorName = parent;
- return constructorName != null &&
- identical(constructorName.type, typeName);
+ ClassElementImpl _getClassElement(SimpleIdentifier identifier) {
+ // TODO(brianwilkerson) Seems like we should be using
+ // ClassDeclaration.getElement().
+ if (identifier == null) {
+ // TODO(brianwilkerson) Report this
+ // Internal error: We should never build a class declaration without a
+ // name.
+ return null;
}
- return false;
+ Element element = identifier.staticElement;
+ if (element is ClassElementImpl) {
+ return element;
+ }
+ // TODO(brianwilkerson) Report this
+ // Internal error: Failed to create an element for a class declaration.
+ return null;
}
/**
- * Checks if the given type name is used as the type in an is expression.
+ * Return an array containing all of the elements associated with the parameters in the given
+ * list.
*
- * @param typeName the type name to analyzer
- * @return `true` if the given type name is used as the type in an is expression
+ * @param parameterList the list of parameters whose elements are to be returned
+ * @return the elements associated with the parameters
*/
- bool _isTypeNameInIsExpression(TypeName typeName) {
- AstNode parent = typeName.parent;
- if (parent is IsExpression) {
- IsExpression isExpression = parent;
- return identical(isExpression.type, typeName);
+ List<ParameterElement> _getElements(FormalParameterList parameterList) {
+ List<ParameterElement> elements = new List<ParameterElement>();
+ for (FormalParameter parameter in parameterList.parameters) {
+ ParameterElement element =
+ parameter.identifier.staticElement as ParameterElement;
+ // TODO(brianwilkerson) Understand why the element would be null.
+ if (element != null) {
+ elements.add(element);
+ }
}
- return false;
+ return elements;
}
/**
- * Checks if the given type name used in a type argument list.
- *
- * @param typeName the type name to analyzer
- * @return `true` if the given type name is in a type argument list
+ * In strong mode we infer "void" as the setter return type (as void is the
+ * only legal return type for a setter). This allows us to give better
+ * errors later if an invalid type is returned.
*/
- bool _isTypeNameInTypeArgumentList(TypeName typeName) =>
- typeName.parent is TypeArgumentList;
+ void _inferSetterReturnType(ExecutableElementImpl element) {
+ if (_strongMode &&
+ element is PropertyAccessorElementImpl &&
+ element.isSetter &&
+ element.hasImplicitReturnType) {
+ element.returnType = VoidTypeImpl.instance;
+ }
+ }
/**
* Record that the static type of the given node is the given type.
@@ -15069,8 +10617,6 @@ class TypeResolverVisitor extends ScopedVisitor {
CompileTimeErrorCode.MIXIN_OF_NON_CLASS);
if (classElement != null) {
classElement.mixins = mixinTypes;
- classElement.withClauseRange =
- new SourceRange(withClause.offset, withClause.length);
}
}
if (implementsClause != null) {
@@ -15100,7 +10646,7 @@ class TypeResolverVisitor extends ScopedVisitor {
Element element2 = identifier2.staticElement;
if (element != null && element == element2) {
detectedRepeatOnIndex[j] = true;
- reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.IMPLEMENTS_REPEATED, typeName2, [name2]);
}
}
@@ -15125,7 +10671,7 @@ class TypeResolverVisitor extends ScopedVisitor {
if (type is InterfaceType) {
ClassElement element = type.element;
if (element != null && element.isEnum) {
- reportErrorForNode(enumTypeError, typeName);
+ errorReporter.reportErrorForNode(enumTypeError, typeName);
return null;
}
return type;
@@ -15133,10 +10679,10 @@ class TypeResolverVisitor extends ScopedVisitor {
// If the type is not an InterfaceType, then visitTypeName() sets the type
// to be a DynamicTypeImpl
Identifier name = typeName.name;
- if (name.name == sc.Keyword.DYNAMIC.syntax) {
- reportErrorForNode(dynamicTypeError, name, [name.name]);
- } else {
- reportErrorForNode(nonTypeError, name, [name.name]);
+ if (name.name == Keyword.DYNAMIC.syntax) {
+ errorReporter.reportErrorForNode(dynamicTypeError, name, [name.name]);
+ } else if (!nameScope.shouldIgnoreUndefined(name)) {
+ errorReporter.reportErrorForNode(nonTypeError, name, [name.name]);
}
return null;
}
@@ -15167,22 +10713,6 @@ class TypeResolverVisitor extends ScopedVisitor {
return types;
}
- void _setElement(Identifier typeName, Element element) {
- if (element != null) {
- if (typeName is SimpleIdentifier) {
- typeName.staticElement = element;
- } else if (typeName is PrefixedIdentifier) {
- PrefixedIdentifier identifier = typeName;
- identifier.identifier.staticElement = element;
- SimpleIdentifier prefix = identifier.prefix;
- Element prefixElement = nameScope.lookup(prefix, definingLibrary);
- if (prefixElement != null) {
- prefix.staticElement = prefixElement;
- }
- }
- }
- }
-
/**
* Given a parameter element, create a function type based on the given return type and parameter
* list and associate the created type with the element.
@@ -15194,196 +10724,14 @@ class TypeResolverVisitor extends ScopedVisitor {
void _setFunctionTypedParameterType(ParameterElementImpl element,
TypeName returnType, FormalParameterList parameterList) {
List<ParameterElement> parameters = _getElements(parameterList);
- FunctionTypeAliasElementImpl aliasElement =
- new FunctionTypeAliasElementImpl.forNode(null);
- aliasElement.synthetic = true;
- aliasElement.shareParameters(parameters);
- aliasElement.returnType = _computeReturnType(returnType);
- // FunctionTypeAliasElementImpl assumes the enclosing element is a
- // CompilationUnitElement (because non-synthetic function types can only be
- // declared at top level), so to avoid breaking things, go find the
- // compilation unit element.
- aliasElement.enclosingElement =
- element.getAncestor((element) => element is CompilationUnitElement);
- FunctionTypeImpl type = new FunctionTypeImpl.forTypedef(aliasElement);
- ClassElement definingClass =
- element.getAncestor((element) => element is ClassElement);
- if (definingClass != null) {
- aliasElement.shareTypeParameters(definingClass.typeParameters);
- type.typeArguments = definingClass.type.typeArguments;
- } else {
- FunctionTypeAliasElement alias =
- element.getAncestor((element) => element is FunctionTypeAliasElement);
- while (alias != null && alias.isSynthetic) {
- alias =
- alias.getAncestor((element) => element is FunctionTypeAliasElement);
- }
- if (alias != null) {
- aliasElement.typeParameters = alias.typeParameters;
- type.typeArguments = alias.type.typeArguments;
- } else {
- type.typeArguments = DartType.EMPTY_LIST;
- }
- }
- element.type = type;
- }
-
- /**
- * @return `true` if the name of the given [TypeName] is an built-in identifier.
- */
- static bool _isBuiltInIdentifier(TypeName node) {
- sc.Token token = node.name.beginToken;
- return token.type == sc.TokenType.KEYWORD;
- }
-
- /**
- * @return `true` if given [TypeName] is used as a type annotation.
- */
- static bool _isTypeAnnotation(TypeName node) {
- AstNode parent = node.parent;
- if (parent is VariableDeclarationList) {
- return identical(parent.type, node);
- }
- if (parent is FieldFormalParameter) {
- return identical(parent.type, node);
- }
- if (parent is SimpleFormalParameter) {
- return identical(parent.type, node);
- }
- return false;
- }
-}
-
-/**
- * The interface `TypeSystem` defines the behavior of an object representing
- * the type system. This provides a common location to put methods that act on
- * types but may need access to more global data structures, and it paves the
- * way for a possible future where we may wish to make the type system
- * pluggable.
- */
-abstract class TypeSystem {
- /**
- * Compute the least upper bound of two types.
- */
- DartType getLeastUpperBound(
- TypeProvider typeProvider, DartType type1, DartType type2);
-
- /**
- * Return `true` if the [leftType] is assignable to the [rightType] (that is,
- * if leftType <==> rightType).
- */
- bool isAssignableTo(DartType leftType, DartType rightType);
-
- /**
- * Return `true` if the [leftType] is a subtype of the [rightType] (that is,
- * if leftType <: rightType).
- */
- bool isSubtypeOf(DartType leftType, DartType rightType);
-
- /**
- * Create either a strong mode or regular type system based on context.
- */
- static TypeSystem create(AnalysisContext context) {
- return (context.analysisOptions.strongMode)
- ? new StrongTypeSystemImpl()
- : new TypeSystemImpl();
- }
-}
-
-/**
- * Implementation of [TypeSystem] using the rules in the Dart specification.
- */
-class TypeSystemImpl implements TypeSystem {
- TypeSystemImpl();
-
- @override
- DartType getLeastUpperBound(
- TypeProvider typeProvider, DartType type1, DartType type2) {
- // The least upper bound relation is reflexive.
- if (identical(type1, type2)) {
- return type1;
- }
- // The least upper bound of dynamic and any type T is dynamic.
- if (type1.isDynamic) {
- return type1;
- }
- if (type2.isDynamic) {
- return type2;
- }
- // The least upper bound of void and any type T != dynamic is void.
- if (type1.isVoid) {
- return type1;
- }
- if (type2.isVoid) {
- return type2;
- }
- // The least upper bound of bottom and any type T is T.
- if (type1.isBottom) {
- return type2;
- }
- if (type2.isBottom) {
- return type1;
- }
- // Let U be a type variable with upper bound B. The least upper bound of U
- // and a type T is the least upper bound of B and T.
- while (type1 is TypeParameterType) {
- // TODO(paulberry): is this correct in the complex of F-bounded
- // polymorphism?
- DartType bound = (type1 as TypeParameterType).element.bound;
- if (bound == null) {
- bound = typeProvider.objectType;
- }
- type1 = bound;
- }
- while (type2 is TypeParameterType) {
- // TODO(paulberry): is this correct in the context of F-bounded
- // polymorphism?
- DartType bound = (type2 as TypeParameterType).element.bound;
- if (bound == null) {
- bound = typeProvider.objectType;
- }
- type2 = bound;
- }
- // The least upper bound of a function type and an interface type T is the
- // least upper bound of Function and T.
- if (type1 is FunctionType && type2 is InterfaceType) {
- type1 = typeProvider.functionType;
- }
- if (type2 is FunctionType && type1 is InterfaceType) {
- type2 = typeProvider.functionType;
- }
-
- // At this point type1 and type2 should both either be interface types or
- // function types.
- if (type1 is InterfaceType && type2 is InterfaceType) {
- InterfaceType result =
- InterfaceTypeImpl.computeLeastUpperBound(type1, type2);
- if (result == null) {
- return typeProvider.dynamicType;
- }
- return result;
- } else if (type1 is FunctionType && type2 is FunctionType) {
- FunctionType result =
- FunctionTypeImpl.computeLeastUpperBound(type1, type2);
- if (result == null) {
- return typeProvider.functionType;
- }
- return result;
- } else {
- // Should never happen. As a defensive measure, return the dynamic type.
- assert(false);
- return typeProvider.dynamicType;
- }
- }
-
- @override
- bool isAssignableTo(DartType leftType, DartType rightType) {
- return leftType.isAssignableTo(rightType);
- }
-
- @override
- bool isSubtypeOf(DartType leftType, DartType rightType) {
- return leftType.isSubtypeOf(rightType);
+ FunctionElementImpl functionElement = new FunctionElementImpl.forNode(null);
+ functionElement.synthetic = true;
+ functionElement.shareParameters(parameters);
+ functionElement.returnType = _computeReturnType(returnType);
+ functionElement.enclosingElement = element;
+ functionElement.shareTypeParameters(element.typeParameters);
+ element.type = new FunctionTypeImpl(functionElement);
+ functionElement.type = element.type;
}
}
@@ -15544,9 +10892,10 @@ class UnusedLocalElementsVerifier extends RecursiveElementVisitor {
*/
class UsedImportedElements {
/**
- * The set of referenced [PrefixElement]s.
+ * The map of referenced [PrefixElement]s and the [Element]s that they prefix.
*/
- final Set<PrefixElement> prefixes = new HashSet<PrefixElement>();
+ final Map<PrefixElement, List<Element>> prefixMap =
+ new HashMap<PrefixElement, List<Element>>();
/**
* The set of referenced top-level [Element]s.
@@ -15593,7 +10942,9 @@ class UsedLocalElements {
factory UsedLocalElements.merge(List<UsedLocalElements> parts) {
UsedLocalElements result = new UsedLocalElements();
- for (UsedLocalElements part in parts) {
+ int length = parts.length;
+ for (int i = 0; i < length; i++) {
+ UsedLocalElements part = parts[i];
result.elements.addAll(part.elements);
result.catchExceptionElements.addAll(part.catchExceptionElements);
result.catchStackTraceElements.addAll(part.catchStackTraceElements);
@@ -15641,6 +10992,11 @@ class VariableResolverVisitor extends ScopedVisitor {
*/
ExecutableElement _enclosingFunction;
+ /**
+ * Information about local variables in the enclosing function or method.
+ */
+ LocalVariableInfo _localVariableInfo;
+
/**
* Initialize a newly created visitor to resolve the nodes in an AST node.
*
@@ -15662,32 +11018,48 @@ class VariableResolverVisitor extends ScopedVisitor {
: super(definingLibrary, source, typeProvider, errorListener,
nameScope: nameScope);
- /**
- * Initialize a newly created visitor to resolve the nodes in a compilation unit.
- *
- * @param library the library containing the compilation unit being resolved
- * @param source the source representing the compilation unit being visited
- * @param typeProvider the object used to access the types from the core library
- *
- * Deprecated. Please use unnamed constructor instead.
- */
- @deprecated
- VariableResolverVisitor.con1(
- Library library, Source source, TypeProvider typeProvider)
- : this(
- library.libraryElement, source, typeProvider, library.errorListener,
- nameScope: library.libraryScope);
+ @override
+ Object visitBlockFunctionBody(BlockFunctionBody node) {
+ assert(_localVariableInfo != null);
+ return super.visitBlockFunctionBody(node);
+ }
+
+ @override
+ Object visitConstructorDeclaration(ConstructorDeclaration node) {
+ ExecutableElement outerFunction = _enclosingFunction;
+ LocalVariableInfo outerLocalVariableInfo = _localVariableInfo;
+ try {
+ _localVariableInfo ??= new LocalVariableInfo();
+ (node.body as FunctionBodyImpl).localVariableInfo = _localVariableInfo;
+ _enclosingFunction = node.element;
+ return super.visitConstructorDeclaration(node);
+ } finally {
+ _localVariableInfo = outerLocalVariableInfo;
+ _enclosingFunction = outerFunction;
+ }
+ }
@override
Object visitExportDirective(ExportDirective node) => null;
+ @override
+ Object visitExpressionFunctionBody(ExpressionFunctionBody node) {
+ assert(_localVariableInfo != null);
+ return super.visitExpressionFunctionBody(node);
+ }
+
@override
Object visitFunctionDeclaration(FunctionDeclaration node) {
ExecutableElement outerFunction = _enclosingFunction;
+ LocalVariableInfo outerLocalVariableInfo = _localVariableInfo;
try {
+ _localVariableInfo ??= new LocalVariableInfo();
+ (node.functionExpression.body as FunctionBodyImpl).localVariableInfo =
+ _localVariableInfo;
_enclosingFunction = node.element;
return super.visitFunctionDeclaration(node);
} finally {
+ _localVariableInfo = outerLocalVariableInfo;
_enclosingFunction = outerFunction;
}
}
@@ -15696,10 +11068,14 @@ class VariableResolverVisitor extends ScopedVisitor {
Object visitFunctionExpression(FunctionExpression node) {
if (node.parent is! FunctionDeclaration) {
ExecutableElement outerFunction = _enclosingFunction;
+ LocalVariableInfo outerLocalVariableInfo = _localVariableInfo;
try {
+ _localVariableInfo ??= new LocalVariableInfo();
+ (node.body as FunctionBodyImpl).localVariableInfo = _localVariableInfo;
_enclosingFunction = node.element;
return super.visitFunctionExpression(node);
} finally {
+ _localVariableInfo = outerLocalVariableInfo;
_enclosingFunction = outerFunction;
}
} else {
@@ -15713,10 +11089,14 @@ class VariableResolverVisitor extends ScopedVisitor {
@override
Object visitMethodDeclaration(MethodDeclaration node) {
ExecutableElement outerFunction = _enclosingFunction;
+ LocalVariableInfo outerLocalVariableInfo = _localVariableInfo;
try {
+ _localVariableInfo ??= new LocalVariableInfo();
+ (node.body as FunctionBodyImpl).localVariableInfo = _localVariableInfo;
_enclosingFunction = node.element;
return super.visitMethodDeclaration(node);
} finally {
+ _localVariableInfo = outerLocalVariableInfo;
_enclosingFunction = outerFunction;
}
}
@@ -15762,26 +11142,12 @@ class VariableResolverVisitor extends ScopedVisitor {
}
// Must be local or parameter.
ElementKind kind = element.kind;
- if (kind == ElementKind.LOCAL_VARIABLE) {
+ if (kind == ElementKind.LOCAL_VARIABLE || kind == ElementKind.PARAMETER) {
node.staticElement = element;
- LocalVariableElementImpl variableImpl =
- element as LocalVariableElementImpl;
if (node.inSetterContext()) {
- variableImpl.markPotentiallyMutatedInScope();
+ _localVariableInfo.potentiallyMutatedInScope.add(element);
if (element.enclosingElement != _enclosingFunction) {
- variableImpl.markPotentiallyMutatedInClosure();
- }
- }
- } else if (kind == ElementKind.PARAMETER) {
- node.staticElement = element;
- if (node.inSetterContext()) {
- ParameterElementImpl parameterImpl = element as ParameterElementImpl;
- parameterImpl.markPotentiallyMutatedInScope();
- // If we are in some closure, check if it is not the same as where
- // variable is declared.
- if (_enclosingFunction != null &&
- (element.enclosingElement != _enclosingFunction)) {
- parameterImpl.markPotentiallyMutatedInClosure();
+ _localVariableInfo.potentiallyMutatedInClosure.add(element);
}
}
}
@@ -15808,7 +11174,7 @@ class _ConstantVerifier_validateInitializerExpression extends ConstantVisitor {
this.parameterElements,
DeclaredVariables declaredVariables,
{TypeSystem typeSystem})
- : _typeSystem = (typeSystem != null) ? typeSystem : new TypeSystemImpl(),
+ : _typeSystem = typeSystem ?? new TypeSystemImpl(),
super(
new ConstantEvaluationEngine(typeProvider, declaredVariables,
typeSystem: typeSystem),
@@ -15817,7 +11183,9 @@ class _ConstantVerifier_validateInitializerExpression extends ConstantVisitor {
@override
DartObjectImpl visitSimpleIdentifier(SimpleIdentifier node) {
Element element = node.staticElement;
- for (ParameterElement parameterElement in parameterElements) {
+ int length = parameterElements.length;
+ for (int i = 0; i < length; i++) {
+ ParameterElement parameterElement = parameterElements[i];
if (identical(parameterElement, element) && parameterElement != null) {
DartType type = parameterElement.type;
if (type != null) {
@@ -15858,29 +11226,6 @@ class _ConstantVerifier_validateInitializerExpression extends ConstantVisitor {
}
}
-class _ElementBuilder_visitClassDeclaration extends UnifyingAstVisitor<Object> {
- final ElementBuilder builder;
-
- List<ClassMember> nonFields;
-
- _ElementBuilder_visitClassDeclaration(this.builder, this.nonFields) : super();
-
- @override
- Object visitConstructorDeclaration(ConstructorDeclaration node) {
- nonFields.add(node);
- return null;
- }
-
- @override
- Object visitMethodDeclaration(MethodDeclaration node) {
- nonFields.add(node);
- return null;
- }
-
- @override
- Object visitNode(AstNode node) => node.accept(builder);
-}
-
class _ResolverVisitor_isVariableAccessedInClosure
extends RecursiveAstVisitor<Object> {
final Element variable;
@@ -15935,38 +11280,3 @@ class _ResolverVisitor_isVariablePotentiallyMutatedIn
return null;
}
}
-
-class _TypeResolverVisitor_visitClassMembersInScope
- extends UnifyingAstVisitor<Object> {
- final TypeResolverVisitor TypeResolverVisitor_this;
-
- List<ClassMember> nonFields;
-
- _TypeResolverVisitor_visitClassMembersInScope(
- this.TypeResolverVisitor_this, this.nonFields)
- : super();
-
- @override
- Object visitConstructorDeclaration(ConstructorDeclaration node) {
- nonFields.add(node);
- return null;
- }
-
- @override
- Object visitExtendsClause(ExtendsClause node) => null;
-
- @override
- Object visitImplementsClause(ImplementsClause node) => null;
-
- @override
- Object visitMethodDeclaration(MethodDeclaration node) {
- nonFields.add(node);
- return null;
- }
-
- @override
- Object visitNode(AstNode node) => node.accept(TypeResolverVisitor_this);
-
- @override
- Object visitWithClause(WithClause node) => null;
-}
« no previous file with comments | « packages/analyzer/lib/src/generated/parser.dart ('k') | packages/analyzer/lib/src/generated/scanner.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698