Version 2.11.0-222.0.dev
Merge commit 'ece17f73b0b1b15417d07e2dcbe79f4974f90ba0' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6234626..16ebe91 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,13 @@
Updated the Linter to `0.1.121`, which includes:
+#### Pub
+
+* New commands `pub add` and `pub remove` that adds and remove new dependencies
+ to your `pubspec.yaml`.
+* `pub publish` will check your pubspec keys for likely typos.
+* `pub get` will print a warning if the resolution is mixed mode.
+
# 0.1.121
* Performance improvements to `always_use_package_imports`,
diff --git a/DEPS b/DEPS
index 46d8ac2..dcadfea 100644
--- a/DEPS
+++ b/DEPS
@@ -128,7 +128,7 @@
"ply_rev": "604b32590ffad5cbb82e4afef1d305512d06ae93",
"pool_rev": "eedbd5fde84f9a1a8da643b475305a81841da599",
"protobuf_rev": "3746c8fd3f2b0147623a8e3db89c3ff4330de760",
- "pub_rev": "04e237f78b2302d7f20d0b362554425e8deb8add",
+ "pub_rev": "f0c7771b38155d3829a60d60b5dba2784b100811",
"pub_semver_tag": "v1.4.4",
"quiver-dart_tag": "246e754fe45cecb6aa5f3f13b4ed61037ff0d784",
"resource_rev": "f8e37558a1c4f54550aa463b88a6a831e3e33cd6",
diff --git a/pkg/analysis_server/lib/src/lsp/constants.dart b/pkg/analysis_server/lib/src/lsp/constants.dart
index 27abeb9..460fa5e 100644
--- a/pkg/analysis_server/lib/src/lsp/constants.dart
+++ b/pkg/analysis_server/lib/src/lsp/constants.dart
@@ -52,6 +52,8 @@
/// A [ProgressToken] used for reporting progress when the server is analyzing.
final analyzingProgressToken = Either2<num, String>.t2('ANALYZING');
+final emptyWorkspaceEdit = WorkspaceEdit();
+
/// Constants for command IDs that are exchanged between LSP client/server.
abstract class Commands {
/// A list of all commands IDs that can be sent to the client to inform which
@@ -127,3 +129,9 @@
/// if it crashes 5 times in the last 180 seconds."
static const ClientServerInconsistentState = ErrorCodes(-32099);
}
+
+/// Strings used in user prompts (window/showMessageRequest).
+abstract class UserPromptActions {
+ static const String cancel = 'Cancel';
+ static const String renameAnyway = 'Rename Anyway';
+}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart
index a0daa67..7375cc2 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart
@@ -126,7 +126,7 @@
if (token.isCancellationRequested) {
return cancelled();
}
- if (initStatus.hasError) {
+ if (initStatus.hasFatalError) {
return error(
ServerErrorCodes.RenameNotValid, initStatus.problem.message, null);
}
@@ -144,9 +144,30 @@
if (token.isCancellationRequested) {
return cancelled();
}
- if (finalStatus.hasError) {
+ if (finalStatus.hasFatalError) {
return error(
ServerErrorCodes.RenameNotValid, finalStatus.problem.message, null);
+ } else if (finalStatus.hasError || finalStatus.hasWarning) {
+ // Ask the user whether to proceed with the rename.
+ final userChoice = await server.showUserPrompt(
+ MessageType.Warning,
+ finalStatus.message,
+ [
+ MessageActionItem(title: UserPromptActions.renameAnyway),
+ MessageActionItem(title: UserPromptActions.cancel),
+ ],
+ );
+
+ if (token.isCancellationRequested) {
+ return cancelled();
+ }
+
+ if (userChoice.title != UserPromptActions.renameAnyway) {
+ // Return an empty workspace edit response so we do not perform any
+ // rename, but also so we do not cause the client to show the user an
+ // error after they clicked cancel.
+ return success(emptyWorkspaceEdit);
+ }
}
// Compute the actual change.
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 85debf8..a2c0d16 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
@@ -599,6 +599,17 @@
));
}
+ /// Shows the user a prompt with some actions to select using ShowMessageRequest.
+ Future<MessageActionItem> showUserPrompt(
+ MessageType type, String message, List<MessageActionItem> actions) async {
+ final response = await sendRequest(
+ Method.window_showMessageRequest,
+ ShowMessageRequestParams(type: type, message: message, actions: actions),
+ );
+
+ return MessageActionItem.fromJson(response.result);
+ }
+
Future<void> shutdown() {
// Defer closing the channel so that the shutdown response can be sent and
// logged.
diff --git a/pkg/analysis_server/test/lsp/rename_test.dart b/pkg/analysis_server/test/lsp/rename_test.dart
index 41469c8..6c14e48 100644
--- a/pkg/analysis_server/test/lsp/rename_test.dart
+++ b/pkg/analysis_server/test/lsp/rename_test.dart
@@ -4,9 +4,11 @@
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/src/lsp/constants.dart';
+import 'package:meta/meta.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
+import '../tool/lsp_spec/matchers.dart';
import 'server_abstract.dart';
void main() {
@@ -130,6 +132,84 @@
content, 'MyNewClass', expectedContent);
}
+ Future<void> test_rename_duplicateName_applyAfterDocumentChanges() async {
+ // Perform a refactor that results in a prompt to the user, but then modify
+ // the document before accepting/rejecting to make the rename invalid.
+ const content = '''
+ class MyOtherClass {}
+ class MyClass {}
+ final a = n^ew MyClass();
+ ''';
+ final result = await _test_rename_prompt(
+ content,
+ 'MyOtherClass',
+ expectedMessage:
+ 'Library already declares class with name \'MyOtherClass\'.',
+ action: UserPromptActions.renameAnyway,
+ beforeResponding: () => replaceFile(999, mainFileUri, 'Updated content'),
+ );
+ expect(result.result, isNull);
+ expect(result.error, isNotNull);
+ expect(result.error, isResponseError(ErrorCodes.ContentModified));
+ }
+
+ Future<void> test_rename_duplicateName_applyAnyway() async {
+ const content = '''
+ class MyOtherClass {}
+ class MyClass {}
+ final a = n^ew MyClass();
+ ''';
+ const expectedContent = '''
+ class MyOtherClass {}
+ class MyOtherClass {}
+ final a = new MyOtherClass();
+ ''';
+ final response = await _test_rename_prompt(
+ content,
+ 'MyOtherClass',
+ expectedMessage:
+ 'Library already declares class with name \'MyOtherClass\'.',
+ action: UserPromptActions.renameAnyway,
+ );
+
+ if (response.error != null) {
+ throw response.error;
+ }
+
+ final result = WorkspaceEdit.fromJson(response.result);
+
+ // Ensure applying the changes will give us the expected content.
+ final contents = {
+ mainFilePath: withoutMarkers(content),
+ };
+ applyDocumentChanges(
+ contents,
+ result.documentChanges,
+ );
+ expect(contents[mainFilePath], equals(expectedContent));
+ }
+
+ Future<void> test_rename_duplicateName_reject() async {
+ const content = '''
+ class MyOtherClass {}
+ class MyClass {}
+ final a = n^ew MyClass();
+ ''';
+ final response = await _test_rename_prompt(
+ content,
+ 'MyOtherClass',
+ expectedMessage:
+ 'Library already declares class with name \'MyOtherClass\'.',
+ action: UserPromptActions.cancel,
+ );
+ // Expect a successful empty response if cancelled.
+ expect(response.error, isNull);
+ expect(
+ WorkspaceEdit.fromJson(response.result),
+ equals(emptyWorkspaceEdit),
+ );
+ }
+
Future<void> test_rename_importPrefix() {
const content = '''
import 'dart:async' as myPr^efix;
@@ -237,17 +317,6 @@
expect(error.message, contains('name must not contain'));
}
- Future<void> test_rename_rejectedForDuplicateName() async {
- const content = '''
- class MyOtherClass {}
- class MyClass {}
- final a = n^ew MyClass();
- ''';
- final error = await _test_rename_failure(content, 'MyOtherClass');
- expect(error.code, equals(ServerErrorCodes.RenameNotValid));
- expect(error.message, contains('already declares class with name'));
- }
-
Future<void> test_rename_rejectedForSameName() async {
const content = '''
class My^Class {}
@@ -413,6 +482,55 @@
return result.error;
}
+ /// Tests a rename that is expected to cause an error, which will trigger
+ /// a ShowMessageRequest from the server to the client to allow the refactor
+ /// to be continued or rejected.
+ Future<ResponseMessage> _test_rename_prompt(
+ String content,
+ String newName, {
+ @required String expectedMessage,
+ Future<void> Function() beforeResponding,
+ @required String action,
+ int openFileVersion = 222,
+ int renameRequestFileVersion = 222,
+ }) async {
+ await initialize(
+ workspaceCapabilities:
+ withDocumentChangesSupport(emptyWorkspaceClientCapabilities),
+ );
+ await openFile(mainFileUri, withoutMarkers(content),
+ version: openFileVersion);
+
+ // Expect the server to call us back with a ShowMessageRequest prompt about
+ // the errors for us to accept/reject.
+ return handleExpectedRequest(
+ Method.window_showMessageRequest,
+ ShowMessageRequestParams.fromJson,
+ () => renameRaw(
+ mainFileUri,
+ renameRequestFileVersion,
+ positionFromMarker(content),
+ newName,
+ ),
+ handler: (ShowMessageRequestParams params) async {
+ // Ensure the warning prompt is as expected.
+ expect(params.type, equals(MessageType.Warning));
+ expect(params.message, equals(expectedMessage));
+ expect(params.actions, hasLength(2));
+ expect(params.actions[0],
+ equals(MessageActionItem(title: UserPromptActions.renameAnyway)));
+ expect(params.actions[1],
+ equals(MessageActionItem(title: UserPromptActions.cancel)));
+
+ // Allow the test to run some code before we send the response.
+ await beforeResponding?.call();
+
+ // Respond to the request with the required action.
+ return MessageActionItem(title: action);
+ },
+ );
+ }
+
Future<void> _test_rename_withDocumentChanges(
String content,
String newName,
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index d49ac6d..6ea2474 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -6,6 +6,7 @@
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/null_safety_understanding_flag.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/analysis/file_state.dart';
@@ -107,6 +108,10 @@
/// Compute analysis results for all units of the library.
Map<FileState, UnitAnalysisResult> analyzeSync() {
+ // Analyzer understands null safety, so it should set
+ // NullSafetyUnderstandingFlag.
+ assert(NullSafetyUnderstandingFlag.isEnabled);
+
timerLibraryAnalyzer.start();
Map<FileState, CompilationUnit> units = {};
diff --git a/pkg/analyzer_cli/lib/src/build_mode.dart b/pkg/analyzer_cli/lib/src/build_mode.dart
index df41c5e..3098758 100644
--- a/pkg/analyzer_cli/lib/src/build_mode.dart
+++ b/pkg/analyzer_cli/lib/src/build_mode.dart
@@ -7,7 +7,6 @@
import 'package:analyzer/dart/analysis/context_locator.dart' as api;
import 'package:analyzer/dart/analysis/declared_variables.dart';
-import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/file_system/file_system.dart';
@@ -179,7 +178,6 @@
PackageBundleAssembler assembler;
- final Map<String, ParsedUnitResult> inputParsedUnitResults = {};
summary2.LinkedElementFactory elementFactory;
// May be null.
@@ -249,13 +247,6 @@
assembler = PackageBundleAssembler();
if (_shouldOutputSummary) {
await logger.runAsync('Build and write output summary', () async {
- // Prepare all unlinked units.
- await logger.runAsync('Prepare unlinked units', () async {
- for (var src in explicitSources) {
- await _prepareUnit('${src.uri}');
- }
- });
-
// Build and assemble linked libraries.
_computeLinkedLibraries2();
@@ -298,8 +289,8 @@
});
}
- /// Use [elementFactory] filled with input summaries, and link prepared
- /// [inputParsedUnitResults] to produce linked libraries in [assembler].
+ /// Use [elementFactory] filled with input summaries, and link libraries
+ /// in [explicitSources] to produce linked libraries in [assembler].
void _computeLinkedLibraries2() {
logger.run('Link output summary2', () {
var inputLibraries = <summary2.LinkInputLibrary>[];
@@ -307,7 +298,7 @@
for (var librarySource in explicitSources) {
var path = librarySource.fullName;
- var parseResult = inputParsedUnitResults[path];
+ var parseResult = analysisDriver.parseFileSync(path);
if (parseResult == null) {
throw ArgumentError('No parsed unit for $path');
}
@@ -338,7 +329,7 @@
}
var partPath = partSource.fullName;
- var partParseResult = inputParsedUnitResults[partPath];
+ var partParseResult = analysisDriver.parseFileSync(partPath);
if (partParseResult == null) {
throw ArgumentError('No parsed unit for part $partPath in $path');
}
@@ -500,22 +491,6 @@
return Packages.empty;
}
- /// Ensure that the parsed unit for [absoluteUri] is available.
- ///
- /// If the unit is in the input [summaryDataStore], do nothing.
- Future<void> _prepareUnit(String absoluteUri) async {
- // Parse the source and serialize its AST.
- var uri = Uri.parse(absoluteUri);
- var source = sourceFactory.forUri2(uri);
- if (!source.exists()) {
- // TODO(paulberry): we should report a warning/error because DDC
- // compilations are unlikely to work.
- return;
- }
- var result = await analysisDriver.parseFile(source.fullName);
- inputParsedUnitResults[result.path] = result;
- }
-
/// Print errors for all explicit sources. If [outputPath] is supplied, output
/// is sent to a new file at that path.
Future<void> _printErrors({String outputPath}) async {
diff --git a/pkg/nnbd_migration/lib/migration_cli.dart b/pkg/nnbd_migration/lib/migration_cli.dart
index 1a68222b..2e6790d 100644
--- a/pkg/nnbd_migration/lib/migration_cli.dart
+++ b/pkg/nnbd_migration/lib/migration_cli.dart
@@ -15,6 +15,7 @@
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
+import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/util/sdk.dart';
@@ -223,9 +224,19 @@
static const String cmdName = 'migrate';
static const String cmdDescription =
- 'Perform a null safety migration on a project or package.'
- '\n\nThe migration tool is in preview; see '
- 'https://dart.dev/go/null-safety-migration for a migration guide.';
+ 'Perform a null safety migration on a project or package.';
+
+ static const String migrationGuideLink =
+ 'See https://dart.dev/go/null-safety-migration for a migration guide.';
+
+ static const String preFlagFlipCaveat =
+ 'Note: this tool is currently running on an SDK version where null '
+ 'safety is not yet enabled by default. You may encounter issues in the '
+ 'migration process - some aspects of the toolchain assume that they are '
+ 'running on an SDK where null safety has been enabled by default.';
+
+ /// Return whether the SDK has null safety on by default.
+ static bool get nullSafetyOnByDefault => IsEnabledByDefault.non_nullable;
final bool verbose;
@@ -247,7 +258,9 @@
}
@override
- String get description => cmdDescription;
+ String get description => nullSafetyOnByDefault
+ ? '$cmdDescription\n\n$migrationGuideLink'
+ : '$cmdDescription\n\n$preFlagFlipCaveat\n\n$migrationGuideLink';
@override
String get invocation {
@@ -726,10 +739,17 @@
logger.stdout('Migrating ${options.directory}');
logger.stdout('');
+ if (!MigrateCommand.nullSafetyOnByDefault) {
+ logger.stdout(MigrateCommand.preFlagFlipCaveat);
+ logger.stdout('');
+ }
+
+ logger.stdout(MigrateCommand.migrationGuideLink);
+ logger.stdout('');
+
if (hasMultipleAnalysisContext) {
- logger
- .stdout('Note: more than one project found; migrating the top-level '
- 'project.');
+ logger.stdout('Note: more than one project found; migrating the '
+ 'top-level project.');
logger.stdout('');
}
diff --git a/tests/standalone/standalone_kernel.status b/tests/standalone/standalone_kernel.status
index bc9439c..1da02b7 100644
--- a/tests/standalone/standalone_kernel.status
+++ b/tests/standalone/standalone_kernel.status
@@ -108,5 +108,5 @@
map_insert_remove_oom_test: Skip # Heap limit too low.
no_support_debugger_test: Skip # kernel-service snapshot not compatible with flag disabled
-[ $compiler != dartk && $compiler != dartkp || $compiler == dartkp && $system == windows ]
-entrypoints_verification_test: SkipByDesign # Requires VM to run. Cannot run in precompiled Windows because the DLL is linked against dart.exe instead of dart_precompiled_runtime.exe.
+[ $builder_tag == crossword || $compiler != dartk && $compiler != dartkp || $compiler == dartkp && $system == windows ]
+entrypoints_verification_test: SkipByDesign # Requires VM to run. Cannot run in precompiled Windows because the DLL is linked against dart.exe instead of dart_precompiled_runtime.exe. Cannot run in cross-word environment as native extension is not built.
diff --git a/tests/standalone_2/standalone_2_kernel.status b/tests/standalone_2/standalone_2_kernel.status
index cf0367e..939b834 100644
--- a/tests/standalone_2/standalone_2_kernel.status
+++ b/tests/standalone_2/standalone_2_kernel.status
@@ -109,5 +109,5 @@
map_insert_remove_oom_test: Skip # Heap limit too low.
no_support_debugger_test: Skip # kernel-service snapshot not compatible with flag disabled
-[ $compiler != dartk && $compiler != dartkp || $compiler == dartkp && $system == windows ]
-entrypoints_verification_test: SkipByDesign # Requires VM to run. Cannot run in precompiled Windows because the DLL is linked against dart.exe instead of dart_precompiled_runtime.exe.
+[ $builder_tag == crossword || $compiler != dartk && $compiler != dartkp || $compiler == dartkp && $system == windows ]
+entrypoints_verification_test: SkipByDesign # Requires VM to run. Cannot run in precompiled Windows because the DLL is linked against dart.exe instead of dart_precompiled_runtime.exe. Cannot run in cross-word environment as native extension is not built.
diff --git a/tools/VERSION b/tools/VERSION
index ba84698..dade2bf 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 11
PATCH 0
-PRERELEASE 221
+PRERELEASE 222
PRERELEASE_PATCH 0
\ No newline at end of file