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

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

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: packages/analyzer/lib/src/dart/resolver/scope.dart
diff --git a/packages/analyzer/lib/src/dart/resolver/scope.dart b/packages/analyzer/lib/src/dart/resolver/scope.dart
new file mode 100644
index 0000000000000000000000000000000000000000..1fc385e678363b91701920a7489b12d30816a4bf
--- /dev/null
+++ b/packages/analyzer/lib/src/dart/resolver/scope.dart
@@ -0,0 +1,1175 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library analyzer.src.dart.resolver.scope;
+
+import 'dart:collection';
+
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/error/error.dart';
+import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/java_engine.dart';
+import 'package:analyzer/src/generated/source.dart';
+
+/**
+ * The scope defined by a block.
+ */
+class BlockScope extends EnclosedScope {
+ /**
+ * Initialize a newly created scope, enclosed within the [enclosingScope],
+ * based on the given [block].
+ */
+ BlockScope(Scope enclosingScope, Block block) : super(enclosingScope) {
+ if (block == null) {
+ throw new ArgumentError("block cannot be null");
+ }
+ _defineElements(block);
+ }
+
+ void _defineElements(Block block) {
+ for (Element element in elementsInBlock(block)) {
+ define(element);
+ }
+ }
+
+ /**
+ * Return the elements that are declared directly in the given [block]. This
+ * does not include elements declared in nested blocks.
+ */
+ static Iterable<Element> elementsInBlock(Block block) sync* {
+ NodeList<Statement> statements = block.statements;
+ int statementCount = statements.length;
+ for (int i = 0; i < statementCount; i++) {
+ Statement statement = statements[i];
+ if (statement is VariableDeclarationStatement) {
+ NodeList<VariableDeclaration> variables = statement.variables.variables;
+ int variableCount = variables.length;
+ for (int j = 0; j < variableCount; j++) {
+ yield variables[j].element;
+ }
+ } else if (statement is FunctionDeclarationStatement) {
+ yield statement.functionDeclaration.element;
+ }
+ }
+ }
+}
+
+/**
+ * The scope defined by a class.
+ */
+class ClassScope extends EnclosedScope {
+ /**
+ * Initialize a newly created scope, enclosed within the [enclosingScope],
+ * based on the given [classElement].
+ */
+ ClassScope(Scope enclosingScope, ClassElement classElement)
+ : super(enclosingScope) {
+ if (classElement == null) {
+ throw new ArgumentError("class element cannot be null");
+ }
+ _defineMembers(classElement);
+ }
+
+ @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]);
+ }
+ }
+ return super.getErrorForDuplicate(existing, duplicate);
+ }
+
+ /**
+ * Define the instance members defined by the given [classElement].
+ */
+ void _defineMembers(ClassElement classElement) {
+ List<PropertyAccessorElement> accessors = classElement.accessors;
+ int accessorLength = accessors.length;
+ for (int i = 0; i < accessorLength; i++) {
+ define(accessors[i]);
+ }
+ List<MethodElement> methods = classElement.methods;
+ int methodLength = methods.length;
+ for (int i = 0; i < methodLength; i++) {
+ define(methods[i]);
+ }
+ }
+}
+
+/**
+ * The scope defined for the initializers in a constructor.
+ */
+class ConstructorInitializerScope extends EnclosedScope {
+ /**
+ * Initialize a newly created scope, enclosed within the [enclosingScope].
+ */
+ ConstructorInitializerScope(Scope enclosingScope, ConstructorElement element)
+ : super(enclosingScope) {
+ _initializeFieldFormalParameters(element);
+ }
+
+ /**
+ * Initialize the local scope with all of the field formal parameters.
+ */
+ void _initializeFieldFormalParameters(ConstructorElement element) {
+ for (ParameterElement parameter in element.parameters) {
+ if (parameter is FieldFormalParameterElement) {
+ define(parameter);
+ }
+ }
+ }
+}
+
+/**
+ * A scope that is lexically enclosed in another scope.
+ */
+class EnclosedScope extends Scope {
+ /**
+ * The scope in which this scope is lexically enclosed.
+ */
+ @override
+ final Scope enclosingScope;
+
+ /**
+ * Initialize a newly created scope, enclosed within the [enclosingScope].
+ */
+ EnclosedScope(this.enclosingScope);
+
+ @override
+ Element internalLookup(
+ Identifier identifier, String name, LibraryElement referencingLibrary) {
+ Element element = localLookup(name, referencingLibrary);
+ if (element != null) {
+ return element;
+ }
+ // Check enclosing scope.
+ return enclosingScope.internalLookup(identifier, name, referencingLibrary);
+ }
+
+ @override
+ Element _internalLookupPrefixed(PrefixedIdentifier identifier, String prefix,
+ String name, LibraryElement referencingLibrary) {
+ return enclosingScope._internalLookupPrefixed(
+ identifier, prefix, name, referencingLibrary);
+ }
+}
+
+/**
+ * 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 ArgumentError("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;
+ List<ParameterElement> parameters = _functionElement.parameters;
+ int length = parameters.length;
+ for (int i = 0; i < length; i++) {
+ ParameterElement parameter = parameters[i];
+ if (!parameter.isInitializingFormal) {
+ parameterScope.define(parameter);
+ }
+ }
+ }
+
+ /**
+ * Define the type parameters for the function.
+ */
+ void _defineTypeParameters() {
+ Scope typeParameterScope = enclosingScope.enclosingScope;
+ List<TypeParameterElement> typeParameters = _functionElement.typeParameters;
+ int length = typeParameters.length;
+ for (int i = 0; i < length; i++) {
+ TypeParameterElement typeParameter = typeParameters[i];
+ 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);
+ }
+ }
+}
+
+/**
+ * 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;
+
+ /**
+ * Initialize a newly created scope, enclosed within the [outerScope],
+ * representing the given [statement].
+ */
+ const ImplicitLabelScope._(this.outerScope, this.statement);
+
+ /**
+ * Return 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);
+}
+
+/**
+ * A scope in which a single label is defined.
+ */
+class LabelScope {
+ /**
+ * The label scope enclosing this label scope.
+ */
+ final LabelScope _outerScope;
+
+ /**
+ * The label defined in this scope.
+ */
+ final String _label;
+
+ /**
+ * The element to which the label resolves.
+ */
+ final LabelElement element;
+
+ /**
+ * The AST node to which the label resolves.
+ */
+ final AstNode node;
+
+ /**
+ * Initialize a newly created scope, enclosed within the [_outerScope],
+ * representing the label [_label]. The [node] is the AST node the label
+ * resolves to. The [element] is the element the label resolves to.
+ */
+ LabelScope(this._outerScope, this._label, this.node, this.element);
+
+ /**
+ * 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;
+ }
+ return _outerScope?.lookup(targetLabel);
+ }
+}
+
+/**
+ * The scope containing all of the names available from imported libraries.
+ */
+class LibraryImportScope extends Scope {
+ /**
+ * The name of the property containing a list of the elements from the SDK
+ * that conflict with the single name imported from non-SDK libraries. The
+ * value of the property is always of type `List<Element>`.
+ */
+ static const String conflictingSdkElements = 'conflictingSdkElements';
+
+ /**
+ * The element representing the library in which this scope is enclosed.
+ */
+ final LibraryElement _definingLibrary;
+
+ /**
+ * A list of the namespaces representing the names that are available in this scope from imported
+ * libraries.
+ */
+ List<Namespace> _importedNamespaces;
+
+ /**
+ * A table mapping prefixes that have been referenced to a map from the names
+ * that have been referenced to the element associated with the prefixed name.
+ */
+ Map<String, Map<String, Element>> _definedPrefixedNames;
+
+ /**
+ * Initialize a newly created scope representing the names imported into the
+ * [_definingLibrary].
+ */
+ LibraryImportScope(this._definingLibrary) {
+ _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 element = localLookup(name, referencingLibrary);
+ if (element != null) {
+ return element;
+ }
+ element = _lookupInImportedNamespaces(
+ identifier, (Namespace namespace) => namespace.get(name));
+ if (element != null) {
+ defineNameWithoutChecking(name, element);
+ }
+ return element;
+ }
+
+ @override
+ bool shouldIgnoreUndefined(Identifier node) {
+ Iterable<NamespaceCombinator> getShowCombinators(
+ ImportElement importElement) =>
+ importElement.combinators.where((NamespaceCombinator combinator) =>
+ combinator is ShowElementCombinator);
+ if (node is PrefixedIdentifier) {
+ String prefix = node.prefix.name;
+ String name = node.identifier.name;
+ List<ImportElement> imports = _definingLibrary.imports;
+ int count = imports.length;
+ for (int i = 0; i < count; i++) {
+ ImportElement importElement = imports[i];
+ if (importElement.prefix?.name == prefix &&
+ importElement.importedLibrary?.isSynthetic != false) {
+ Iterable<NamespaceCombinator> showCombinators =
+ getShowCombinators(importElement);
+ if (showCombinators.isEmpty) {
+ return true;
+ }
+ for (ShowElementCombinator combinator in showCombinators) {
+ if (combinator.shownNames.contains(name)) {
+ return true;
+ }
+ }
+ }
+ }
+ } else if (node is SimpleIdentifier) {
+ String name = node.name;
+ List<ImportElement> imports = _definingLibrary.imports;
+ int count = imports.length;
+ for (int i = 0; i < count; i++) {
+ ImportElement importElement = imports[i];
+ if (importElement.prefix == null &&
+ importElement.importedLibrary?.isSynthetic != false) {
+ for (ShowElementCombinator combinator
+ in getShowCombinators(importElement)) {
+ if (combinator.shownNames.contains(name)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 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.
+ */
+ 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]);
+ }
+ }
+
+ /**
+ * Add the given [element] to this scope without checking for duplication or
+ * hiding.
+ */
+ void _definePrefixedNameWithoutChecking(
+ String prefix, String name, Element element) {
+ _definedPrefixedNames ??= new HashMap<String, Map<String, Element>>();
+ Map<String, Element> unprefixedNames = _definedPrefixedNames.putIfAbsent(
+ prefix, () => new HashMap<String, Element>());
+ unprefixedNames[name] = element;
+ }
+
+ @override
+ Element _internalLookupPrefixed(PrefixedIdentifier identifier, String prefix,
+ String name, LibraryElement referencingLibrary) {
+ Element element = _localPrefixedLookup(prefix, name);
+ if (element != null) {
+ return element;
+ }
+ element = _lookupInImportedNamespaces(identifier.identifier,
+ (Namespace namespace) => namespace.getPrefixed(prefix, name));
+ if (element != null) {
+ _definePrefixedNameWithoutChecking(prefix, name, element);
+ }
+ return element;
+ }
+
+ /**
+ * Return the element with which the given [prefix] and [name] are associated,
+ * or `null` if the name is not defined within this scope.
+ */
+ Element _localPrefixedLookup(String prefix, String name) {
+ if (_definedPrefixedNames != null) {
+ Map<String, Element> unprefixedNames = _definedPrefixedNames[prefix];
+ if (unprefixedNames != null) {
+ return unprefixedNames[name];
+ }
+ }
+ return null;
+ }
+
+ Element _lookupInImportedNamespaces(
+ Identifier identifier, Element lookup(Namespace namespace)) {
+ Set<Element> sdkElements = new HashSet<Element>();
+ Set<Element> nonSdkElements = new HashSet<Element>();
+ for (int i = 0; i < _importedNamespaces.length; i++) {
+ Element element = lookup(_importedNamespaces[i]);
+ if (element != null) {
+ if (element.library.isInSdk) {
+ sdkElements.add(element);
+ } else {
+ nonSdkElements.add(element);
+ }
+ }
+ }
+ int nonSdkCount = nonSdkElements.length;
+ int sdkCount = sdkElements.length;
+ if (nonSdkCount == 0) {
+ if (sdkCount == 0) {
+ return null;
+ } else if (sdkCount == 1) {
+ return sdkElements.first;
+ }
+ }
+ if (nonSdkCount == 1) {
+ if (sdkCount > 0) {
+ identifier.setProperty(
+ conflictingSdkElements, sdkElements.toList(growable: false));
+ }
+ return nonSdkElements.first;
+ }
+ return new MultiplyDefinedElementImpl(
+ _definingLibrary.context,
+ sdkElements.toList(growable: false),
+ nonSdkElements.toList(growable: false));
+ }
+}
+
+/**
+ * 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
+ * [definingLibrary].
+ */
+ LibraryScope(LibraryElement definingLibrary)
+ : super(new LibraryImportScope(definingLibrary)) {
+ _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 [compilationUnit].
+ */
+ 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
+ * [definingLibrary].
+ */
+ void _defineTopLevelNames(LibraryElement definingLibrary) {
+ for (PrefixElement prefix in definingLibrary.prefixes) {
+ define(prefix);
+ }
+ _defineLocalNames(definingLibrary.definingCompilationUnit);
+ for (CompilationUnitElement compilationUnit in definingLibrary.parts) {
+ _defineLocalNames(compilationUnit);
+ }
+ }
+}
+
+/**
+ * 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 [_definedNames].
+ */
+ Namespace(this._definedNames);
+
+ /**
+ * 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, or `null` if there is no such element.
+ */
+ Element get(String name) => _definedNames[name];
+
+ /**
+ * Return the element in this namespace whose name is the result of combining
+ * the [prefix] and the [name], separated by a period, or `null` if there is
+ * no such element.
+ */
+ Element getPrefixed(String prefix, String name) => null;
+}
+
+/**
+ * The builder 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 [element].
+ */
+ 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> exportedNames = _getExportMapping(exportedLibrary);
+ exportedNames = _applyCombinators(exportedNames, element.combinators);
+ return new Namespace(exportedNames);
+ }
+
+ /**
+ * Create a namespace representing the export namespace of the given [library].
+ */
+ Namespace createExportNamespaceForLibrary(LibraryElement library) {
+ HashMap<String, Element> exportedNames = _getExportMapping(library);
+ return new Namespace(exportedNames);
+ }
+
+ /**
+ * Create a namespace representing the import namespace of the given [element].
+ */
+ 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> exportedNames = _getExportMapping(importedLibrary);
+ exportedNames = _applyCombinators(exportedNames, element.combinators);
+ PrefixElement prefix = element.prefix;
+ if (prefix != null) {
+ return new PrefixedNamespace(prefix.name, exportedNames);
+ }
+ return new Namespace(exportedNames);
+ }
+
+ /**
+ * Create a namespace representing the public namespace of the given
+ * [library].
+ */
+ 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 table of
+ * [definedNames].
+ */
+ void _addAllFromNamespace(
+ Map<String, Element> definedNames, Namespace namespace) {
+ if (namespace != null) {
+ definedNames.addAll(namespace.definedNames);
+ }
+ }
+
+ /**
+ * Add the given [element] to the table of [definedNames] if it has a
+ * publicly visible name.
+ */
+ void _addIfPublic(Map<String, Element> definedNames, Element element) {
+ String name = element.name;
+ if (name != null && !Scope.isPrivateName(name)) {
+ definedNames[name] = element;
+ }
+ }
+
+ /**
+ * Add to the table of [definedNames] all of the public top-level names that
+ * are defined in the given [compilationUnit].
+ * 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 table of
+ * [definedNames].
+ */
+ 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;
+ }
+
+ /**
+ * Create a mapping table representing the export namespace of the given
+ * [library]. The set of [visitedElements] contains the 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.
+ */
+ HashMap<String, Element> _computeExportMapping(
+ LibraryElement library, HashSet<LibraryElement> visitedElements) {
+ 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 =
+ _computeExportMapping(exportedLibrary, visitedElements);
+ exportedNames = _applyCombinators(exportedNames, element.combinators);
+ definedNames.addAll(exportedNames);
+ }
+ }
+ _addAllFromNamespace(
+ definedNames,
+ (library.context as InternalAnalysisContext)
+ .getPublicNamespace(library));
+ return definedNames;
+ } finally {
+ visitedElements.remove(library);
+ }
+ }
+
+ HashMap<String, Element> _getExportMapping(LibraryElement library) {
+ if (library is LibraryElementImpl) {
+ if (library.exportNamespace != null) {
+ return library.exportNamespace.definedNames;
+ } else {
+ HashMap<String, Element> exportMapping =
+ _computeExportMapping(library, new HashSet<LibraryElement>());
+ library.exportNamespace = new Namespace(exportMapping);
+ return exportMapping;
+ }
+ }
+ return _computeExportMapping(library, new HashSet<LibraryElement>());
+ }
+
+ /**
+ * 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;
+ }
+}
+
+/**
+ * A mapping of identifiers to the elements represented by those identifiers.
+ * Namespaces are the building blocks for scopes.
+ */
+class PrefixedNamespace implements Namespace {
+ /**
+ * The prefix that is prepended to each of the defined names.
+ */
+ final String _prefix;
+
+ /**
+ * The length of the prefix.
+ */
+ final int _length;
+
+ /**
+ * 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 names resulting from
+ * prefixing each of the [_definedNames] with the given [_prefix] (and a
+ * period).
+ */
+ PrefixedNamespace(String prefix, this._definedNames)
+ : _prefix = prefix,
+ _length = prefix.length;
+
+ @override
+ Map<String, Element> get definedNames {
+ Map<String, Element> definedNames = <String, Element>{};
+ _definedNames.forEach((String name, Element element) {
+ definedNames["$_prefix.$name"] = element;
+ });
+ return definedNames;
+ }
+
+ @override
+ Element get(String name) {
+ if (name.length > _length && name.startsWith(_prefix)) {
+ if (name.codeUnitAt(_length) == '.'.codeUnitAt(0)) {
+ return _definedNames[name.substring(_length + 1)];
+ }
+ }
+ return null;
+ }
+
+ @override
+ Element getPrefixed(String prefix, String name) {
+ if (prefix == _prefix) {
+ return _definedNames[name];
+ }
+ return null;
+ }
+}
+
+/**
+ * A name scope 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 = null;
+
+ /**
+ * Return the scope in which this scope is lexically enclosed.
+ */
+ Scope get enclosingScope => null;
+
+ /**
+ * Add the given [element] to this scope. If there is already an element with
+ * the given name defined in this scope, then the original element will
+ * continue to be mapped to the name.
+ */
+ void define(Element element) {
+ String name = _getName(element);
+ if (name != null && !name.isEmpty) {
+ _definedNames ??= new HashMap<String, Element>();
+ _definedNames.putIfAbsent(name, () => element);
+ }
+ }
+
+ /**
+ * Add the given [element] to this scope without checking for duplication or
+ * hiding.
+ */
+ void defineNameWithoutChecking(String name, Element element) {
+ _definedNames ??= new HashMap<String, Element>();
+ _definedNames[name] = element;
+ }
+
+ /**
+ * Add the given [element] to this scope without checking for duplication or
+ * hiding.
+ */
+ void defineWithoutChecking(Element element) {
+ _definedNames ??= new HashMap<String, Element>();
+ _definedNames[_getName(element)] = element;
+ }
+
+ /**
+ * 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.
+ * [existing] is the first element to be declared with the conflicting name,
+ * while [duplicate] another element declared with the conflicting name.
+ */
+ 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.
+ */
+ Source getSource(AstNode identifier) {
+ CompilationUnit unit =
+ identifier.getAncestor((node) => node is CompilationUnit);
+ if (unit != null) {
+ CompilationUnitElement unitElement = unit.element;
+ if (unitElement != null) {
+ return unitElement.source;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return the element with which the given [name] is associated, or `null` if
+ * the name is not defined within this scope. The [identifier] is the
+ * identifier node to lookup element for, used to report correct kind of a
+ * problem and associate problem with. The [referencingLibrary] is the library
+ * that contains the reference to the name, used to implement library-level
+ * privacy.
+ */
+ 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. The [referencingLibrary] is the library that
+ * contains the reference to the name, used to implement library-level privacy.
+ */
+ Element localLookup(String name, LibraryElement referencingLibrary) {
+ if (_definedNames != null) {
+ 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. The
+ * [referencingLibrary] is the library that contains the reference to the
+ * name, used to implement library-level privacy.
+ */
+ Element lookup(Identifier identifier, LibraryElement referencingLibrary) {
+ if (identifier is PrefixedIdentifier) {
+ return _internalLookupPrefixed(identifier, identifier.prefix.name,
+ identifier.identifier.name, referencingLibrary);
+ }
+ return internalLookup(identifier, identifier.name, referencingLibrary);
+ }
+
+ /**
+ * Return `true` if the fact that the given [node] is not defined should be
+ * ignored (from the perspective of error reporting). This will be the case if
+ * there is at least one import that defines the node's prefix, and if that
+ * import either has no show combinators or has a show combinator that
+ * explicitly lists the node's name.
+ */
+ bool shouldIgnoreUndefined(Identifier node) {
+ if (enclosingScope != null) {
+ return enclosingScope.shouldIgnoreUndefined(node);
+ }
+ return false;
+ }
+
+ /**
+ * 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;
+ }
+ }
+ return element.name;
+ }
+
+ /**
+ * Return the element with which the given [prefix] and [name] are associated,
+ * or `null` if the name is not defined within this scope. The [identifier] is
+ * the identifier node to lookup element for, used to report correct kind of a
+ * problem and associate problem with. The [referencingLibrary] is the library
+ * that contains the reference to the name, used to implement library-level
+ * privacy.
+ */
+ Element _internalLookupPrefixed(PrefixedIdentifier identifier, String prefix,
+ String name, LibraryElement referencingLibrary);
+
+ /**
+ * 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 scope defined by the type parameters in a class.
+ */
+class TypeParameterScope extends EnclosedScope {
+ /**
+ * Initialize a newly created scope, enclosed within the [enclosingScope],
+ * that defined the type parameters from the given [classElement].
+ */
+ TypeParameterScope(Scope enclosingScope, ClassElement classElement)
+ : super(enclosingScope) {
+ if (classElement == null) {
+ throw new ArgumentError("class element cannot be null");
+ }
+ _defineTypeParameters(classElement);
+ }
+
+ /**
+ * Define the type parameters declared by the [classElement].
+ */
+ void _defineTypeParameters(ClassElement classElement) {
+ for (TypeParameterElement typeParameter in classElement.typeParameters) {
+ define(typeParameter);
+ }
+ }
+}
« no previous file with comments | « packages/analyzer/lib/src/dart/resolver/inheritance_manager.dart ('k') | packages/analyzer/lib/src/dart/scanner/reader.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698