Skip to content

Commit e43a6b2

Browse files
FMorschelCommit Queue
authored andcommitted
[DAS] Fixes completion suggestion for setter based on inheritance
[email protected] Bug: #60298 Change-Id: I43b89744c74bd3bbfc664a505c0fc4d05cc87f5e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/414920 Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]> Reviewed-by: Samuel Rawlins <[email protected]> Auto-Submit: Felipe Morschel <[email protected]>
1 parent d612b0f commit e43a6b2

File tree

2 files changed

+104
-20
lines changed

2 files changed

+104
-20
lines changed

pkg/analysis_server/lib/src/services/completion/dart/declaration_helper.dart

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,7 +1076,7 @@ class DeclarationHelper {
10761076
var referencingInterface = _referencingInterfaceFor(type.element3);
10771077
for (var entry in membersByName.entries) {
10781078
var members = entry.value;
1079-
var rawMember = members.bestMember;
1079+
var rawMember = _bestMember(members);
10801080
if (rawMember is MethodElement) {
10811081
if (includeMethods) {
10821082
if (rawMember.isOperator) {
@@ -1505,6 +1505,54 @@ class DeclarationHelper {
15051505
}
15061506
}
15071507

1508+
/// Returns the element in [list] that is the best element to suggest.
1509+
///
1510+
/// - If [mustBeAssignable] is `true` we look for the first setter:
1511+
/// - If the setter is synthetic and contains a corresponding getter, we
1512+
/// return the getter.
1513+
/// - Otherwise, we return the setter.
1514+
/// - If [mustBeAssignable] is `false` or if there is no setter that meets the
1515+
/// above criteria, we return the first getter.
1516+
/// - If the above are not possible, the first element in the list is
1517+
/// returned under the assumption that it's lower in the hierarchy.
1518+
ExecutableElement _bestMember(List<ExecutableElement> list) {
1519+
var firstMember = list.first;
1520+
if (mustBeAssignable) {
1521+
if (firstMember case SetterElementImpl(
1522+
:var isSynthetic,
1523+
:var correspondingGetter2,
1524+
)) {
1525+
if (isSynthetic && correspondingGetter2 != null) {
1526+
return correspondingGetter2;
1527+
} else {
1528+
return firstMember;
1529+
}
1530+
}
1531+
for (var i = 1; i < list.length; i++) {
1532+
var member = list[i];
1533+
if (member case SetterElementImpl(
1534+
:var isSynthetic,
1535+
:var correspondingGetter2,
1536+
)) {
1537+
if (isSynthetic && correspondingGetter2 != null) {
1538+
return correspondingGetter2;
1539+
} else {
1540+
return member;
1541+
}
1542+
}
1543+
}
1544+
}
1545+
if (firstMember is SetterElement) {
1546+
for (var i = 1; i < list.length; i++) {
1547+
var member = list[i];
1548+
if (member is GetterElement) {
1549+
return member;
1550+
}
1551+
}
1552+
}
1553+
return firstMember;
1554+
}
1555+
15081556
bool _canAccessInstanceMember(ExecutableElement element) {
15091557
if (element.isStatic) {
15101558
return false;
@@ -2535,25 +2583,6 @@ extension on Element {
25352583
}
25362584
}
25372585

2538-
extension on List<ExecutableElement> {
2539-
/// Returns the element in this list that is the best element to suggest.
2540-
///
2541-
/// Getters are preferred over setters, otherwise the first element in the
2542-
/// list is returned under the assumption that it's lower in the hierarchy.
2543-
ExecutableElement get bestMember {
2544-
ExecutableElement bestMember = this[0];
2545-
if (bestMember is SetterElement) {
2546-
for (var i = 1; i < length; i++) {
2547-
var member = this[i];
2548-
if (member is GetterElement) {
2549-
return member;
2550-
}
2551-
}
2552-
}
2553-
return bestMember;
2554-
}
2555-
}
2556-
25572586
extension on PropertyAccessorElement {
25582587
/// Whether this accessor is an accessor for a constant variable.
25592588
bool get isConst {

pkg/analysis_server/test/services/completion/dart/declaration/class_member_test.dart

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,61 @@ mixin ClassMemberTestCases on AbstractCompletionDriverTest {
2121
@override
2222
bool get includeKeywords => false;
2323

24+
Future<void> test_abstractImplementation_missing() async {
25+
await computeSuggestions('''
26+
class A {
27+
A? v1;
28+
}
29+
30+
class O extends A {}
31+
32+
abstract class A2 implements A {
33+
@override
34+
O? get v1;
35+
}
36+
37+
void f(A2 p) {
38+
p.^ = null;
39+
}
40+
''');
41+
assertResponse(r'''
42+
suggestions
43+
v1
44+
kind: field
45+
''');
46+
}
47+
48+
Future<void> test_abstractImplementation_missing2() async {
49+
await computeSuggestions('''
50+
class A {
51+
A? get v1;
52+
}
53+
54+
class O extends A {}
55+
56+
class O2 extends O {}
57+
58+
abstract class A2 implements A {
59+
@override
60+
O? v1;
61+
}
62+
63+
abstract class A3 implements A2 {
64+
@override
65+
O2? get v1;
66+
}
67+
68+
void f(A3 p) {
69+
p.^ = null;
70+
}
71+
''');
72+
assertResponse(r'''
73+
suggestions
74+
v1
75+
kind: field
76+
''');
77+
}
78+
2479
Future<void> test_inheritedFromPrivateClass() async {
2580
newFile('$testPackageLibPath/b.dart', '''
2681
library B;

0 commit comments

Comments
 (0)