Issue 24812. Fix for detached CompilationUnitElement(s) after package version switch.

R=brianwilkerson@google.com
BUG= https://github.com/dart-lang/sdk/issues/24812

Review URL: https://codereview.chromium.org/1441013003 .
diff --git a/pkg/analyzer/lib/src/task/dart_work_manager.dart b/pkg/analyzer/lib/src/task/dart_work_manager.dart
index 62a25ea..793d9ce 100644
--- a/pkg/analyzer/lib/src/task/dart_work_manager.dart
+++ b/pkg/analyzer/lib/src/task/dart_work_manager.dart
@@ -88,7 +88,8 @@
     analysisCache.onResultInvalidated.listen((InvalidatedResult event) {
       if (event.descriptor == LIBRARY_ERRORS_READY) {
         CacheEntry entry = event.entry;
-        if (entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY) {
+        if (entry.explicitlyAdded &&
+            entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY) {
           librarySourceQueue.add(entry.target);
         }
       }
@@ -392,7 +393,10 @@
         unitTargets.add(target);
         Source library = target.library;
         if (context.exists(library)) {
-          librarySourceQueue.add(library);
+          CacheEntry entry = iterator.value;
+          if (entry.explicitlyAdded) {
+            librarySourceQueue.add(library);
+          }
         }
       }
     }
@@ -408,6 +412,8 @@
         entry.setState(EXPLICITLY_IMPORTED_LIBRARIES, CacheState.INVALID);
         entry.setState(EXPORTED_LIBRARIES, CacheState.INVALID);
         entry.setState(INCLUDED_PARTS, CacheState.INVALID);
+        entry.setState(LIBRARY_SPECIFIC_UNITS, CacheState.INVALID);
+        entry.setState(UNITS, CacheState.INVALID);
       }
     }
   }
diff --git a/pkg/analyzer/test/src/context/abstract_context.dart b/pkg/analyzer/test/src/context/abstract_context.dart
index 09c30b2..135fd42 100644
--- a/pkg/analyzer/test/src/context/abstract_context.dart
+++ b/pkg/analyzer/test/src/context/abstract_context.dart
@@ -28,6 +28,9 @@
   AnalysisCache analysisCache;
   AnalysisDriver analysisDriver;
 
+  UriResolver sdkResolver;
+  UriResolver resourceResolver;
+
   AnalysisTask task;
   Map<ResultDescriptor<dynamic>, dynamic> oldOutputs;
   Map<ResultDescriptor<dynamic>, dynamic> outputs;
