Skip to content

Commit b2f2dc0

Browse files
kallentuCommit Queue
authored andcommitted
[analysis_server] Dot shorthands: Update AddEnumConstant fix.
Update `AddEnumConstant` fix to handle dot shorthands. Added some relevant unit tests. This revealed a bug in `change_builder_dart` where we'd crash with an enum with no constants. Added some tests for that in `create_field_test`. Bug: #60994 Change-Id: Id75b79b8717305ddca3092fc35cadec7677177df Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/443363 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Kallen Tu <[email protected]>
1 parent 6fc1427 commit b2f2dc0

File tree

6 files changed

+287
-8
lines changed

6 files changed

+287
-8
lines changed

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import 'package:analysis_server/src/services/correction/fix.dart';
6+
import 'package:analysis_server/src/services/correction/util.dart';
67
import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
78
import 'package:analyzer/dart/ast/ast.dart';
89
import 'package:analyzer/dart/element/element.dart';
@@ -30,13 +31,19 @@ class AddEnumConstant extends ResolvedCorrectionProducer {
3031
Future<void> compute(ChangeBuilder builder) async {
3132
var node = this.node;
3233
if (node is! SimpleIdentifier) return;
33-
var parent = node.parent;
34-
if (parent is! PrefixedIdentifier) return;
35-
3634
_constantName = node.name;
37-
var target = parent.prefix;
3835

39-
var targetElement = target.element;
36+
Element? targetElement;
37+
var parent = node.parent;
38+
if (parent is PrefixedIdentifier) {
39+
targetElement = parent.prefix.element;
40+
} else if (parent is DotShorthandPropertyAccess) {
41+
targetElement = computeDotShorthandContextTypeElement(
42+
parent,
43+
unitResult.libraryElement,
44+
);
45+
}
46+
4047
if (targetElement is! EnumElement) return;
4148
if (targetElement.library.isInSdk) return;
4249

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,7 @@ final _builtInNonLintGenerators = <DiagnosticCode, List<ProducerGenerator>>{
630630
RemoveRequired.new,
631631
],
632632
CompileTimeErrorCode.DOT_SHORTHAND_UNDEFINED_GETTER: [
633+
AddEnumConstant.new,
633634
ChangeTo.getterOrSetter,
634635
CreateGetter.new,
635636
CreateField.new,

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

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,23 @@ E e() {
3737
''', matchFixMessage: "Add enum constant 'TWO'");
3838
}
3939

40+
Future<void> test_add_dotShorthand() async {
41+
await resolveTestCode('''
42+
enum E { ONE }
43+
44+
E e() {
45+
return .TWO;
46+
}
47+
''');
48+
await assertHasFix('''
49+
enum E { ONE, TWO }
50+
51+
E e() {
52+
return .TWO;
53+
}
54+
''', matchFixMessage: "Add enum constant 'TWO'");
55+
}
56+
4057
Future<void> test_differentLibrary() async {
4158
newFile('$testPackageLibPath/a.dart', '''
4259
enum E {ONE}
@@ -55,6 +72,24 @@ enum E {ONE, TWO}
5572
''', target: '$testPackageLibPath/a.dart');
5673
}
5774

75+
Future<void> test_differentLibrary_dotShorthand() async {
76+
newFile('$testPackageLibPath/a.dart', '''
77+
enum E { ONE }
78+
''');
79+
80+
await resolveTestCode('''
81+
import 'a.dart';
82+
83+
E e() {
84+
return .TWO;
85+
}
86+
''');
87+
88+
await assertHasFix('''
89+
enum E { ONE, TWO }
90+
''', target: '$testPackageLibPath/a.dart');
91+
}
92+
5893
Future<void> test_named() async {
5994
await resolveTestCode('''
6095
enum E {
@@ -81,6 +116,32 @@ E e() {
81116
''');
82117
}
83118

119+
Future<void> test_named_dotShorthand() async {
120+
await resolveTestCode('''
121+
enum E {
122+
ONE.named();
123+
124+
const E.named();
125+
}
126+
127+
E e() {
128+
return .TWO;
129+
}
130+
''');
131+
132+
await assertHasFix('''
133+
enum E {
134+
ONE.named(), TWO.named();
135+
136+
const E.named();
137+
}
138+
139+
E e() {
140+
return .TWO;
141+
}
142+
''');
143+
}
144+
84145
Future<void> test_named_factory() async {
85146
await resolveTestCode('''
86147
enum E {
@@ -109,6 +170,34 @@ E e() {
109170
''');
110171
}
111172

173+
Future<void> test_named_factory_dotShorthand() async {
174+
await resolveTestCode('''
175+
enum E {
176+
ONE.named();
177+
178+
const E.named();
179+
factory E.f() => ONE;
180+
}
181+
182+
E e() {
183+
return .TWO;
184+
}
185+
''');
186+
187+
await assertHasFix('''
188+
enum E {
189+
ONE.named(), TWO.named();
190+
191+
const E.named();
192+
factory E.f() => ONE;
193+
}
194+
195+
E e() {
196+
return .TWO;
197+
}
198+
''');
199+
}
200+
112201
Future<void> test_named_named() async {
113202
await resolveTestCode('''
114203
enum E {
@@ -126,6 +215,23 @@ E e() {
126215
await assertNoFix();
127216
}
128217

218+
Future<void> test_named_named_dotShorthand() async {
219+
await resolveTestCode('''
220+
enum E {
221+
ONE.something(), TWO.other();
222+
223+
const E.something();
224+
const E.other();
225+
}
226+
227+
E e() {
228+
return .THREE;
229+
}
230+
''');
231+
232+
await assertNoFix();
233+
}
234+
129235
Future<void> test_named_non_zero() async {
130236
await resolveTestCode('''
131237
enum E {
@@ -143,6 +249,23 @@ E e() {
143249
await assertNoFix();
144250
}
145251

252+
Future<void> test_named_non_zero_dotShorthand() async {
253+
await resolveTestCode('''
254+
enum E {
255+
ONE.named(1);
256+
257+
final int i;
258+
const E.named(this.i);
259+
}
260+
261+
E e() {
262+
return .TWO;
263+
}
264+
''');
265+
266+
await assertNoFix();
267+
}
268+
146269
Future<void> test_named_unnamed() async {
147270
await resolveTestCode('''
148271
enum E {
@@ -182,6 +305,29 @@ void f() {
182305
);
183306
}
184307

308+
Future<void> test_toEmpty_dotShorthand() async {
309+
await resolveTestCode('''
310+
enum E {}
311+
312+
E e() {
313+
return .ONE;
314+
}
315+
''');
316+
await assertHasFix(
317+
'''
318+
enum E {ONE}
319+
320+
E e() {
321+
return .ONE;
322+
}
323+
''',
324+
errorFilter: (e) {
325+
return e.diagnosticCode ==
326+
CompileTimeErrorCode.DOT_SHORTHAND_UNDEFINED_GETTER;
327+
},
328+
);
329+
}
330+
185331
Future<void> test_unnamed() async {
186332
await resolveTestCode('''
187333
enum E {

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

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import 'package:analysis_server/src/services/correction/fix.dart';
6+
import 'package:analyzer/src/error/codes.dart';
67
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
78
import 'package:test_reflective_loader/test_reflective_loader.dart';
89

@@ -238,6 +239,73 @@ void f() {
238239
''');
239240
}
240241

242+
Future<void> test_dotShorthand_enum() async {
243+
await resolveTestCode('''
244+
enum A {
245+
ONE
246+
}
247+
A f() {
248+
return .test;
249+
}
250+
''');
251+
await assertHasFix('''
252+
enum A {
253+
ONE;
254+
255+
static final A test;
256+
}
257+
A f() {
258+
return .test;
259+
}
260+
''');
261+
}
262+
263+
Future<void> test_dotShorthand_enum_empty() async {
264+
await resolveTestCode('''
265+
enum A {}
266+
A f() {
267+
return .test;
268+
}
269+
''');
270+
await assertHasFix(
271+
'''
272+
enum A {;
273+
static final A test;
274+
}
275+
A f() {
276+
return .test;
277+
}
278+
''',
279+
errorFilter: (e) {
280+
return e.diagnosticCode ==
281+
CompileTimeErrorCode.DOT_SHORTHAND_UNDEFINED_GETTER;
282+
},
283+
);
284+
}
285+
286+
Future<void> test_dotShorthand_enum_empty_semicolon() async {
287+
await resolveTestCode('''
288+
enum A {;}
289+
A f() {
290+
return .test;
291+
}
292+
''');
293+
await assertHasFix(
294+
'''
295+
enum A {;
296+
static final A test;
297+
}
298+
A f() {
299+
return .test;
300+
}
301+
''',
302+
errorFilter: (e) {
303+
return e.diagnosticCode ==
304+
CompileTimeErrorCode.DOT_SHORTHAND_UNDEFINED_GETTER;
305+
},
306+
);
307+
}
308+
241309
Future<void> test_dotShorthand_extensionType() async {
242310
await resolveTestCode('''
243311
extension type A(int x) {}

pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2762,10 +2762,10 @@ class _InsertionPreparer {
27622762
var semicolon = declaration.semicolon;
27632763
if (semicolon != null) {
27642764
return semicolon.end;
2765+
} else if (declaration.constants.isNotEmpty) {
2766+
var lastConstant = declaration.constants.last;
2767+
return lastConstant.end;
27652768
}
2766-
2767-
var lastConstant = declaration.constants.last;
2768-
return lastConstant.end;
27692769
}
27702770

27712771
// At the beginning of the class.

pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,63 @@ class DartEditBuilderImplTest extends AbstractContextTest
184184
);
185185
}
186186

187+
Future<void> test_insertField_enum() async {
188+
var path = convertPath('/home/test/lib/test.dart');
189+
var content = 'enum E { ONE }';
190+
addSource(path, content);
191+
192+
var resolvedUnit = await resolveFile(path);
193+
var findNode = FindNode(resolvedUnit.content, resolvedUnit.unit);
194+
var enumNode = findNode.enumDeclaration('E { ONE }');
195+
196+
var builder = await newBuilder();
197+
await builder.addDartFileEdit(path, (builder) {
198+
builder.insertField(enumNode, (builder) {
199+
builder.writeFieldDeclaration('f');
200+
});
201+
});
202+
var edit = getEdit(builder);
203+
expect(edit.replacement, equalsIgnoringWhitespace('; var f;'));
204+
}
205+
206+
Future<void> test_insertField_enum_empty() async {
207+
var path = convertPath('/home/test/lib/test.dart');
208+
var content = 'enum E {}';
209+
addSource(path, content);
210+
211+
var resolvedUnit = await resolveFile(path);
212+
var findNode = FindNode(resolvedUnit.content, resolvedUnit.unit);
213+
var enumNode = findNode.enumDeclaration('E {}');
214+
215+
var builder = await newBuilder();
216+
await builder.addDartFileEdit(path, (builder) {
217+
builder.insertField(enumNode, (builder) {
218+
builder.writeFieldDeclaration('f');
219+
});
220+
});
221+
var edit = getEdit(builder);
222+
expect(edit.replacement, equalsIgnoringWhitespace('; var f;'));
223+
}
224+
225+
Future<void> test_insertField_enum_empty_semicolon() async {
226+
var path = convertPath('/home/test/lib/test.dart');
227+
var content = 'enum E {;}';
228+
addSource(path, content);
229+
230+
var resolvedUnit = await resolveFile(path);
231+
var findNode = FindNode(resolvedUnit.content, resolvedUnit.unit);
232+
var enumNode = findNode.enumDeclaration('E {;}');
233+
234+
var builder = await newBuilder();
235+
await builder.addDartFileEdit(path, (builder) {
236+
builder.insertField(enumNode, (builder) {
237+
builder.writeFieldDeclaration('f');
238+
});
239+
});
240+
var edit = getEdit(builder);
241+
expect(edit.replacement, equalsIgnoringWhitespace('var f;'));
242+
}
243+
187244
Future<void> test_writeClassDeclaration_interfaces() async {
188245
var path = convertPath('$testPackageRootPath/lib/test.dart');
189246
addSource(path, 'class A {}');

0 commit comments

Comments
 (0)