Create a new session on a file change.

This should restore throwing InconsistentAnalysisException.

Internal presubmit looks green.
https://fusion2.corp.google.com/presubmit/tap/424245133/OCL:424245133:BASE:424340361:1643210802936:6f26b405

Change-Id: I286d5db0fe51e8ea00d0fe83a70d4c9d59052715
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/230040
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2022-01-27 00:44:39 +00:00 committed by Commit Bot
parent 17563522da
commit da48a6fb12
6 changed files with 159 additions and 92 deletions

View file

@ -223,12 +223,7 @@ class AnalysisDriver implements AnalysisDriverGeneric {
/// Whether resolved units should be indexed.
final bool enableIndex;
/// The current analysis session.
late final AnalysisSessionImpl _currentSession = AnalysisSessionImpl(this);
/// The current library context, consistent with the [_currentSession].
///
/// TODO(scheglov) We probably should tie it into the session.
/// The context in which libraries should be analyzed.
LibraryContext? _libraryContext;
/// Whether `dart:core` has been transitively discovered.
@ -311,7 +306,9 @@ class AnalysisDriver implements AnalysisDriverGeneric {
AnalysisOptions get analysisOptions => _analysisOptions;
/// Return the current analysis session.
AnalysisSessionImpl get currentSession => _currentSession;
AnalysisSessionImpl get currentSession {
return libraryContext.elementFactory.analysisSession;
}
/// Return the stream that produces [ExceptionResult]s.
Stream<ExceptionResult> get exceptions => _exceptionController.stream;
@ -337,7 +334,7 @@ class AnalysisDriver implements AnalysisDriverGeneric {
LibraryContext get libraryContext {
return _libraryContext ??= LibraryContext(
testView: _testView.libraryContextTestView,
session: currentSession,
analysisSession: AnalysisSessionImpl(this),
logger: _logger,
byteStore: _byteStore,
analysisOptions: _analysisOptions,
@ -515,9 +512,7 @@ class AnalysisDriver implements AnalysisDriverGeneric {
/// periodically.
@visibleForTesting
void clearLibraryContext() {
_libraryContext?.invalidAllLibraries();
_libraryContext = null;
_currentSession.clearHierarchies();
}
/// Some state on which analysis depends has changed, so the driver needs to be
@ -654,7 +649,7 @@ class AnalysisDriver implements AnalysisDriverGeneric {
FileState file = _fileTracker.getFile(path);
return FileResultImpl(
_currentSession, path, file.uri, file.lineInfo, file.isPart);
currentSession, path, file.uri, file.lineInfo, file.isPart);
}
/// Return the [FileResult] for the Dart file with the given [path].
@ -1328,7 +1323,7 @@ class AnalysisDriver implements AnalysisDriverGeneric {
declaredVariables,
sourceFactory,
libraryContext.elementFactory.libraryOfUri2(library.uriStr),
libraryContext.analysisSession.inheritanceManager,
libraryContext.elementFactory.analysisSession.inheritanceManager,
library,
testingData: testingData,
).analyzeForCompletion(
@ -1441,7 +1436,7 @@ class AnalysisDriver implements AnalysisDriverGeneric {
declaredVariables,
sourceFactory,
libraryContext.elementFactory.libraryOfUri2(library.uriStr),
libraryContext.analysisSession.inheritanceManager,
libraryContext.elementFactory.analysisSession.inheritanceManager,
library,
testingData: testingData,
).analyze();
@ -1512,7 +1507,7 @@ class AnalysisDriver implements AnalysisDriverGeneric {
declaredVariables,
sourceFactory,
libraryContext.elementFactory.libraryOfUri2(library.uriStr),
libraryContext.analysisSession.inheritanceManager,
libraryContext.elementFactory.analysisSession.inheritanceManager,
library,
testingData: testingData)
.analyze();
@ -1779,6 +1774,10 @@ class AnalysisDriver implements AnalysisDriverGeneric {
_libraryContext?.elementFactory.removeLibraries(
affected.map((e) => e.uriStr).toSet(),
);
_libraryContext?.elementFactory.replaceAnalysisSession(
AnalysisSessionImpl(this),
);
}
void _reportException(String path, Object exception, StackTrace stackTrace) {

View file

@ -39,8 +39,6 @@ class LibraryContext {
final LibraryContextTestView testView;
final PerformanceLog logger;
final ByteStore byteStore;
final AnalysisSessionImpl analysisSession;
final SummaryDataStore? externalSummaries;
final SummaryDataStore store = SummaryDataStore([]);
late final AnalysisContextImpl analysisContext;
@ -48,21 +46,35 @@ class LibraryContext {
LibraryContext({
required this.testView,
required AnalysisSessionImpl session,
required AnalysisSessionImpl analysisSession,
required PerformanceLog logger,
required ByteStore byteStore,
required AnalysisOptionsImpl analysisOptions,
required DeclaredVariables declaredVariables,
required SourceFactory sourceFactory,
required this.externalSummaries,
required SummaryDataStore? externalSummaries,
}) : logger = logger,
byteStore = byteStore,
analysisSession = session {
byteStore = byteStore {
var synchronousSession =
SynchronousSession(analysisOptions, declaredVariables);
analysisContext = AnalysisContextImpl(synchronousSession, sourceFactory);
_createElementFactory();
elementFactory = LinkedElementFactory(
analysisContext,
analysisSession,
Reference.root(),
);
if (externalSummaries != null) {
for (var bundle in externalSummaries.bundles) {
elementFactory.addBundle(
BundleReader(
elementFactory: elementFactory,
resolutionBytes: bundle.resolutionBytes,
unitsInformativeBytes: {},
),
);
}
}
}
/// Computes a [CompilationUnitElement] for the given library/unit pair.
@ -86,11 +98,6 @@ class LibraryContext {
return elementFactory.libraryOfUriIfReady(uriStr);
}
/// We are about to discard this context, mark all libraries invalid.
void invalidAllLibraries() {
elementFactory.invalidateAllLibraries();
}
/// Load data required to access elements of the given [targetLibrary].
void load2(FileState targetLibrary) {
timerLoad2.start();
@ -222,25 +229,6 @@ class LibraryContext {
timerLoad2.stop();
}
void _createElementFactory() {
elementFactory = LinkedElementFactory(
analysisContext,
analysisSession,
Reference.root(),
);
if (externalSummaries != null) {
for (var bundle in externalSummaries!.bundles) {
elementFactory.addBundle(
BundleReader(
elementFactory: elementFactory,
resolutionBytes: bundle.resolutionBytes,
unitsInformativeBytes: {},
),
);
}
}
}
/// Ensure that type provider is created.
void _createElementFactoryTypeProvider() {
if (!analysisContext.hasTypeProvider) {

View file

@ -3586,13 +3586,7 @@ class LibraryElementImpl extends _ExistingElementImpl
final AnalysisContext context;
@override
final AnalysisSession session;
/// If `true`, then this library is valid in the session.
///
/// A library becomes invalid when one of its files, or one of its
/// dependencies, changes.
bool isValid = true;
AnalysisSession session;
/// The language version for the library.
LibraryLanguageVersion? _languageVersion;

View file

@ -13,7 +13,7 @@ import 'package:analyzer/src/summary2/reference.dart';
class LinkedElementFactory {
final AnalysisContextImpl analysisContext;
final AnalysisSessionImpl analysisSession;
AnalysisSessionImpl analysisSession;
final Reference rootReference;
final Map<String, LibraryReader> _libraryReaders = {};
final Map<String, List<Reference>> _exportsOfLibrary = {};
@ -165,13 +165,6 @@ class LinkedElementFactory {
return _libraryReaders[uriStr] != null;
}
/// We are about to discard this factory, mark all libraries invalid.
void invalidateAllLibraries() {
for (var libraryReference in rootReference.children) {
_invalidateLibrary(libraryReference);
}
}
LibraryElementImpl? libraryOfUri(String uriStr) {
var reference = rootReference.getChild(uriStr);
return elementOfReference(reference) as LibraryElementImpl?;
@ -204,8 +197,7 @@ class LinkedElementFactory {
for (var uriStr in uriStrSet) {
_exportsOfLibrary.remove(uriStr);
_libraryReaders.remove(uriStr);
var libraryReference = rootReference.removeChild(uriStr);
_invalidateLibrary(libraryReference);
rootReference.removeChild(uriStr);
}
analysisSession.classHierarchy.removeOfLibraries(uriStrSet);
@ -230,6 +222,16 @@ class LinkedElementFactory {
}
}
void replaceAnalysisSession(AnalysisSessionImpl newSession) {
analysisSession = newSession;
for (var libraryReference in rootReference.children) {
var libraryElement = libraryReference.element;
if (libraryElement is LibraryElementImpl) {
libraryElement.session = newSession;
}
}
}
/// Set exports of the library with [uriStr], after building exports during
/// linking, or after reading a linked bundle.
void setExportsOfLibrary(String uriStr, List<Reference> exports) {
@ -255,11 +257,4 @@ class LinkedElementFactory {
libraryElement.createLoadLibraryFunction();
}
void _invalidateLibrary(Reference? libraryReference) {
var libraryElement = libraryReference?.element;
if (libraryElement is LibraryElementImpl) {
libraryElement.isValid = false;
}
}
}

View file

@ -819,12 +819,13 @@ import 'c.dart';
],
);
// All libraries are valid.
expect(a_element.isValid, true);
expect(b_element.isValid, true);
expect(c_element.isValid, true);
expect(d_element.isValid, true);
expect(e_element.isValid, true);
// All libraries have the current session.
var session1 = driver.currentSession;
expect(a_element.session, session1);
expect(b_element.session, session1);
expect(c_element.session, session1);
expect(d_element.session, session1);
expect(e_element.session, session1);
// Change `b.dart`, also removes `c.dart` and `d.dart` that import it.
// But `a.dart` and `d.dart` is not affected.
@ -841,12 +842,17 @@ import 'c.dart';
],
);
// Only `a.dart` and `e.dart` are still valid.
expect(a_element.isValid, true);
expect(b_element.isValid, false);
expect(c_element.isValid, false);
expect(d_element.isValid, false);
expect(e_element.isValid, true);
// We have a new session.
var session2 = driver.currentSession;
expect(session2, isNot(session1));
// `a.dart` and `e.dart` moved to the new session.
// Invalidated libraries stuck with the old session.
expect(a_element.session, session2);
expect(b_element.session, session1);
expect(c_element.session, session1);
expect(d_element.session, session1);
expect(e_element.session, session2);
}
test_changeFile_potentiallyAffected_part() async {
@ -880,10 +886,11 @@ import 'b.dart';
],
);
// All libraries are valid.
expect(b_element.isValid, true);
expect(c_element.isValid, true);
expect(d_element.isValid, true);
// All libraries have the current session.
var session1 = driver.currentSession;
expect(b_element.session, session1);
expect(c_element.session, session1);
expect(d_element.session, session1);
// Change `a.dart`, remove `b.dart` that part it.
// Removes `c.dart` that imports `b.dart`.
@ -899,10 +906,15 @@ import 'b.dart';
],
);
// Only `d.dart` is still valid.
expect(b_element.isValid, false);
expect(c_element.isValid, false);
expect(d_element.isValid, true);
// We have a new session.
var session2 = driver.currentSession;
expect(session2, isNot(session1));
// `d.dart` moved to the new session.
// Invalidated libraries stuck with the old session.
expect(b_element.session, session1);
expect(c_element.session, session1);
expect(d_element.session, session2);
}
test_changeFile_selfConsistent() async {
@ -1180,9 +1192,8 @@ const x = 1;
var session2 = driver.currentSession;
expect(session2, isNotNull);
// We don't discard the session anymore.
// So, the session is always the same.
expect(session2, same(session1));
// We get a new session.
expect(session2, isNot(session1));
}
test_discoverAvailableFiles_packages() async {

View file

@ -134,6 +134,16 @@ class AnalysisSessionImplTest extends PubPackageResolutionTest {
expect(errorsResult.errors, isNotEmpty);
}
test_getErrors_inconsistent() async {
var test = newFile(testFilePath, content: '');
var session = contextFor(test.path).currentSession;
driverFor(test.path).changeFile(test.path);
expect(
() => session.getErrors(test.path),
throwsA(isA<InconsistentAnalysisException>()),
);
}
test_getErrors_invalidPath_notAbsolute() async {
var session = contextFor(testFilePath).currentSession;
var errorsResult = await session.getErrors('not_absolute.dart');
@ -146,6 +156,16 @@ class AnalysisSessionImplTest extends PubPackageResolutionTest {
expect(errorsResult, isA<InvalidPathResult>());
}
test_getFileSync_inconsistent() async {
var test = newFile(testFilePath, content: '');
var session = contextFor(test.path).currentSession;
driverFor(test.path).changeFile(test.path);
expect(
() => session.getFile(test.path),
throwsA(isA<InconsistentAnalysisException>()),
);
}
test_getFileSync_library() async {
var a = newFile('$testPackageLibPath/a.dart', content: '');
@ -180,6 +200,16 @@ class B {}
expect(library.getType('C'), isNull);
}
test_getLibraryByUri_inconsistent() async {
var test = newFile(testFilePath, content: '');
var session = contextFor(test.path).currentSession;
driverFor(test.path).changeFile(test.path);
expect(
() => session.getLibraryByUriValid('package:test/test.dart'),
throwsA(isA<InconsistentAnalysisException>()),
);
}
test_getLibraryByUri_unresolvedUri() async {
var session = contextFor(testFilePath).currentSession;
var result = await session.getLibraryByUri('package:foo/foo.dart');
@ -267,6 +297,16 @@ int foo = 0;
expect(parsedLibrary.getElementDeclaration(fooElement.setter!), isNull);
}
test_getParsedLibrary_inconsistent() async {
var test = newFile(testFilePath, content: '');
var session = contextFor(test.path).currentSession;
driverFor(test.path).changeFile(test.path);
expect(
() => session.getParsedLibrary(test.path),
throwsA(isA<InconsistentAnalysisException>()),
);
}
test_getParsedLibrary_invalidPartUri() async {
var test = newFile(testFilePath, content: r'''
part 'a.dart';
@ -407,6 +447,16 @@ class B {}
expect(unitResult.unit.declarations, hasLength(2));
}
test_getParsedUnit_inconsistent() async {
var test = newFile(testFilePath, content: '');
var session = contextFor(test.path).currentSession;
driverFor(test.path).changeFile(test.path);
expect(
() => session.getParsedUnit(test.path),
throwsA(isA<InconsistentAnalysisException>()),
);
}
test_getParsedUnit_invalidPath_notAbsolute() async {
var session = contextFor(testFilePath).currentSession;
var result = session.getParsedUnit('not_absolute.dart');
@ -512,6 +562,16 @@ int foo = 0;
expect(resolvedLibrary.getElementDeclaration(fooElement.setter!), isNull);
}
test_getResolvedLibrary_inconsistent() async {
var test = newFile(testFilePath, content: '');
var session = contextFor(test.path).currentSession;
driverFor(test.path).changeFile(test.path);
expect(
() => session.getResolvedLibrary(test.path),
throwsA(isA<InconsistentAnalysisException>()),
);
}
test_getResolvedLibrary_invalidPartUri() async {
var test = newFile(testFilePath, content: r'''
part 'a.dart';
@ -600,6 +660,16 @@ class B {}
expect(unitResult.libraryElement, isNotNull);
}
test_getResolvedUnit_inconsistent() async {
var test = newFile(testFilePath, content: '');
var session = contextFor(test.path).currentSession;
driverFor(test.path).changeFile(test.path);
expect(
() => session.getResolvedUnit(test.path),
throwsA(isA<InconsistentAnalysisException>()),
);
}
test_getUnitElement() async {
var test = newFile(testFilePath, content: r'''
class A {}
@ -614,6 +684,16 @@ class B {}
expect(unitResult.element.classes, hasLength(2));
}
test_getUnitElement_inconsistent() async {
var test = newFile(testFilePath, content: '');
var session = contextFor(test.path).currentSession;
driverFor(test.path).changeFile(test.path);
expect(
() => session.getUnitElement(test.path),
throwsA(isA<InconsistentAnalysisException>()),
);
}
test_resourceProvider() async {
var session = contextFor(testFilePath).currentSession;
expect(session.resourceProvider, resourceProvider);