Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 04cf6ad

Browse files
author
Dart CI
committed
Version 2.11.0-222.0.dev
Merge commit 'ece17f73b0b1b15417d07e2dcbe79f4974f90ba0' into 'dev'
2 parents 78d20d3 + ece17f7 commit 04cf6ad

File tree

12 files changed

+220
-55
lines changed

12 files changed

+220
-55
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@
2525

2626
Updated the Linter to `0.1.121`, which includes:
2727

28+
#### Pub
29+
30+
* New commands `pub add` and `pub remove` that adds and remove new dependencies
31+
to your `pubspec.yaml`.
32+
* `pub publish` will check your pubspec keys for likely typos.
33+
* `pub get` will print a warning if the resolution is mixed mode.
34+
2835
# 0.1.121
2936

3037
* Performance improvements to `always_use_package_imports`,

DEPS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ vars = {
128128
"ply_rev": "604b32590ffad5cbb82e4afef1d305512d06ae93",
129129
"pool_rev": "eedbd5fde84f9a1a8da643b475305a81841da599",
130130
"protobuf_rev": "3746c8fd3f2b0147623a8e3db89c3ff4330de760",
131-
"pub_rev": "04e237f78b2302d7f20d0b362554425e8deb8add",
131+
"pub_rev": "f0c7771b38155d3829a60d60b5dba2784b100811",
132132
"pub_semver_tag": "v1.4.4",
133133
"quiver-dart_tag": "246e754fe45cecb6aa5f3f13b4ed61037ff0d784",
134134
"resource_rev": "f8e37558a1c4f54550aa463b88a6a831e3e33cd6",

pkg/analysis_server/lib/src/lsp/constants.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ const dartTypeFormattingCharacters = ['}', ';'];
5252
/// A [ProgressToken] used for reporting progress when the server is analyzing.
5353
final analyzingProgressToken = Either2<num, String>.t2('ANALYZING');
5454

55+
final emptyWorkspaceEdit = WorkspaceEdit();
56+
5557
/// Constants for command IDs that are exchanged between LSP client/server.
5658
abstract class Commands {
5759
/// A list of all commands IDs that can be sent to the client to inform which
@@ -127,3 +129,9 @@ abstract class ServerErrorCodes {
127129
/// if it crashes 5 times in the last 180 seconds."
128130
static const ClientServerInconsistentState = ErrorCodes(-32099);
129131
}
132+
133+
/// Strings used in user prompts (window/showMessageRequest).
134+
abstract class UserPromptActions {
135+
static const String cancel = 'Cancel';
136+
static const String renameAnyway = 'Rename Anyway';
137+
}

pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ class RenameHandler extends MessageHandler<RenameParams, WorkspaceEdit> {
126126
if (token.isCancellationRequested) {
127127
return cancelled();
128128
}
129-
if (initStatus.hasError) {
129+
if (initStatus.hasFatalError) {
130130
return error(
131131
ServerErrorCodes.RenameNotValid, initStatus.problem.message, null);
132132
}
@@ -144,9 +144,30 @@ class RenameHandler extends MessageHandler<RenameParams, WorkspaceEdit> {
144144
if (token.isCancellationRequested) {
145145
return cancelled();
146146
}
147-
if (finalStatus.hasError) {
147+
if (finalStatus.hasFatalError) {
148148
return error(
149149
ServerErrorCodes.RenameNotValid, finalStatus.problem.message, null);
150+
} else if (finalStatus.hasError || finalStatus.hasWarning) {
151+
// Ask the user whether to proceed with the rename.
152+
final userChoice = await server.showUserPrompt(
153+
MessageType.Warning,
154+
finalStatus.message,
155+
[
156+
MessageActionItem(title: UserPromptActions.renameAnyway),
157+
MessageActionItem(title: UserPromptActions.cancel),
158+
],
159+
);
160+
161+
if (token.isCancellationRequested) {
162+
return cancelled();
163+
}
164+
165+
if (userChoice.title != UserPromptActions.renameAnyway) {
166+
// Return an empty workspace edit response so we do not perform any
167+
// rename, but also so we do not cause the client to show the user an
168+
// error after they clicked cancel.
169+
return success(emptyWorkspaceEdit);
170+
}
150171
}
151172

152173
// Compute the actual change.

pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,17 @@ class LspAnalysisServer extends AbstractAnalysisServer {
599599
));
600600
}
601601

602+
/// Shows the user a prompt with some actions to select using ShowMessageRequest.
603+
Future<MessageActionItem> showUserPrompt(
604+
MessageType type, String message, List<MessageActionItem> actions) async {
605+
final response = await sendRequest(
606+
Method.window_showMessageRequest,
607+
ShowMessageRequestParams(type: type, message: message, actions: actions),
608+
);
609+
610+
return MessageActionItem.fromJson(response.result);
611+
}
612+
602613
Future<void> shutdown() {
603614
// Defer closing the channel so that the shutdown response can be sent and
604615
// logged.

pkg/analysis_server/test/lsp/rename_test.dart

Lines changed: 129 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
66
import 'package:analysis_server/src/lsp/constants.dart';
7+
import 'package:meta/meta.dart';
78
import 'package:test/test.dart';
89
import 'package:test_reflective_loader/test_reflective_loader.dart';
910

11+
import '../tool/lsp_spec/matchers.dart';
1012
import 'server_abstract.dart';
1113

1214
void main() {
@@ -130,6 +132,84 @@ class RenameTest extends AbstractLspAnalysisServerTest {
130132
content, 'MyNewClass', expectedContent);
131133
}
132134

135+
Future<void> test_rename_duplicateName_applyAfterDocumentChanges() async {
136+
// Perform a refactor that results in a prompt to the user, but then modify
137+
// the document before accepting/rejecting to make the rename invalid.
138+
const content = '''
139+
class MyOtherClass {}
140+
class MyClass {}
141+
final a = n^ew MyClass();
142+
''';
143+
final result = await _test_rename_prompt(
144+
content,
145+
'MyOtherClass',
146+
expectedMessage:
147+
'Library already declares class with name \'MyOtherClass\'.',
148+
action: UserPromptActions.renameAnyway,
149+
beforeResponding: () => replaceFile(999, mainFileUri, 'Updated content'),
150+
);
151+
expect(result.result, isNull);
152+
expect(result.error, isNotNull);
153+
expect(result.error, isResponseError(ErrorCodes.ContentModified));
154+
}
155+
156+
Future<void> test_rename_duplicateName_applyAnyway() async {
157+
const content = '''
158+
class MyOtherClass {}
159+
class MyClass {}
160+
final a = n^ew MyClass();
161+
''';
162+
const expectedContent = '''
163+
class MyOtherClass {}
164+
class MyOtherClass {}
165+
final a = new MyOtherClass();
166+
''';
167+
final response = await _test_rename_prompt(
168+
content,
169+
'MyOtherClass',
170+
expectedMessage:
171+
'Library already declares class with name \'MyOtherClass\'.',
172+
action: UserPromptActions.renameAnyway,
173+
);
174+
175+
if (response.error != null) {
176+
throw response.error;
177+
}
178+
179+
final result = WorkspaceEdit.fromJson(response.result);
180+
181+
// Ensure applying the changes will give us the expected content.
182+
final contents = {
183+
mainFilePath: withoutMarkers(content),
184+
};
185+
applyDocumentChanges(
186+
contents,
187+
result.documentChanges,
188+
);
189+
expect(contents[mainFilePath], equals(expectedContent));
190+
}
191+
192+
Future<void> test_rename_duplicateName_reject() async {
193+
const content = '''
194+
class MyOtherClass {}
195+
class MyClass {}
196+
final a = n^ew MyClass();
197+
''';
198+
final response = await _test_rename_prompt(
199+
content,
200+
'MyOtherClass',
201+
expectedMessage:
202+
'Library already declares class with name \'MyOtherClass\'.',
203+
action: UserPromptActions.cancel,
204+
);
205+
// Expect a successful empty response if cancelled.
206+
expect(response.error, isNull);
207+
expect(
208+
WorkspaceEdit.fromJson(response.result),
209+
equals(emptyWorkspaceEdit),
210+
);
211+
}
212+
133213
Future<void> test_rename_importPrefix() {
134214
const content = '''
135215
import 'dart:async' as myPr^efix;
@@ -237,17 +317,6 @@ class RenameTest extends AbstractLspAnalysisServerTest {
237317
expect(error.message, contains('name must not contain'));
238318
}
239319

240-
Future<void> test_rename_rejectedForDuplicateName() async {
241-
const content = '''
242-
class MyOtherClass {}
243-
class MyClass {}
244-
final a = n^ew MyClass();
245-
''';
246-
final error = await _test_rename_failure(content, 'MyOtherClass');
247-
expect(error.code, equals(ServerErrorCodes.RenameNotValid));
248-
expect(error.message, contains('already declares class with name'));
249-
}
250-
251320
Future<void> test_rename_rejectedForSameName() async {
252321
const content = '''
253322
class My^Class {}
@@ -413,6 +482,55 @@ class RenameTest extends AbstractLspAnalysisServerTest {
413482
return result.error;
414483
}
415484

485+
/// Tests a rename that is expected to cause an error, which will trigger
486+
/// a ShowMessageRequest from the server to the client to allow the refactor
487+
/// to be continued or rejected.
488+
Future<ResponseMessage> _test_rename_prompt(
489+
String content,
490+
String newName, {
491+
@required String expectedMessage,
492+
Future<void> Function() beforeResponding,
493+
@required String action,
494+
int openFileVersion = 222,
495+
int renameRequestFileVersion = 222,
496+
}) async {
497+
await initialize(
498+
workspaceCapabilities:
499+
withDocumentChangesSupport(emptyWorkspaceClientCapabilities),
500+
);
501+
await openFile(mainFileUri, withoutMarkers(content),
502+
version: openFileVersion);
503+
504+
// Expect the server to call us back with a ShowMessageRequest prompt about
505+
// the errors for us to accept/reject.
506+
return handleExpectedRequest(
507+
Method.window_showMessageRequest,
508+
ShowMessageRequestParams.fromJson,
509+
() => renameRaw(
510+
mainFileUri,
511+
renameRequestFileVersion,
512+
positionFromMarker(content),
513+
newName,
514+
),
515+
handler: (ShowMessageRequestParams params) async {
516+
// Ensure the warning prompt is as expected.
517+
expect(params.type, equals(MessageType.Warning));
518+
expect(params.message, equals(expectedMessage));
519+
expect(params.actions, hasLength(2));
520+
expect(params.actions[0],
521+
equals(MessageActionItem(title: UserPromptActions.renameAnyway)));
522+
expect(params.actions[1],
523+
equals(MessageActionItem(title: UserPromptActions.cancel)));
524+
525+
// Allow the test to run some code before we send the response.
526+
await beforeResponding?.call();
527+
528+
// Respond to the request with the required action.
529+
return MessageActionItem(title: action);
530+
},
531+
);
532+
}
533+
416534
Future<void> _test_rename_withDocumentChanges(
417535
String content,
418536
String newName,

pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:analyzer/dart/analysis/declared_variables.dart';
66
import 'package:analyzer/dart/analysis/features.dart';
77
import 'package:analyzer/dart/ast/ast.dart';
88
import 'package:analyzer/dart/element/element.dart';
9+
import 'package:analyzer/dart/element/null_safety_understanding_flag.dart';
910
import 'package:analyzer/error/error.dart';
1011
import 'package:analyzer/error/listener.dart';
1112
import 'package:analyzer/src/dart/analysis/file_state.dart';
@@ -107,6 +108,10 @@ class LibraryAnalyzer {
107108

108109
/// Compute analysis results for all units of the library.
109110
Map<FileState, UnitAnalysisResult> analyzeSync() {
111+
// Analyzer understands null safety, so it should set
112+
// NullSafetyUnderstandingFlag.
113+
assert(NullSafetyUnderstandingFlag.isEnabled);
114+
110115
timerLibraryAnalyzer.start();
111116
Map<FileState, CompilationUnit> units = {};
112117

pkg/analyzer_cli/lib/src/build_mode.dart

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import 'dart:isolate';
77

88
import 'package:analyzer/dart/analysis/context_locator.dart' as api;
99
import 'package:analyzer/dart/analysis/declared_variables.dart';
10-
import 'package:analyzer/dart/analysis/results.dart';
1110
import 'package:analyzer/dart/ast/ast.dart';
1211
import 'package:analyzer/error/error.dart';
1312
import 'package:analyzer/file_system/file_system.dart';
@@ -179,7 +178,6 @@ class BuildMode with HasContextMixin {
179178

180179
PackageBundleAssembler assembler;
181180

182-
final Map<String, ParsedUnitResult> inputParsedUnitResults = {};
183181
summary2.LinkedElementFactory elementFactory;
184182

185183
// May be null.
@@ -249,13 +247,6 @@ class BuildMode with HasContextMixin {
249247
assembler = PackageBundleAssembler();
250248
if (_shouldOutputSummary) {
251249
await logger.runAsync('Build and write output summary', () async {
252-
// Prepare all unlinked units.
253-
await logger.runAsync('Prepare unlinked units', () async {
254-
for (var src in explicitSources) {
255-
await _prepareUnit('${src.uri}');
256-
}
257-
});
258-
259250
// Build and assemble linked libraries.
260251
_computeLinkedLibraries2();
261252

@@ -298,16 +289,16 @@ class BuildMode with HasContextMixin {
298289
});
299290
}
300291

301-
/// Use [elementFactory] filled with input summaries, and link prepared
302-
/// [inputParsedUnitResults] to produce linked libraries in [assembler].
292+
/// Use [elementFactory] filled with input summaries, and link libraries
293+
/// in [explicitSources] to produce linked libraries in [assembler].
303294
void _computeLinkedLibraries2() {
304295
logger.run('Link output summary2', () {
305296
var inputLibraries = <summary2.LinkInputLibrary>[];
306297

307298
for (var librarySource in explicitSources) {
308299
var path = librarySource.fullName;
309300

310-
var parseResult = inputParsedUnitResults[path];
301+
var parseResult = analysisDriver.parseFileSync(path);
311302
if (parseResult == null) {
312303
throw ArgumentError('No parsed unit for $path');
313304
}
@@ -338,7 +329,7 @@ class BuildMode with HasContextMixin {
338329
}
339330

340331
var partPath = partSource.fullName;
341-
var partParseResult = inputParsedUnitResults[partPath];
332+
var partParseResult = analysisDriver.parseFileSync(partPath);
342333
if (partParseResult == null) {
343334
throw ArgumentError('No parsed unit for part $partPath in $path');
344335
}
@@ -500,22 +491,6 @@ class BuildMode with HasContextMixin {
500491
return Packages.empty;
501492
}
502493

503-
/// Ensure that the parsed unit for [absoluteUri] is available.
504-
///
505-
/// If the unit is in the input [summaryDataStore], do nothing.
506-
Future<void> _prepareUnit(String absoluteUri) async {
507-
// Parse the source and serialize its AST.
508-
var uri = Uri.parse(absoluteUri);
509-
var source = sourceFactory.forUri2(uri);
510-
if (!source.exists()) {
511-
// TODO(paulberry): we should report a warning/error because DDC
512-
// compilations are unlikely to work.
513-
return;
514-
}
515-
var result = await analysisDriver.parseFile(source.fullName);
516-
inputParsedUnitResults[result.path] = result;
517-
}
518-
519494
/// Print errors for all explicit sources. If [outputPath] is supplied, output
520495
/// is sent to a new file at that path.
521496
Future<void> _printErrors({String outputPath}) async {

0 commit comments

Comments
 (0)