Skip to content

Commit 78e254f

Browse files
munificentCommit Queue
authored andcommitted
Handle private named parameters in ConvertAllFormalParametersToNamed.
If all of the positional parameters in the parameter list are public, then it happily converts them to public named parameters. If any of them have private names and they can be private named parameters, it does that too. For that to be true: - The library containing the declaration needs to support private named parameters. - The parameter needs to be an initializing formal (or a declaring parameter when those are supported). That implies it must also be in a constructor. If there are any positional parameters that are private and don't fit that, then it doesn't allow the refactoring. Also, in cases where it does convert a private positional parameter to a private named parameter, this CL makes sure that the added argument name is the corresponding public name. This also fixes #62284 by not applying the refactoring unless the result yields valid private named parameters. Fix #62284. Change-Id: Id00831c2631ff3bf6587ac63d90865f7b0fb367c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/475267 Commit-Queue: Bob Nystrom <rnystrom@google.com> Auto-Submit: Bob Nystrom <rnystrom@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
1 parent 4ad9268 commit 78e254f

4 files changed

Lines changed: 128 additions & 11 deletions

File tree

pkg/analysis_server/lib/src/services/refactoring/agnostic/change_method_signature.dart

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'package:analysis_server/src/services/search/hierarchy.dart';
1212
import 'package:analysis_server/src/services/search/search_engine.dart';
1313
import 'package:analysis_server_plugin/edit/correction_utils.dart';
1414
import 'package:analysis_server_plugin/src/utilities/selection.dart';
15+
import 'package:analyzer/dart/analysis/features.dart';
1516
import 'package:analyzer/dart/analysis/results.dart';
1617
import 'package:analyzer/dart/ast/ast.dart';
1718
import 'package:analyzer/dart/ast/token.dart';
@@ -63,11 +64,48 @@ sealed class Available extends Availability {
6364

6465
Available({required this.refactoringContext});
6566

66-
bool get hasPositionalParameters;
67+
/// Whether there are any positional parameters and, if so, if all of them
68+
/// can be converted to named parameters.
69+
///
70+
/// Even if a parameter is positional, it may not be convertible if it has a
71+
/// private name and isn't in a context where it could become a private named
72+
/// parameter. If that case, this returns `false`.
73+
bool get hasPositionalParametersToConvertToNamed {
74+
var supportsPrivateNamedParameters = refactoringContext
75+
.resolvedLibraryResult
76+
.element
77+
.featureSet
78+
.isEnabled(Feature.private_named_parameters);
79+
80+
var hasPositional = false;
81+
for (var parameter in _formalParameters) {
82+
if (parameter.isNamed) continue;
83+
84+
hasPositional = true;
85+
86+
// If the parameter has a private name, we must be able to convert it to
87+
// a private named parameter.
88+
if (Identifier.isPrivateName(parameter.name!)) {
89+
if (!supportsPrivateNamedParameters) {
90+
return false;
91+
}
92+
93+
// TODO(rnystrom): Check for primary constructor declaring parameter
94+
// here once those are implemented.
95+
if (parameter is! FieldFormalParameterElement) {
96+
return false;
97+
}
98+
}
99+
}
100+
101+
return hasPositional;
102+
}
67103

68104
bool get hasSelectedFormalParametersToConvertToNamed => false;
69105

70106
bool get hasSelectedFormalParametersToMoveLeft => false;
107+
108+
List<FormalParameterElement> get _formalParameters;
71109
}
72110

73111
/// The supertype return types from [computeSourceChange].
@@ -408,11 +446,6 @@ final class _AvailableWithDeclaration extends Available {
408446
required this.declaration,
409447
});
410448

411-
@override
412-
bool get hasPositionalParameters {
413-
return declaration.element.formalParameters.any((e) => e.isPositional);
414-
}
415-
416449
@override
417450
bool get hasSelectedFormalParametersToConvertToNamed {
418451
var selected = declaration.selected;
@@ -472,6 +505,10 @@ final class _AvailableWithDeclaration extends Available {
472505

473506
return true;
474507
}
508+
509+
@override
510+
List<FormalParameterElement> get _formalParameters =>
511+
declaration.element.formalParameters;
475512
}
476513

