| 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>, …, 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;
|
| -}
|
|
|