Add FileResolver.linkLibraries(), fix null typeProvider.

Change-Id: I4222097336407736e4cf0c3e922e9fac8c762725
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/167840
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Keerti Parthasarathy <keertip@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_context.dart b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
index f28f239..1402523 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_context.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
@@ -235,14 +235,6 @@
         librariesLoaded += cycle.libraries.length;
       }
 
-      // We are about to load dart:core, but if we have just linked it, the
-      // linker might have set the type provider. So, clear it, and recreate
-      // the element factory - it is empty anyway.
-      if (!elementFactory.hasDartCore) {
-        analysisContext.clearTypeProvider();
-        _createElementFactory();
-      }
-
       var bundle = LinkedNodeBundle.fromBuffer(bytes);
       inputBundles.add(bundle);
       elementFactory.addBundle(
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index 53409d0..bd7ab2fa 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -271,6 +271,49 @@
     return file.libraryCycle.signatureStr;
   }
 
+  /// Ensure that libraries necessary for resolving [path] are linked.
+  ///
+  /// Libraries are linked in library cycles, from the bottom to top, so that
+  /// when we link a cycle, everything it transitively depends is ready. We
+  /// load newly linked libraries from bytes, and when we link a new library
+  /// cycle we partially resynthesize AST and elements from previously
+  /// loaded libraries.
+  ///
+  /// But when we are done linking libraries, and want to resolve just the
+  /// very top library that transitively depends on the whole dependency
+  /// tree, this library will not reference as many elements in the
+  /// dependencies as we needed for linking. Most probably it references
+  /// elements from directly imported libraries, and a couple of layers below.
+  /// So, keeping all previously resynthesized data is usually a waste.
+  ///
+  /// This method ensures that we discard the libraries context, with all its
+  /// partially resynthesized data, and so prepare for loading linked summaries
+  /// from bytes, which will be done by [getErrors]. It is OK for it to
+  /// spend some more time on this.
+  void linkLibraries({
+    @required String path,
+  }) {
+    _throwIfNotAbsoluteNormalizedPath(path);
+
+    var performance = OperationPerformanceImpl('<unused>');
+
+    _withLibraryContextReset(() {
+      var fileContext = getFileContext(
+        path: path,
+        performance: performance,
+      );
+      var file = fileContext.file;
+      var libraryFile = file.partOfLibrary ?? file;
+
+      libraryContext.load2(
+        targetLibrary: libraryFile,
+        performance: performance,
+      );
+    });
+
+    _resetContextObjects();
+  }
+
   /// The [completionLine] and [completionColumn] are zero based.
   ResolvedUnitResult resolve({
     int completionLine,
@@ -531,6 +574,13 @@
     }
   }
 
+  void _resetContextObjects() {
+    if (libraryContext != null) {
+      contextObjects = null;
+      libraryContext = null;
+    }
+  }
+
   void _throwIfNotAbsoluteNormalizedPath(String path) {
     var pathContext = resourceProvider.pathContext;
     if (pathContext.normalize(path) != path) {
@@ -691,14 +741,6 @@
         performance.getDataInt('libraryLoadCount').add(cycle.libraries.length);
       }
 
-      // We are about to load dart:core, but if we have just linked it, the
-      // linker might have set the type provider. So, clear it, and recreate
-      // the element factory - it is empty anyway.
-      if (!elementFactory.hasDartCore) {
-        contextObjects.analysisContext.clearTypeProvider();
-        elementFactory.declareDartCoreDynamicNever();
-      }
-
       var cBundle = CiderLinkedLibraryCycle.fromBuffer(bytes);
       inputBundles.add(cBundle.bundle);
       elementFactory.addBundle(
@@ -715,6 +757,9 @@
           );
         }
       }
+
+      // We might have just linked dart:core, ensure the type provider.
+      _createElementFactoryTypeProvider();
     }
 
     logger.run('Prepare linked bundles', () {
@@ -732,7 +777,7 @@
   /// If we need these libraries later, we will relink and reattach them.
   void remove(List<FileState> removed) {
     elementFactory.removeLibraries(
-      removed.map((e) => e.uriStr).toList(),
+      removed.map((e) => e.uriStr).toSet(),
     );
 
     var removedSet = removed.toSet();
@@ -741,6 +786,16 @@
     });
   }
 
