blob: 8228058b4d5b083a9da24252a9111db4c379a523 [file] [log] [blame]
// Copyright (c) 2019, 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:async';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
import 'package:analyzer/src/dart/analysis/file_state_filter.dart';
class TopLevelDeclarations {
final ResolvedUnitResult resolvedUnit;
TopLevelDeclarations(this.resolvedUnit);
DriverBasedAnalysisContext get _analysisContext {
var analysisContext = resolvedUnit.session.analysisContext;
return analysisContext as DriverBasedAnalysisContext;
}
/// Return the first public library that exports (but does not necessary
/// declare) [element].
Future<LibraryElement?> publiclyExporting(Element element,
{Map<Element, LibraryElement?>? resultCache}) async {
if (resultCache?.containsKey(element) ?? false) {
return resultCache![element];
}
var declarationFilePath = element.source?.fullName;
if (declarationFilePath == null) {
return null;
}
var analysisDriver = _analysisContext.driver;
var fsState = analysisDriver.fsState;
await analysisDriver.discoverAvailableFiles();
var declarationFile = fsState.getFileForPath(declarationFilePath);
var declarationPackage = declarationFile.uriProperties.packageName;
for (var file in fsState.knownFiles.toList()) {
var uri = file.uriProperties;
// Only search the package that contains the declaration and its public
// libraries.
if (uri.packageName != declarationPackage || uri.isSrc) {
continue;
}
var elementResult = await analysisDriver.getLibraryByUri(file.uriStr);
if (elementResult is! LibraryElementResult) {
continue;
}
if (_findElement(elementResult.element, element.displayName)
?.nonSynthetic ==
element.nonSynthetic) {
resultCache?[element] = elementResult.element;
return elementResult.element;
}
}
return null;
}
/// Return the first public library that exports (but does not necessary
/// declare) [element].
Future<LibraryElement2?> publiclyExporting2(Element2 element,
{Map<Element2, LibraryElement2?>? resultCache}) async {
if (resultCache?.containsKey(element) ?? false) {
return resultCache![element];
}
var declarationFilePath = element.library2?.firstFragment.source.fullName;
if (declarationFilePath == null) {
return null;
}
var analysisDriver = _analysisContext.driver;
var fsState = analysisDriver.fsState;
await analysisDriver.discoverAvailableFiles();
var declarationFile = fsState.getFileForPath(declarationFilePath);
var declarationPackage = declarationFile.uriProperties.packageName;
for (var file in fsState.knownFiles.toList()) {
var uri = file.uriProperties;
// Only search the package that contains the declaration and its public
// libraries.
if (uri.packageName != declarationPackage || uri.isSrc) {
continue;
}
var elementResult = await analysisDriver.getLibraryByUri(file.uriStr);
if (elementResult is! LibraryElementResult) {
continue;
}
var elementLibrary = elementResult.element2;
if (_findElement2(elementLibrary, element.displayName)?.nonSynthetic2 ==
element.nonSynthetic2) {
resultCache?[element] = elementLibrary;
return elementLibrary;
}
}
return null;
}
/// Return the mapping from a library (that is available to this context) to
/// a top-level declaration that is exported (not necessary declared) by this
/// library, and has the requested base name. For getters and setters the
/// corresponding top-level variable is returned.
Future<Map<LibraryElement, Element>> withName(String baseName) async {
var analysisDriver = _analysisContext.driver;
await analysisDriver.discoverAvailableFiles();
var fsState = analysisDriver.fsState;
var filter = FileStateFilter(
fsState.getFileForPath(resolvedUnit.path),
);
var result = <LibraryElement, Element>{};
for (var file in fsState.knownFiles.toList()) {
if (!filter.shouldInclude(file)) {
continue;
}
var elementResult = await analysisDriver.getLibraryByUri(file.uriStr);
if (elementResult is! LibraryElementResult) {
continue;
}
addElement(result, elementResult.element, baseName);
}
return result;
}
static void addElement(
Map<LibraryElement, Element> result,
LibraryElement libraryElement,
String baseName,
) {
var element = _findElement(libraryElement, baseName);
if (element != null) {
result[libraryElement] = element;
}
}
static Element? _findElement(LibraryElement libraryElement, String name) {
var element = libraryElement.exportNamespace.get(name) ??
libraryElement.exportNamespace.get('$name=');
if (element is PropertyAccessorElement) {
var variable = element.variable2;
if (variable != null) {
return variable;
}
}
return element;
}
static Element2? _findElement2(LibraryElement2 libraryElement, String name) {
var element = libraryElement.exportNamespace.get2(name) ??
libraryElement.exportNamespace.get2('$name=');
if (element is GetterElement) {
var variable = element.variable3;
if (variable != null) {
return variable;
}
} else if (element is SetterElement) {
var variable = element.variable3;
if (variable != null) {
return variable;
}
}
return element;
}
}