Skip to content

Commit 3feede1

Browse files
bwilkersonCommit Bot
authored and
Commit Bot
committed
Remove final when converting to a super parameter
Fixes: #48698 Change-Id: I523f8e645dabc6879cbe56420cb370fd838d58ad Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/239540 Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]>
1 parent 3a4adb7 commit 3feede1

File tree

2 files changed

+130
-7
lines changed

2 files changed

+130
-7
lines changed

pkg/analysis_server/lib/src/services/correction/dart/convert_to_super_parameters.dart

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:analysis_server/src/services/correction/fix.dart';
88
import 'package:analysis_server/src/utilities/extensions/range_factory.dart';
99
import 'package:analyzer/dart/analysis/features.dart';
1010
import 'package:analyzer/dart/ast/ast.dart';
11+
import 'package:analyzer/dart/ast/token.dart';
1112
import 'package:analyzer/dart/ast/visitor.dart';
1213
import 'package:analyzer/dart/element/element.dart';
1314
import 'package:analyzer/source/source_range.dart';
@@ -136,15 +137,49 @@ class ConvertToSuperParameters extends CorrectionProducer {
136137
await builder.addDartFileEdit(file, (builder) {
137138
// Convert the parameters.
138139
for (var parameterData in allParameters) {
140+
var keyword = parameterData.finalKeyword;
141+
142+
void insertSuper() {
143+
if (keyword == null) {
144+
builder.addSimpleInsertion(parameterData.nameOffset, 'super.');
145+
} else {
146+
var tokenAfterKeyword = keyword.next!;
147+
if (tokenAfterKeyword.offset == parameterData.nameOffset) {
148+
builder.addSimpleReplacement(
149+
range.startStart(keyword, tokenAfterKeyword), 'super.');
150+
} else {
151+
builder.addDeletion(range.startStart(keyword, tokenAfterKeyword));
152+
builder.addSimpleInsertion(parameterData.nameOffset, 'super.');
153+
}
154+
}
155+
}
156+
139157
var typeToDelete = parameterData.typeToDelete;
140158
if (typeToDelete == null) {
141-
builder.addSimpleInsertion(parameterData.nameOffset, 'super.');
159+
insertSuper();
142160
} else {
143161
var primaryRange = typeToDelete.primaryRange;
144162
if (primaryRange == null) {
163+
// This only happens when the type is an inline function type with
164+
// no return type, such as `f(int i)`. Inline function types can't
165+
// have a `final` keyword unless there's an error in the code.
145166
builder.addSimpleInsertion(parameterData.nameOffset, 'super.');
146167
} else {
147-
builder.addSimpleReplacement(primaryRange, 'super.');
168+
if (keyword == null) {
169+
builder.addSimpleReplacement(primaryRange, 'super.');
170+
} else {
171+
var tokenAfterKeyword = keyword.next!;
172+
if (tokenAfterKeyword.offset == primaryRange.offset) {
173+
builder.addSimpleReplacement(
174+
range.startOffsetEndOffset(
175+
keyword.offset, primaryRange.end),
176+
'super.');
177+
} else {
178+
builder
179+
.addDeletion(range.startStart(keyword, tokenAfterKeyword));
180+
builder.addSimpleReplacement(primaryRange, 'super.');
181+
}
182+
}
148183
}
149184
var parameterRange = typeToDelete.parameterRange;
150185
if (parameterRange != null) {
@@ -200,20 +235,24 @@ class ConvertToSuperParameters extends CorrectionProducer {
200235
if (!typeSystem.isSubtypeOf(thisType, superType)) {
201236
return null;
202237
}
203-
var identifier = parameter.parameter.identifier;
238+
239+
var parameterNode = parameter.parameter;
240+
var identifier = parameterNode.identifier;
204241
if (identifier == null) {
205242
// This condition should never occur, but the test is here to promote the
206243
// type.
207244
return null;
208245
}
246+
209247
// Return the data.
210248
return _ParameterData(
211249
argumentIndex: argumentIndex,
212-
defaultValueRange: _defaultValueRange(
213-
parameter.parameter, superParameter, parameter.element),
250+
defaultValueRange:
251+
_defaultValueRange(parameterNode, superParameter, parameter.element),
252+
finalKeyword: _finalKeyword(parameterNode),
214253
nameOffset: identifier.offset,
215254
parameterIndex: parameter.index,
216-
typeToDelete: superType == thisType ? _type(parameter.parameter) : null,
255+
typeToDelete: superType == thisType ? _type(parameterNode) : null,
217256
);
218257
}
219258

@@ -235,6 +274,21 @@ class ConvertToSuperParameters extends CorrectionProducer {
235274
return null;
236275
}
237276

277+
/// Return data about the type annotation on the [parameter]. This is the
278+
/// information about the ranges of text that need to be removed in order to
279+
/// remove the type annotation.
280+
Token? _finalKeyword(FormalParameter parameter) {
281+
if (parameter is DefaultFormalParameter) {
282+
return _finalKeyword(parameter.parameter);
283+
} else if (parameter is SimpleFormalParameter) {
284+
var keyword = parameter.keyword;
285+
if (keyword?.type == Keyword.FINAL) {
286+
return keyword;
287+
}
288+
}
289+
return null;
290+
}
291+
238292
/// Return the constructor to be converted, or `null` if the cursor is not on
239293
/// the name of a constructor.
240294
ConstructorDeclaration? _findConstructor() {
@@ -376,6 +430,10 @@ class _Parameter {
376430

377431
/// Information used to convert a single parameter.
378432
class _ParameterData {
433+
/// The `final` keyword on the parameter, or `null` if there is no `final`
434+
/// keyword.
435+
final Token? finalKeyword;
436+
379437
/// The type annotation that should be deleted from the parameter list, or
380438
/// `null` if there is no type annotation to delete or if the type should not
381439
/// be deleted.
@@ -397,7 +455,8 @@ class _ParameterData {
397455

398456
/// Initialize a newly create data object.
399457
_ParameterData(
400-
{required this.typeToDelete,
458+
{required this.finalKeyword,
459+
required this.typeToDelete,
401460
required this.nameOffset,
402461
required this.defaultValueRange,
403462
required this.parameterIndex,

pkg/analysis_server/test/src/services/correction/assist/convert_to_super_parameters_test.dart

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,70 @@ class B extends A {
164164
''');
165165
}
166166

167+
Future<void> test_final_named_withoutType() async {
168+
await resolveTestCode('''
169+
class A {
170+
A({required int x});
171+
}
172+
class B extends A {
173+
B({required final x}) : super(x: x);
174+
}
175+
''');
176+
// `dynamic` is not a subtype of `int`
177+
await assertNoAssistAt('B(');
178+
}
179+
180+
Future<void> test_final_named_withType() async {
181+
await resolveTestCode('''
182+
class A {
183+
A({required int x});
184+
}
185+
class B extends A {
186+
B({required final int x}) : super(x: x);
187+
}
188+
''');
189+
await assertHasAssistAt('B(', '''
190+
class A {
191+
A({required int x});
192+
}
193+
class B extends A {
194+
B({required super.x});
195+
}
196+
''');
197+
}
198+
199+
Future<void> test_final_positional_withoutType() async {
200+
await resolveTestCode('''
201+
class A {
202+
A(int x);
203+
}
204+
class B extends A {
205+
B(final x) : super(x);
206+
}
207+
''');
208+
// `dynamic` is not a subtype of `int`
209+
await assertNoAssistAt('B(');
210+
}
211+
212+
Future<void> test_final_positional_withType() async {
213+
await resolveTestCode('''
214+
class A {
215+
A(int x);
216+
}
217+
class B extends A {
218+
B(final int x) : super(x);
219+
}
220+
''');
221+
await assertHasAssistAt('B(', '''
222+
class A {
223+
A(int x);
224+
}
225+
class B extends A {
226+
B(super.x);
227+
}
228+
''');
229+
}
230+
167231
Future<void> test_functionTypedFormalParameter() async {
168232
await resolveTestCode('''
169233
class A {

0 commit comments

Comments
 (0)