@@ -107,10 +110,10 @@
   }
 
   void prepareAnalysisContext([AnalysisOptions options]) {
-    sourceFactory = new SourceFactory(<UriResolver>[
-      new DartUriResolver(sdk),
-      new ResourceUriResolver(resourceProvider)
-    ]);
+    sdkResolver = new DartUriResolver(sdk);
+    resourceResolver = new ResourceUriResolver(resourceProvider);
+    sourceFactory =
+        new SourceFactory(<UriResolver>[sdkResolver, resourceResolver]);
     context = createAnalysisContext();
     if (options != null) {
       context.analysisOptions = options;
diff --git a/pkg/analyzer/test/src/context/context_test.dart b/pkg/analyzer/test/src/context/context_test.dart
index f0d2110..4d68fd3 100644
--- a/pkg/analyzer/test/src/context/context_test.dart
+++ b/pkg/analyzer/test/src/context/context_test.dart
@@ -7,6 +7,7 @@
 import 'dart:async';
 
 import 'package:analyzer/file_system/memory_file_system.dart';
+import 'package:analyzer/source/package_map_resolver.dart';
 import 'package:analyzer/src/cancelable_future.dart';
 import 'package:analyzer/src/context/cache.dart';
 import 'package:analyzer/src/context/context.dart';
@@ -34,6 +35,7 @@
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/generated/scanner.dart';
 import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/generated/utilities_collection.dart';
 import 'package:analyzer/src/task/dart.dart';
 import 'package:analyzer/src/task/html.dart';
 import 'package:analyzer/task/dart.dart';
@@ -1954,6 +1956,60 @@
     expect(resolvedUnitUris, contains('file:///test.dart'));
   }
 
+  void test_performAnalysisTask_switchPackageVersion() {
+    // version 1
+    resourceProvider.newFile(
+        '/pkgs/crypto-1/lib/crypto.dart',
+        r'''
+library crypto;
+part 'src/hash_utils.dart';
+''');
+    resourceProvider.newFile(
+        '/pkgs/crypto-1/lib/src/hash_utils.dart',
+        r'''
+part of crypto;
+const _MASK_8 = 0xff;
+''');
+    // version 2
+    resourceProvider.newFile(
+        '/pkgs/crypto-2/lib/crypto.dart',
+        r'''
+library crypto;
+part 'src/hash_utils.dart';
+''');
+    resourceProvider.newFile(
+        '/pkgs/crypto-2/lib/src/hash_utils.dart',
+        r'''
+part of crypto;
+const _MASK_8 = 0xff;
+''');
+    // use version 1
+    context.sourceFactory = new SourceFactory(<UriResolver>[
+      sdkResolver,
+      resourceResolver,
+      new PackageMapUriResolver(resourceProvider, {
+        'crypto': [resourceProvider.getFolder('/pkgs/crypto-1/lib')]
+      })
+    ]);
+    // analyze
+    addSource(
+        "/test.dart",
+        r'''
+import 'package:crypto/crypto.dart';
+''');
+    _analyzeAll_assertFinished();
+    // use version 2
+    context.sourceFactory = new SourceFactory(<UriResolver>[
+      sdkResolver,
+      resourceResolver,
+      new PackageMapUriResolver(resourceProvider, {
+        'crypto': [resourceProvider.getFolder('/pkgs/crypto-2/lib')]
+      })
+    ]);
+    _analyzeAll_assertFinished();
+    _assertNoExceptions();
+  }
+
   void test_resolveCompilationUnit_import_relative() {
     Source sourceA =
         addSource("/libA.dart", "library libA; import 'libB.dart'; class A{}");
@@ -1993,6 +2049,13 @@
     expect(compilationUnit.element, isNotNull);
   }
 
+  void test_resolveCompilationUnit_source() {
+    Source source = addSource("/lib.dart", "library lib;");
+    CompilationUnit compilationUnit =
+        context.resolveCompilationUnit2(source, source);
+    expect(compilationUnit, isNotNull);
+  }
+
 //  void test_resolveCompilationUnit_sourceChangeDuringResolution() {
 //    _context = new _AnalysisContext_sourceChangeDuringResolution();
 //    AnalysisContextFactory.initContextWithCore(_context);
@@ -2004,13 +2067,6 @@
 //    expect(_context.getLineInfo(source), isNotNull);
 //  }
 
-  void test_resolveCompilationUnit_source() {
-    Source source = addSource("/lib.dart", "library lib;");
-    CompilationUnit compilationUnit =
-        context.resolveCompilationUnit2(source, source);
-    expect(compilationUnit, isNotNull);
-  }
-
   void test_setAnalysisOptions() {
     AnalysisOptionsImpl options = new AnalysisOptionsImpl();
     options.cacheSize = 42;
@@ -2263,6 +2319,24 @@
     fail("performAnalysisTask failed to terminate after analyzing all sources");
   }
 
+  void _assertNoExceptions() {
+    MapIterator<AnalysisTarget, CacheEntry> iterator = analysisCache.iterator();
+    String exceptionsStr = '';
+    while (iterator.moveNext()) {
+      CaughtException exception = iterator.value.exception;
+      if (exception != null) {
+        AnalysisTarget target = iterator.key;
+        exceptionsStr +=
+            '============= key: $target   source: ${target.source}\n';
+        exceptionsStr += exception.toString();
+        exceptionsStr += '\n';
+      }
+    }
+    if (exceptionsStr.isNotEmpty) {
+      fail(exceptionsStr);
+    }
+  }
+
   void _changeSource(TestSource source, String contents) {
     source.setContents(contents);
     ChangeSet changeSet = new ChangeSet();
diff --git a/pkg/analyzer/test/src/task/dart_work_manager_test.dart b/pkg/analyzer/test/src/task/dart_work_manager_test.dart
index 8dd7dbc..a10cfef 100644
--- a/pkg/analyzer/test/src/task/dart_work_manager_test.dart
+++ b/pkg/analyzer/test/src/task/dart_work_manager_test.dart
@@ -64,10 +64,10 @@
   void setUp() {
     cache = context.analysisCache;
     manager = new DartWorkManager(context);
-    entry1 = context.getCacheEntry(source1);
-    entry2 = context.getCacheEntry(source2);
-    entry3 = context.getCacheEntry(source3);
-    entry4 = context.getCacheEntry(source4);
+    entry1 = _getOrCreateEntry(source1);
+    entry2 = _getOrCreateEntry(source2);
+    entry3 = _getOrCreateEntry(source3);
+    entry4 = _getOrCreateEntry(source4);
   }
 
   void test_applyChange_add() {
@@ -509,6 +509,8 @@
   }
 
   void test_onResultInvalidated_scheduleInvalidatedLibraries() {
+    // make source3 implicit
+    entry3.explicitlyAdded = false;
     // set SOURCE_KIND
     entry1.setValue(SOURCE_KIND, SourceKind.LIBRARY, []);
     entry2.setValue(SOURCE_KIND, SourceKind.PART, []);
@@ -519,9 +521,9 @@
     // invalidate LIBRARY_ERRORS_READY for source1, schedule it
     entry1.setState(LIBRARY_ERRORS_READY, CacheState.INVALID);
     expect_librarySourceQueue([source1]);
-    // invalidate LIBRARY_ERRORS_READY for source3, schedule it
+    // invalidate LIBRARY_ERRORS_READY for source3, implicit, not scheduled
     entry3.setState(LIBRARY_ERRORS_READY, CacheState.INVALID);
-    expect_librarySourceQueue([source1, source3]);
+    expect_librarySourceQueue([source1]);
   }
 
   void test_onSourceFactoryChanged() {
@@ -532,6 +534,8 @@
     entry1.setValue(EXPLICITLY_IMPORTED_LIBRARIES, <Source>[], []);
     entry1.setValue(EXPORTED_LIBRARIES, <Source>[], []);
     entry1.setValue(INCLUDED_PARTS, <Source>[], []);
+    entry1.setValue(LIBRARY_SPECIFIC_UNITS, <LibrarySpecificUnit>[], []);
+    entry1.setValue(UNITS, <Source>[], []);
     // configure LibrarySpecificUnit
     LibrarySpecificUnit unitTarget = new LibrarySpecificUnit(source2, source3);
     CacheEntry unitEntry = new CacheEntry(unitTarget);
@@ -548,6 +552,8 @@
     expect(entry1.getState(EXPLICITLY_IMPORTED_LIBRARIES), CacheState.INVALID);
     expect(entry1.getState(EXPORTED_LIBRARIES), CacheState.INVALID);
     expect(entry1.getState(INCLUDED_PARTS), CacheState.INVALID);
+    expect(entry1.getState(LIBRARY_SPECIFIC_UNITS), CacheState.INVALID);
+    expect(entry1.getState(UNITS), CacheState.INVALID);
   }
 
   void test_resultsComputed_errors_forLibrarySpecificUnit() {
@@ -745,10 +751,11 @@
     expect(manager.libraryPartsMap, isEmpty);
   }
 
-  CacheEntry _getOrCreateEntry(Source source) {
+  CacheEntry _getOrCreateEntry(Source source, [bool explicit = true]) {
     CacheEntry entry = cache.get(source);
     if (entry == null) {
       entry = new CacheEntry(source);
+      entry.explicitlyAdded = explicit;
       cache.put(entry);
     }
     return entry;