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);