Version 2.19.0-19.0.dev
Merge commit '2c891cda56a285ce93b8f198b6f26326c5a8c946' into 'dev'
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_text_document_changes.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_text_document_changes.dart
index 4376b67..ab45689 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_text_document_changes.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_text_document_changes.dart
@@ -103,10 +103,6 @@
);
server.onOverlayCreated(path, doc.text);
- // If the file did not exist, and is "overlay only", it still should be
- // analyzed. Add it to driver to which it should have been added.
- server.contextManager.getDriverFor(path)?.addFile(path);
-
await server.addPriorityFile(path);
return success(null);
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 f28d646..4c7ca36 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
@@ -497,10 +497,33 @@
}
void onOverlayCreated(String path, String content) {
+ final currentFile = resourceProvider.getFile(path);
+ String? currentContent;
+
+ try {
+ currentContent = currentFile.readAsStringSync();
+ } on FileSystemException {
+ // It's possible we're creating an overlay for a file that doesn't yet
+ // exist on disk so must handle missing file exceptions. Checking for
+ // exists first would introduce a race.
+ }
+
resourceProvider.setOverlay(path,
content: content, modificationStamp: overlayModificationStamp++);
- _afterOverlayChanged(path, plugin.AddContentOverlay(content));
+ // If the overlay is exactly the same as the previous content we can skip
+ // notifying drivers which avoids re-analyzing the same content.
+ if (content != currentContent) {
+ _afterOverlayChanged(path, plugin.AddContentOverlay(content));
+
+ // If the file did not exist, and is "overlay only", it still should be
+ // analyzed. Add it to driver to which it should have been added.
+ contextManager.getDriverFor(path)?.addFile(path);
+ } else {
+ // If we skip the work above, we still need to ensure plugins are notified
+ // of the new overlay (which usually happens in `_afterOverlayChanged`).
+ _notifyPluginsOverlayChanged(path, plugin.AddContentOverlay(content));
+ }
}
void onOverlayDestroyed(String path) {
@@ -772,9 +795,7 @@
for (var driver in driverMap.values) {
driver.changeFile(path);
}
- pluginManager.setAnalysisUpdateContentParams(
- plugin.AnalysisUpdateContentParams({path: changeForPlugins}),
- );
+ _notifyPluginsOverlayChanged(path, changeForPlugins);
notifyDeclarationsTracker(path);
notifyFlutterWidgetDescriptions(path);
@@ -834,6 +855,13 @@
}
}
+ void _notifyPluginsOverlayChanged(
+ String path, plugin.HasToJson changeForPlugins) {
+ pluginManager.setAnalysisUpdateContentParams(
+ plugin.AnalysisUpdateContentParams({path: changeForPlugins}),
+ );
+ }
+
void _onPluginsChanged() {
capabilitiesComputer.performDynamicRegistration();
}
diff --git a/pkg/analysis_server/test/lsp/document_changes_test.dart b/pkg/analysis_server/test/lsp/document_changes_test.dart
index db65c63..cf146e8 100644
--- a/pkg/analysis_server/test/lsp/document_changes_test.dart
+++ b/pkg/analysis_server/test/lsp/document_changes_test.dart
@@ -114,6 +114,49 @@
expect(driverForInside.addedFiles, isNot(contains(fileOutsideRootPath)));
}
+ Future<void> test_documentOpen_contentChanged_analysis() async {
+ const content = '// original content';
+ const newContent = '// new content';
+ newFile(mainFilePath, content);
+
+ // Wait for initial analysis to provide diagnostics for the file.
+ await Future.wait([
+ waitForDiagnostics(mainFileUri),
+ initialize(),
+ ]);
+
+ // Capture any further diagnostics sent after we open the file.
+ List<Diagnostic>? diagnostics;
+ unawaited(waitForDiagnostics(mainFileUri).then((d) => diagnostics = d));
+ await openFile(mainFileUri, newContent);
+ await pumpEventQueue(times: 5000);
+
+ // Expect diagnostics, because changing the content will have triggered
+ // analysis.
+ expect(diagnostics, isNotNull);
+ }
+
+ Future<void> test_documentOpen_contentUnchanged_noAnalysis() async {
+ const content = '// original content';
+ newFile(mainFilePath, content);
+
+ // Wait for initial analysis to provide diagnostics for the file.
+ await Future.wait([
+ waitForDiagnostics(mainFileUri),
+ initialize(),
+ ]);
+
+ // Capture any further diagnostics sent after we open the file.
+ List<Diagnostic>? diagnostics;
+ unawaited(waitForDiagnostics(mainFileUri).then((d) => diagnostics = d));
+ await openFile(mainFileUri, content);
+ await pumpEventQueue(times: 5000);
+
+ // Expect no diagnostics because the file didn't actually change content
+ // when the overlay was created, so it should not have triggered analysis.
+ expect(diagnostics, isNull);
+ }
+
Future<void> test_documentOpen_createsOverlay() async {
await _initializeAndOpen();
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 15ea65d..f1c3150 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -684,7 +684,6 @@
errorReporter: containerErrorReporter,
);
} else if (directive is LibraryAugmentationDirectiveImpl) {
- // TODO(scheglov) test
directive.element = containerElement;
} else if (directive is LibraryDirectiveImpl) {
directive.element = containerElement;
diff --git a/pkg/analyzer/test/src/dart/resolution/library_augmentation_test.dart b/pkg/analyzer/test/src/dart/resolution/library_augmentation_test.dart
new file mode 100644
index 0000000..d115d4b
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolution/library_augmentation_test.dart
@@ -0,0 +1,43 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// 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:test_reflective_loader/test_reflective_loader.dart';
+
+import 'context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(LibraryAugmentationDirectiveResolutionTest);
+ });
+}
+
+@reflectiveTest
+class LibraryAugmentationDirectiveResolutionTest
+ extends PubPackageResolutionTest {
+ test_directive() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+import augment 'b.dart';
+''');
+
+ final b = newFile('$testPackageLibPath/b.dart', r'''
+library augment 'a.dart';
+''');
+
+ newFile('$testPackageLibPath/c.dart', '');
+
+ await resolveFile2(b.path);
+ assertNoErrorsInResult();
+
+ final node = findNode.libraryAugmentation('a.dart');
+ assertResolvedNodeText(node, r'''
+LibraryAugmentationDirective
+ libraryKeyword: library
+ augmentKeyword: augment
+ uri: SimpleStringLiteral
+ literal: 'a.dart'
+ semicolon: ;
+ element: package:test/a.dart::@augmentation::package:test/b.dart
+''');
+ }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/test_all.dart b/pkg/analyzer/test/src/dart/resolution/test_all.dart
index 82538d6..f070c0e 100644
--- a/pkg/analyzer/test/src/dart/resolution/test_all.dart
+++ b/pkg/analyzer/test/src/dart/resolution/test_all.dart
@@ -39,6 +39,7 @@
as instance_member_inference_mixin;
import 'interpolation_string_test.dart' as interpolation_string;
import 'language_version_test.dart' as language_version;
+import 'library_augmentation_test.dart' as library_element2;
import 'library_element_test.dart' as library_element;
import 'library_export_test.dart' as library_export;
import 'library_import_prefix_test.dart' as library_import_prefix;
@@ -106,6 +107,7 @@
instance_member_inference_mixin.main();
interpolation_string.main();
language_version.main();
+ library_element2.main();
library_element.main();
library_export.main();
library_import_prefix.main();
diff --git a/pkg/analyzer/test/src/summary/resolved_ast_printer.dart b/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
index c41a403..ac8fd7c 100644
--- a/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
+++ b/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
@@ -774,11 +774,7 @@
_writeln('LibraryAugmentationDirective');
_withIndent(() {
_writeNamedChildEntities(node);
- // TODO(scheglov) Implement.
- // _writeElement('element', node.element);
- // _writeRaw('uriContent', node.uriContent);
- // _writeElement('uriElement', node.uriElement);
- // _writeSource('uriSource', node.uriSource);
+ _writeElement('element', node.element);
});
}
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index c24f897..e10b3e2 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -814,6 +814,10 @@
w.Label loop = b.loop();
_branchIf(node.condition, block, negated: true);
node.body.accept(this);
+
+ // If the loop variable is captured then we have to create a new context for
+ // each iteration of the loop. We must also ensure the local pointing to the
+ // [oldContext] now points to [newContext].
if (node.variables.any((v) => closures.captures.containsKey(v))) {
w.Local oldContext = context!.currentLocal;
allocateContext(node);
@@ -821,12 +825,14 @@
for (VariableDeclaration variable in node.variables) {
Capture? capture = closures.captures[variable];
if (capture != null) {
+ b.local_get(newContext);
b.local_get(oldContext);
b.struct_get(context.struct, capture.fieldIndex);
- b.local_get(newContext);
b.struct_set(context.struct, capture.fieldIndex);
}
}
+ b.local_get(newContext);
+ b.local_set(oldContext);
} else {
allocateContext(node);
}
diff --git a/pkg/dart2wasm/lib/transformers.dart b/pkg/dart2wasm/lib/transformers.dart
index 3351fa7..fe9d23e 100644
--- a/pkg/dart2wasm/lib/transformers.dart
+++ b/pkg/dart2wasm/lib/transformers.dart
@@ -51,10 +51,25 @@
return result;
}
- /// We can reuse a superclass' `_typeArguments` method if the subclass and the
- /// superclass have the exact same type parameters in the exact same order.
+ /// Checks to see if it is safe to reuse `super._typeArguments`.
bool canReuseSuperMethod(Class cls) {
- Supertype supertype = cls.supertype!;
+ // We search for the first non-abstract super in [cls]'s inheritance chain
+ // to see if we can reuse its `_typeArguments` method.
+ Class classIter = cls;
+ late Supertype supertype;
+ while (classIter.supertype != null) {
+ Supertype supertypeIter = classIter.supertype!;
+ Class superclass = supertypeIter.classNode;
+ if (!superclass.isAbstract) {
+ supertype = supertypeIter;
+ break;
+ }
+ classIter = classIter.supertype!.classNode;
+ }
+
+ // We can reuse a superclass' `_typeArguments` method if the subclass and
+ // the superclass have the exact same type parameters in the exact same
+ // order.
if (cls.typeParameters.length != supertype.typeArguments.length) {
return false;
}
diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_shared.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_shared.dart
index ded97c0..ac03b93 100644
--- a/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_shared.dart
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_shared.dart
@@ -936,22 +936,15 @@
try {
var entity = fileSystem.entityForUri(uri);
if (await entity.existsAsyncIfPossible()) {
- if (request.method == 'HEAD') {
- var headers = {
- 'content-length': '-1',
- ...request.headers,
- };
- return Response.ok(null, headers: headers);
- }
-
- if (request.method == 'GET') {
+ if (request.method == 'HEAD' || request.method == 'GET') {
// 'readAsBytes'
var contents = await entity.readAsBytesAsyncIfPossible();
var headers = {
'content-length': '${contents.length}',
...request.headers,
};
- return Response.ok(contents, headers: headers);
+ return Response.ok(request.method == 'GET' ? contents : null,
+ headers: headers);
}
}
return Response.notFound(path);
diff --git a/tools/VERSION b/tools/VERSION
index c27657f..cf62a55 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 19
PATCH 0
-PRERELEASE 18
+PRERELEASE 19
PRERELEASE_PATCH 0
\ No newline at end of file