Skip to content

Commit 50ff1c3

Browse files
FMorschelCommit Queue
authored andcommitted
[DAS] Fixes rename field add this to fix shadowing
[email protected] Fixes: #59954 Change-Id: I860e48989566a09ea09092ddd1771be61f89121b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/413901 Commit-Queue: Phil Quitslund <[email protected]> Reviewed-by: Phil Quitslund <[email protected]> Reviewed-by: Samuel Rawlins <[email protected]> Auto-Submit: Felipe Morschel <[email protected]>
1 parent c066e29 commit 50ff1c3

File tree

2 files changed

+166
-13
lines changed

2 files changed

+166
-13
lines changed

pkg/analysis_server/lib/src/services/refactoring/legacy/rename_class_member.dart

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ class RenameClassMemberRefactoringImpl extends RenameRefactoringImpl {
171171
}
172172
}
173173

174+
Future<void> _addThisEdit(SourceReference reference, String newName) async {
175+
reference.addEdit(change, 'this.$newName');
176+
}
177+
174178
String _newPotentialId() {
175179
assert(includePotential);
176180
var id = potentialEditIds.length.toString();
@@ -180,6 +184,9 @@ class RenameClassMemberRefactoringImpl extends RenameRefactoringImpl {
180184

181185
Future<void> _updateReferences() async {
182186
var references = getSourceReferences(_validator.references);
187+
var unshadowed = getSourceReferences(
188+
_validator.unshadowed,
189+
).map((r) => r.element);
183190

184191
for (var reference in references) {
185192
var element = reference.element;
@@ -194,6 +201,12 @@ class RenameClassMemberRefactoringImpl extends RenameRefactoringImpl {
194201
continue;
195202
}
196203

204+
if (unshadowed.contains(reference.element) &&
205+
reference.element is ExecutableElement2) {
206+
await _addThisEdit(reference, newName);
207+
continue;
208+
}
209+
197210
reference.addEdit(change, newName);
198211
}
199212
}
@@ -317,7 +330,7 @@ class _CreateClassMemberValidator extends _BaseClassMemberValidator {
317330

318331
class _LocalElementsCollector extends GeneralizingAstVisitor<void> {
319332
final String name;
320-
final List<LocalElement2> elements = [];
333+
final List<Element2> elements = [];
321334

322335
_LocalElementsCollector(this.name);
323336

@@ -345,6 +358,17 @@ class _LocalElementsCollector extends GeneralizingAstVisitor<void> {
345358
super.visitSimpleFormalParameter(node);
346359
}
347360

361+
@override
362+
void visitSimpleIdentifier(SimpleIdentifier node) {
363+
var element = node.element;
364+
if (element is GetterElement && element.name3 == name) {
365+
if (element.variable3 case TopLevelVariableElement2 variable) {
366+
elements.add(variable);
367+
}
368+
}
369+
super.visitSimpleIdentifier(node);
370+
}
371+
348372
@override
349373
void visitVariableDeclaration(VariableDeclaration node) {
350374
if (node.name.lexeme == name) {
@@ -358,11 +382,19 @@ class _LocalElementsCollector extends GeneralizingAstVisitor<void> {
358382
}
359383
}
360384

361-
class _MatchShadowedByLocal {
385+
class _MatchShadowedBy {
362386
final SearchMatch match;
363-
final LocalElement2 localElement;
387+
final Element2 element;
388+
389+
_MatchShadowedBy(this.match, this.element);
390+
}
391+
392+
class _MatchShadowedByLocal extends _MatchShadowedBy {
393+
@override
394+
final LocalElement2 element;
364395

365-
_MatchShadowedByLocal(this.match, this.localElement);
396+
_MatchShadowedByLocal(SearchMatch match, this.element)
397+
: super(match, element);
366398
}
367399

368400
/// Helper to check if the renamed [element] will cause any conflicts.
@@ -371,6 +403,7 @@ class _RenameClassMemberValidator extends _BaseClassMemberValidator {
371403

372404
Set<Element2> elements = {};
373405
List<SearchMatch> references = [];
406+
List<SearchMatch> unshadowed = [];
374407

375408
_RenameClassMemberValidator(
376409
SearchEngine searchEngine,
@@ -404,15 +437,15 @@ class _RenameClassMemberValidator extends _BaseClassMemberValidator {
404437
}
405438
// usage of the renamed Element is shadowed by a local element
406439
{
407-
var conflict = await _getShadowingLocalElement();
440+
var conflict = await _getLocalShadowingElement();
408441
if (conflict != null) {
409-
var localElement = conflict.localElement;
442+
var element = conflict.element;
410443
result.addError(
411444
format(
412445
"Usage of renamed {0} will be shadowed by {1} '{2}'.",
413446
elementKind.displayName,
414-
getElementKindName(localElement),
415-
localElement.displayName,
447+
getElementKindName(element),
448+
element.displayName,
416449
),
417450
newLocation_fromMatch(conflict.match),
418451
);
@@ -426,11 +459,11 @@ class _RenameClassMemberValidator extends _BaseClassMemberValidator {
426459
return result;
427460
}
428461

429-
Future<_MatchShadowedByLocal?> _getShadowingLocalElement() async {
430-
var localElementMap = <LibraryFragment, List<LocalElement2>>{};
431-
var visibleRangeMap = <LocalElement2, SourceRange>{};
462+
Future<_MatchShadowedBy?> _getLocalShadowingElement() async {
463+
var localElementMap = <LibraryFragment, List<Element2>>{};
464+
var visibleRangeMap = <Element2, SourceRange>{};
432465

433-
Future<List<LocalElement2>> getLocalElements(Element2 element) async {
466+
Future<List<Element2>> getLocalElements(Element2 element) async {
434467
var unitFragment = element.firstFragment.libraryFragment;
435468
if (unitFragment == null) {
436469
return const [];
@@ -464,11 +497,20 @@ class _RenameClassMemberValidator extends _BaseClassMemberValidator {
464497
}
465498
// Check local elements that might shadow the reference.
466499
var localElements = await getLocalElements(match.element);
500+
if (element.kind == ElementKind.FIELD) {
501+
for (var element in localElements.avoidableShadowsForField) {
502+
unshadowed.addAll(await searchEngine.searchReferences(element));
503+
localElements.remove(element);
504+
}
505+
}
467506
for (var localElement in localElements) {
468507
var elementRange = visibleRangeMap[localElement];
469508
if (elementRange != null &&
470509
elementRange.intersects(match.sourceRange)) {
471-
return _MatchShadowedByLocal(match, localElement);
510+
if (localElement is LocalElement2) {
511+
return _MatchShadowedByLocal(match, localElement);
512+
}
513+
return _MatchShadowedBy(match, localElement);
472514
}
473515
}
474516
}
@@ -513,3 +555,20 @@ class _RenameClassMemberValidator extends _BaseClassMemberValidator {
513555
}
514556
}
515557
}
558+
559+
extension on Element2 {
560+
bool get canAvoidShadowForField {
561+
return switch (kind) {
562+
ElementKind.LOCAL_VARIABLE ||
563+
ElementKind.TOP_LEVEL_VARIABLE ||
564+
ElementKind.PARAMETER => true,
565+
_ => false,
566+
};
567+
}
568+
}
569+
570+
extension on List<Element2> {
571+
List<Element2> get avoidableShadowsForField {
572+
return where((e) => e.canAvoidShadowForField).toList();
573+
}
574+
}

pkg/analysis_server/test/services/refactoring/legacy/rename_class_member_test.dart

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,100 @@ class A<NewName> {
12421242
''');
12431243
}
12441244

1245+
Future<void> test_shadowingLocalVariable_addsThis() async {
1246+
await indexTestUnit('''
1247+
class A {
1248+
final int? _value;
1249+
1250+
const A(this._value);
1251+
1252+
A copyWith() {
1253+
var value = _get();
1254+
return A(value ?? _value);
1255+
}
1256+
1257+
int? _get() => null;
1258+
}
1259+
''');
1260+
createRenameRefactoringAtString('_value;');
1261+
// check status
1262+
refactoring.newName = 'value';
1263+
await assertSuccessfulRefactoring('''
1264+
class A {
1265+
final int? value;
1266+
1267+
const A(this.value);
1268+
1269+
A copyWith() {
1270+
var value = _get();
1271+
return A(value ?? this.value);
1272+
}
1273+
1274+
int? _get() => null;
1275+
}
1276+
''');
1277+
}
1278+
1279+
Future<void> test_shadowingParameter_addsThis() async {
1280+
await indexTestUnit('''
1281+
class A {
1282+
final int? _value;
1283+
1284+
const A(this._value);
1285+
1286+
A copyWith({int? value}) {
1287+
return A(value ?? _value);
1288+
}
1289+
}
1290+
''');
1291+
createRenameRefactoringAtString('_value;');
1292+
// check status
1293+
refactoring.newName = 'value';
1294+
await assertSuccessfulRefactoring('''
1295+
class A {
1296+
final int? value;
1297+
1298+
const A(this.value);
1299+
1300+
A copyWith({int? value}) {
1301+
return A(value ?? this.value);
1302+
}
1303+
}
1304+
''');
1305+
}
1306+
1307+
Future<void> test_shadowingTopLevelVariable_addsThis() async {
1308+
await indexTestUnit('''
1309+
int? value = 0;
1310+
1311+
class A {
1312+
final int? _value;
1313+
1314+
const A(this._value);
1315+
1316+
A copyWith() {
1317+
return A(value ?? _value);
1318+
}
1319+
}
1320+
''');
1321+
createRenameRefactoringAtString('_value;');
1322+
// check status
1323+
refactoring.newName = 'value';
1324+
await assertSuccessfulRefactoring('''
1325+
int? value = 0;
1326+
1327+
class A {
1328+
final int? value;
1329+
1330+
const A(this.value);
1331+
1332+
A copyWith() {
1333+
return A(value ?? this.value);
1334+
}
1335+
}
1336+
''');
1337+
}
1338+
12451339
Future<void> test_subclass_namedSuper_otherLibrary() async {
12461340
await indexTestUnit('''
12471341
class Base {

0 commit comments

Comments
 (0)