[analyzer] `RemoveTypeAnnotation` to handle type arguments
Fixes #49227
Change-Id: I0f754d84e3e4e89800abdec26925229587202bca
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/247968
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_type_annotation.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_type_annotation.dart
index 0b1f3c6..853558be 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/remove_type_annotation.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_type_annotation.dart
@@ -49,8 +49,8 @@
Future<void> _removeFromDeclarationList(
ChangeBuilder builder, VariableDeclarationList declarationList) async {
// we need a type
- var typeNode = declarationList.type;
- if (typeNode == null) {
+ var type = declarationList.type;
+ if (type == null) {
return;
}
// ignore if an incomplete variable declaration
@@ -63,19 +63,60 @@
if (selectionOffset > firstVariable.name.end) {
return;
}
+
+ var initializer = firstVariable.initializer;
// The variable must have an initializer, otherwise there is no other
// source for its type.
- if (firstVariable.initializer == null) {
+ if (initializer == null) {
+ return;
+ }
+
+ String? typeArgumentsText;
+ int? typeArgumentsOffset;
+ if (type is NamedType) {
+ var typeArguments = type.typeArguments;
+ if (typeArguments != null) {
+ if (initializer is CascadeExpression) {
+ initializer = initializer.target;
+ }
+ if (initializer is TypedLiteral) {
+ if (initializer.typeArguments == null) {
+ typeArgumentsText = utils.getNodeText(typeArguments);
+ if (initializer is ListLiteral) {
+ typeArgumentsOffset = initializer.leftBracket.offset;
+ } else if (initializer is SetOrMapLiteral) {
+ typeArgumentsOffset = initializer.leftBracket.offset;
+ } else {
+ throw StateError('Unhandled subclass of TypedLiteral');
+ }
+ }
+ } else if (initializer is InstanceCreationExpression) {
+ if (initializer.constructorName.type.typeArguments == null) {
+ typeArgumentsText = utils.getNodeText(typeArguments);
+ typeArgumentsOffset = initializer.constructorName.type.end;
+ }
+ }
+ }
+ }
+ if (initializer is SetOrMapLiteral &&
+ initializer.typeArguments == null &&
+ typeArgumentsText == null) {
+ // This is to prevent the fix from converting a valid map or set literal
+ // into an ambiguous literal. We could apply this in more places
+ // by examining the elements of the collection.
return;
}
var keyword = declarationList.keyword;
await builder.addDartFileEdit(file, (builder) {
- var typeRange = range.startStart(typeNode, firstVariable);
+ var typeRange = range.startStart(type, firstVariable);
if (keyword != null && keyword.lexeme != 'var') {
builder.addSimpleReplacement(typeRange, '');
} else {
builder.addSimpleReplacement(typeRange, 'var ');
}
+ if (typeArgumentsText != null && typeArgumentsOffset != null) {
+ builder.addSimpleInsertion(typeArgumentsOffset, typeArgumentsText);
+ }
});
}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/replace_with_var.dart b/pkg/analysis_server/lib/src/services/correction/dart/replace_with_var.dart
index 49ddf35..3c5a55b 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/replace_with_var.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/replace_with_var.dart
@@ -64,6 +64,8 @@
typeArgumentsOffset = initializer.leftBracket.offset;
} else if (initializer is SetOrMapLiteral) {
typeArgumentsOffset = initializer.leftBracket.offset;
+ } else {
+ throw StateError('Unhandled subclass of TypedLiteral');
}
}
} else if (initializer is InstanceCreationExpression) {
@@ -77,9 +79,9 @@
if (initializer is SetOrMapLiteral &&
initializer.typeArguments == null &&
typeArgumentsText == null) {
- // TODO(brianwilkerson) This is to prevent the fix from converting a
- // valid map or set literal into an ambiguous literal. We could apply
- // this in more places by examining the elements of the collection.
+ // This is to prevent the fix from converting a valid map or set literal
+ // into an ambiguous literal. We could apply this in more places
+ // by examining the elements of the collection.
return;
}
await builder.addDartFileEdit(file, (builder) {
diff --git a/pkg/analysis_server/test/src/services/correction/assist/remove_type_annotation_test.dart b/pkg/analysis_server/test/src/services/correction/assist/remove_type_annotation_test.dart
index 99f4ab7..d5fdf25 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/remove_type_annotation_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/remove_type_annotation_test.dart
@@ -54,6 +54,45 @@
await assertNoAssistAt('v');
}
+ Future<void> test_generic_instanceCreation_withoutArguments() async {
+ await resolveTestCode('''
+C<int> c = C();
+class C<T> {}
+''');
+ await assertHasAssistAt('c = ', '''
+var c = C<int>();
+class C<T> {}
+''');
+ }
+
+ Future<void> test_generic_listLiteral() async {
+ await resolveTestCode('''
+List<int> l = [];
+''');
+ await assertHasAssistAt('l = ', '''
+var l = <int>[];
+''');
+ }
+
+ Future<void> test_generic_setLiteral_ambiguous() async {
+ await resolveTestCode('''
+Set f() {
+ /*caret*/Set s = {};
+ return s;
+}
+''');
+ await assertNoAssist();
+ }
+
+ Future<void> test_generic_setLiteral_cascade() async {
+ await resolveTestCode('''
+Set<String> s = {}..addAll([]);
+''');
+ await assertHasAssistAt('s = ', '''
+var s = <String>{}..addAll([]);
+''');
+ }
+
Future<void> test_instanceCreation_freeStanding() async {
await resolveTestCode('''
class A {}