Backport AnalysisSession.getResolvedLibrary()/ByElement().

Change-Id: I83c1dbe9d758be12212fcd892bb57ae69407d8be
Reviewed-on: https://dart-review.googlesource.com/c/85147
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
diff --git a/pkg/analyzer/lib/dart/analysis/session.dart b/pkg/analyzer/lib/dart/analysis/session.dart
index 1760f2a..b945ed6 100644
--- a/pkg/analyzer/lib/dart/analysis/session.dart
+++ b/pkg/analyzer/lib/dart/analysis/session.dart
@@ -91,6 +91,21 @@
    */
   Future<ResolveResult> getResolvedAst(String path);
 
+  /// Return a future that will complete with information about the results of
+  /// resolving all of the files in the library with the given absolute,
+  /// normalized [path].
+  ///
+  /// Throw [ArgumentError] if the given [path] is not the defining compilation
+  /// unit for a library (that is, is a part of a library).
+  Future<ResolvedLibraryResult> getResolvedLibrary(String path);
+
+  /// Return a future that will complete with information about the results of
+  /// resolving all of the files in the library with the library [element].
+  ///
+  /// Throw [ArgumentError] if the [element] was not produced by this session.
+  Future<ResolvedLibraryResult> getResolvedLibraryByElement(
+      LibraryElement element);
+
   /**
    * Return a future that will complete with the source kind of the file with
    * the given absolute, normalized [path]. If the path does not represent a
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 000fcae..6f2d3ec 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -8,6 +8,7 @@
 
 import 'package:analyzer/dart/analysis/declared_variables.dart';
 import 'package:analyzer/dart/analysis/results.dart' as results;
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/session.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart'
@@ -24,6 +25,7 @@
 import 'package:analyzer/src/dart/analysis/library_analyzer.dart';
 import 'package:analyzer/src/dart/analysis/library_context.dart';
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
+import 'package:analyzer/src/dart/analysis/results.dart';
 import 'package:analyzer/src/dart/analysis/search.dart';
 import 'package:analyzer/src/dart/analysis/session.dart';
 import 'package:analyzer/src/dart/analysis/status.dart';
@@ -187,6 +189,13 @@
   final _requestedFiles = <String, List<Completer<AnalysisResult>>>{};
 
   /**
+   * The mapping from the files for which analysis was requested using
+   * [getResolvedLibrary] to the [Completer]s to report the result.
+   */
+  final _requestedLibraries =
+      <String, List<Completer<ResolvedLibraryResult>>>{};
+
+  /**
    * The task that discovers available files.  If this field is not `null`,
    * and the task is not completed, it should be performed and completed
    * before any name searching task.
@@ -468,6 +477,9 @@
     if (_requestedFiles.isNotEmpty) {
       return AnalysisDriverPriority.interactive;
     }
+    if (_requestedLibraries.isNotEmpty) {
+      return AnalysisDriverPriority.interactive;
+    }
     if (_discoverAvailableFilesTask != null &&
         !_discoverAvailableFilesTask.isCompleted) {
       return AnalysisDriverPriority.interactive;
@@ -725,6 +737,83 @@
     return unitResult.element.library;
   }
 
+  /**
+   * Return a [Future] that completes with a [ResolvedLibraryResult] for the
+   * Dart library file with the given [path].  If the file is not a Dart file
+   * or cannot be analyzed, the [Future] completes with `null`.
+   *
+   * Throw [ArgumentError] if the given [path] is not the defining compilation
+   * unit for a library (that is, is a part of a library).
+   *
+   * The [path] must be absolute and normalized.
+   *
+   * The [path] can be any file - explicitly or implicitly analyzed, or neither.
+   *
+   * Invocation of this method causes the analysis state to transition to
+   * "analyzing" (if it is not in that state already), the driver will produce
+   * the resolution result for it, which is consistent with the current file
+   * state (including new states of the files previously reported using
+   * [changeFile]), prior to the next time the analysis state transitions
+   * to "idle".
+   */
+  Future<ResolvedLibraryResult> getResolvedLibrary(String path) {
+    _throwIfNotAbsolutePath(path);
+    if (!_fsState.hasUri(path)) {
+      return new Future.value();
+    }
+
+    FileState file = _fsState.getFileForPath(path);
+
+    if (file.isExternalLibrary) {
+      return Future.value(
+        ResolvedLibraryResultImpl.external(currentSession, file.uri),
+      );
+    }
+
+    if (file.isPart) {
+      throw ArgumentError('Is a part: $path');
+    }
+
+    // Schedule analysis.
+    var completer = new Completer<ResolvedLibraryResult>();
+    _requestedLibraries
+        .putIfAbsent(path, () => <Completer<ResolvedLibraryResult>>[])
+        .add(completer);
+    _scheduler.notify(this);
+    return completer.future;
+  }
+
+  /**
+   * Return a [Future] that completes with a [ResolvedLibraryResult] for the
+   * Dart library file with the given [uri].
+   *
+   * Throw [ArgumentError] if the given [uri] is not the defining compilation
+   * unit for a library (that is, is a part of a library).
+   *
+   * Invocation of this method causes the analysis state to transition to
+   * "analyzing" (if it is not in that state already), the driver will produce
+   * the resolution result for it, which is consistent with the current file
+   * state (including new states of the files previously reported using
+   * [changeFile]), prior to the next time the analysis state transitions
+   * to "idle".
+   */
+  Future<ResolvedLibraryResult> getResolvedLibraryByUri(Uri uri) {
+    FileState file = _fsState.getFileForUri(uri);
+
+    if (file.isExternalLibrary) {
+      return Future.value(
+        ResolvedLibraryResultImpl.external(currentSession, file.uri),
+      );
+    }
+
+    if (file.isPart) {
+      throw ArgumentError('Is a part: $uri');
+    }
+
+    // The file is a local file, we can get the result.
+    return getResolvedLibrary(file.path);
+  }
+
   ApiSignature getResolvedUnitKeyByPath(String path) {
     _throwIfNotAbsolutePath(path);
     ApiSignature signature = getUnitKeyByPath(path);
@@ -959,6 +1048,22 @@
       return;
     }
 
