From 57b192f3a63ac109cd7550071c52cec69e637aeb Mon Sep 17 00:00:00 2001 From: Ahmed Ashour Date: Sun, 12 Jun 2022 17:24:06 +0000 Subject: [PATCH] [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 Commit-Queue: Brian Wilkerson --- .../dart/remove_type_annotation.dart | 49 +++++++++++++++++-- .../correction/dart/replace_with_var.dart | 8 +-- .../assist/remove_type_annotation_test.dart | 39 +++++++++++++++ 3 files changed, 89 insertions(+), 7 deletions(-) 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 0b1f3c6f7b21b..853558be605b9 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 @@ class RemoveTypeAnnotation extends CorrectionProducer { Future _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 @@ class RemoveTypeAnnotation extends CorrectionProducer { 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 49ddf35559c13..3c5a55bd9d3d0 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 @@ class ReplaceWithVar extends CorrectionProducer { 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 @@ class ReplaceWithVar extends CorrectionProducer { 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 99f4ab75380c1..d5fdf25c776b4 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 @@ class A { await assertNoAssistAt('v'); } + Future test_generic_instanceCreation_withoutArguments() async { + await resolveTestCode(''' +C c = C(); +class C {} +'''); + await assertHasAssistAt('c = ', ''' +var c = C(); +class C {} +'''); + } + + Future test_generic_listLiteral() async { + await resolveTestCode(''' +List l = []; +'''); + await assertHasAssistAt('l = ', ''' +var l = []; +'''); + } + + Future test_generic_setLiteral_ambiguous() async { + await resolveTestCode(''' +Set f() { + /*caret*/Set s = {}; + return s; +} +'''); + await assertNoAssist(); + } + + Future test_generic_setLiteral_cascade() async { + await resolveTestCode(''' +Set s = {}..addAll([]); +'''); + await assertHasAssistAt('s = ', ''' +var s = {}..addAll([]); +'''); + } + Future test_instanceCreation_freeStanding() async { await resolveTestCode(''' class A {}