477514
final class _AvailableWithExecutableElement extends Available {
@@ -483,9 +520,8 @@ final class _AvailableWithExecutableElement extends Available {
483520
});
484521

485522
@override
486-
bool get hasPositionalParameters {
487-
return element.formalParameters.any((e) => e.isPositional);
488-
}
523+
List<FormalParameterElement> get _formalParameters =>
524+
element.formalParameters;
489525
}
490526

491527
/// The target method declaration.

pkg/analysis_server/lib/src/services/refactoring/convert_all_formal_parameters_to_named.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ class ConvertAllFormalParametersToNamed extends RefactoringProducer {
8787
if (availability is! Available) {
8888
return false;
8989
}
90-
return availability.hasPositionalParameters;
90+
91+
return availability.hasPositionalParametersToConvertToNamed;
9192
}
9293
}

pkg/analysis_server/lib/src/services/refactoring/framework/write_invocation_arguments.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:_fe_analyzer_shared/src/scanner/token_impl.dart';
56
import 'package:analysis_server/src/services/refactoring/framework/formal_parameter.dart';
67
import 'package:analysis_server_plugin/edit/correction_utils.dart';
78
import 'package:analyzer/dart/analysis/results.dart';
@@ -49,8 +50,11 @@ Future<WriteArgumentsStatus> writeArguments({
4950
return WriteArgumentsStatusFailure();
5051
}
5152
if (update is FormalParameterUpdateExistingNamed) {
53+
// If the parameter's name is private, then the argument uses the
54+
// corresponding public name.
55+
var name = correspondingPublicName(update.name) ?? update.name;
5256
newArguments.add(
53-
_ArgumentAddName(name: update.name, argument: argument),
57+
_ArgumentAddName(name: name, argument: argument),
5458
);
5559
} else {
5660
newArguments.add(_ArgumentAsIs(argument: argument));

pkg/analysis_server/test/src/services/refactoring/convert_all_formal_parameters_to_named_test.dart

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,82 @@ void f() {
155155
await _assertNoRefactoring();
156156
}
157157

158+
Future<void> test_private_constructor() async {
159+
addTestSource(r'''
160+
class C {
161+
int? _a;
162+
int? _b;
163+
164+
^C(this._a, {this._b});
165+
}
166+
167+
void f() {
168+
C(0, b: 1);
169+
}
170+
''');
171+
172+
await verifyRefactoring(r'''
173+
>>>>>>>>>> lib/main.dart
174+
class C {
175+
int? _a;
176+
int? _b;
177+
178+
C({required this._a, this._b});
179+
}
180+
181+
void f() {
182+
C(a: 0, b: 1);
183+
}
184+
''');
185+
}
186+
187+
Future<void> test_private_constructor_unsupported() async {
188+
addTestSource(r'''
189+
// @dart=3.10
190+
class C {
191+
int? _a;
192+
int? _b;
193+
194+
^C(this._a, {int? b}) : _b = b;
195+
}
196+
197+
void f() {
198+
C(0, b: 1);
199+
}
200+
''');
201+
202+
await _assertNoRefactoring();
203+
}
204+
205+
Future<void> test_private_function() async {
206+
// Functions can't have private named parameters, so the refactoring can't
207+
// be applied.
208+
addTestSource(r'''
209+
^test(int _a, int _b) {}
210+
211+
void f() {
212+
test(0, 1);
213+
}
214+
''');
215+
216+
await _assertNoRefactoring();
217+
}
218+
219+
Future<void> test_private_function_unsupported() async {
220+
// Functions can't have private named parameters, so the refactoring can't
221+
// be applied.
222+
addTestSource(r'''
223+
// @dart=3.10
224+
^test(int _a, int _b) {}
225+
226+
void f() {
227+
test(0, 1);
228+
}
229+
''');
230+
231+
await _assertNoRefactoring();
232+
}
233+
158234
Future<void> test_target_methodInvocation_name() async {
159235
addTestSource(r'''
160236
void test(int a) {}

0 commit comments

Comments
 (0)