Version 2.13.0-82.0.dev
Merge commit '8833cbed7d7021420e6a969eba48f006bb7050f4' into 'dev'
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index 122bfc6..c7afc20 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -429,16 +429,6 @@
});
}
- /// Returns `true` if errors should be reported for [file] with the given
- /// absolute path.
- bool shouldSendErrorsNotificationFor(String file) {
- // Errors should not be reported for things that are explicitly skipped
- // during normal analysis (for example dot folders are skipped over in
- // _handleWatchEventImpl).
- return contextManager.isInAnalysisRoot(file) &&
- !contextManager.isContainedInDotFolder(file);
- }
-
Future<void> shutdown() {
if (options.analytics != null) {
options.analytics
@@ -674,7 +664,7 @@
analysisDriver.results.listen((result) {
var path = result.path;
filesToFlush.add(path);
- if (analysisServer.shouldSendErrorsNotificationFor(path)) {
+ if (analysisServer.isAnalyzed(path)) {
_notificationManager.recordAnalysisErrors(NotificationManager.serverId,
path, server.doAnalysisError_listFromEngine(result));
}
diff --git a/pkg/analysis_server/lib/src/analysis_server_abstract.dart b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
index 5b32212..0853e07 100644
--- a/pkg/analysis_server/lib/src/analysis_server_abstract.dart
+++ b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
@@ -348,6 +348,12 @@
});
}
+ /// Return `true` if the file or directory with the given [path] will be
+ /// analyzed in one of the analysis contexts.
+ bool isAnalyzed(String path) {
+ return contextManager.isAnalyzed(path);
+ }
+
void logExceptionResult(nd.ExceptionResult result) {
var message = 'Analysis failed: ${result.filePath}';
if (result.contextKey != null) {
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index 42ef0da..abacc10 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -87,15 +87,9 @@
/// If no driver contains the given path, `null` is returned.
AnalysisDriver getDriverFor(String path);
- /// Determine whether the given [path], when interpreted relative to innermost
- /// context root, contains a folder whose name starts with '.'.
- ///
- /// TODO(scheglov) Remove it, just [isInAnalysisRoot] should be enough.
- bool isContainedInDotFolder(String path);
-
- /// Return `true` if the given absolute [path] is in one of the current
- /// root folders and is not excluded.
- bool isInAnalysisRoot(String path);
+ /// Return `true` if the file or directory with the given [path] will be
+ /// analyzed in one of the analysis contexts.
+ bool isAnalyzed(String path);
/// Rebuild the set of contexts from scratch based on the data last sent to
/// [setRoots].
@@ -221,21 +215,8 @@
return getContextFor(path)?.driver;
}
- /// Determine whether the given [path], when interpreted relative to innermost
- /// context root, contains a folder whose name starts with '.'.
@override
- bool isContainedInDotFolder(String path) {
- for (var analysisContext in _collection.contexts) {
- var contextImpl = analysisContext as DriverBasedAnalysisContext;
- if (_isContainedInDotFolder(contextImpl.contextRoot.root.path, path)) {
- return true;
- }
- }
- return false;
- }
-
- @override
- bool isInAnalysisRoot(String path) {
+ bool isAnalyzed(String path) {
var collection = _collection;
if (collection == null) {
return false;
@@ -431,9 +412,6 @@
_watchBazelFilesIfNeeded(rootFolder, driver);
for (var file in contextImpl.contextRoot.analyzedFiles()) {
- if (_isContainedInDotFolder(contextImpl.contextRoot.root.path, file)) {
- continue;
- }
if (file_paths.isAndroidManifestXml(pathContext, file)) {
_analyzeAndroidManifestXml(driver, file);
} else if (file_paths.isDart(pathContext, file)) {
@@ -554,12 +532,11 @@
var analysisContext = analysisContext_ as DriverBasedAnalysisContext;
switch (type) {
case ChangeType.ADD:
- // TODO(scheglov) Why not `isInAnalysisRoot()`?
- if (_isContainedInDotFolder(
- analysisContext.contextRoot.root.path, path)) {
- return;
+ if (analysisContext.contextRoot.isAnalyzed(path)) {
+ analysisContext.driver.addFile(path);
+ } else {
+ analysisContext.driver.changeFile(path);
}
- analysisContext.driver.addFile(path);
break;
case ChangeType.MODIFY:
analysisContext.driver.changeFile(path);
@@ -592,25 +569,6 @@
refresh();
}
- /// Determine whether the given [path], when interpreted relative to the
- /// context root [root], contains a folder whose name starts with '.' but is
- /// not included in [exclude].
- bool _isContainedInDotFolder(String root, String path,
- {Set<String> exclude}) {
- var pathDir = pathContext.dirname(path);
- var rootPrefix = root + pathContext.separator;
- if (pathDir.startsWith(rootPrefix)) {
- var suffixPath = pathDir.substring(rootPrefix.length);
- for (var pathComponent in pathContext.split(suffixPath)) {
- if (pathComponent.startsWith('.') &&
- !(exclude?.contains(pathComponent) ?? false)) {
- return true;
- }
- }
- }
- return false;
- }
-
/// Read the contents of the file at the given [path], or throw an exception
/// if the contents cannot be read.
String _readFile(String path) {
diff --git a/pkg/analysis_server/lib/src/edit/edit_dartfix.dart b/pkg/analysis_server/lib/src/edit/edit_dartfix.dart
index 574c964..c36f2bb 100644
--- a/pkg/analysis_server/lib/src/edit/edit_dartfix.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_dartfix.dart
@@ -175,7 +175,7 @@
if (res is Folder) {
for (var child in res.getChildren()) {
if (!child.shortName.startsWith('.') &&
- contextManager.isInAnalysisRoot(child.path)) {
+ server.isAnalyzed(child.path)) {
resources.add(child);
}
}
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index 9c223e0..be204d6 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -261,7 +261,7 @@
return;
}
- if (!server.contextManager.isInAnalysisRoot(file)) {
+ if (!server.isAnalyzed(file)) {
server.sendResponse(Response.getFixesInvalidFile(request));
return;
}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
index f291e25..762a4f8 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
@@ -46,7 +46,7 @@
}
final path = pathOfDoc(params.textDocument);
- if (!path.isError && !server.isAnalyzedFile(path.result)) {
+ if (!path.isError && !server.isAnalyzed(path.result)) {
return success(const []);
}
diff --git a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
index d243989..6a5bbc1 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
@@ -338,14 +338,6 @@
}, socketError);
}
- /// Returns `true` if the [file] with the given absolute path is included
- /// in an analysis root and not excluded.
- bool isAnalyzedFile(String file) {
- return contextManager.isInAnalysisRoot(file) &&
- // Dot folders are not analyzed (skipped over in _handleWatchEventImpl)
- !contextManager.isContainedInDotFolder(file);
- }
-
/// Logs the error on the client using window/logMessage.
void logErrorToClient(String message) {
channel.sendNotification(NotificationMessage(
@@ -584,13 +576,7 @@
// workspace.
return initializationOptions.closingLabels &&
priorityFiles.contains(file) &&
- contextManager.isInAnalysisRoot(file);
- }
-
- /// Returns `true` if errors should be reported for [file] with the given
- /// absolute path.
- bool shouldSendErrorsNotificationFor(String file) {
- return isAnalyzedFile(file);
+ isAnalyzed(file);
}
/// Returns `true` if Flutter outlines should be sent for [file] with the
@@ -817,7 +803,7 @@
analysisDriver.results.listen((result) {
var path = result.path;
filesToFlush.add(path);
- if (analysisServer.shouldSendErrorsNotificationFor(path)) {
+ if (analysisServer.isAnalyzed(path)) {
final serverErrors = protocol.mapEngineErrors(
result,
result.errors.where(_shouldSendDiagnostic).toList(),
diff --git a/pkg/analysis_server/test/domain_analysis_test.dart b/pkg/analysis_server/test/domain_analysis_test.dart
index 9266e21..2138863 100644
--- a/pkg/analysis_server/test/domain_analysis_test.dart
+++ b/pkg/analysis_server/test/domain_analysis_test.dart
@@ -454,6 +454,35 @@
assertHasErrors(a_path);
}
+ Future<void> test_fileSystem_addFile_dart_dotFolder() async {
+ var a_path = '$projectPath/lib/.foo/a.dart';
+ var b_path = '$projectPath/lib/b.dart';
+
+ newFile(b_path, content: r'''
+import '.foo/a.dart';
+void f(A a) {}
+''');
+
+ createProject();
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // We don't have a.dart, so the import cannot be resolved.
+ assertHasErrors(b_path);
+
+ newFile(a_path, content: r'''
+class A {}
+''');
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // 'a.dart' is in a dot-folder, so excluded from analysis.
+ assertNoErrorsNotification(a_path);
+
+ // We added a.dart with `A`, so no errors.
+ assertNoErrors(b_path);
+ }
+
Future<void> test_fileSystem_addFile_dart_excluded() async {
var a_path = '$projectPath/lib/a.dart';
var b_path = '$projectPath/lib/b.dart';
@@ -747,6 +776,42 @@
assertNoErrors(b_path);
}
+ Future<void> test_fileSystem_changeFile_dart_dotFolder() async {
+ var a_path = '$testPackageLibPath/.foo/a.dart';
+ var b_path = '$testPackageLibPath/b.dart';
+
+ newFile(a_path, content: r'''
+class B {}
+''');
+
+ newFile(b_path, content: r'''
+import '.foo/a.dart';
+void f(A a) {}
+''');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // 'a.dart' is in a dot-folder, so excluded from analysis.
+ assertNoErrorsNotification(a_path);
+
+ // We have `B`, not `A`, in a.dart, so has errors.
+ assertHasErrors(b_path);
+
+ newFile(a_path, content: r'''
+class A {}
+''');
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // 'a.dart' is in a dot-folder, so excluded from analysis.
+ assertNoErrorsNotification(a_path);
+
+ // We changed a.dart, to have `A`, so no errors.
+ assertNoErrors(b_path);
+ }
+
Future<void> test_fileSystem_changeFile_dart_excluded() async {
var a_path = '$testPackageLibPath/a.dart';
var b_path = '$testPackageLibPath/b.dart';
diff --git a/pkg/analysis_server/test/src/cider/fixes_test.dart b/pkg/analysis_server/test/src/cider/fixes_test.dart
index 4f4b240..6eeaf47 100644
--- a/pkg/analysis_server/test/src/cider/fixes_test.dart
+++ b/pkg/analysis_server/test/src/cider/fixes_test.dart
@@ -37,6 +37,20 @@
expect(resultContent, expected);
}
+ Future<void> test_cachedResolvedFiles() async {
+ await _compute(r'''
+var a = 0^ var b = 1
+''');
+
+ // Only the first fix is applied.
+ assertHasFix(DartFixKind.INSERT_SEMICOLON, r'''
+var a = 0; var b = 1
+''');
+
+ // The file was resolved only once, even though we have 2 errors.
+ expect(fileResolver.testView.resolvedFiles, [testPath]);
+ }
+
Future<void> test_createMethod() async {
await _compute(r'''
class A {
diff --git a/pkg/analyzer/lib/src/context/packages.dart b/pkg/analyzer/lib/src/context/packages.dart
index 94b1686..f3dad37 100644
--- a/pkg/analyzer/lib/src/context/packages.dart
+++ b/pkg/analyzer/lib/src/context/packages.dart
@@ -99,11 +99,6 @@
jsonLanguageVersion.minor,
0,
);
- // New features were added in `2.2.2` over `2.2.0`.
- // But `2.2.2` is not representable, so we special case it.
- if (languageVersion.major == 2 && languageVersion.minor == 2) {
- languageVersion = Version(2, 2, 2);
- }
}
map[name] = Package(
diff --git a/pkg/analyzer/lib/src/dart/analysis/context_root.dart b/pkg/analyzer/lib/src/dart/analysis/context_root.dart
index 4eecb80..6ef4330 100644
--- a/pkg/analyzer/lib/src/dart/analysis/context_root.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/context_root.dart
@@ -107,10 +107,11 @@
bool _isExcluded(String path) {
Context context = resourceProvider.pathContext;
- for (String pathComponent in context.split(path)) {
- if (pathComponent.startsWith('.')) {
+ for (var current = path; current != root.path;) {
+ if (context.basename(current).startsWith('.')) {
return true;
}
+ current = context.dirname(current);
}
for (String excludedPath in excludedPaths) {
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index 79a9088..583f799 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -80,6 +80,13 @@
/// to release the cache items and is then cleared.
final Set<int> removedCacheIds = {};
+ /// The cache of file results, cleared on [changeFile].
+ ///
+ /// It is used to allow assists and fixes without resolving the same file
+ /// multiple times, as we compute more than one assist, or fixes when there
+ /// are more than one error on a line.
+ final Map<String, ResolvedUnitResult> _cachedResults = {};
+
FileResolver(
PerformanceLog logger,
ResourceProvider resourceProvider,
@@ -127,6 +134,9 @@
return;
}
+ // Forget all results, anything is potentially affected.
+ _cachedResults.clear();
+
// Remove this file and all files that transitively depend on it.
var removedFiles = <FileState>[];
fsState!.changeFile(path, removedFiles);
@@ -329,6 +339,11 @@
performance ??= OperationPerformanceImpl('<default>');
+ var cachedResult = _cachedResults[path];
+ if (cachedResult != null) {
+ return cachedResult;
+ }
+
return logger.run('Resolve $path', () {
var fileContext = getFileContext(
path: path,
@@ -403,7 +418,7 @@
});
UnitAnalysisResult fileResult = results[file]!;
- return ResolvedUnitResultImpl(
+ var result = ResolvedUnitResultImpl(
contextObjects!.analysisSession,
path,
file.uri,
@@ -414,6 +429,8 @@
fileResult.unit,
fileResult.errors,
);
+ _cachedResults[path] = result;
+ return result;
});
}
diff --git a/pkg/analyzer/test/src/context/packages_test.dart b/pkg/analyzer/test/src/context/packages_test.dart
index 204ca42..792753b 100644
--- a/pkg/analyzer/test/src/context/packages_test.dart
+++ b/pkg/analyzer/test/src/context/packages_test.dart
@@ -209,32 +209,6 @@
);
}
- /// New features were added in `2.2.2` over `2.2.0`.
- /// But `2.2.2` is not representable, so we special case it.
- test_parsePackageConfigJsonFile_version222() {
- var file = newFile('/test/.dart_tool/package_config.json', content: '''
-{
- "configVersion": 2,
- "packages": [
- {
- "name": "test",
- "rootUri": "../",
- "packageUri": "lib/",
- "languageVersion": "2.2"
- }
- ]
-}
-''');
- var packages = parsePackageConfigJsonFile(resourceProvider, file);
-
- _assertPackage(
- packages,
- name: 'test',
- expectedLibPath: '/test/lib',
- expectedVersion: Version(2, 2, 2),
- );
- }
-
test_parsePackagesFile_dotPackages() {
var path = convertPath('/test/.packages');
newFile(path, content: '''
diff --git a/pkg/analyzer/test/src/dart/analysis/context_root_test.dart b/pkg/analyzer/test/src/dart/analysis/context_root_test.dart
index 22b64ae..39eaf04 100644
--- a/pkg/analyzer/test/src/dart/analysis/context_root_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/context_root_test.dart
@@ -2,11 +2,13 @@
// 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 'package:analyzer/dart/analysis/context_root.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/dart/analysis/context_root.dart';
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
import 'package:analyzer/src/workspace/basic.dart';
import 'package:analyzer/src/workspace/workspace.dart';
+import 'package:path/path.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -78,6 +80,14 @@
expect(contextRoot.isAnalyzed(filePath), isFalse);
}
+ test_isAnalyzed_implicitlyExcluded_dotFolder_containsRoot() {
+ var contextRoot = _createContextRoot('/home/.foo/root');
+
+ expect(_isAnalyzed(contextRoot, ''), isTrue);
+ expect(_isAnalyzed(contextRoot, 'lib/a.dart'), isTrue);
+ expect(_isAnalyzed(contextRoot, 'lib/.bar/a.dart'), isFalse);
+ }
+
test_isAnalyzed_implicitlyExcluded_dotFolder_directParent() {
String filePath = convertPath('/test/root/lib/.aaa/a.dart');
expect(contextRoot.isAnalyzed(filePath), isFalse);
@@ -88,6 +98,14 @@
expect(contextRoot.isAnalyzed(filePath), isFalse);
}
+ test_isAnalyzed_implicitlyExcluded_dotFolder_isRoot() {
+ var contextRoot = _createContextRoot('/home/.root');
+
+ expect(_isAnalyzed(contextRoot, ''), isTrue);
+ expect(_isAnalyzed(contextRoot, 'lib/a.dart'), isTrue);
+ expect(_isAnalyzed(contextRoot, 'lib/.bar/a.dart'), isFalse);
+ }
+
test_isAnalyzed_included() {
String filePath = convertPath('/test/root/lib/root.dart');
expect(contextRoot.isAnalyzed(filePath), isTrue);
@@ -110,4 +128,24 @@
newFolder(folderPath);
expect(contextRoot.isAnalyzed(folderPath), isTrue);
}
+
+ ContextRootImpl _createContextRoot(String posixPath) {
+ var rootPath = convertPath(posixPath);
+ var rootFolder = newFolder(rootPath);
+ var workspace = BasicWorkspace.find(resourceProvider, {}, rootPath);
+ var contextRoot = ContextRootImpl(resourceProvider, rootFolder, workspace);
+ contextRoot.included.add(rootFolder);
+ return contextRoot;
+ }
+
+ static bool _isAnalyzed(ContextRoot contextRoot, String relPosix) {
+ var pathContext = contextRoot.resourceProvider.pathContext;
+ var path = pathContext.join(
+ contextRoot.root.path,
+ pathContext.joinAll(
+ posix.split(relPosix),
+ ),
+ );
+ return contextRoot.isAnalyzed(path);
+ }
}
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 b842d4b..8709a1f 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
@@ -528,6 +528,38 @@
''');
}
+ test_resolveFile_cache() async {
+ var path = convertPath('/workspace/dart/test/lib/test.dart');
+ newFile(path, content: 'var a = 0;');
+
+ // No resolved files yet.
+ expect(fileResolver.testView!.resolvedFiles, isEmpty);
+
+ await resolveFile2(path);
+ var result1 = result;
+
+ // The file was resolved.
+ expect(fileResolver.testView!.resolvedFiles, [path]);
+
+ // Ask again, no changes, not resolved.
+ await resolveFile2(path);
+ expect(fileResolver.testView!.resolvedFiles, [path]);
+
+ // The same result was returned.
+ expect(result, same(result1));
+
+ // Change a file.
+ var a_path = convertPath('/workspace/dart/test/lib/a.dart');
+ fileResolver.changeFile(a_path);
+
+ // The was a change to a file, no matter which, resolve again.
+ await resolveFile2(path);
+ expect(fileResolver.testView!.resolvedFiles, [path, path]);
+
+ // Get should get a new result.
+ expect(result, isNot(same(result1)));
+ }
+
test_reuse_compatibleOptions() async {
newFile('/workspace/dart/aaa/BUILD', content: '');
newFile('/workspace/dart/bbb/BUILD', content: '');
diff --git a/tools/VERSION b/tools/VERSION
index e4f0ca7..40d7e5d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 81
+PRERELEASE 82
PRERELEASE_PATCH 0
\ No newline at end of file