+    // Analyze a requested library.
+    if (_requestedLibraries.isNotEmpty) {
+      String path = _requestedLibraries.keys.first;
+      try {
+        var result = _computeResolvedLibrary(path);
+        _requestedLibraries.remove(path).forEach((completer) {
+          completer.complete(result);
+        });
+      } catch (exception, stackTrace) {
+        _requestedLibraries.remove(path).forEach((completer) {
+          completer.completeError(exception, stackTrace);
+        });
+      }
+      return;
+    }
+
     // Process an index request.
     if (_indexRequestedFiles.isNotEmpty) {
       String path = _indexRequestedFiles.keys.first;
@@ -1256,7 +1361,7 @@
           if (!_fsState.getFileForUri(Uri.parse('dart:async')).exists) {
             return _newMissingDartLibraryResult(file, 'dart:async');
           }
-          libraryContext = await _createLibraryContext(library);
+          libraryContext = _createLibraryContext(library);
 
           LibraryAnalyzer analyzer = new LibraryAnalyzer(
               analysisOptions,
@@ -1267,7 +1372,7 @@
               libraryContext.resynthesizer,
               library,
               _resourceProvider);
-          Map<FileState, UnitAnalysisResult> results = await analyzer.analyze();
+          Map<FileState, UnitAnalysisResult> results = analyzer.analyze();
 
           List<int> bytes;
           CompilationUnit resolvedUnit;
@@ -1319,6 +1424,62 @@
     return analysisResult._index;
   }
 
+  /**
+   * Return the newly computed resolution result of the library with the
+   * given [path].
+   */
+  ResolvedLibraryResultImpl _computeResolvedLibrary(String path) {
+    FileState library = _fsState.getFileForPath(path);
+
+    return _logger.run('Compute resolved library $path', () {
+      _testView.numOfAnalyzedLibraries++;
+      var libraryContext = _createLibraryContext(library);
+
+      LibraryAnalyzer analyzer = new LibraryAnalyzer(
+          analysisOptions,
+          declaredVariables,
+          sourceFactory,
+          libraryContext.isLibraryUri,
+          libraryContext.analysisContext,
+          libraryContext.resynthesizer,
+          library,
+          _resourceProvider);
+      Map<FileState, UnitAnalysisResult> unitResults = analyzer.analyze();
+      var resolvedUnits = <ResolvedUnitResult>[];
+
+      for (var unitFile in unitResults.keys) {
+        if (unitFile.path != null) {
+          var unitResult = unitResults[unitFile];
+          resolvedUnits.add(
+            new AnalysisResult(
+              this,
+              _sourceFactory,
+              unitFile.path,
+              unitFile.uri,
+              unitFile.exists,
+              unitFile.content,
+              unitFile.lineInfo,
+              unitFile.isPart,
+              null,
+              unitResult.unit,
+              unitResult.errors,
+              null,
+            ),
+          );
+        }
+      }
+
+      return new ResolvedLibraryResultImpl(
+        currentSession,
+        library.path,
+        library.uri,
+        resolvedUnits.first.libraryElement,
+        libraryContext.analysisContext.typeProvider,
+        resolvedUnits,
+      );
+    });
+  }
+
   Future<UnitElementResult> _computeUnitElement(String path,
       {bool asIsIfPartWithoutLibrary: false}) async {
     // TODO(brianwilkerson) Determine whether this await is necessary.
@@ -1335,7 +1496,7 @@
       }
     }
 
