Skip to content

Commit c42bdd1

Browse files
kallentuCommit Queue
authored andcommitted
[analysis_server] Dot shorthands: Update ChangeTo fix.
Updates the `ChangeTo` fix to handle miswritten dot shorthands. We use the calculated context type to determine the closest element that the user might be attempting to write. Bug: #60994 Change-Id: If69b6b30fa70a9cd047a3f2946ae1e0883f322e8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/442168 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Kallen Tu <[email protected]>
1 parent 1ddbc44 commit c42bdd1

File tree

6 files changed

+166
-10
lines changed

6 files changed

+166
-10
lines changed

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,23 @@ class _ContextTypeVisitor extends SimpleAstVisitor<DartType> {
697697
return null;
698698
}
699699

700+
@override
701+
DartType? visitDotShorthandConstructorInvocation(
702+
DotShorthandConstructorInvocation node,
703+
) {
704+
return _visitParent(node);
705+
}
706+
707+
@override
708+
DartType? visitDotShorthandInvocation(DotShorthandInvocation node) {
709+
return _visitParent(node);
710+
}
711+
712+
@override
713+
DartType? visitDotShorthandPropertyAccess(DotShorthandPropertyAccess node) {
714+
return _visitParent(node);
715+
}
716+
700717
@override
701718
DartType? visitExpressionFunctionBody(ExpressionFunctionBody node) {
702719
if (range.endEnd(node.functionDefinition, node).contains(offset)) {

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

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,28 @@ class ChangeTo extends ResolvedCorrectionProducer {
176176
await _suggest(builder, node, finder._element?.displayName);
177177
}
178178

179+
/// Using the context type of the [dotShorthandNode], we'll propose the
180+
/// closest element that we believe the user was attempting to write.
181+
Future<void> _proposeClassOrMixinMemberForDotShorthand(
182+
ChangeBuilder builder,
183+
Expression dotShorthandNode,
184+
Token dotShorthandIdentifier,
185+
_ElementPredicate predicate,
186+
) async {
187+
var contextElement = computeDotShorthandContextTypeElement(
188+
dotShorthandNode,
189+
unitResult.libraryElement,
190+
);
191+
if (contextElement == null) return;
192+
193+
var finder = _ClosestElementFinder(
194+
dotShorthandIdentifier.lexeme,
195+
predicate,
196+
);
197+
_updateFinderWithClassMembers(finder, contextElement);
198+
await _suggest(builder, node, finder._element?.displayName);
199+
}
200+
179201
Future<void> _proposeField(ChangeBuilder builder) async {
180202
var node = this.node;
181203
if (node is! FieldFormalParameter) return;
@@ -257,9 +279,18 @@ class ChangeTo extends ResolvedCorrectionProducer {
257279
Future<void> _proposeGetterOrSetter(ChangeBuilder builder) async {
258280
var node = this.node;
259281
if (node is SimpleIdentifier) {
282+
var parent = node.parent;
283+
if (parent is DotShorthandPropertyAccess) {
284+
await _proposeClassOrMixinMemberForDotShorthand(
285+
builder,
286+
parent,
287+
node.token,
288+
(element) => element is GetterElement || element is FieldElement,
289+
);
290+
}
291+
260292
// prepare target
261293
Expression? target;
262-
var parent = node.parent;
263294
if (parent is PrefixedIdentifier) {
264295
target = parent.prefix;
265296
} else if (parent is PropertyAccess) {
@@ -285,13 +316,22 @@ class ChangeTo extends ResolvedCorrectionProducer {
285316
Future<void> _proposeMethod(ChangeBuilder builder) async {
286317
var node = this.node;
287318
var parent = node.parent;
288-
if (parent is MethodInvocation && node is SimpleIdentifier) {
289-
await _proposeClassOrMixinMember(
290-
builder,
291-
node.token,
292-
parent.realTarget,
293-
(element) => element is MethodElement && !element.isOperator,
294-
);
319+
if (node is SimpleIdentifier) {
320+
if (parent is DotShorthandInvocation) {
321+
await _proposeClassOrMixinMemberForDotShorthand(
322+
builder,
323+
parent,
324+
node.token,
325+
(element) => element is MethodElement,
326+
);
327+
} else if (parent is MethodInvocation) {
328+
await _proposeClassOrMixinMember(
329+
builder,
330+
node.token,
331+
parent.realTarget,
332+
(element) => element is MethodElement && !element.isOperator,
333+
);
334+
}
295335
}
296336
}
297337

pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -517,9 +517,9 @@ CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION:
517517
CompileTimeErrorCode.DOT_SHORTHAND_MISSING_CONTEXT:
518518
status: needsEvaluation
519519
CompileTimeErrorCode.DOT_SHORTHAND_UNDEFINED_GETTER:
520-
status: needsEvaluation
520+
status: hasFix
521521
CompileTimeErrorCode.DOT_SHORTHAND_UNDEFINED_INVOCATION:
522-
status: needsEvaluation
522+
status: hasFix
523523
CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_DEFAULT:
524524
status: noFix
525525
notes: |-

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,10 @@ final _builtInNonLintGenerators = <DiagnosticCode, List<ProducerGenerator>>{
626626
RemoveDefaultValue.new,
627627
RemoveRequired.new,
628628
],
629+
CompileTimeErrorCode.DOT_SHORTHAND_UNDEFINED_GETTER: [
630+
ChangeTo.getterOrSetter,
631+
],
632+
CompileTimeErrorCode.DOT_SHORTHAND_UNDEFINED_INVOCATION: [ChangeTo.method],
629633
CompileTimeErrorCode.EMPTY_MAP_PATTERN: [
630634
ReplaceEmptyMapPattern.any,
631635
ReplaceEmptyMapPattern.empty,

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'dart:math';
66

77
import 'package:_fe_analyzer_shared/src/scanner/token.dart';
8+
import 'package:analysis_server/src/services/completion/dart/feature_computer.dart';
89
import 'package:analyzer/dart/ast/precedence.dart';
910
import 'package:analyzer/dart/ast/visitor.dart';
1011
import 'package:analyzer/dart/element/element.dart';
@@ -34,6 +35,26 @@ Expression climbPropertyAccess(Expression node) {
3435
}
3536
}
3637

38+
/// Computes the [InterfaceElement] of the context type of a given
39+
/// [dotShorthandNode].
40+
///
41+
/// Returns `null` if no context type exists or the context type isn't an
42+
/// [InterfaceType].
43+
InterfaceElement? computeDotShorthandContextTypeElement(
44+
AstNode dotShorthandNode,
45+
LibraryElement libraryElement,
46+
) {
47+
var featureComputer = FeatureComputer(
48+
libraryElement.typeSystem,
49+
libraryElement.typeProvider,
50+
);
51+
var contextType = featureComputer.computeContextType(
52+
dotShorthandNode,
53+
dotShorthandNode.offset,
54+
);
55+
return contextType is InterfaceType ? contextType.element : null;
56+
}
57+
3758
/// Return references to the [element] inside the [root] node.
3859
List<AstNode> findImportPrefixElementReferences(
3960
AstNode root,

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

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,23 @@ E e() {
160160
''');
161161
}
162162

163+
Future<void> test_enum_constant_dotShorthand() async {
164+
await resolveTestCode('''
165+
enum E { ONE }
166+
167+
E e() {
168+
return .OEN;
169+
}
170+
''');
171+
await assertHasFix('''
172+
enum E { ONE }
173+
174+
E e() {
175+
return .ONE;
176+
}
177+
''');
178+
}
179+
163180
Future<void> test_enum_getter() async {
164181
await resolveTestCode('''
165182
enum E { ONE }
@@ -397,6 +414,25 @@ void f() {
397414
''');
398415
}
399416

417+
Future<void> test_getter_qualified_static_dotShorthand() async {
418+
await resolveTestCode('''
419+
class A {
420+
static int MY_NAME = 1;
421+
}
422+
A f() {
423+
return .MY_NAM;
424+
}
425+
''');
426+
await assertHasFix('''
427+
class A {
428+
static int MY_NAME = 1;
429+
}
430+
A f() {
431+
return .MY_NAME;
432+
}
433+
''');
434+
}
435+
400436
Future<void> test_getter_static() async {
401437
await resolveTestCode('''
402438
extension E on int {
@@ -580,6 +616,44 @@ void f() {
580616
''');
581617
}
582618

619+
Future<void> test_method_static_dotShorthand_class() async {
620+
await resolveTestCode('''
621+
class E {
622+
static E myMethod() => E();
623+
}
624+
E f() {
625+
return .myMehod();
626+
}
627+
''');
628+
await assertHasFix('''
629+
class E {
630+
static E myMethod() => E();
631+
}
632+
E f() {
633+
return .myMethod();
634+
}
635+
''');
636+
}
637+
638+
Future<void> test_method_static_dotShorthand_extensionType() async {
639+
await resolveTestCode('''
640+
extension type E(int x) {
641+
static E myMethod() => E(1);
642+
}
643+
E f() {
644+
return .myMehod();
645+
}
646+
''');
647+
await assertHasFix('''
648+
extension type E(int x) {
649+
static E myMethod() => E(1);
650+
}
651+
E f() {
652+
return .myMethod();
653+
}
654+
''');
655+
}
656+
583657
Future<void> test_method_unqualified_superClass() async {
584658
await resolveTestCode('''
585659
class A {

0 commit comments

Comments
 (0)