diff --git a/build_resolvers/CHANGELOG.md b/build_resolvers/CHANGELOG.md index 70d22a83a..21761af19 100644 --- a/build_resolvers/CHANGELOG.md +++ b/build_resolvers/CHANGELOG.md @@ -1,4 +1,7 @@ -## 2.0.9-dev +## 2.0.9 + +- Fix a new case of `InconsistentAnalysisException` errors that can occur with + the newer analyzer. ## 2.0.8 diff --git a/build_resolvers/lib/src/build_asset_uri_resolver.dart b/build_resolvers/lib/src/build_asset_uri_resolver.dart index 926dd7b6a..c210e4f5e 100644 --- a/build_resolvers/lib/src/build_asset_uri_resolver.dart +++ b/build_resolvers/lib/src/build_asset_uri_resolver.dart @@ -2,6 +2,7 @@ // 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 'dart:collection'; import 'package:analyzer/dart/analysis/utilities.dart'; @@ -49,13 +50,17 @@ class BuildAssetUriResolver extends UriResolver { /// input, subsequent calls to a resolver, or a transitive import thereof. final _buildStepTransitivelyResolvedAssets = >{}; - /// Updates [resourceProvider] and [driver] with updated versions of - /// [entryPoints]. + /// Updates [resourceProvider] and the analysis driver given by + /// `withDriverResource` with updated versions of [entryPoints]. /// /// If [transitive], then all the transitive imports from [entryPoints] are /// also updated. - Future performResolve(BuildStep buildStep, List entryPoints, - AnalysisDriverForPackageBuild driver, + Future performResolve( + BuildStep buildStep, + List entryPoints, + Future Function( + FutureOr Function(AnalysisDriverForPackageBuild)) + withDriverResource, {required bool transitive}) async { final transitivelyResolved = _buildStepTransitivelyResolvedAssets .putIfAbsent(buildStep, () => HashSet()); @@ -74,13 +79,14 @@ class BuildAssetUriResolver extends UriResolver { for (final id in uncrawledIds) (await _updateCachedAssetState(id, buildStep))! ]; - - for (final state in assetStates) { - if (_needsChangeFile.remove(state.path)) { - driver.changeFile(state.path); + await withDriverResource((driver) async { + for (final state in assetStates) { + if (_needsChangeFile.remove(state.path)) { + driver.changeFile(state.path); + } } - } - await driver.applyPendingFileChanges(); + await driver.applyPendingFileChanges(); + }); } /// Updates the internal state for [id], if it has changed. diff --git a/build_resolvers/lib/src/resolver.dart b/build_resolvers/lib/src/resolver.dart index e878a6782..f392ae7b6 100644 --- a/build_resolvers/lib/src/resolver.dart +++ b/build_resolvers/lib/src/resolver.dart @@ -2,6 +2,7 @@ // 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 'dart:collection'; import 'dart:convert'; import 'dart:io'; @@ -40,11 +41,12 @@ Future _packagePath(String package) async { /// down from entrypoints. class PerActionResolver implements ReleasableResolver { final AnalyzerResolver _delegate; + final Pool _driverPool; final BuildStep _step; final _entryPoints = {}; - PerActionResolver(this._delegate, this._step); + PerActionResolver(this._delegate, this._driverPool, this._step); @override Stream get libraries async* { @@ -124,22 +126,25 @@ class PerActionResolver implements ReleasableResolver { allowSyntaxErrors: allowSyntaxErrors); }); - // Ensures that we finish resolving one thing before attempting to resolve - // another, otherwise there are race conditions with `_entryPoints` being - // updated before it is actually ready, or resolved more than once. - final _resolvePool = Pool(1); + // Ensures we only resolve one entrypoint at a time from the same build step, + // otherwise there are race conditions with `_entryPoints` being updated + // before it is actually ready, or resolving entrypoints more than once. + final Pool _perActionResolvePool = Pool(1); Future _resolveIfNecessary(AssetId id, {required bool transitive}) => - _resolvePool.withResource(() async { + _perActionResolvePool.withResource(() async { if (!_entryPoints.contains(id)) { // We only want transitively resolved ids in `_entrypoints`. if (transitive) _entryPoints.add(id); - // the resolver will only visit assets that haven't been resolved in this - // step yet + // the resolver will only visit assets that haven't been resolved in + // this step yet await _step.trackStage( 'Resolving library $id', () => _delegate._uriResolver.performResolve( - _step, [id], _delegate._driver, + _step, + [id], + (withDriver) => _driverPool + .withResource(() => withDriver(_delegate._driver)), transitive: transitive)); } }); @@ -158,21 +163,23 @@ class PerActionResolver implements ReleasableResolver { class AnalyzerResolver implements ReleasableResolver { final BuildAssetUriResolver _uriResolver; final AnalysisDriverForPackageBuild _driver; + final Pool _driverPool; - AnalyzerResolver(this._driver, this._uriResolver); + AnalyzerResolver(this._driver, this._driverPool, this._uriResolver); @override Future isLibrary(AssetId assetId) async { if (assetId.extension != '.dart') return false; - if (!_driver.isUriOfExistingFile(assetId.uri)) return false; - var result = - _driver.currentSession.getFile(assetPath(assetId)) as FileResult; - return !result.isPart; + return _driverPool.withResource(() { + if (!_driver.isUriOfExistingFile(assetId.uri)) return false; + var result = + _driver.currentSession.getFile(assetPath(assetId)) as FileResult; + return !result.isPart; + }); } @override Future astNodeFor(Element element, {bool resolve = false}) async { - var session = _driver.currentSession; final library = element.library; if (library == null) { // Invalid elements (e.g. an MultiplyDefinedElement) are not part of any @@ -181,49 +188,58 @@ class AnalyzerResolver implements ReleasableResolver { } var path = library.source.fullName; - if (resolve) { - return (await session.getResolvedLibrary(path) as ResolvedLibraryResult) - .getElementDeclaration(element) - ?.node; - } else { - return (session.getParsedLibrary(path) as ParsedLibraryResult) - .getElementDeclaration(element) - ?.node; - } + return _driverPool.withResource(() async { + var session = _driver.currentSession; + if (resolve) { + return (await session.getResolvedLibrary(path) as ResolvedLibraryResult) + .getElementDeclaration(element) + ?.node; + } else { + return (session.getParsedLibrary(path) as ParsedLibraryResult) + .getElementDeclaration(element) + ?.node; + } + }); } @override Future compilationUnitFor(AssetId assetId, - {bool allowSyntaxErrors = false}) async { - if (!_driver.isUriOfExistingFile(assetId.uri)) { - throw AssetNotFoundException(assetId); - } + {bool allowSyntaxErrors = false}) { + return _driverPool.withResource(() async { + if (!_driver.isUriOfExistingFile(assetId.uri)) { + throw AssetNotFoundException(assetId); + } - var path = assetPath(assetId); - var parsedResult = - _driver.currentSession.getParsedUnit(path) as ParsedUnitResult; - if (!allowSyntaxErrors && parsedResult.errors.isNotEmpty) { - throw SyntaxErrorInAssetException(assetId, [parsedResult]); - } - return parsedResult.unit; + var path = assetPath(assetId); + + var parsedResult = + _driver.currentSession.getParsedUnit(path) as ParsedUnitResult; + if (!allowSyntaxErrors && parsedResult.errors.isNotEmpty) { + throw SyntaxErrorInAssetException(assetId, [parsedResult]); + } + return parsedResult.unit; + }); } @override Future libraryFor(AssetId assetId, {bool allowSyntaxErrors = false}) async { - var uri = assetId.uri; - if (!_driver.isUriOfExistingFile(uri)) { - throw AssetNotFoundException(assetId); - } + final library = await _driverPool.withResource(() async { + var uri = assetId.uri; + if (!_driver.isUriOfExistingFile(uri)) { + throw AssetNotFoundException(assetId); + } - var path = assetPath(assetId); - var parsedResult = _driver.currentSession.getParsedUnit(path); - if (parsedResult is! ParsedUnitResult || parsedResult.isPart) { - throw NonLibraryAssetException(assetId); - } + var path = assetPath(assetId); + var parsedResult = _driver.currentSession.getParsedUnit(path); + if (parsedResult is! ParsedUnitResult || parsedResult.isPart) { + throw NonLibraryAssetException(assetId); + } + + return await _driver.currentSession.getLibraryByUri(uri.toString()) + as LibraryElementResult; + }); - final library = await _driver.currentSession.getLibraryByUri(uri.toString()) - as LibraryElementResult; if (!allowSyntaxErrors) { final errors = await _syntacticErrorsFor(library.element); if (errors.isNotEmpty) { @@ -254,14 +270,16 @@ class AnalyzerResolver implements ReleasableResolver { final relevantResults = []; - for (final path in paths) { - final result = await _driver.currentSession.getErrors(path); - if (result is ErrorsResult && - result.errors.any( - (error) => error.errorCode.type == ErrorType.SYNTACTIC_ERROR)) { - relevantResults.add(result); + await _driverPool.withResource(() async { + for (final path in paths) { + final result = await _driver.currentSession.getErrors(path); + if (result is ErrorsResult && + result.errors.any( + (error) => error.errorCode.type == ErrorType.SYNTACTIC_ERROR)) { + relevantResults.add(result); + } } - } + }); return relevantResults; } @@ -304,6 +322,12 @@ class AnalyzerResolvers implements Resolvers { /// Nullable, the default analysis options are used if not provided. final AnalysisOptions _analysisOptions; + /// Used to protect all usages of the analysis driver from + /// `InconsistentAnalysisException` errors, which might occur if we are in the + /// middle of some calls to `changeFile` but have not yet completed + /// `applyPendingFileChanges`. + final _driverPool = Pool(1); + /// A function that returns the path to the SDK summary when invoked. /// /// Defaults to [_defaultSdkSummaryGenerator]. @@ -353,14 +377,14 @@ class AnalyzerResolvers implements Resolvers { await loadPackageConfigUri((await Isolate.packageConfig)!); var driver = await analysisDriver(uriResolver, _analysisOptions, await _sdkSummaryGenerator(), loadedConfig); - _resolver = AnalyzerResolver(driver, uriResolver); + _resolver = AnalyzerResolver(driver, _driverPool, uriResolver); }())); } @override Future get(BuildStep buildStep) async { await _ensureInitialized(); - return PerActionResolver(_resolver, buildStep); + return PerActionResolver(_resolver, _driverPool, buildStep); } /// Must be called between each build. diff --git a/build_resolvers/pubspec.yaml b/build_resolvers/pubspec.yaml index f7ce5882c..4404c18e2 100644 --- a/build_resolvers/pubspec.yaml +++ b/build_resolvers/pubspec.yaml @@ -1,5 +1,5 @@ name: build_resolvers -version: 2.0.9-dev +version: 2.0.9 description: Resolve Dart code in a Builder repository: https://github.com/dart-lang/build/tree/master/build_resolvers