-    LibraryContext libraryContext = await _createLibraryContext(library);
+    LibraryContext libraryContext = _createLibraryContext(library);
     try {
       CompilationUnitElement element =
           libraryContext.computeUnitElement(library.source, file.source);
@@ -1389,9 +1550,7 @@
   /**
    * Return the context in which the [library] should be analyzed.
    */
-  Future<LibraryContext> _createLibraryContext(FileState library) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
+  LibraryContext _createLibraryContext(FileState library) {
     _testView.numOfCreatedLibraryContexts++;
     return new LibraryContext.forSingleLibrary(
         library,
@@ -1955,7 +2114,7 @@
     FileState library = driver.fsState.getFileForPath(libraryPath);
     // TODO(brianwilkerson) Determine whether this await is necessary.
     await null;
-    LibraryContext libraryContext = await driver._createLibraryContext(library);
+    LibraryContext libraryContext = driver._createLibraryContext(library);
     try {
       return libraryContext.store;
     } finally {
@@ -1975,7 +2134,7 @@
  * Every result is independent, and is not guaranteed to be consistent with
  * any previously returned result, even inside of the same library.
  */
-class AnalysisResult extends FileResult implements results.ResolveResult {
+class AnalysisResult extends FileResult implements results.ResolvedUnitResult {
   static final _UNCHANGED = new AnalysisResult(
       null, null, null, null, null, null, null, null, null, null, null, null);
 
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index f9da8aa..e627abb 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -214,10 +214,25 @@
   List<FileState> get importedFiles => _importedFiles;
 
   /**
+   * Return `true` if the file is a stub created for a library in the provided
+   * external summary store.
+   */
+  bool get isExternalLibrary {
+    return _fsState.externalSummaries != null &&
+        _fsState.externalSummaries.linkedMap.containsKey(uriStr);
+  }
+
+  /**
    * Return `true` if the file does not have a `library` directive, and has a
    * `part of` directive, so is probably a part.
    */
-  bool get isPart => _unlinked.libraryNameOffset == 0 && _unlinked.isPartOf;
+  bool get isPart {
+    if (_fsState.externalSummaries != null &&
+        _fsState.externalSummaries.unlinkedMap.containsKey(uriStr)) {
+      return !_fsState.externalSummaries.linkedMap.containsKey(uriStr);
+    }
+    return _unlinked.libraryNameOffset == 0 && _unlinked.isPartOf;
+  }
 
   /**
    * Return `true` if the file is the "unresolved" file, which does not have
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 6c421b8..ed45474 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'dart:async';
-
 import 'package:analyzer/dart/analysis/declared_variables.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/visitor.dart';
@@ -94,10 +92,8 @@
   /**
    * Compute analysis results for all units of the library.
    */
-  Future<Map<FileState, UnitAnalysisResult>> analyze() async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
-    return PerformanceStatistics.analysis.makeCurrentWhileAsync(() async {
+  Map<FileState, UnitAnalysisResult> analyze() {
+    return PerformanceStatistics.analysis.makeCurrentWhile(() {
       return analyzeSync();
     });
   }
diff --git a/pkg/analyzer/lib/src/dart/analysis/results.dart b/pkg/analyzer/lib/src/dart/analysis/results.dart
index 2f1eb6c..2b9ac07 100644
--- a/pkg/analyzer/lib/src/dart/analysis/results.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/results.dart
@@ -144,9 +144,6 @@
   final LibraryElement element;
 
   @override
-  final ResultState state = ResultState.VALID;
-
-  @override
   final TypeProvider typeProvider;
 
   @override
@@ -156,8 +153,23 @@
       this.element, this.typeProvider, this.units)
       : super(session, path, uri);
 
+  ResolvedLibraryResultImpl.external(AnalysisSession session, Uri uri)
+      : this(session, null, uri, null, null, null);
+
+  @override
+  ResultState get state {
+    if (path == null) {
+      return ResultState.NOT_A_FILE;
+    }
+    return ResultState.VALID;
+  }
+
   @override
   ElementDeclarationResult getElementDeclaration(Element element) {
+    if (state != ResultState.VALID) {
+      throw StateError('The result is not valid: $state');
+    }
+
     var elementPath = element.source.fullName;
     var unitResult = units.firstWhere(
       (r) => r.path == elementPath,
diff --git a/pkg/analyzer/lib/src/dart/analysis/session.dart b/pkg/analyzer/lib/src/dart/analysis/session.dart
index 982f3bb..b44d556 100644
--- a/pkg/analyzer/lib/src/dart/analysis/session.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/session.dart
@@ -130,6 +130,20 @@
   }
 
   @override
+  Future<ResolvedLibraryResult> getResolvedLibrary(String path) {
+    _checkConsistency();
+    return _driver.getResolvedLibrary(path);
+  }
+
+  @override
+  Future<ResolvedLibraryResult> getResolvedLibraryByElement(
+      LibraryElement element) {
+    _checkConsistency();
+    _checkElementOfThisSession(element);
+    return _driver.getResolvedLibraryByUri(element.source.uri);
+  }
+
+  @override
   Future<SourceKind> getSourceKind(String path) {
     _checkConsistency();
     return _driver.getSourceKind(path);
@@ -163,4 +177,13 @@
       throw new InconsistentAnalysisException();
     }
   }
+
+  void _checkElementOfThisSession(Element element) {
+    // TODO(scheglov) Requires 2.2 implementation
+//    if (element.session != this) {
+//      throw new ArgumentError(
+//          '(${element.runtimeType}) $element was not produced by '
+//          'this session.');
+//    }
+  }
 }
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index 1afe213..0345738 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -4,6 +4,7 @@
 
 import 'dart:async';
 
+import 'package:analyzer/dart/analysis/results.dart' as results;
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/standard_resolution_map.dart';
 import 'package:analyzer/dart/element/element.dart';
@@ -1572,6 +1573,89 @@
     expect(coreLibrary.getType('Object'), isNotNull);
   }
 
+  test_getResolvedLibrary_external() async {
+    var a1 = _p('/aaa/lib/a1.dart');
+    var a2 = _p('/aaa/lib/a2.dart');
+
+    var a1UriStr = 'package:aaa/a1.dart';
+    var a2UriStr = 'package:aaa/a2.dart';
+
+    provider.newFile(a1, "part 'a2.dart';  class A {}");
+    provider.newFile(a2, "part of 'a1.dart';");
+
+    // Build the store with the library.
+    var store = await createAnalysisDriver().test.getSummaryStore(a1);
+    expect(store.unlinkedMap.keys, contains(a1UriStr));
+    expect(store.unlinkedMap.keys, contains(a2UriStr));
+    expect(store.linkedMap.keys, contains(a1UriStr));
+
+    var driver = createAnalysisDriver(externalSummaries: store);
+    var libraryElement = await driver.getLibraryByUri(a1UriStr);
+    var classA = libraryElement.library.getType('A');
+
+    var resolvedLibrary = await driver.getResolvedLibrary(a1);
+    expect(resolvedLibrary, isNotNull);
+    expect(resolvedLibrary.state, results.ResultState.NOT_A_FILE);
+    expect(() {
+      resolvedLibrary.getElementDeclaration(classA);
+    }, throwsStateError);
+
+    // It is an error to ask for a library when we know that it is a part.
+    expect(() async {
+      await driver.getResolvedLibrary(a2);
+    }, throwsArgumentError);
+  }
+
+  test_getResolvedLibraryByUri_external() async {
+    var a1 = _p('/aaa/lib/a1.dart');
+    var a2 = _p('/aaa/lib/a2.dart');
+
+    var a1UriStr = 'package:aaa/a1.dart';
+    var a2UriStr = 'package:aaa/a2.dart';
+
+    var a1Uri = Uri.parse(a1UriStr);
+    var a2Uri = Uri.parse(a2UriStr);
+
+    provider.newFile(a1, "part 'a2.dart';  class A {}");
+    provider.newFile(a2, "part of 'a1.dart';");
+
+    // Build the store with the library.
+    var store = await createAnalysisDriver().test.getSummaryStore(a1);
+    expect(store.unlinkedMap.keys, contains(a1UriStr));
+    expect(store.unlinkedMap.keys, contains(a2UriStr));
+    expect(store.linkedMap.keys, contains(a1UriStr));
+
+    var driver = createAnalysisDriver(externalSummaries: store);
+    var libraryElement = await driver.getLibraryByUri(a1UriStr);
+    var classA = libraryElement.library.getType('A');
+
+    {
+      var resolvedLibrary = await driver.getResolvedLibraryByUri(a1Uri);
+      expect(resolvedLibrary, isNotNull);
+      expect(resolvedLibrary.state, results.ResultState.NOT_A_FILE);
+      expect(() {
+        resolvedLibrary.getElementDeclaration(classA);
+      }, throwsStateError);
+    }
+
+    // We can also get the result from the session.
+    {
+      var session = driver.currentSession;
+      var resolvedLibrary =
+          await session.getResolvedLibraryByElement(libraryElement);
+      expect(resolvedLibrary, isNotNull);
+      expect(resolvedLibrary.state, results.ResultState.NOT_A_FILE);
+      expect(() {
+        resolvedLibrary.getElementDeclaration(classA);
+      }, throwsStateError);
+    }
+
+    // It is an error to ask for a library when we know that it is a part.
+    expect(() async {
+      await driver.getResolvedLibraryByUri(a2Uri);
+    }, throwsArgumentError);
+  }
+
   test_getResult() async {
     String content = 'int f() => 42;';
     addTestFile(content, priority: true);
diff --git a/pkg/analyzer/test/src/dart/analysis/session_test.dart b/pkg/analyzer/test/src/dart/analysis/session_test.dart
index 0c00794..a5b9b87 100644
--- a/pkg/analyzer/test/src/dart/analysis/session_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/session_test.dart
@@ -2,23 +2,18 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'dart:async';
-
-import 'package:analyzer/dart/analysis/session.dart';
-import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/file_system/memory_file_system.dart';
-import 'package:analyzer/src/dart/analysis/driver.dart';
+import 'package:analyzer/dart/analysis/analysis_context.dart';
+import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/src/dart/analysis/session.dart';
-import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
-import 'package:analyzer/src/dart/element/element.dart';
-import 'package:analyzer/src/dart/element/type.dart';
-import 'package:analyzer/src/generated/engine.dart'
-    show AnalysisOptions, AnalysisOptionsImpl;
 import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
+import '../../context/mock_sdk.dart';
+
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(AnalysisSessionImplTest);
@@ -26,214 +21,281 @@
 }
 
 @reflectiveTest
-class AnalysisSessionImplTest {
-  MockAnalysisDriver driver;
+class AnalysisSessionImplTest with ResourceProviderMixin {
+  AnalysisContextCollection contextCollection;
+  AnalysisContext context;
   AnalysisSessionImpl session;
 
+  String testContextPath;
+  String aaaContextPath;
+  String bbbContextPath;
+
+  String testPath;
+
   void setUp() {
-    driver = new MockAnalysisDriver();
-    session = new AnalysisSessionImpl(driver);
-    driver.currentSession = session;
+    MockSdk(resourceProvider: resourceProvider);
+
+    testContextPath = newFolder('/home/test').path;
+    aaaContextPath = newFolder('/home/aaa').path;
+    bbbContextPath = newFolder('/home/bbb').path;
+
+    newFile('/home/test/.packages', content: r'''
+test:lib/
+''');
+
+    contextCollection = AnalysisContextCollectionImpl(
+      includedPaths: [testContextPath, aaaContextPath, bbbContextPath],
+      resourceProvider: resourceProvider,
+      sdkPath: convertPath(sdkRoot),
+    );
+    context = contextCollection.contextFor(testContextPath);
+    session = context.currentSession;
+
+    testPath = convertPath('/home/test/lib/test.dart');
   }
 
   test_getErrors() async {
-    ErrorsResult result = new ErrorsResult(null, null, null, null, null, null);
-    driver.errorsResult = result;
-    expect(await session.getErrors('path'), result);
+    newFile(testPath, content: 'class C {');
+    var errorsResult = await session.getErrors(testPath);
+    expect(errorsResult.session, session);
+    expect(errorsResult.path, testPath);
+    expect(errorsResult.errors, isNotEmpty);
   }
 
   test_getLibraryByUri() async {
-    String uri = 'uri';
+    newFile(testPath, content: r'''
+class A {}
+class B {}
+''');
 
-    var source = new _SourceMock(Uri.parse(uri));
-    var unit = new CompilationUnitElementImpl()
-      ..librarySource = source
-      ..source = source;
-    var library = new LibraryElementImpl(null, null, null, null)
-      ..definingCompilationUnit = unit;
-
-    driver.libraryMap[uri] = library;
-    expect(await session.getLibraryByUri(uri), library);
+    var library = await session.getLibraryByUri('package:test/test.dart');
+    expect(library.getType('A'), isNotNull);
+    expect(library.getType('B'), isNotNull);
+    expect(library.getType('C'), isNull);
   }
 
-  test_getParsedAst() async {
-    ParseResult result =
-        new ParseResult(null, null, null, null, null, null, null, null);
-    driver.parseResult = result;
-    expect(await session.getParsedAst('path'), result);
+  test_getParsedAstSync() async {
+    newFile(testPath, content: r'''
+class A {}
+class B {}
+''');
+
+    var unitResult = session.getParsedAstSync(testPath);
+    expect(unitResult.session, session);
+    expect(unitResult.path, testPath);
+    expect(unitResult.uri, Uri.parse('package:test/test.dart'));
+    expect(unitResult.unit.declarations, hasLength(2));
   }
 
   test_getResolvedAst() async {
-    AnalysisResult result = new AnalysisResult(driver, null, null, null, null,
-        null, null, null, null, null, null, null);
-    driver.result = result;
-    expect(await session.getResolvedAst('path'), result);
+    newFile(testPath, content: r'''
+class A {}
+class B {}
+''');
+
+    var unitResult = await session.getResolvedAst(testPath);
+    expect(unitResult.session, session);
+    expect(unitResult.path, testPath);
+    expect(unitResult.uri, Uri.parse('package:test/test.dart'));
+    expect(unitResult.unit.declarations, hasLength(2));
+    expect(unitResult.typeProvider, isNotNull);
+    expect(unitResult.libraryElement, isNotNull);
+  }
+
+  test_getResolvedLibrary() async {
+    var a = convertPath('/home/test/lib/a.dart');
+    var b = convertPath('/home/test/lib/b.dart');
+
+    var aContent = r'''
+part 'b.dart';
+
+class A /*a*/ {}
+''';
+    newFile(a, content: aContent);
+
+    var bContent = r'''
+part of 'a.dart';
+
+class B /*b*/ {}
+class B2 extends X {}
+''';
+    newFile(b, content: bContent);
+
+    var resolvedLibrary = await session.getResolvedLibrary(a);
+    expect(resolvedLibrary.session, session);
+    expect(resolvedLibrary.path, a);
+    expect(resolvedLibrary.uri, Uri.parse('package:test/a.dart'));
+
+    var typeProvider = resolvedLibrary.typeProvider;
+    expect(typeProvider.intType.element.name, 'int');
+
+    var libraryElement = resolvedLibrary.element;
+    expect(libraryElement, isNotNull);
+
+    var aClass = libraryElement.getType('A');
+    expect(aClass, isNotNull);
+
+    var bClass = libraryElement.getType('B');
+    expect(bClass, isNotNull);
+
+    var aUnitResult = resolvedLibrary.units[0];
+    expect(aUnitResult.path, a);
+    expect(aUnitResult.uri, Uri.parse('package:test/a.dart'));
+    expect(aUnitResult.content, aContent);
+    expect(aUnitResult.unit, isNotNull);
+    expect(aUnitResult.unit.directives, hasLength(1));
+    expect(aUnitResult.unit.declarations, hasLength(1));
+    expect(aUnitResult.errors, isEmpty);
+
+    var bUnitResult = resolvedLibrary.units[1];
+    expect(bUnitResult.path, b);
+    expect(bUnitResult.uri, Uri.parse('package:test/b.dart'));
+    expect(bUnitResult.content, bContent);
+    expect(bUnitResult.unit, isNotNull);
+    expect(bUnitResult.unit.directives, hasLength(1));
+    expect(bUnitResult.unit.declarations, hasLength(2));
+    expect(bUnitResult.errors, isNotEmpty);
+
+    var aDeclaration = resolvedLibrary.getElementDeclaration(aClass);
+    ClassDeclaration aNode = aDeclaration.node;
+    expect(aNode.name.name, 'A');
+    expect(aNode.offset, 16);
+    expect(aNode.length, 16);
+    expect(aNode.name.staticElement.name, 'A');
+
+    var bDeclaration = resolvedLibrary.getElementDeclaration(bClass);
+    ClassDeclaration bNode = bDeclaration.node;
+    expect(bNode.name.name, 'B');
+    expect(bNode.offset, 19);
+    expect(bNode.length, 16);
+    expect(bNode.name.staticElement.name, 'B');
+  }
+
+  test_getResolvedLibrary_getElementDeclaration_notThisLibrary() async {
+    newFile(testPath, content: '');
+
+    var resolvedLibrary = await session.getResolvedLibrary(testPath);
+
+    expect(() {
+      var intClass = resolvedLibrary.typeProvider.intType.element;
+      resolvedLibrary.getElementDeclaration(intClass);
+    }, throwsArgumentError);
+  }
+
+  test_getResolvedLibrary_getElementDeclaration_synthetic() async {
+    newFile(testPath, content: r'''
+int foo = 0;
+''');
+
+    var resolvedLibrary = await session.getResolvedLibrary(testPath);
+    var unitElement = resolvedLibrary.element.definingCompilationUnit;
+
+    var fooElement = unitElement.topLevelVariables[0];
+    expect(fooElement.name, 'foo');
+
+    // We can get the variable element declaration.
+    var fooDeclaration = resolvedLibrary.getElementDeclaration(fooElement);
+    VariableDeclaration fooNode = fooDeclaration.node;
+    expect(fooNode.name.name, 'foo');
+    expect(fooNode.offset, 4);
+    expect(fooNode.length, 7);
+    expect(fooNode.name.staticElement.name, 'foo');
+
+    // Synthetic elements don't have nodes.
+    expect(resolvedLibrary.getElementDeclaration(fooElement.getter), isNull);
+    expect(resolvedLibrary.getElementDeclaration(fooElement.setter), isNull);
+  }
+
+  test_getResolvedLibrary_invalidPartUri() async {
+    newFile(testPath, content: r'''
+part 'a.dart';
+part ':[invalid uri].dart';
+part 'c.dart';
+''');
+
+    var resolvedLibrary = await session.getResolvedLibrary(testPath);
+
+    expect(resolvedLibrary.units, hasLength(3));
+    expect(
+      resolvedLibrary.units[0].path,
+      convertPath('/home/test/lib/test.dart'),
+    );
+    expect(
+      resolvedLibrary.units[1].path,
+      convertPath('/home/test/lib/a.dart'),
+    );
+    expect(
+      resolvedLibrary.units[2].path,
+      convertPath('/home/test/lib/c.dart'),
+    );
+  }
+
+  test_getResolvedLibrary_notLibrary() async {
+    newFile(testPath, content: 'part of "a.dart";');
+
+    expect(() {
+      session.getResolvedLibrary(testPath);
+    }, throwsArgumentError);
+  }
+
+  test_getResolvedLibraryByElement() async {
+    newFile(testPath, content: '');
+
+    var element = await session.getLibraryByUri('package:test/test.dart');
+
+    var resolvedLibrary = await session.getResolvedLibraryByElement(element);
+    expect(resolvedLibrary.session, session);
+    expect(resolvedLibrary.path, testPath);
+    expect(resolvedLibrary.uri, Uri.parse('package:test/test.dart'));
+    expect(resolvedLibrary.units, hasLength(1));
+    expect(resolvedLibrary.units[0].unit.declaredElement, isNotNull);
   }
 
   test_getSourceKind() async {
-    SourceKind kind = SourceKind.LIBRARY;
-    driver.sourceKind = kind;
-    expect(await session.getSourceKind('path'), kind);
+    newFile(testPath, content: 'class C {}');
+
+    var kind = await session.getSourceKind(testPath);
+    expect(kind, SourceKind.LIBRARY);
   }
 
-  test_getTopLevelDeclarations() async {
-    List<TopLevelDeclarationInSource> declarations = [];
-    driver.topLevelDeclarations = declarations;
-    expect(await session.getTopLevelDeclarations('path'), declarations);
+  test_getSourceKind_part() async {
+    newFile(testPath, content: 'part of "a.dart";');
+
+    var kind = await session.getSourceKind(testPath);
+    expect(kind, SourceKind.PART);
   }
 
   test_getUnitElement() async {
-    UnitElementResult result =
-        new UnitElementResult(null, null, null, null, null);
-    driver.unitElementResult = result;
-    expect(await session.getUnitElement('path'), result);
+    newFile(testPath, content: r'''
+class A {}
+class B {}
+''');
+
+    var unitResult = await session.getUnitElement(testPath);
+    expect(unitResult.session, session);
+    expect(unitResult.path, testPath);
+    expect(unitResult.uri, Uri.parse('package:test/test.dart'));
+    expect(unitResult.element.types, hasLength(2));
+
+    var signature = await session.getUnitElementSignature(testPath);
+    expect(unitResult.signature, signature);
   }
 
-  test_getUnitElementSignature() async {
-    String signature = 'xyzzy';
-    driver.unitElementSignature = signature;
-    expect(await session.getUnitElementSignature('path'), signature);
-  }
-
-  test_resourceProvider() {
-    ResourceProvider resourceProvider = new MemoryResourceProvider();
-    driver.resourceProvider = resourceProvider;
+  test_resourceProvider() async {
     expect(session.resourceProvider, resourceProvider);
   }
 
-  test_sourceFactory() {
-    SourceFactory sourceFactory = new SourceFactory([]);
-    driver.sourceFactory = sourceFactory;
-    expect(session.sourceFactory, sourceFactory);
-  }
-
   test_typeProvider() async {
-    _initializeSDK();
-    expect(await session.typeProvider, isNotNull);
+    var typeProvider = await session.typeProvider;
+    expect(typeProvider.intType.element.name, 'int');
   }
 
   test_typeSystem() async {
-    _initializeSDK();
-    expect(await session.typeSystem, isNotNull);
-  }
-
-  void _initializeSDK() {
-    CompilationUnitElementImpl newUnit(String name) {
-      CompilationUnitElementImpl unit = new CompilationUnitElementImpl();
-      unit.accessors = [];
-      unit.enums = [];
-      unit.functions = [];
-      unit.typeAliases = [];
-      return unit;
-    }
-
-    ClassElementImpl newClass(String name) {
-      TypeParameterElementImpl param = new TypeParameterElementImpl('E', 0);
-      param.type = new TypeParameterTypeImpl(param);
-      ClassElementImpl element = new ClassElementImpl(name, 0);
-      element.typeParameters = [param];
-      return element;
-    }
-
-    {
-      CompilationUnitElementImpl coreUnit = newUnit('dart.core');
-      coreUnit.types = <ClassElement>[newClass('Iterable')];
-      LibraryElementImpl core = new LibraryElementImpl(null, null, null, null);
-      core.definingCompilationUnit = coreUnit;
-      driver.libraryMap['dart:core'] = core;
-    }
-    {
-      CompilationUnitElementImpl asyncUnit = newUnit('dart.async');
-      asyncUnit.types = <ClassElement>[
-        newClass('Future'),
-        newClass('FutureOr'),
-        newClass('Stream')
-      ];
-      LibraryElementImpl async = new LibraryElementImpl(null, null, null, null);
-      async.definingCompilationUnit = asyncUnit;
-      driver.libraryMap['dart:async'] = async;
-    }
-  }
-}
-
-class MockAnalysisDriver implements AnalysisDriver {
-  @override
-  AnalysisSession currentSession;
-
-  ErrorsResult errorsResult;
-  Map<String, LibraryElement> libraryMap = <String, LibraryElement>{};
-  ParseResult parseResult;
-  ResourceProvider resourceProvider;
-  AnalysisResult result;
-  SourceFactory sourceFactory;
-  SourceKind sourceKind;
-  List<TopLevelDeclarationInSource> topLevelDeclarations;
-  UnitElementResult unitElementResult;
-  String unitElementSignature;
-
-  AnalysisOptions get analysisOptions => new AnalysisOptionsImpl();
-
-  @override
-  Future<ErrorsResult> getErrors(String path) async {
-    return errorsResult;
-  }
-
-  @override
-  Future<LibraryElement> getLibraryByUri(String uri) async {
-    return libraryMap[uri];
-  }
-
-  @override
-  Future<AnalysisResult> getResult(String path,
-      {bool sendCachedToStream: false}) async {
-    return result;
-  }
-
-  @override
-  Future<SourceKind> getSourceKind(String path) async {
-    return sourceKind;
-  }
-
-  @override
-  Future<List<TopLevelDeclarationInSource>> getTopLevelNameDeclarations(
-      String name) async {
-    return topLevelDeclarations;
-  }
-
-  @override
-  Future<UnitElementResult> getUnitElement(String path) async {
-    return unitElementResult;
-  }
-
-  @override
-  Future<String> getUnitElementSignature(String path) async {
-    return unitElementSignature;
-  }
-
-  @override
-  dynamic noSuchMethod(Invocation invocation) {
-    fail('Unexpected invocation of ${invocation.memberName}');
-  }
-
-  @override
-  Future<ParseResult> parseFile(String path) async {
-    return parseResult;
-  }
-
-  @override
-  ParseResult parseFileSync(String path) {
-    return parseResult;
-  }
-}
-
-class _SourceMock implements Source {
-  @override
-  final Uri uri;
-
-  _SourceMock(this.uri);
-
-  @override
-  noSuchMethod(Invocation invocation) {
-    throw new StateError('Unexpected invocation of ${invocation.memberName}');
+    var typeSystem = await session.typeSystem;
+    var typeProvider = typeSystem.typeProvider;
+    expect(
+      typeSystem.isSubtypeOf(typeProvider.intType, typeProvider.numType),
+      isTrue,
+    );
   }
 }