diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart index be0762c6a6f..165e57f8164 100644 --- a/pkg/analyzer/lib/src/dart/analysis/driver.dart +++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart @@ -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 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) { diff --git a/pkg/analyzer/lib/src/dart/analysis/library_context.dart b/pkg/analyzer/lib/src/dart/analysis/library_context.dart index 4795da184c7..c9ee80d9124 100644 --- a/pkg/analyzer/lib/src/dart/analysis/library_context.dart +++ b/pkg/analyzer/lib/src/dart/analysis/library_context.dart @@ -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) { diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart index 0966306ea02..b79c1a0d24a 100644 --- a/pkg/analyzer/lib/src/dart/element/element.dart +++ b/pkg/analyzer/lib/src/dart/element/element.dart @@ -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; diff --git a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart index b91377d0557..21183bdcb3c 100644 --- a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart +++ b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart @@ -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 _libraryReaders = {}; final Map> _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 exports) { @@ -255,11 +257,4 @@ class LinkedElementFactory { libraryElement.createLoadLibraryFunction(); } - - void _invalidateLibrary(Reference? libraryReference) { - var libraryElement = libraryReference?.element; - if (libraryElement is LibraryElementImpl) { - libraryElement.isValid = false; - } - } } diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart index 1f83aec8041..807866cc8a1 100644 --- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart +++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart @@ -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 { diff --git a/pkg/analyzer/test/src/dart/analysis/session_test.dart b/pkg/analyzer/test/src/dart/analysis/session_test.dart index fb40314a0b8..6953e85086d 100644 --- a/pkg/analyzer/test/src/dart/analysis/session_test.dart +++ b/pkg/analyzer/test/src/dart/analysis/session_test.dart @@ -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()), + ); + } + 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()); } + 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()), + ); + } + 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()), + ); + } + 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()), + ); + } + 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()), + ); + } + 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()), + ); + } + 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()), + ); + } + 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()), + ); + } + test_resourceProvider() async { var session = contextFor(testFilePath).currentSession; expect(session.resourceProvider, resourceProvider);