Revert 'Import Library' quick fix changes.
This basically reverts https://dart-review.googlesource.com/c/sdk/+/103921
because internally IntelliJ does not provide module dependencies yet.
Change-Id: I7717b2841bf3d6391b991875a594c6df9e246ff1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106482
Reviewed-by: Ari Aye <ariaye@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/lib/plugin/edit/fix/fix_dart.dart b/pkg/analysis_server/lib/plugin/edit/fix/fix_dart.dart
index 499c925..fb3c5ec 100644
--- a/pkg/analysis_server/lib/plugin/edit/fix/fix_dart.dart
+++ b/pkg/analysis_server/lib/plugin/edit/fix/fix_dart.dart
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
-import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
@@ -22,10 +21,4 @@
* The workspace in which the fix contributor operates.
*/
ChangeWorkspace get workspace;
-
- /**
- * Return top-level declarations with the [name] in libraries that are
- * available to this context.
- */
- List<TopLevelDeclaration> getTopLevelDeclarations(String name);
}
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index 5941656..99a39bd 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -23,7 +23,6 @@
import 'package:analysis_server/src/services/correction/change_workspace.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analysis_server/src/services/correction/fix/analysis_options/fix_generator.dart';
-import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
import 'package:analysis_server/src/services/correction/fix/manifest/fix_generator.dart';
import 'package:analysis_server/src/services/correction/fix/pubspec/fix_generator.dart';
import 'package:analysis_server/src/services/correction/fix_internal.dart';
@@ -635,16 +634,7 @@
int errorLine = lineInfo.getLocation(error.offset).lineNumber;
if (errorLine == requestLine) {
var workspace = DartChangeWorkspace(server.currentSessions);
- var context =
- new DartFixContextImpl(workspace, result, error, (name) {
- var tracker = server.declarationsTracker;
- var provider = TopLevelDeclarationsProvider(tracker);
- return provider.get(
- result.session.analysisContext,
- result.path,
- name,
- );
- });
+ var context = new DartFixContextImpl(workspace, result, error);
List<Fix> fixes =
await new DartFixContributor().computeFixes(context);
if (fixes.isNotEmpty) {
diff --git a/pkg/analysis_server/lib/src/edit/fix/fix_error_task.dart b/pkg/analysis_server/lib/src/edit/fix/fix_error_task.dart
index 378b3b1..e71fd1d 100644
--- a/pkg/analysis_server/lib/src/edit/fix/fix_error_task.dart
+++ b/pkg/analysis_server/lib/src/edit/fix/fix_error_task.dart
@@ -28,12 +28,7 @@
Future<void> fixError(ResolvedUnitResult result, AnalysisError error) async {
final workspace = DartChangeWorkspace(listener.server.currentSessions);
- final dartContext = new DartFixContextImpl(
- workspace,
- result,
- error,
- (name) => [],
- );
+ final dartContext = new DartFixContextImpl(workspace, result, error);
final processor = new FixProcessor(dartContext);
Fix fix = await processor.computeFix();
final location = listener.locationFor(result, error.offset, error.length);
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
index e1926bd..920eb6a 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
@@ -18,7 +18,6 @@
import 'package:analysis_server/src/services/correction/assist_internal.dart';
import 'package:analysis_server/src/services/correction/change_workspace.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
-import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
import 'package:analysis_server/src/services/correction/fix_internal.dart';
import 'package:analysis_server/src/services/refactoring/refactoring.dart';
import 'package:analyzer/dart/analysis/results.dart';
@@ -195,14 +194,7 @@
int errorLine = lineInfo.getLocation(error.offset).lineNumber - 1;
if (errorLine >= range.start.line && errorLine <= range.end.line) {
var workspace = DartChangeWorkspace(server.currentSessions);
- var context = new DartFixContextImpl(workspace, unit, error, (name) {
- var tracker = server.declarationsTracker;
- return TopLevelDeclarationsProvider(tracker).get(
- unit.session.analysisContext,
- unit.path,
- name,
- );
- });
+ var context = new DartFixContextImpl(workspace, unit, error);
final fixes = await fixContributor.computeFixes(context);
if (fixes.isNotEmpty) {
fixes.sort(Fix.SORT_BY_RELEVANCE);
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index adfc446..7ca77e9 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/plugin/edit/fix/fix_dart.dart';
-import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
import 'package:analysis_server/src/services/correction/fix_internal.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/error/error.dart';
@@ -120,16 +119,7 @@
@override
final AnalysisError error;
- final List<TopLevelDeclaration> Function(String name)
- getTopLevelDeclarationsFunction;
-
- DartFixContextImpl(this.workspace, this.resolveResult, this.error,
- this.getTopLevelDeclarationsFunction);
-
- @override
- List<TopLevelDeclaration> getTopLevelDeclarations(String name) {
- return getTopLevelDeclarationsFunction(name);
- }
+ DartFixContextImpl(this.workspace, this.resolveResult, this.error);
}
/// An enumeration of quick fix kinds found in a Dart file.
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/dart/top_level_declarations.dart b/pkg/analysis_server/lib/src/services/correction/fix/dart/top_level_declarations.dart
deleted file mode 100644
index ef4b987..0000000
--- a/pkg/analysis_server/lib/src/services/correction/fix/dart/top_level_declarations.dart
+++ /dev/null
@@ -1,107 +0,0 @@
-// 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 'package:analyzer/dart/analysis/analysis_context.dart';
-import 'package:analyzer/src/services/available_declarations.dart';
-
-/// Information about a single top-level declaration.
-class TopLevelDeclaration {
- /// The path of the library that exports this declaration.
- final String path;
-
- /// The URI of the library that exports this declaration.
- final Uri uri;
-
- final TopLevelDeclarationKind kind;
-
- final String name;
-
- /// Is `true` if the declaration is exported, not declared in the [path].
- final bool isExported;
-
- TopLevelDeclaration(
- this.path,
- this.uri,
- this.kind,
- this.name,
- this.isExported,
- );
-
- @override
- String toString() => '($path, $uri, $kind, $name, $isExported)';
-}
-
-/// Kind of a top-level declaration.
-///
-/// We don't need it to be precise, just enough to support quick fixes.
-enum TopLevelDeclarationKind { type, function, variable }
-
-class TopLevelDeclarationsProvider {
- final DeclarationsTracker tracker;
-
- TopLevelDeclarationsProvider(this.tracker);
-
- void doTrackerWork() {
- while (tracker.hasWork) {
- tracker.doWork();
- }
- }
-
- List<TopLevelDeclaration> get(
- AnalysisContext analysisContext,
- String path,
- String name,
- ) {
- var declarations = <TopLevelDeclaration>[];
-
- void addDeclarations(Library library) {
- for (var declaration in library.declarations) {
- if (declaration.name != name) continue;
-
- var kind = _getTopKind(declaration.kind);
- if (kind == null) continue;
-
- declarations.add(
- TopLevelDeclaration(
- library.path,
- library.uri,
- kind,
- name,
- declaration.locationLibraryUri != library.uri,
- ),
- );
- }
- }
-
- var declarationsContext = tracker.getContext(analysisContext);
- var libraries = declarationsContext.getLibraries(path);
- libraries.context.forEach(addDeclarations);
- libraries.dependencies.forEach(addDeclarations);
- libraries.sdk.forEach(addDeclarations);
-
- return declarations;
- }
-
- TopLevelDeclarationKind _getTopKind(DeclarationKind kind) {
- switch (kind) {
- case DeclarationKind.CLASS:
- case DeclarationKind.CLASS_TYPE_ALIAS:
- case DeclarationKind.ENUM:
- case DeclarationKind.FUNCTION_TYPE_ALIAS:
- case DeclarationKind.MIXIN:
- return TopLevelDeclarationKind.type;
- break;
- case DeclarationKind.FUNCTION:
- return TopLevelDeclarationKind.function;
- break;
- case DeclarationKind.GETTER:
- case DeclarationKind.SETTER:
- case DeclarationKind.VARIABLE:
- return TopLevelDeclarationKind.variable;
- break;
- default:
- return null;
- }
- }
-}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 4cd9e33..dc43fd4 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -10,7 +10,6 @@
import 'package:analysis_server/plugin/edit/fix/fix_dart.dart';
import 'package:analysis_server/src/services/completion/dart/utilities.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
-import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
import 'package:analysis_server/src/services/correction/levenshtein.dart';
import 'package:analysis_server/src/services/correction/namespace.dart';
import 'package:analysis_server/src/services/correction/strings.dart';
@@ -27,6 +26,7 @@
import 'package:analyzer/error/error.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/dart/analysis/session_helper.dart';
+import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/token.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
@@ -105,11 +105,7 @@
// For each fix, put the fix into the HashMap.
for (int i = 0; i < allAnalysisErrors.length; i++) {
final FixContext fixContextI = new DartFixContextImpl(
- context.workspace,
- context.resolveResult,
- allAnalysisErrors[i],
- (name) => [],
- );
+ context.workspace, context.resolveResult, allAnalysisErrors[i]);
final FixProcessor processorI = new FixProcessor(fixContextI);
final List<Fix> fixesListI = await processorI.compute();
for (Fix f in fixesListI) {
@@ -2416,7 +2412,7 @@
}
// may be there is an existing import,
// but it is with prefix and we don't use this prefix
- var alreadyImportedWithPrefix = new Set<String>();
+ Set<Source> alreadyImportedWithPrefix = new Set<Source>();
for (ImportElement imp in unitLibraryElement.imports) {
// prepare element
LibraryElement libraryElement = imp.importedLibrary;
@@ -2458,7 +2454,7 @@
libraryName = libraryElement.source.shortName;
}
// don't add this library again
- alreadyImportedWithPrefix.add(libraryElement.source.fullName);
+ alreadyImportedWithPrefix.add(libraryElement.source);
// update library
String newShowCode = 'show ${showNames.join(', ')}';
int offset = showCombinator.offset;
@@ -2477,21 +2473,25 @@
}
// Find new top-level declarations.
{
- var declarations = await context.getTopLevelDeclarations(name);
- for (var declaration in declarations) {
+ var declarations = await session.getTopLevelDeclarations(name);
+ for (TopLevelDeclarationInSource declaration in declarations) {
// Check the kind.
- if (!kinds2.contains(declaration.kind)) {
+ if (!kinds2.contains(declaration.declaration.kind)) {
continue;
}
// Check the source.
- if (alreadyImportedWithPrefix.contains(declaration.path)) {
+ Source librarySource = declaration.source;
+ if (alreadyImportedWithPrefix.contains(librarySource)) {
+ continue;
+ }
+ if (!_isSourceVisibleToLibrary(librarySource)) {
continue;
}
// Compute the fix kind.
FixKind fixKind;
- if (declaration.uri.isScheme('dart')) {
+ if (librarySource.isInSystemLibrary) {
fixKind = DartFixKind.IMPORT_LIBRARY_SDK;
- } else if (_isLibSrcPath(declaration.path)) {
+ } else if (_isLibSrcPath(librarySource.fullName)) {
// Bad: non-API.
fixKind = DartFixKind.IMPORT_LIBRARY_PROJECT3;
} else if (declaration.isExported) {
@@ -2503,8 +2503,8 @@
}
// Add the fix.
var relativeURI =
- _getRelativeURIFromLibrary(unitLibraryElement, declaration.path);
- await _addFix_importLibrary(fixKind, declaration.uri, relativeURI);
+ _getRelativeURIFromLibrary(unitLibraryElement, librarySource);
+ await _addFix_importLibrary(fixKind, librarySource.uri, relativeURI);
}
}
}
@@ -4246,20 +4246,21 @@
}
/**
- * Return the relative uri from the passed [library] to the given [path].
- * If the [path] is not in the LibraryElement, `null` is returned.
+ * Return the relative uri from the passed [library] to the passed
+ * [source]. If the [source] is not in the LibraryElement, `null` is returned.
*/
- String _getRelativeURIFromLibrary(LibraryElement library, String path) {
+ String _getRelativeURIFromLibrary(LibraryElement library, Source source) {
var librarySource = library?.librarySource;
if (librarySource == null) {
return null;
}
var pathCtx = resourceProvider.pathContext;
var libraryDirectory = pathCtx.dirname(librarySource.fullName);
- var sourceDirectory = pathCtx.dirname(path);
- if (pathCtx.isWithin(libraryDirectory, path) ||
+ var sourceDirectory = pathCtx.dirname(source.fullName);
+ if (pathCtx.isWithin(libraryDirectory, source.fullName) ||
pathCtx.isWithin(sourceDirectory, libraryDirectory)) {
- String relativeFile = pathCtx.relative(path, from: libraryDirectory);
+ String relativeFile =
+ pathCtx.relative(source.fullName, from: libraryDirectory);
return pathCtx.split(relativeFile).join('/');
}
return null;
@@ -4468,6 +4469,30 @@
return false;
}
+ /**
+ * Return `true` if the [source] can be imported into current library.
+ */
+ bool _isSourceVisibleToLibrary(Source source) {
+ String path = source.fullName;
+
+ var contextRoot = context.resolveResult.session.analysisContext.contextRoot;
+ if (contextRoot == null) {
+ return true;
+ }
+
+ // We don't want to use private libraries of other packages.
+ if (source.uri.isScheme('package') && _isLibSrcPath(path)) {
+ return contextRoot.root.contains(path);
+ }
+
+ // We cannot use relative URIs to reference files outside of our package.
+ if (source.uri.isScheme('file')) {
+ return contextRoot.root.contains(path);
+ }
+
+ return true;
+ }
+
bool _isToListMethodElement(MethodElement method) {
if (method.name != 'toList') {
return false;
diff --git a/pkg/analysis_server/test/analysis_abstract.dart b/pkg/analysis_server/test/analysis_abstract.dart
index 22715a4..f4593b4 100644
--- a/pkg/analysis_server/test/analysis_abstract.dart
+++ b/pkg/analysis_server/test/analysis_abstract.dart
@@ -135,12 +135,6 @@
handleSuccessfulRequest(request, handler: analysisHandler);
}
- void doAllDeclarationsTrackerWork() {
- while (server.declarationsTracker.hasWork) {
- server.declarationsTracker.doWork();
- }
- }
-
/**
* Returns the offset of [search] in [testCode].
* Fails if not found.
diff --git a/pkg/analysis_server/test/edit/fixes_test.dart b/pkg/analysis_server/test/edit/fixes_test.dart
index 22a7245..e80a447 100644
--- a/pkg/analysis_server/test/edit/fixes_test.dart
+++ b/pkg/analysis_server/test/edit/fixes_test.dart
@@ -40,7 +40,6 @@
}
''');
await waitForTasksFinished();
- doAllDeclarationsTrackerWork();
List<AnalysisErrorFixes> errorFixes =
await _getFixesAt('Completer<String>');
expect(errorFixes, hasLength(1));
@@ -144,6 +143,9 @@
bbb: any
''');
+ // Ensure that the target is analyzed as an implicit source.
+ newFile('/aaa/lib/foo.dart', content: 'import "package:bbb/target.dart";');
+
newFolder('/bbb');
newFile('/bbb/.packages', content: '''
bbb:${toUri('/bbb/lib')}
@@ -161,7 +163,6 @@
_addOverlay(testFile, testCode);
await waitForTasksFinished();
- doAllDeclarationsTrackerWork();
List<String> fixes = (await _getFixesAt('Foo()'))
.single
diff --git a/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart b/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
index 4dbfa14..beb7025 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
@@ -7,12 +7,9 @@
import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
import 'package:analysis_server/src/services/correction/change_workspace.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
-import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
import 'package:analysis_server/src/services/correction/fix_internal.dart';
import 'package:analyzer/error/error.dart';
-import 'package:analyzer/src/dart/analysis/byte_store.dart';
import 'package:analyzer/src/dart/error/lint_codes.dart';
-import 'package:analyzer/src/services/available_declarations.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart'
hide AnalysisError;
import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
@@ -253,19 +250,7 @@
/// Computes fixes for the given [error] in [testUnit].
Future<List<Fix>> _computeFixes(AnalysisError error) async {
- var tracker = DeclarationsTracker(MemoryByteStore(), resourceProvider);
- tracker.addContext(driver.analysisContext);
-
- var context = new DartFixContextImpl(
- workspace,
- testAnalysisResult,
- error,
- (name) {
- var provider = TopLevelDeclarationsProvider(tracker);
- provider.doTrackerWork();
- return provider.get(driver.analysisContext, testFile, name);
- },
- );
+ var context = new DartFixContextImpl(workspace, testAnalysisResult, error);
return await new DartFixContributor().computeFixes(context);
}
diff --git a/pkg/analyzer/lib/dart/analysis/session.dart b/pkg/analyzer/lib/dart/analysis/session.dart
index 542661b..6f31a7b 100644
--- a/pkg/analyzer/lib/dart/analysis/session.dart
+++ b/pkg/analyzer/lib/dart/analysis/session.dart
@@ -11,6 +11,7 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/exception/exception.dart';
import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/source.dart';
@@ -121,6 +122,11 @@
/// complete with [SourceKind.UNKNOWN].
Future<SourceKind> getSourceKind(String path);
+ /// Return a future that will complete with a list of the top-level
+ /// declarations with the given [name] in all known libraries.
+ Future<List<TopLevelDeclarationInSource>> getTopLevelDeclarations(
+ String name);
+
/// Return a future that will complete with information about the results of
/// building the element model for the file with the given absolute,
/// normalized[path].
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 2865d23..ead50fe5 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -28,6 +28,7 @@
import 'package:analyzer/src/dart/analysis/search.dart';
import 'package:analyzer/src/dart/analysis/session.dart';
import 'package:analyzer/src/dart/analysis/status.dart';
+import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/engine.dart'
show
@@ -216,6 +217,11 @@
final _referencingNameTasks = <_FilesReferencingNameTask>[];
/**
+ * The list of tasks to compute top-level declarations of a name.
+ */
+ final _topLevelNameDeclarationsTasks = <_TopLevelNameDeclarationsTask>[];
+
+ /**
* The mapping from the files for which the index was requested using
* [getIndex] to the [Completer]s to report the result.
*/
@@ -513,6 +519,9 @@
if (_unitElementRequestedFiles.isNotEmpty) {
return AnalysisDriverPriority.interactive;
}
+ if (_topLevelNameDeclarationsTasks.isNotEmpty) {
+ return AnalysisDriverPriority.interactive;
+ }
if (_priorityFiles.isNotEmpty) {
for (String path in _priorityFiles) {
if (_fileTracker.isFilePending(path)) {
@@ -962,6 +971,19 @@
}
/**
+ * Return a [Future] that completes with top-level declarations with the
+ * given [name] in all known libraries.
+ */
+ Future<List<TopLevelDeclarationInSource>> getTopLevelNameDeclarations(
+ String name) {
+ _discoverAvailableFiles();
+ var task = new _TopLevelNameDeclarationsTask(this, name);
+ _topLevelNameDeclarationsTasks.add(task);
+ _scheduler.notify(this);
+ return task.completer.future;
+ }
+
+ /**
* Return a [Future] that completes with the [UnitElementResult] for the
* file with the given [path], or with `null` if the file cannot be analyzed.
*/
@@ -1186,6 +1208,16 @@
return;
}
+ // Compute top-level declarations.
+ if (_topLevelNameDeclarationsTasks.isNotEmpty) {
+ _TopLevelNameDeclarationsTask task = _topLevelNameDeclarationsTasks.first;
+ bool isDone = task.perform();
+ if (isDone) {
+ _topLevelNameDeclarationsTasks.remove(task);
+ }
+ return;
+ }
+
// Analyze a priority file.
if (_priorityFiles.isNotEmpty) {
for (String path in _priorityFiles) {
@@ -2515,3 +2547,66 @@
return true;
}
}
+
+/**
+ * Task that computes top-level declarations for a certain name in all
+ * known libraries.
+ */
+class _TopLevelNameDeclarationsTask {
+ final AnalysisDriver driver;
+ final String name;
+ final Completer<List<TopLevelDeclarationInSource>> completer =
+ new Completer<List<TopLevelDeclarationInSource>>();
+
+ final List<TopLevelDeclarationInSource> libraryDeclarations =
+ <TopLevelDeclarationInSource>[];
+ final Set<String> checkedFiles = new Set<String>();
+ final List<String> filesToCheck = <String>[];
+
+ _TopLevelNameDeclarationsTask(this.driver, this.name);
+
+ /**
+ * Perform a single piece of work, and either complete the [completer] and
+ * return `true` to indicate that the task is done, return `false` to indicate
+ * that the task should continue to be run.
+ */
+ bool perform() {
+ // Prepare files to check.
+ if (filesToCheck.isEmpty) {
+ filesToCheck.addAll(driver.addedFiles.difference(checkedFiles));
+ filesToCheck.addAll(driver.knownFiles.difference(checkedFiles));
+ }
+
+ // If no more files to check, complete and done.
+ if (filesToCheck.isEmpty) {
+ completer.complete(libraryDeclarations);
+ return true;
+ }
+
+ // Check the next file.
+ String path = filesToCheck.removeLast();
+ if (checkedFiles.add(path)) {
+ FileState file = driver._fsState.getFileForPath(path);
+ if (!file.isPart) {
+ bool isExported = false;
+
+ TopLevelDeclaration declaration;
+ for (FileState part in file.libraryFiles) {
+ declaration ??= part.topLevelDeclarations[name];
+ }
+
+ if (declaration == null) {
+ declaration = file.exportedTopLevelDeclarations[name];
+ isExported = true;
+ }
+ if (declaration != null) {
+ libraryDeclarations.add(new TopLevelDeclarationInSource(
+ file.source, declaration, isExported));
+ }
+ }
+ }
+
+ // We're not done yet.
+ return false;
+ }
+}
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index f716911..665f248 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -18,6 +18,7 @@
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
import 'package:analyzer/src/dart/analysis/referenced_names.dart';
import 'package:analyzer/src/dart/analysis/unlinked_api_signature.dart';
+import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
import 'package:analyzer/src/dart/scanner/reader.dart';
import 'package:analyzer/src/dart/scanner/scanner.dart';
import 'package:analyzer/src/generated/engine.dart';
@@ -84,6 +85,11 @@
* should be called.
*/
class FileState {
+ /**
+ * The next value for [_exportDeclarationsId].
+ */
+ static int _exportDeclarationsNextId = 0;
+
final FileSystemState _fsState;
/**
@@ -136,6 +142,10 @@
String _transitiveSignature;
String _transitiveSignatureLinked;
+ Map<String, TopLevelDeclaration> _topLevelDeclarations;
+ Map<String, TopLevelDeclaration> _exportedTopLevelDeclarations;
+ int _exportDeclarationsId = 0;
+
/**
* The flag that shows whether the file has an error or warning that
* might be fixed by a change to another file.
@@ -206,6 +216,18 @@
*/
List<FileState> get exportedFiles => _exportedFiles;
+ /**
+ * Return [TopLevelDeclaration]s exported from the this library file. The
+ * keys to the map are names of declarations.
+ */
+ Map<String, TopLevelDeclaration> get exportedTopLevelDeclarations {
+ if (AnalysisDriver.useSummary2) {
+ return <String, TopLevelDeclaration>{};
+ }
+ _exportDeclarationsNextId = 1;
+ return _computeExportedDeclarations().declarations;
+ }
+
@override
int get hashCode => uri.hashCode;
@@ -304,6 +326,63 @@
FileStateTestView get test => new FileStateTestView(this);
/**
+ * Return public top-level declarations declared in the file. The keys to the
+ * map are names of declarations.
+ */
+ Map<String, TopLevelDeclaration> get topLevelDeclarations {
+ if (AnalysisDriver.useSummary2) {
+ return <String, TopLevelDeclaration>{};
+ }
+
+ if (_topLevelDeclarations == null) {
+ _topLevelDeclarations = <String, TopLevelDeclaration>{};
+
+ void addDeclaration(TopLevelDeclarationKind kind, String name) {
+ if (!name.startsWith('_')) {
+ _topLevelDeclarations[name] = new TopLevelDeclaration(kind, name);
+ }
+ }
+
+ // Add types.
+ for (UnlinkedClass type in unlinked.classes) {
+ addDeclaration(TopLevelDeclarationKind.type, type.name);
+ }
+ for (UnlinkedEnum type in unlinked.enums) {
+ addDeclaration(TopLevelDeclarationKind.type, type.name);
+ }
+ for (UnlinkedClass type in unlinked.mixins) {
+ addDeclaration(TopLevelDeclarationKind.type, type.name);
+ }
+ for (UnlinkedTypedef type in unlinked.typedefs) {
+ addDeclaration(TopLevelDeclarationKind.type, type.name);
+ }
+ // Add functions and variables.
+ Set<String> addedVariableNames = new Set<String>();
+ for (UnlinkedExecutable executable in unlinked.executables) {
+ String name = executable.name;
+ if (executable.kind == UnlinkedExecutableKind.functionOrMethod) {
+ addDeclaration(TopLevelDeclarationKind.function, name);
+ } else if (executable.kind == UnlinkedExecutableKind.getter ||
+ executable.kind == UnlinkedExecutableKind.setter) {
+ if (executable.kind == UnlinkedExecutableKind.setter) {
+ name = name.substring(0, name.length - 1);
+ }
+ if (addedVariableNames.add(name)) {
+ addDeclaration(TopLevelDeclarationKind.variable, name);
+ }
+ }
+ }
+ for (UnlinkedVariable variable in unlinked.variables) {
+ String name = variable.name;
+ if (addedVariableNames.add(name)) {
+ addDeclaration(TopLevelDeclarationKind.variable, name);
+ }
+ }
+ }
+ return _topLevelDeclarations;
+ }
+
+ /**
* Return the set of transitive files - the file itself and all of the
* directly or indirectly referenced files.
*/
@@ -472,6 +551,10 @@
library.libraryCycle?.invalidate();
}
}
+
+ for (FileState file in _fsState._uriToFile.values) {
+ file._exportedTopLevelDeclarations = null;
+ }
}
// This file is potentially not a library for its previous parts anymore.
@@ -538,6 +621,69 @@
@override
String toString() => path ?? '<unresolved>';
+ /**
+ * Compute the full or partial map of exported declarations for this library.
+ */
+ _ExportedDeclarations _computeExportedDeclarations() {
+ // If we know exported declarations, return them.
+ if (_exportedTopLevelDeclarations != null) {
+ return new _ExportedDeclarations(0, _exportedTopLevelDeclarations);
+ }
+
+ // If we are already computing exported declarations for this library,
+ // report that we found a cycle.
+ if (_exportDeclarationsId != 0) {
+ return new _ExportedDeclarations(_exportDeclarationsId, null);
+ }
+
+ var declarations = <String, TopLevelDeclaration>{};
+
+ // Give each library a unique identifier.
+ _exportDeclarationsId = _exportDeclarationsNextId++;
+
+ // Append the exported declarations.
+ int firstCycleId = 0;
+ for (int i = 0; i < _exportedFiles.length; i++) {
+ var exported = _exportedFiles[i]._computeExportedDeclarations();
+ if (exported.declarations != null) {
+ for (TopLevelDeclaration t in exported.declarations.values) {
+ if (_exportFilters[i].accepts(t.name)) {
+ declarations[t.name] = t;
+ }
+ }
+ }
+ if (exported.firstCycleId > 0) {
+ if (firstCycleId == 0 || firstCycleId > exported.firstCycleId) {
+ firstCycleId = exported.firstCycleId;
+ }
+ }
+ }
+
+ // If this library is the first component of the cycle, then we are at
+ // the beginning of this cycle, and combination of partial export
+ // namespaces of other exported libraries and declarations of this library
+ // is the full export namespace of this library.
+ if (firstCycleId != 0 && firstCycleId == _exportDeclarationsId) {
+ firstCycleId = 0;
+ }
+
+ // We're done with this library, successfully or not.
+ _exportDeclarationsId = 0;
+
+ // Append the library declarations.
+ for (FileState file in libraryFiles) {
+ declarations.addAll(file.topLevelDeclarations);
+ }
+
+ // Record the declarations only if it is the full result.
+ if (firstCycleId == 0) {
+ _exportedTopLevelDeclarations = declarations;
+ }
+
+ // Return the full or partial result.
+ return new _ExportedDeclarations(firstCycleId, declarations);
+ }
+
CompilationUnit _createEmptyCompilationUnit(FeatureSet featureSet) {
var token = new Token.eof(0);
return astFactory.compilationUnit2(
@@ -573,6 +719,7 @@
_definedTopLevelNames = null;
_definedClassMemberNames = null;
_referencedNames = null;
+ _topLevelDeclarations = null;
if (_driverUnlinkedUnit != null) {
for (var name in _driverUnlinkedUnit.subtypedNames) {
@@ -687,6 +834,10 @@
library.libraryCycle?.invalidate();
}
}
+
+ for (FileState file in _fsState._uriToFile.values) {
+ file._exportedTopLevelDeclarations = null;
+ }
}
// This file is potentially not a library for its previous parts anymore.
@@ -1109,6 +1260,23 @@
.where((f) => f._libraryCycle == null)
.toSet();
}
+
+ Set<FileState> get librariesWithComputedExportedDeclarations {
+ return state._uriToFile.values
+ .where((f) => !f.isPart && f._exportedTopLevelDeclarations != null)
+ .toSet();
+ }
+}
+
+/**
+ * The result of computing exported top-level declarations.
+ * It can be full (when [firstCycleId] is zero), or partial (when a cycle)
+ */
+class _ExportedDeclarations {
+ final int firstCycleId;
+ final Map<String, TopLevelDeclaration> declarations;
+
+ _ExportedDeclarations(this.firstCycleId, this.declarations);
}
/**
diff --git a/pkg/analyzer/lib/src/dart/analysis/session.dart b/pkg/analyzer/lib/src/dart/analysis/session.dart
index 376e315..30db6fa 100644
--- a/pkg/analyzer/lib/src/dart/analysis/session.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/session.dart
@@ -12,6 +12,7 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/dart/analysis/driver.dart' as driver;
+import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
import 'package:analyzer/src/dart/analysis/uri_converter.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
import 'package:analyzer/src/generated/resolver.dart';
@@ -158,6 +159,13 @@
}
@override
+ Future<List<TopLevelDeclarationInSource>> getTopLevelDeclarations(
+ String name) {
+ _checkConsistency();
+ return _driver.getTopLevelNameDeclarations(name);
+ }
+
+ @override
Future<UnitElementResult> getUnitElement(String path) {
_checkConsistency();
return _driver.getUnitElement(path);
diff --git a/pkg/analyzer/lib/src/dart/analysis/top_level_declaration.dart b/pkg/analyzer/lib/src/dart/analysis/top_level_declaration.dart
new file mode 100644
index 0000000..1658aad
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/analysis/top_level_declaration.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2016, 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/src/generated/source.dart';
+
+/**
+ * Information about a single top-level declaration.
+ */
+class TopLevelDeclaration {
+ final TopLevelDeclarationKind kind;
+ final String name;
+
+ TopLevelDeclaration(this.kind, this.name);
+
+ @override
+ String toString() => '($kind, $name)';
+}
+
+/**
+ * A declaration in a source.
+ */
+class TopLevelDeclarationInSource {
+ /**
+ * The declaring source.
+ */
+ final Source source;
+
+ /**
+ * The declaration.
+ */
+ final TopLevelDeclaration declaration;
+
+ /**
+ * Is `true` if the [declaration] is exported, not declared in the [source].
+ */
+ final bool isExported;
+
+ TopLevelDeclarationInSource(this.source, this.declaration, this.isExported);
+
+ @override
+ String toString() => '($source, $declaration, $isExported)';
+}
+
+/**
+ * Kind of a top-level declaration.
+ *
+ * We don't need it to be precise, just enough to support quick fixes.
+ */
+enum TopLevelDeclarationKind { type, function, variable }
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index dcc2fad..eac7525 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -15,6 +15,7 @@
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
import 'package:analyzer/src/dart/analysis/status.dart';
+import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
import 'package:analyzer/src/dart/constant/evaluation.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/error/codes.dart';
@@ -2227,6 +2228,102 @@
expect(await driver.getSourceKind(path), SourceKind.PART);
}
+ test_getTopLevelNameDeclarations() async {
+ var a = convertPath('/test/lib/a.dart');
+ var b = convertPath('/test/lib/b.dart');
+ var c = convertPath('/test/lib/c.dart');
+ var d = convertPath('/test/lib/d.dart');
+
+ newFile(a, content: 'class A {}');
+ newFile(b, content: 'export "a.dart"; class B {}');
+ newFile(c, content: 'import "d.dart"; class C {}');
+ newFile(d, content: 'class D {}');
+
+ driver.addFile(a);
+ driver.addFile(b);
+ driver.addFile(c);
+ // Don't add d.dart, it is referenced implicitly.
+
+ _assertTopLevelDeclarations(
+ await driver.getTopLevelNameDeclarations('A'), [a, b], [false, true]);
+
+ _assertTopLevelDeclarations(
+ await driver.getTopLevelNameDeclarations('B'), [b], [false]);
+
+ _assertTopLevelDeclarations(
+ await driver.getTopLevelNameDeclarations('C'), [c], [false]);
+
+ _assertTopLevelDeclarations(
+ await driver.getTopLevelNameDeclarations('D'), [d], [false]);
+
+ _assertTopLevelDeclarations(
+ await driver.getTopLevelNameDeclarations('X'), [], []);
+ }
+
+ test_getTopLevelNameDeclarations_discover() async {
+ var t = convertPath('/test/lib/test.dart');
+ var a1 = convertPath('/aaa/lib/a1.dart');
+ var a2 = convertPath('/aaa/lib/src/a2.dart');
+ var b = convertPath('/bbb/lib/b.dart');
+ var c = convertPath('/ccc/lib/c.dart');
+
+ newFile(t, content: 'class T {}');
+ newFile(a1, content: 'class A1 {}');
+ newFile(a2, content: 'class A2 {}');
+ newFile(b, content: 'class B {}');
+ newFile(c, content: 'class C {}');
+
+ driver.addFile(t);
+ // Don't add a1.dart, a2.dart, or b.dart - they should be discovered.
+ // And c.dart is not in .packages, so should not be discovered.
+
+ _assertTopLevelDeclarations(
+ await driver.getTopLevelNameDeclarations('T'), [t], [false]);
+
+ _assertTopLevelDeclarations(
+ await driver.getTopLevelNameDeclarations('A1'), [a1], [false]);
+
+ _assertTopLevelDeclarations(
+ await driver.getTopLevelNameDeclarations('A2'), [a2], [false]);
+
+ _assertTopLevelDeclarations(
+ await driver.getTopLevelNameDeclarations('B'), [b], [false]);
+
+ _assertTopLevelDeclarations(
+ await driver.getTopLevelNameDeclarations('C'), [], []);
+ }
+
+ test_getTopLevelNameDeclarations_parts() async {
+ var a = convertPath('/test/lib/a.dart');
+ var b = convertPath('/test/lib/b.dart');
+ var c = convertPath('/test/lib/c.dart');
+
+ newFile(a, content: r'''
+library lib;
+part 'b.dart';
+part 'c.dart';
+class A {}
+''');
+ newFile(b, content: 'part of lib; class B {}');
+ newFile(c, content: 'part of lib; class C {}');
+
+ driver.addFile(a);
+ driver.addFile(b);
+ driver.addFile(c);
+
+ _assertTopLevelDeclarations(
+ await driver.getTopLevelNameDeclarations('A'), [a], [false]);
+
+ _assertTopLevelDeclarations(
+ await driver.getTopLevelNameDeclarations('B'), [a], [false]);
+
+ _assertTopLevelDeclarations(
+ await driver.getTopLevelNameDeclarations('C'), [a], [false]);
+
+ _assertTopLevelDeclarations(
+ await driver.getTopLevelNameDeclarations('X'), [], []);
+ }
+
test_getUnitElement() async {
String content = r'''
foo(int p) {}
@@ -3416,6 +3513,20 @@
}
}
+ void _assertTopLevelDeclarations(
+ List<TopLevelDeclarationInSource> declarations,
+ List<String> expectedFiles,
+ List<bool> expectedIsExported) {
+ expect(expectedFiles, hasLength(expectedIsExported.length));
+ for (int i = 0; i < expectedFiles.length; i++) {
+ expect(declarations,
+ contains(predicate((TopLevelDeclarationInSource declaration) {
+ return declaration.source.fullName == expectedFiles[i] &&
+ declaration.isExported == expectedIsExported[i];
+ })));
+ }
+ }
+
void _expectCircularityError(EvaluationResultImpl evaluationResult) {
expect(evaluationResult, isNotNull);
expect(evaluationResult.value, isNull);
diff --git a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
index 0eb6a6e..98a279e 100644
--- a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
@@ -11,6 +11,7 @@
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/analysis/library_graph.dart';
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
+import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
import 'package:analyzer/src/file_system/file_system.dart';
import 'package:analyzer/src/generated/engine.dart'
show AnalysisOptions, AnalysisOptionsImpl;
@@ -104,6 +105,249 @@
unorderedEquals(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']));
}
+ test_exportedTopLevelDeclarations_cycle() {
+ String a = convertPath('/aaa/lib/a.dart');
+ String b = convertPath('/aaa/lib/b.dart');
+ String c = convertPath('/aaa/lib/c.dart');
+ newFile(a, content: r'''
+export 'b.dart';
+class A {}
+''');
+ newFile(b, content: r'''
+export 'c.dart';
+class B {}
+''');
+ newFile(c, content: r'''
+export 'a.dart';
+class C {}
+''');
+ _assertExportedTopLevelDeclarations(a, ['A', 'B', 'C']);
+
+ // We asked for 'a', and it was computed.
+ // But 'b' and 'c' are not computed, because we detect that there is
+ // cycle with 'a', so we cannot get all exported declarations of 'a'.
+ _assertHasComputedExportedDeclarations([a]);
+ }
+
+ test_exportedTopLevelDeclarations_cycle_anotherOutsideCycle() {
+ String a = convertPath('/aaa/lib/a.dart');
+ String b = convertPath('/aaa/lib/b.dart');
+ String c = convertPath('/aaa/lib/c.dart');
+ String d = convertPath('/aaa/lib/d.dart');
+ newFile(a, content: r'''
+export 'b.dart';
+class A {}
+''');
+ newFile(b, content: r'''
+export 'c.dart';
+class B {}
+''');
+ newFile(c, content: r'''
+export 'b.dart';
+export 'd.dart';
+class C {}
+''');
+ newFile(d, content: r'''
+class D {}
+''');
+ _assertExportedTopLevelDeclarations(a, ['A', 'B', 'C', 'D']);
+
+ // To compute 'a' we compute 'b'.
+ // But 'c' is not computed, because of the cycle [b, c].
+ // However 'd' is not a part of a cycle, so it is computed too.
+ _assertHasComputedExportedDeclarations([a, b, d]);
+ }
+
+ test_exportedTopLevelDeclarations_cycle_onSequence() {
+ String a = convertPath('/aaa/lib/a.dart');
+ String b = convertPath('/aaa/lib/b.dart');
+ String c = convertPath('/aaa/lib/c.dart');
+ String d = convertPath('/aaa/lib/d.dart');
+ String e = convertPath('/aaa/lib/e.dart');
+ newFile(a, content: r'''
+export 'b.dart';
+class A {}
+''');
+ newFile(b, content: r'''
+export 'c.dart';
+class B {}
+''');
+ newFile(c, content: r'''
+export 'd.dart';
+class C {}
+''');
+ newFile(d, content: r'''
+export 'e.dart';
+class D {}
+''');
+ newFile(e, content: r'''
+export 'c.dart';
+class E {}
+''');
+ // We compute 'a'.
+ // To compute it we also compute 'b' and 'c'.
+ // But 'd' and 'e' are not computed, because of the cycle [c, d, e].
+ _assertExportedTopLevelDeclarations(a, ['A', 'B', 'C', 'D', 'E']);
+ _assertHasComputedExportedDeclarations([a, b, c]);
+
+ // We compute 'd', and try to compute 'e', because 'd' needs 'e'; 'e' can
+ // be computed because 'c' is ready, so the cycle [c, d, e] is broken.
+ _assertExportedTopLevelDeclarations(d, ['C', 'D', 'E']);
+ _assertHasComputedExportedDeclarations([a, b, c, d, e]);
+ }
+
+ test_exportedTopLevelDeclarations_export() {
+ String a = convertPath('/aaa/lib/a.dart');
+ String b = convertPath('/aaa/lib/b.dart');
+ newFile(a, content: r'''
+class A {}
+''');
+ newFile(b, content: r'''
+export 'a.dart';
+class B {}
+''');
+ _assertExportedTopLevelDeclarations(b, ['A', 'B']);
+ _assertHasComputedExportedDeclarations([a, b]);
+ }
+
+ test_exportedTopLevelDeclarations_export2_show() {
+ String a = convertPath('/aaa/lib/a.dart');
+ String b = convertPath('/aaa/lib/b.dart');
+ String c = convertPath('/aaa/lib/c.dart');
+ newFile(a, content: r'''
+class A1 {}
+class A2 {}
+class A3 {}
+''');
+ newFile(b, content: r'''
+export 'a.dart' show A1, A2;
+class B1 {}
+class B2 {}
+''');
+ newFile(c, content: r'''
+export 'b.dart' show A2, A3, B1;
+class C {}
+''');
+ _assertExportedTopLevelDeclarations(c, ['A2', 'B1', 'C']);
+ _assertHasComputedExportedDeclarations([a, b, c]);
+ }
+
+ test_exportedTopLevelDeclarations_export_flushOnChange() {
+ String a = convertPath('/aaa/lib/a.dart');
+ String b = convertPath('/aaa/lib/b.dart');
+ newFile(a, content: r'''
+class A {}
+''');
+ newFile(b, content: r'''
+export 'a.dart';
+class B {}
+''');
+
+ // Initial exported declarations.
+ _assertExportedTopLevelDeclarations(b, ['A', 'B']);
+
+ // Update a.dart, so a.dart and b.dart exported declarations are flushed.
+ newFile(a, content: 'class A {} class A2 {}');
+ fileSystemState.getFileForPath(a).refresh();
+ _assertExportedTopLevelDeclarations(b, ['A', 'A2', 'B']);
+ }
+
+ test_exportedTopLevelDeclarations_export_hide() {
+ String a = convertPath('/aaa/lib/a.dart');
+ String b = convertPath('/aaa/lib/b.dart');
+ newFile(a, content: r'''
+class A1 {}
+class A2 {}
+class A3 {}
+''');
+ newFile(b, content: r'''
+export 'a.dart' hide A2;
+class B {}
+''');
+ _assertExportedTopLevelDeclarations(b, ['A1', 'A3', 'B']);
+ }
+
+ test_exportedTopLevelDeclarations_export_preferLocal() {
+ String a = convertPath('/aaa/lib/a.dart');
+ String b = convertPath('/aaa/lib/b.dart');
+ newFile(a, content: r'''
+class V {}
+''');
+ newFile(b, content: r'''
+export 'a.dart';
+int V;
+''');
+ FileState file = fileSystemState.getFileForPath(b);
+ Map<String, TopLevelDeclaration> declarations =
+ file.exportedTopLevelDeclarations;
+ expect(declarations.keys, unorderedEquals(['V']));
+ expect(declarations['V'].kind, TopLevelDeclarationKind.variable);
+ }
+
+ test_exportedTopLevelDeclarations_export_show() {
+ String a = convertPath('/aaa/lib/a.dart');
+ String b = convertPath('/aaa/lib/b.dart');
+ newFile(a, content: r'''
+class A1 {}
+class A2 {}
+''');
+ newFile(b, content: r'''
+export 'a.dart' show A2;
+class B {}
+''');
+ _assertExportedTopLevelDeclarations(b, ['A2', 'B']);
+ }
+
+ test_exportedTopLevelDeclarations_export_show2() {
+ String a = convertPath('/aaa/lib/a.dart');
+ String b = convertPath('/aaa/lib/b.dart');
+ String c = convertPath('/aaa/lib/c.dart');
+ String d = convertPath('/aaa/lib/d.dart');
+ newFile(a, content: r'''
+export 'b.dart' show Foo;
+export 'c.dart' show Bar;
+''');
+ newFile(b, content: r'''
+export 'd.dart';
+''');
+ newFile(c, content: r'''
+export 'd.dart';
+''');
+ newFile(d, content: r'''
+class Foo {}
+class Bar {}
+''');
+ _assertExportedTopLevelDeclarations(a, ['Foo', 'Bar']);
+ }
+
+ test_exportedTopLevelDeclarations_import() {
+ String a = convertPath('/aaa/lib/a.dart');
+ String b = convertPath('/aaa/lib/b.dart');
+ newFile(a, content: r'''
+class A {}
+''');
+ newFile(b, content: r'''
+import 'a.dart';
+class B {}
+''');
+ _assertExportedTopLevelDeclarations(b, ['B']);
+ }
+
+ test_exportedTopLevelDeclarations_parts() {
+ String a = convertPath('/aaa/lib/a.dart');
+ String a2 = convertPath('/aaa/lib/a2.dart');
+ newFile(a, content: r'''
+library lib;
+part 'a2.dart';
+class A1 {}
+''');
+ newFile(a2, content: r'''
+part of lib;
+class A2 {}
+''');
+ _assertExportedTopLevelDeclarations(a, ['A1', 'A2']);
+ }
+
test_getFileForPath_doesNotExist() {
String path = convertPath('/aaa/lib/a.dart');
FileState file = fileSystemState.getFileForPath(path);
@@ -597,6 +841,52 @@
expect(file.referencedNames, unorderedEquals(['A', 'B', 'C', 'D']));
}
+ test_topLevelDeclarations() {
+ String path = convertPath('/aaa/lib/a.dart');
+ newFile(path, content: r'''
+class C {}
+typedef F();
+enum E {E1, E2}
+mixin M {}
+void f() {}
+var V1;
+get V2 => null;
+set V3(_) {}
+get V4 => null;
+set V4(_) {}
+
+class _C {}
+typedef _F();
+enum _E {E1, E2}
+mixin _M {}
+void _f() {}
+var _V1;
+get _V2 => null;
+set _V3(_) {}
+''');
+ FileState file = fileSystemState.getFileForPath(path);
+
+ Map<String, TopLevelDeclaration> declarations = file.topLevelDeclarations;
+
+ void assertHas(String name, TopLevelDeclarationKind kind) {
+ expect(declarations[name]?.kind, kind);
+ }
+
+ expect(
+ declarations.keys,
+ unorderedEquals(['C', 'F', 'E', 'M', 'f', 'V1', 'V2', 'V3', 'V4']),
+ );
+ assertHas('C', TopLevelDeclarationKind.type);
+ assertHas('F', TopLevelDeclarationKind.type);
+ assertHas('E', TopLevelDeclarationKind.type);
+ assertHas('M', TopLevelDeclarationKind.type);
+ assertHas('f', TopLevelDeclarationKind.function);
+ assertHas('V1', TopLevelDeclarationKind.variable);
+ assertHas('V2', TopLevelDeclarationKind.variable);
+ assertHas('V3', TopLevelDeclarationKind.variable);
+ assertHas('V4', TopLevelDeclarationKind.variable);
+ }
+
test_transitiveSignature() {
String pa = convertPath('/aaa/lib/a.dart');
String pb = convertPath('/aaa/lib/b.dart');
@@ -657,11 +947,24 @@
expect(bSignature, isNot(aSignature));
}
+ void _assertExportedTopLevelDeclarations(String path, List<String> expected) {
+ FileState file = fileSystemState.getFileForPath(path);
+ Map<String, TopLevelDeclaration> declarations =
+ file.exportedTopLevelDeclarations;
+ expect(declarations.keys, unorderedEquals(expected));
+ }
+
void _assertFilesWithoutLibraryCycle(List<FileState> expected) {
var actual = fileSystemState.test.filesWithoutLibraryCycle;
expect(_excludeSdk(actual), unorderedEquals(expected));
}
+ void _assertHasComputedExportedDeclarations(List<String> expectedPathList) {
+ FileSystemStateTestView test = fileSystemState.test;
+ expect(test.librariesWithComputedExportedDeclarations.map((f) => f.path),
+ unorderedEquals(expectedPathList));
+ }
+
void _assertIsUnresolvedFile(FileState file) {
expect(file.path, isNull);
expect(file.uri, isNull);