+  /// Ensure that type provider is created.
+  void _createElementFactoryTypeProvider() {
+    var analysisContext = contextObjects.analysisContext;
+    if (analysisContext.typeProviderNonNullableByDefault == null) {
+      var dartCore = elementFactory.libraryOfUri('dart:core');
+      var dartAsync = elementFactory.libraryOfUri('dart:async');
+      elementFactory.createTypeProviders(dartCore, dartAsync);
+    }
+  }
+
   static CiderLinkedLibraryCycleBuilder serializeBundle(
       List<int> signature, link2.LinkResult linkResult) {
     return CiderLinkedLibraryCycleBuilder(
@@ -791,10 +846,7 @@
       if (resetTimeout != null) {
         _timer = Timer(resetTimeout, () {
           _timer = null;
-          if (fileResolver.libraryContext != null) {
-            fileResolver.contextObjects = null;
-            fileResolver.libraryContext = null;
-          }
+          fileResolver._resetContextObjects();
         });
       }
     }
diff --git a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
index 8dd1b7c..abfaeec 100644
--- a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
+++ b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
@@ -28,7 +28,7 @@
   ) {
     ArgumentError.checkNotNull(analysisContext, 'analysisContext');
     ArgumentError.checkNotNull(analysisSession, 'analysisSession');
-    declareDartCoreDynamicNever();
+    _declareDartCoreDynamicNever();
   }
 
   Reference get dynamicRef {
@@ -95,12 +95,6 @@
     }
   }
 
-  void declareDartCoreDynamicNever() {
-    var dartCoreRef = rootReference.getChild('dart:core');
-    dartCoreRef.getChild('dynamic').element = DynamicElementImpl.instance;
-    dartCoreRef.getChild('Never').element = NeverElementImpl.instance;
-  }
-
   Element elementOfReference(Reference reference) {
     if (reference.element != null) {
       return reference.element;
@@ -147,19 +141,38 @@
   /// We have linked the bundle, and need to disconnect its libraries, so
   /// that the client can re-add the bundle, this time read from bytes.
   void removeBundle(LinkedBundleContext context) {
-    var uriStrList = context.libraryMap.keys.toList();
-    removeLibraries(uriStrList);
+    var uriStrSet = context.libraryMap.keys.toSet();
+    removeLibraries(uriStrSet);
+
+    // This is the bundle with dart:core and dart:async, based on full ASTs.
+    // To link them, the linker set the type provider. We are removing these
+    // libraries, and we should also remove the type provider.
+    if (uriStrSet.contains('dart:core')) {
+      if (!uriStrSet.contains('dart:async')) {
+        throw StateError(
+          'Expected to link dart:core and dart:async together: '
+          '${uriStrSet.toList()}',
+        );
+      }
+      if (libraryMap.isNotEmpty) {
+        throw StateError(
+          'Expected to link dart:core and dart:async first: '
+          '${libraryMap.keys.toList()}',
+        );
+      }
+      analysisContext.clearTypeProvider();
+      _declareDartCoreDynamicNever();
+    }
   }
 
   /// Remove libraries with the specified URIs from the reference tree, and
   /// any session level caches.
-  void removeLibraries(List<String> uriStrList) {
-    for (var uriStr in uriStrList) {
+  void removeLibraries(Set<String> uriStrSet) {
+    for (var uriStr in uriStrSet) {
       libraryMap.remove(uriStr);
       rootReference.removeChild(uriStr);
     }
 
-    var uriStrSet = uriStrList.toSet();
     analysisSession.classHierarchy.removeOfLibraries(uriStrSet);
     analysisSession.inheritanceManager.removeOfLibraries(uriStrSet);
   }
@@ -181,6 +194,12 @@
     }
   }
 
+  void _declareDartCoreDynamicNever() {
+    var dartCoreRef = rootReference.getChild('dart:core');
+    dartCoreRef.getChild('dynamic').element = DynamicElementImpl.instance;
+    dartCoreRef.getChild('Never').element = NeverElementImpl.instance;
+  }
+
   void _setLibraryTypeSystem(LibraryElementImpl libraryElement) {
     // During linking we create libraries when typeProvider is not ready.
     // And if we link dart:core and dart:async, we cannot create it.
diff --git a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
index 45500b8..5a7a974 100644
--- a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
+++ b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
@@ -464,6 +464,24 @@
     ]);
   }
 
+  test_linkLibraries_getErrors() {
+    addTestFile(r'''
+var a = b;
+var foo = 0;
+''');
+
+    var path = convertPath('/workspace/dart/test/lib/test.dart');
+    fileResolver.linkLibraries(path: path);
+
+    var result = getTestErrors();
+    expect(result.path, path);
+    expect(result.uri.toString(), 'package:dart.test/test.dart');
+    assertErrorsInList(result.errors, [
+      error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 8, 1),
+    ]);
+    expect(result.lineInfo.lineStarts, [0, 11, 24]);
+  }
+
   test_nullSafety_enabled() async {
     typeToStringWithNullability = true;