| // 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; |
| } |
| } |