blob: 34e34b51ec49b13c6f002516f97a1984c1a74e71 [file] [log] [blame]
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:collection';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/generated/engine.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 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].declaredElement;
}
} else if (statement is FunctionDeclarationStatement) {
yield statement.functionDeclaration.declaredElement;
}
}
}
}
/// 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 ArgumentError("class element cannot be null");
}
_defineMembers(classElement);
}
/// 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(String name) {
Element element = localLookup(name);
if (element != null) {
return element;
}
// Check enclosing scope.
return enclosingScope.internalLookup(name);
}
@override
Element _internalLookupPrefixed(String prefix, String name) {
return enclosingScope._internalLookupPrefixed(prefix, name);
}
}
/// The scope defined by an extension.
class ExtensionScope extends EnclosedScope {
/// Initialize a newly created scope, enclosed within the [enclosingScope],
/// that represents the given [_extensionElement].
ExtensionScope(Scope enclosingScope, ExtensionElement extensionElement)
: super(enclosingScope) {
_defineMembers(extensionElement);
}
/// Define the static members defined by the given [extensionElement]. The
/// instance members should only be found if they would be found by normal
/// lookup on `this`.
void _defineMembers(ExtensionElement extensionElement) {
List<PropertyAccessorElement> accessors = extensionElement.accessors;
int accessorLength = accessors.length;
for (int i = 0; i < accessorLength; i++) {
define(accessors[i]);
}
List<MethodElement> methods = extensionElement.methods;
int methodLength = methods.length;
for (int i = 0; i < methodLength; i++) {
define(methods[i]);
}
}
}
/// The scope defined by a function.
class FunctionScope extends EnclosedScope {
/// The element representing the function that defines this scope.
final FunctionTypedElement _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(EnclosedScope(EnclosedScope(enclosingScope))) {
if (_functionElement == null) {
throw 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(EnclosedScope(enclosingScope)) {
_defineTypeParameters();
}
/// Define the parameters for the function type alias.
void defineParameters() {
if (_parametersDefined) {
return;
}
_parametersDefined = true;
for (ParameterElement parameter in _typeElement.function.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 = 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) =>
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 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;
/// Cache of public extensions defined in this library's imported namespaces.
List<ExtensionElement> _extensions;
/// Initialize a newly created scope representing the names imported into the
/// [_definingLibrary].
LibraryImportScope(this._definingLibrary) {
_createImportedNamespaces();
}
@override
List<ExtensionElement> get extensions {
if (_extensions == null) {
_extensions = [];
List<ImportElement> imports = _definingLibrary.imports;
int count = imports.length;
for (int i = 0; i < count; i++) {
for (var element in imports[i].namespace.definedNames.values) {
if (element is ExtensionElement && !_extensions.contains(element)) {
_extensions.add(element);
}
}
}
}
return _extensions;
}
@override
void define(Element element) {
if (!Scope.isPrivateName(element.displayName)) {
super.define(element);
}
}
@override
Element internalLookup(String name) {
return localLookup(name);
}
@override
Element localLookup(String name) {
var element = super.localLookup(name);
if (element != null) {
return element;
}
element = _lookupInImportedNamespaces((namespace) {
return namespace.get(name);
});
if (element != null) {
defineNameWithoutChecking(name, element);
}
return element;
}
@override
bool shouldIgnoreUndefined(Identifier node) {
Iterable<NamespaceCombinator> getShowCombinators(
ImportElement importElement) {
return importElement.combinators.whereType<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() {
List<ImportElement> imports = _definingLibrary.imports;
int count = imports.length;
_importedNamespaces = List<Namespace>(count);
for (int i = 0; i < count; i++) {
_importedNamespaces[i] = imports[i].namespace;
}
}
/// Add the given [element] to this scope without checking for duplication or
/// hiding.
void _definePrefixedNameWithoutChecking(
String prefix, String name, Element element) {
_definedPrefixedNames ??= HashMap<String, Map<String, Element>>();
Map<String, Element> unprefixedNames = _definedPrefixedNames.putIfAbsent(
prefix, () => HashMap<String, Element>());
unprefixedNames[name] = element;
}
@override
Element _internalLookupPrefixed(String prefix, String name) {
Element element = _localPrefixedLookup(prefix, name);
if (element != null) {
return element;
}
element = _lookupInImportedNamespaces(
(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(
Element Function(Namespace namespace) lookup) {
Element result;
bool hasPotentialConflict = false;
for (int i = 0; i < _importedNamespaces.length; i++) {
Element element = lookup(_importedNamespaces[i]);
if (element != null) {
if (result == null || result == element) {
result = element;
} else {
hasPotentialConflict = true;
}
}
}
if (hasPotentialConflict) {
var sdkElements = <Element>{};
var nonSdkElements = <Element>{};
for (int i = 0; i < _importedNamespaces.length; i++) {
Element element = lookup(_importedNamespaces[i]);
if (element != null) {
if (element is NeverElementImpl || element.library.isInSdk) {
sdkElements.add(element);
} else {
nonSdkElements.add(element);
}
}
}
if (sdkElements.length > 1 || nonSdkElements.length > 1) {
var conflictingElements = <Element>[
...sdkElements,
...nonSdkElements,
];
return MultiplyDefinedElementImpl(
_definingLibrary.context,
_definingLibrary.session,
conflictingElements.first.name,
conflictingElements);
}
if (nonSdkElements.isNotEmpty) {
result = nonSdkElements.first;
} else if (sdkElements.isNotEmpty) {
result = sdkElements.first;
}
}
return result;
}
}
/// A scope containing all of the names defined in a given library.
class LibraryScope extends EnclosedScope {
final List<ExtensionElement> _extensions = <ExtensionElement>[];
/// Initialize a newly created scope representing the names defined in the
/// [definingLibrary].
LibraryScope(LibraryElement definingLibrary)
: super(LibraryImportScope(definingLibrary)) {
_defineTopLevelNames(definingLibrary);
// For `dart:core` to be able to pass analysis, it has to have `dynamic`
// added to its library scope. Note that this is not true of, for instance,
// `Object`, because `Object` has a source definition which is not possible
// for `dynamic`.
if (definingLibrary.isDartCore) {
define(DynamicElementImpl.instance);
}
}
@override
List<ExtensionElement> get extensions =>
enclosingScope.extensions.toList()..addAll(_extensions);
/// 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 (ExtensionElement element in compilationUnit.extensions) {
define(element);
_extensions.add(element);
}
for (FunctionElement element in compilationUnit.functions) {
define(element);
}
for (FunctionTypeAliasElement element
in compilationUnit.functionTypeAliases) {
define(element);
}
for (ClassElement element in compilationUnit.mixins) {
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 = Namespace(HashMap<String, Element>());
/// A table mapping names that are defined in this namespace to the element
/// representing the thing declared with that name.
final Map<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;
}
Map<String, Element> exportedNames = _getExportMapping(exportedLibrary);
exportedNames = _applyCombinators(exportedNames, element.combinators);
return Namespace(exportedNames);
}
/// Create a namespace representing the export namespace of the given
/// [library].
Namespace createExportNamespaceForLibrary(LibraryElement library) {
Map<String, Element> exportedNames = _getExportMapping(library);
return 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;
}
Map<String, Element> exportedNames = _getExportMapping(importedLibrary);
exportedNames = _applyCombinators(exportedNames, element.combinators);
PrefixElement prefix = element.prefix;
if (prefix != null) {
return PrefixedNamespace(prefix.name, exportedNames);
}
return Namespace(exportedNames);
}
/// Create a namespace representing the public namespace of the given
/// [library].
Namespace createPublicNamespaceForLibrary(LibraryElement library) {
Map<String, Element> definedNames = HashMap<String, Element>();
_addPublicNames(definedNames, library.definingCompilationUnit);
for (CompilationUnitElement compilationUnit in library.parts) {
_addPublicNames(definedNames, compilationUnit);
}
// For libraries that import `dart:core` with a prefix, we have to add
// `dynamic` to the `dart:core` [Namespace] specially. Note that this is not
// true of, for instance, `Object`, because `Object` has a source definition
// which is not possible for `dynamic`.
if (library.isDartCore) {
definedNames['dynamic'] = DynamicElementImpl.instance;
definedNames['Never'] = NeverTypeImpl.instance.element;
}
return 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 && name.isNotEmpty && !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 (ExtensionElement element in compilationUnit.extensions) {
_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.mixins) {
_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].
Map<String, Element> _applyCombinators(Map<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.instrumentationService
.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.
Map<String, Element> _computeExportMapping(
LibraryElement library, HashSet<LibraryElement> visitedElements) {
visitedElements.add(library);
try {
Map<String, Element> definedNames = 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.
//
Map<String, Element> exportedNames =
_computeExportMapping(exportedLibrary, visitedElements);
exportedNames = _applyCombinators(exportedNames, element.combinators);
definedNames.addAll(exportedNames);
}
}
_addAllFromNamespace(
definedNames,
createPublicNamespaceForLibrary(library),
);
return definedNames;
} finally {
visitedElements.remove(library);
}
}
Map<String, Element> _getExportMapping(LibraryElement library) {
if (library.exportNamespace != null) {
return library.exportNamespace.definedNames;
}
if (library is LibraryElementImpl) {
Map<String, Element> exportMapping =
_computeExportMapping(library, HashSet<LibraryElement>());
library.exportNamespace = Namespace(exportMapping);
return exportMapping;
}
return _computeExportMapping(library, HashSet<LibraryElement>());
}
/// Return a new map of names which has all the names from [definedNames]
/// with exception of [hiddenNames].
Map<String, Element> _hide(
Map<String, Element> definedNames, List<String> hiddenNames) {
Map<String, Element> newNames = 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].
Map<String, Element> _show(
Map<String, Element> definedNames, List<String> shownNames) {
Map<String, Element> newNames = 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.
@override
final Map<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.
Map<String, Element> _definedNames;
/// Return the scope in which this scope is lexically enclosed.
Scope get enclosingScope => null;
/// The list of extensions defined in this scope.
List<ExtensionElement> get extensions =>
enclosingScope == null ? <ExtensionElement>[] : enclosingScope.extensions;
/// 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.isNotEmpty) {
_definedNames ??= 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 ??= HashMap<String, Element>();
_definedNames[name] = element;
}
/// Add the given [element] to this scope without checking for duplication or
/// hiding.
void defineWithoutChecking(Element element) {
_definedNames ??= HashMap<String, Element>();
_definedNames[_getName(element)] = element;
}
/// Return the element with which the given [name] is associated, or `null` if
/// the name is not defined within this scope.
Element internalLookup(String name);
/// 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.
Element localLookup(String name) {
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.prefix.name, identifier.identifier.name);
}
return internalLookup(identifier.name);
}
/// 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.isEmpty) {
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.
Element _internalLookupPrefixed(String prefix, String name);
/// Return `true` if the given [name] is a library-private name.
static bool isPrivateName(String name) =>
name != null && name.startsWith('_');
}
/// The scope defined by the type parameters in an element that defines type
/// parameters.
class TypeParameterScope extends EnclosedScope {
/// Initialize a newly created scope, enclosed within the [enclosingScope],
/// that defines the type parameters from the given [element].
TypeParameterScope(Scope enclosingScope, TypeParameterizedElement element)
: super(enclosingScope) {
if (element == null) {
throw ArgumentError("element cannot be null");
}
_defineTypeParameters(element);
}
/// Define the type parameters declared by the [element].
void _defineTypeParameters(TypeParameterizedElement element) {
for (TypeParameterElement typeParameter in element.typeParameters) {
define(typeParameter);
}
}
}