#62878 quick fix incorrectly treats positional arguments

Closes https://github.com/dart-lang/sdk/pull/62921

GitOrigin-RevId: 5f21333b6fc53b56897ab2a83e9aa8638911a56a
Change-Id: Ie24702e8e7b1eda21b34f9972f02fd94387dc1f0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/488525
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
D.ildo
2026-03-20 07:51:50 -07:00
committed by Commit Queue
parent c2c76aae00
commit 7cad9ad43d
3 changed files with 100 additions and 11 deletions
@@ -1437,6 +1437,44 @@ class A {
);
}
Future<void> test_createUnqualified_parameters_named_mixedOrder() async {
await resolveTestCode('''
class C {}
f(C c) {
c.m(arg1: true, 0, arg3: 'string');
}
''');
await assertHasFix('''
class C {
void m(int i, {required bool arg1, required String arg3}) {}
}
f(C c) {
c.m(arg1: true, 0, arg3: 'string');
}
''');
}
Future<void> test_createUnqualified_parameters_named_only() async {
await resolveTestCode('''
class C {}
f(C c) {
c.m(arg1: true, arg3: 'string');
}
''');
await assertHasFix('''
class C {
void m({required bool arg1, required String arg3}) {}
}
f(C c) {
c.m(arg1: true, arg3: 'string');
}
''');
}
Future<void> test_createUnqualified_returnType() async {
await resolveTestCode('''
class A {
@@ -825,28 +825,51 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder {
ArgumentList argumentList, {
List<TypeParameterElement>? typeParametersInScope,
}) {
// TODO(brianwilkerson): Handle the case when there are required parameters
// after named parameters.
var usedNames = <String>{};
var arguments = argumentList.arguments;
var hasNamedParameters = false;
for (var i = 0; i < argumentList.arguments.length; i++) {
var positionalArguments = <(int, Expression)>[];
var namedArguments = <(int, NamedExpression)>[];
for (var i = 0; i < arguments.length; i++) {
var argument = arguments[i];
if (i > 0) {
if (argument is NamedExpression) {
namedArguments.add((i, argument));
usedNames.add(argument.name.label.name);
} else {
positionalArguments.add((i, argument));
}
}
var hasWrittenParameter = false;
for (var (index, argument) in positionalArguments) {
if (hasWrittenParameter) {
write(', ');
}
if (argument is NamedExpression && !hasNamedParameters) {
hasNamedParameters = true;
write('{');
}
hasWrittenParameter = true;
writeParameterMatchingArgument(
argument,
i,
index,
usedNames,
typeParametersInScope: typeParametersInScope,
);
}
if (hasNamedParameters) {
if (namedArguments.isNotEmpty) {
if (hasWrittenParameter) {
write(', ');
}
write('{');
for (var i = 0; i < namedArguments.length; i++) {
if (i > 0) {
write(', ');
}
var (index, argument) = namedArguments[i];
writeParameterMatchingArgument(
argument,
index,
usedNames,
typeParametersInScope: typeParametersInScope,
);
}
write('}');
}
}
@@ -1543,6 +1543,34 @@ f(int i, String s) {
);
}
Future<void> test_writeParametersMatchingArguments_named_mixedOrder() async {
var path = convertPath('/home/test/lib/test.dart');
var content = '''
f(bool b, int i, String s) {
g(arg1: b, i, arg3: s);
}''';
addSource(path, content);
var unit = (await resolveFile(path)).unit;
var f = unit.declarations[0] as FunctionDeclaration;
var body = f.functionExpression.body as BlockFunctionBody;
var statement = body.block.statements[0] as ExpressionStatement;
var invocation = statement.expression as MethodInvocation;
var builder = await newBuilder();
await builder.addDartFileEdit(path, (builder) {
builder.addInsertion(content.length - 1, (builder) {
builder.writeParametersMatchingArguments(invocation.argumentList);
});
});
var edit = getEdit(builder);
expect(
edit.replacement,
equalsIgnoringWhitespace(
'int i, {required bool arg1, required String arg3}',
),
);
}
Future<void> test_writeParametersMatchingArguments_required() async {
var path = convertPath('/home/test/lib/test.dart');
var content = '''