blob: e9461fd856c34229a450bb57a3cc7b121a147aff [file] [log] [blame]
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/resolver/scope.dart';
/// Returns the [Element] exported from the given [LibraryElement].
Element? getExportedElement(LibraryElement? library, String name) {
if (library == null) {
return null;
}
return _getExportNamespaceForLibrary(library)[name];
}
/// Return the [LibraryImportElement] that is referenced by [prefixNode], or
/// `null` if the node does not reference a prefix or if we cannot determine
/// which import is being referenced.
LibraryImportElement? getImportElement(SimpleIdentifier prefixNode) {
var parent = prefixNode.parent;
if (parent is ImportDirective) {
return parent.element;
}
return _getImportElementInfo(prefixNode);
}
/// Returns the export namespace of the given [LibraryElement].
Map<String, Element> _getExportNamespaceForLibrary(LibraryElement library) {
var namespace = NamespaceBuilder().createExportNamespaceForLibrary(library);
return namespace.definedNames;
}
/// Return the [LibraryImportElement] that declared [prefix] and imports [element].
///
/// [libraryElement] - the [LibraryElement] where reference is.
/// [prefix] - the import prefix, maybe `null`.
/// [element] - the referenced element.
LibraryImportElement? _getImportElement(
LibraryElement libraryElement, String prefix, Element element) {
if (element.enclosingElement is! CompilationUnitElement) {
return null;
}
var usedLibrary = element.library;
// find ImportElement that imports used library with used prefix
List<LibraryImportElement>? candidates;
for (var importElement in libraryElement.libraryImports) {
// required library
if (importElement.importedLibrary != usedLibrary) {
continue;
}
// required prefix
var prefixElement = importElement.prefix?.element;
if (prefixElement == null) {
continue;
}
if (prefix != prefixElement.name) {
continue;
}
// no combinators => only possible candidate
if (importElement.combinators.isEmpty) {
return importElement;
}
// OK, we have candidate
candidates ??= [];
candidates.add(importElement);
}
// no candidates, probably element is defined in this library
if (candidates == null) {
return null;
}
// one candidate
if (candidates.length == 1) {
return candidates[0];
}
var importElementsMap = <LibraryImportElement, Set<Element>>{};
// ensure that each ImportElement has set of elements
for (var importElement in candidates) {
if (importElementsMap.containsKey(importElement)) {
continue;
}
var namespace = importElement.namespace;
var elements = Set<Element>.from(namespace.definedNames.values);
importElementsMap[importElement] = elements;
}
// use import namespace to choose correct one
for (var entry in importElementsMap.entries) {
var importElement = entry.key;
var elements = entry.value;
if (elements.contains(element)) {
return importElement;
}
}
// not found
return null;
}
/// Returns the [LibraryImportElement] that is referenced by [prefixNode] with a
/// [PrefixElement], maybe `null`.
LibraryImportElement? _getImportElementInfo(SimpleIdentifier prefixNode) {
// prepare environment
var parent = prefixNode.parent;
var unit = prefixNode.thisOrAncestorOfType<CompilationUnit>();
var libraryElement = unit?.declaredElement?.library;
if (libraryElement == null) {
return null;
}
// prepare used element
Element? usedElement;
if (parent is PrefixedIdentifier) {
var prefixed = parent;
if (prefixed.prefix == prefixNode) {
usedElement = prefixed.staticElement;
}
} else if (parent is MethodInvocation) {
var invocation = parent;
if (invocation.target == prefixNode) {
usedElement = invocation.methodName.staticElement;
}
}
// we need used Element
if (usedElement == null) {
return null;
}
// find ImportElement
var prefix = prefixNode.name;
return _getImportElement(libraryElement, prefix, usedElement);
}