Skip to content

Commit d643f20

Browse files
munificentCommit Queue
authored andcommitted
Update CreateConstructorForFinalFields to handle private named parameters.
In a library where private named parameters are supported, it produces a private named initializing formal. Otherwise, it falls back to the existing behavior of a public parameter with an explicit initializer. Bug: #62496 Change-Id: I51b0c9893959c47976a08881f58152629a90c3c7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/475480 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com> Auto-Submit: Bob Nystrom <rnystrom@google.com>
1 parent 5ee0d52 commit d643f20

2 files changed

Lines changed: 86 additions & 58 deletions

File tree

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

Lines changed: 56 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import '../../../utilities/extensions/ast.dart';
2020

2121
/// The boolean value indicates whether the field is required.
2222
/// The string value is the field/parameter name.
23-
typedef _FieldRecord = (bool, String);
23+
typedef _FieldRecord = ({bool required, String parameter});
2424

2525
class CreateConstructorForFinalFields extends ResolvedCorrectionProducer {
2626
final _Style _style;
@@ -294,42 +294,24 @@ class CreateConstructorForFinalFields extends ResolvedCorrectionProducer {
294294
}
295295
buffer.write('super.');
296296
buffer.write(formalParameter.name!);
297-
parameters.add((required, buffer.toString()));
297+
parameters.add((required: required, parameter: buffer.toString()));
298298
}
299299
}
300-
for (var field in fields) {
301-
buffer.clear();
302-
if (field.namedFormalParameterName == field.fieldName) {
303-
buffer.write('required this.');
304-
buffer.write(field.fieldName);
305-
} else {
306-
buffer.write('required ');
307-
buffer.write(utils.getNodeText(field.typeAnnotation));
308-
buffer.write(' ');
309-
buffer.write(field.namedFormalParameterName);
310-
fieldsForInitializers.add(field);
311-
}
312-
parameters.add((true, buffer.toString()));
313-
}
314300

315-
if (requiredNamedParametersFirst) {
316-
parameters.sort((a, b) {
317-
if (a.$1 == b.$1) {
318-
return 0;
319-
} else if (a.$1) {
320-
return -1;
321-
} else {
322-
return 1;
323-
}
324-
});
325-
}
301+
_writeParameters(
302+
fields,
303+
parameters,
304+
fieldsForInitializers,
305+
alwaysRequired: true,
306+
requiredNamedParametersFirst: requiredNamedParametersFirst,
307+
);
326308

327309
var hasWritten = false;
328310
for (var parameter in parameters) {
329311
if (hasWritten) {
330312
builder.write(', ');
331313
}
332-
builder.write(parameter.$2);
314+
builder.write(parameter.parameter);
333315
hasWritten = true;
334316
}
335317

@@ -417,15 +399,43 @@ class CreateConstructorForFinalFields extends ResolvedCorrectionProducer {
417399
var childrenLast = fields.stablePartition((field) => !field.isChild);
418400

419401
var parameters = <_FieldRecord>[];
420-
var buffer = StringBuffer();
421-
for (var field in childrenLast) {
422-
var required = false;
423-
buffer.clear();
424-
if (field.hasNonNullableType) {
425-
required = true;
426-
buffer.write('required ');
402+
_writeParameters(
403+
childrenLast,
404+
parameters,
405+
fieldsForInitializers,
406+
requiredNamedParametersFirst: requiredNamedParametersFirst,
407+
);
408+
409+
// If this is false, then key is added first.
410+
var isFirst = requiredNamedParametersFirst;
411+
for (var parameter in parameters) {
412+
if (isFirst) {
413+
isFirst = false;
414+
} else {
415+
builder.write(', ');
427416
}
428-
if (field.namedFormalParameterName == field.fieldName) {
417+
builder.write(parameter.parameter);
418+
}
419+
}
420+
421+
void _writeParameters(
422+
List<_Field> fields,
423+
List<_FieldRecord> parameters,
424+
List<_Field> fieldsForInitializers, {
425+
bool? alwaysRequired,
426+
required bool requiredNamedParametersFirst,
427+
}) {
428+
for (var field in fields) {
429+
var isRequired = alwaysRequired ?? field.hasNonNullableType;
430+
431+
var buffer = StringBuffer();
432+
if (isRequired) buffer.write('required ');
433+
434+
// If the field is private and we're in a library that doesn't support
435+
// private named parameters, then we have to make a public parameter and
436+
// initialize the field explicitly.
437+
if (field.namedFormalParameterName == field.fieldName ||
438+
isEnabled(Feature.private_named_parameters)) {
429439
buffer.write('this.');
430440
buffer.write(field.fieldName);
431441
} else {
@@ -434,28 +444,18 @@ class CreateConstructorForFinalFields extends ResolvedCorrectionProducer {
434444
buffer.write(field.namedFormalParameterName);
435445
fieldsForInitializers.add(field);
436446
}
437-
parameters.add((required, buffer.toString()));
447+
parameters.add((required: isRequired, parameter: buffer.toString()));
438448
}
449+
439450
if (requiredNamedParametersFirst) {
440-
parameters.sort((a, b) {
441-
if (a.$1 == b.$1) {
442-
return 0;
443-
} else if (a.$1) {
444-
return -1;
445-
} else {
446-
return 1;
447-
}
448-
});
449-
}
450-
// If this is false, then key is added first.
451-
var isFirst = requiredNamedParametersFirst;
452-
for (var parameter in parameters) {
453-
if (isFirst) {
454-
isFirst = false;
455-
} else {
456-
builder.write(', ');
457-
}
458-
builder.write(parameter.$2);
451+
parameters.sort(
452+
(a, b) => switch ((a.required, b.required)) {
453+
(false, false) => 0,
454+
(false, true) => 1,
455+
(true, false) => 0,
456+
(true, true) => -1,
457+
},
458+
);
459459
}
460460
}
461461
}

pkg/analysis_server/test/src/services/correction/fix/create_constructor_for_final_fields_test.dart

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ import 'package:flutter/widgets.dart';
6161
class Test extends StatelessWidget {
6262
final int _a;
6363
64-
const Test({super.key, required int a}) : _a = a;
64+
const Test({super.key, required this._a});
6565
}
6666
''',
6767
filter: (error) {
@@ -308,7 +308,7 @@ class Test {
308308
final int _b;
309309
final int c;
310310
311-
Test({required int a, required int b, required this.c}) : _a = a, _b = b;
311+
Test({required this._a, required this._b, required this.c});
312312
}
313313
''',
314314
filter: (error) {
@@ -333,6 +333,34 @@ class Test {
333333
);
334334
}
335335

336+
Future<void>
337+
test_class_noSuperClass_hasPrivate_unsupportedPrivateNamedParameters() async {
338+
await resolveTestCode('''
339+
// @dart=3.10
340+
class Test {
341+
final int _a;
342+
final int _b;
343+
final int c;
344+
}
345+
''');
346+
await assertHasFix(
347+
'''
348+
// @dart=3.10
349+
class Test {
350+
final int _a;
351+
final int _b;
352+
final int c;
353+
354+
Test({required int a, required int b, required this.c}) : _a = a, _b = b;
355+
}
356+
''',
357+
filter: (error) {
358+
return error.diagnosticCode == diag.finalNotInitialized &&
359+
error.message.contains("'_a'");
360+
},
361+
);
362+
}
363+
336364
Future<void> test_enum() async {
337365
await resolveTestCode('''
338366
enum E {

0 commit comments

Comments
 (0)