Skip to content

Commit e2f1460

Browse files
bwilkersoncommit-bot@chromium.org
authored andcommitted
Add a new replace-cascade-with-dot fix
Change-Id: I1e3416e8d92c4c63f26e5627c68ed587ab260cc0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/154921 Commit-Queue: Brian Wilkerson <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]>
1 parent ea7a928 commit e2f1460

File tree

6 files changed

+263
-0
lines changed

6 files changed

+263
-0
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
6+
import 'package:analysis_server/src/services/correction/fix.dart';
7+
import 'package:analyzer/dart/ast/ast.dart';
8+
import 'package:analyzer/dart/ast/token.dart';
9+
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
10+
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
11+
import 'package:analyzer_plugin/utilities/range_factory.dart';
12+
13+
class ReplaceCascadeWithDot extends CorrectionProducer {
14+
@override
15+
FixKind get fixKind => DartFixKind.REPLACE_CASCADE_WITH_DOT;
16+
17+
@override
18+
Future<void> compute(ChangeBuilder builder) async {
19+
var node = this.node;
20+
if (node is CascadeExpression) {
21+
var sections = node.cascadeSections;
22+
if (sections.length == 1) {
23+
var section = sections[0];
24+
Token cascadeOperator;
25+
if (section is MethodInvocation) {
26+
cascadeOperator = section.operator;
27+
} else if (section is PropertyAccess) {
28+
cascadeOperator = section.operator;
29+
} else if (section is IndexExpression) {
30+
await _handleIndexExpression(builder, section);
31+
return;
32+
} else if (section is AssignmentExpression) {
33+
var leftHandSide = section.leftHandSide;
34+
if (leftHandSide is PropertyAccess) {
35+
cascadeOperator = leftHandSide.operator;
36+
} else if (leftHandSide is IndexExpression) {
37+
await _handleIndexExpression(builder, leftHandSide);
38+
return;
39+
} else {
40+
return;
41+
}
42+
} else {
43+
return;
44+
}
45+
var type = cascadeOperator.type;
46+
if (type == TokenType.PERIOD_PERIOD ||
47+
type == TokenType.QUESTION_PERIOD_PERIOD) {
48+
await builder.addDartFileEdit(file, (builder) {
49+
var end = cascadeOperator.end;
50+
builder.addDeletion(range.startOffsetEndOffset(end - 1, end));
51+
});
52+
}
53+
}
54+
}
55+
}
56+
57+
void _handleIndexExpression(
58+
ChangeBuilder builder, IndexExpression section) async {
59+
var cascadeOperator = section.period;
60+
var type = cascadeOperator.type;
61+
if (type == TokenType.PERIOD_PERIOD) {
62+
await builder.addDartFileEdit(file, (builder) {
63+
builder.addDeletion(
64+
range.startStart(cascadeOperator, section.leftBracket));
65+
});
66+
} else if (type == TokenType.QUESTION_PERIOD_PERIOD) {
67+
await builder.addDartFileEdit(file, (builder) {
68+
builder.addSimpleReplacement(range.token(cascadeOperator), '?');
69+
});
70+
}
71+
}
72+
73+
/// Return an instance of this class. Used as a tear-off in `FixProcessor`.
74+
static ReplaceCascadeWithDot newInstance() => ReplaceCascadeWithDot();
75+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,8 @@ class DartFixKind {
407407
static const REPLACE_BOOLEAN_WITH_BOOL = FixKind(
408408
'dart.fix.replace.booleanWithBool', 50, "Replace 'boolean' with 'bool'",
409409
appliedTogetherMessage: "Replace all 'boolean' with 'bool' in file");
410+
static const REPLACE_CASCADE_WITH_DOT =
411+
FixKind('dart.fix.replace.cascadeWithDot', 50, "Replace '..' with '.'");
410412
static const REPLACE_COLON_WITH_EQUALS =
411413
FixKind('dart.fix.replace.colonWithEquals', 50, "Replace ':' with '='");
412414
static const REPLACE_WITH_FILLED = FixKind(

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ import 'package:analysis_server/src/services/correction/dart/remove_unused_local
115115
import 'package:analysis_server/src/services/correction/dart/remove_unused_parameter.dart';
116116
import 'package:analysis_server/src/services/correction/dart/rename_to_camel_case.dart';
117117
import 'package:analysis_server/src/services/correction/dart/replace_boolean_with_bool.dart';
118+
import 'package:analysis_server/src/services/correction/dart/replace_cascade_with_dot.dart';
118119
import 'package:analysis_server/src/services/correction/dart/replace_colon_with_equals.dart';
119120
import 'package:analysis_server/src/services/correction/dart/replace_final_with_const.dart';
120121
import 'package:analysis_server/src/services/correction/dart/replace_new_with_const.dart';
@@ -302,6 +303,11 @@ class FixProcessor extends BaseProcessor {
302303
AddAsync.newInstance,
303304
WrapInFuture.newInstance,
304305
],
306+
LintNames.avoid_single_cascade_in_expression_statements: [
307+
// TODO(brianwilkerson) This fix should be applied to some non-lint
308+
// diagnostics and should also be available as an assist.
309+
ReplaceCascadeWithDot.newInstance,
310+
],
305311
LintNames.avoid_types_as_parameter_names: [
306312
ConvertToOnType.newInstance,
307313
],

pkg/analysis_server/lib/src/services/linter/lint_names.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ class LintNames {
2323
'avoid_return_types_on_setters';
2424
static const String avoid_returning_null_for_future =
2525
'avoid_returning_null_for_future';
26+
static const String avoid_single_cascade_in_expression_statements =
27+
'avoid_single_cascade_in_expression_statements';
2628
static const String avoid_types_as_parameter_names =
2729
'avoid_types_as_parameter_names';
2830
static const String avoid_types_on_closure_parameters =
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analysis_server/src/services/correction/fix.dart';
6+
import 'package:analysis_server/src/services/linter/lint_names.dart';
7+
import 'package:analyzer/src/dart/analysis/experiments.dart';
8+
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
9+
import 'package:test_reflective_loader/test_reflective_loader.dart';
10+
11+
import 'fix_processor.dart';
12+
13+
void main() {
14+
defineReflectiveSuite(() {
15+
defineReflectiveTests(ReplaceCascadeWithDotTest);
16+
defineReflectiveTests(ReplaceCascadeWithDotWithNNBDTest);
17+
});
18+
}
19+
20+
@reflectiveTest
21+
class ReplaceCascadeWithDotTest extends FixProcessorLintTest {
22+
@override
23+
FixKind get kind => DartFixKind.REPLACE_CASCADE_WITH_DOT;
24+
25+
@override
26+
String get lintCode =>
27+
LintNames.avoid_single_cascade_in_expression_statements;
28+
29+
Future<void> test_assignment_index_normalCascade() async {
30+
await resolveTestUnit('''
31+
void f(List<int> l) {
32+
l..[0] = 0;
33+
}
34+
''');
35+
await assertHasFix('''
36+
void f(List<int> l) {
37+
l[0] = 0;
38+
}
39+
''');
40+
}
41+
42+
Future<void> test_assignment_property_normalCascade() async {
43+
await resolveTestUnit('''
44+
void f(C c) {
45+
c..s = 0;
46+
}
47+
class C {
48+
set s(int i) {}
49+
}
50+
''');
51+
await assertHasFix('''
52+
void f(C c) {
53+
c.s = 0;
54+
}
55+
class C {
56+
set s(int i) {}
57+
}
58+
''');
59+
}
60+
61+
Future<void> test_getter_normalCascade() async {
62+
await resolveTestUnit('''
63+
void f(String s) {
64+
s..length;
65+
}
66+
''');
67+
await assertHasFix('''
68+
void f(String s) {
69+
s.length;
70+
}
71+
''');
72+
}
73+
74+
Future<void> test_index_normalCascade() async {
75+
await resolveTestUnit('''
76+
void f(String s) {
77+
s..[0];
78+
}
79+
''');
80+
await assertHasFix('''
81+
void f(String s) {
82+
s[0];
83+
}
84+
''');
85+
}
86+
87+
Future<void> test_method_normalCascade() async {
88+
await resolveTestUnit('''
89+
void f(String s) {
90+
s..substring(0, 3);
91+
}
92+
''');
93+
await assertHasFix('''
94+
void f(String s) {
95+
s.substring(0, 3);
96+
}
97+
''');
98+
}
99+
}
100+
101+
@reflectiveTest
102+
class ReplaceCascadeWithDotWithNNBDTest extends ReplaceCascadeWithDotTest {
103+
@override
104+
List<String> get experiments => [EnableString.non_nullable];
105+
106+
Future<void> test_assignment_index_nullAwareCascade() async {
107+
await resolveTestUnit('''
108+
void f(List<int>? l) {
109+
l?..[0] = 0;
110+
}
111+
''');
112+
await assertHasFix('''
113+
void f(List<int>? l) {
114+
l?[0] = 0;
115+
}
116+
''');
117+
}
118+
119+
Future<void> test_assignment_property_nullAwareCascade() async {
120+
await resolveTestUnit('''
121+
void f(C? c) {
122+
c?..s = 0;
123+
}
124+
class C {
125+
set s(int i) {}
126+
}
127+
''');
128+
await assertHasFix('''
129+
void f(C? c) {
130+
c?.s = 0;
131+
}
132+
class C {
133+
set s(int i) {}
134+
}
135+
''');
136+
}
137+
138+
Future<void> test_getter_nullAwareCascade() async {
139+
await resolveTestUnit('''
140+
void f(String? s) {
141+
s?..length;
142+
}
143+
''');
144+
await assertHasFix('''
145+
void f(String? s) {
146+
s?.length;
147+
}
148+
''');
149+
}
150+
151+
Future<void> test_index_nullAwareCascade() async {
152+
await resolveTestUnit('''
153+
void f(String? s) {
154+
s?..[0];
155+
}
156+
''');
157+
await assertHasFix('''
158+
void f(String? s) {
159+
s?[0];
160+
}
161+
''');
162+
}
163+
164+
Future<void> test_method_nullAwareCascade() async {
165+
await resolveTestUnit('''
166+
void f(String? s) {
167+
s?..substring(0, 3);
168+
}
169+
''');
170+
await assertHasFix('''
171+
void f(String? s) {
172+
s?.substring(0, 3);
173+
}
174+
''');
175+
}
176+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ import 'remove_unused_local_variable_test.dart' as remove_unused_local_variable;
131131
import 'remove_unused_parameter_test.dart' as remove_unused_parameter;
132132
import 'rename_to_camel_case_test.dart' as rename_to_camel_case;
133133
import 'replace_boolean_with_bool_test.dart' as replace_boolean_with_bool;
134+
import 'replace_cascade_with_dot_test.dart' as replace_cascade_with_dot;
134135
import 'replace_colon_with_equals_test.dart' as replace_colon_with_equals;
135136
import 'replace_final_with_const_test.dart' as replace_final_with_const;
136137
import 'replace_new_with_const_test.dart' as replace_new_with_const;
@@ -277,6 +278,7 @@ void main() {
277278
remove_unused_parameter.main();
278279
rename_to_camel_case.main();
279280
replace_boolean_with_bool.main();
281+
replace_cascade_with_dot.main();
280282
replace_colon_with_equals.main();
281283
replace_final_with_const.main();
282284
replace_new_with_const.main();

0 commit comments

Comments
 (0)