@@ -10,6 +10,7 @@ import 'package:analyzer/dart/ast/token.dart';
1010import 'package:analyzer/dart/ast/visitor.dart' ;
1111import 'package:analyzer/source/source_range.dart' ;
1212import 'package:analyzer/src/lint/constants.dart' ;
13+ import 'package:analyzer_plugin/protocol/protocol_common.dart' ;
1314import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_dart.dart' ;
1415import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart' ;
1516import 'package:analyzer_plugin/utilities/fixes/fixes.dart' ;
@@ -31,35 +32,27 @@ class AddConst extends ResolvedCorrectionProducer {
3132
3233 @override
3334 Future <void > compute (ChangeBuilder builder) async {
34- AstNode ? targetNode = node ;
35- if (targetNode is SimpleIdentifier ) {
36- targetNode = targetNode.parent ;
35+ var targetNode = _computeTargetNode () ;
36+ if (targetNode == null ) {
37+ return ;
3738 }
39+
3840 if (targetNode is ConstructorDeclaration ) {
39- var node_final = targetNode;
41+ var offset = targetNode.firstTokenAfterCommentAndMetadata.offset ;
4042 await builder.addDartFileEdit (file, (builder) {
41- var offset = node_final.firstTokenAfterCommentAndMetadata.offset;
4243 builder.addSimpleInsertion (offset, 'const ' );
4344 });
4445 return ;
4546 }
4647
47- Future <void > addParensAndConst (AstNode node_final ) async {
48- var offset = node_final .offset;
48+ Future <void > addParensAndConst (AstNode parent ) async {
49+ var offset = parent .offset;
4950 await builder.addDartFileEdit (file, (builder) {
50- builder.addSimpleInsertion (offset + node_final .length, ')' );
51+ builder.addSimpleInsertion (offset + parent .length, ')' );
5152 builder.addSimpleInsertion (offset, 'const (' );
5253 });
5354 }
5455
55- if (targetNode is TypeArgumentList ) {
56- while (targetNode is ! CompilationUnit && targetNode is ! ConstantPattern ) {
57- targetNode = targetNode? .parent;
58- }
59- }
60- if (targetNode is CompilationUnit ) {
61- return ;
62- }
6356 if (targetNode is ConstantPattern ) {
6457 var expression = targetNode.expression;
6558 var canBeConst = expression.canBeConst;
@@ -69,83 +62,40 @@ class AddConst extends ResolvedCorrectionProducer {
6962 builder.addSimpleInsertion (offset, 'const ' );
7063 });
7164 } else if (expression is TypeLiteral ) {
72- var node_final = targetNode.parent;
73- if (node_final is ParenthesizedPattern ) {
65+ var parent = targetNode.parent! ;
66+ if (parent is ParenthesizedPattern ) {
7467 await builder.addDartFileEdit (file, (builder) {
75- builder.addSimpleInsertion (node_final .offset, 'const ' );
68+ builder.addSimpleInsertion (parent .offset, 'const ' );
7669 });
7770 } else {
78- await addParensAndConst (node_final ! );
71+ await addParensAndConst (parent );
7972 }
8073 }
8174 return ;
8275 }
83- if (targetNode is BinaryExpression || targetNode is PrefixExpression ) {
84- var node_final = targetNode? .parent;
85- if (node_final ? .parent is ParenthesizedPattern ) {
86- // add const
87- var offset = node_final ! .parent! .offset;
76+ if (targetNode case BinaryExpression () || PrefixExpression () ) {
77+ var parent = targetNode.parent! ;
78+ if (parent .parent is ParenthesizedPattern ) {
79+ // Add ` const`.
80+ var offset = parent .parent! .offset;
8881 await builder.addDartFileEdit (file, (builder) {
8982 builder.addSimpleInsertion (offset, 'const ' );
9083 });
9184 } else {
92- // add const and parenthesis
93- await addParensAndConst (node_final ! );
85+ // Add ` const` and parentheses.
86+ await addParensAndConst (parent );
9487 }
9588 return ;
9689 }
9790
98- bool isParentConstant (
99- DartFileEditBuilderImpl builder,
100- Expression targetNode,
101- ) {
102- var edits = builder.fileEdit.edits;
103- var child = targetNode.parent;
104- while (child is Expression ||
105- child is ArgumentList ||
106- child is VariableDeclaration ||
107- child is VariableDeclarationList ) {
108- if (edits.any (
109- (element) =>
110- element.replacement.startsWith ('const' ) &&
111- element.offset == child! .offset,
112- )) {
113- return true ;
114- }
115- child = child! .parent;
116- }
117- return false ;
118- }
119-
120- Future <void > insertAtOffset (Expression targetNode) async {
121- var finder = _ConstRangeFinder ();
122- targetNode.accept (finder);
123- await builder.addDartFileEdit (file, (builder) {
124- if (builder is DartFileEditBuilderImpl &&
125- isParentConstant (builder, targetNode)) {
126- return ;
127- }
128- builder.addSimpleInsertion (targetNode.offset, 'const ' );
129- for (var range in finder.ranges) {
130- builder.addDeletion (range);
131- }
132- });
133- }
134-
13591 if (targetNode is ListLiteral ) {
136- await insertAtOffset ( targetNode);
92+ await _insertBeforeNode (builder, targetNode);
13793 return ;
13894 }
13995 if (targetNode is SetOrMapLiteral ) {
140- await insertAtOffset ( targetNode);
96+ await _insertBeforeNode (builder, targetNode);
14197 return ;
14298 }
143- if (targetNode is NamedType ) {
144- targetNode = targetNode.parent;
145- }
146- if (targetNode is ConstructorName ) {
147- targetNode = targetNode.parent;
148- }
14999 if (targetNode case InstanceCreationExpression (: var parent, : var keyword)) {
150100 var constDeclarations =
151101 getCodeStyleOptions (unitResult.file).preferConstDeclarations;
@@ -162,12 +112,35 @@ class AddConst extends ResolvedCorrectionProducer {
162112 }
163113 }
164114 if (keyword == null ) {
165- await insertAtOffset ( targetNode);
115+ await _insertBeforeNode (builder, targetNode);
166116 return ;
167117 }
168118 }
169119 }
170120
121+ /// Computes the node which this correction should treat as the target.
122+ AstNode ? _computeTargetNode () {
123+ AstNode ? targetNode = node;
124+ if (targetNode is SimpleIdentifier ) {
125+ targetNode = targetNode.parent;
126+ }
127+ if (targetNode is TypeArgumentList ) {
128+ while (targetNode is ! CompilationUnit && targetNode is ! ConstantPattern ) {
129+ targetNode = targetNode? .parent;
130+ }
131+ }
132+ if (targetNode is CompilationUnit ) {
133+ return null ;
134+ }
135+ if (targetNode is NamedType ) {
136+ targetNode = targetNode.parent;
137+ }
138+ if (targetNode is ConstructorName ) {
139+ targetNode = targetNode.parent;
140+ }
141+ return targetNode;
142+ }
143+
171144 /// Considers if the given [variables] to be declared with `const` if all of
172145 /// them are contained in the list of errors that suggest using `const` (
173146 /// `prefer_const_constructors` triggers).
@@ -218,6 +191,46 @@ class AddConst extends ResolvedCorrectionProducer {
218191 // If each of the variable ranges is contained in the list of error ranges.
219192 return variablesRanges.every (errorsRanges.contains);
220193 }
194+
195+ /// Inserts `const ` before [targetNode] .
196+ Future <void > _insertBeforeNode (
197+ ChangeBuilder builder,
198+ Expression targetNode,
199+ ) async {
200+ var finder = _ConstRangeFinder ();
201+ targetNode.accept (finder);
202+ await builder.addDartFileEdit (file, (builder) {
203+ if (builder is DartFileEditBuilderImpl &&
204+ _isAncestorConstant (builder.fileEdit.edits, targetNode)) {
205+ return ;
206+ }
207+ builder.addSimpleInsertion (targetNode.offset, 'const ' );
208+ for (var range in finder.ranges) {
209+ builder.addDeletion (range);
210+ }
211+ });
212+ }
213+
214+ /// Returns whether any [edits] start with 'const' and have the same offset as
215+ /// one of [targetNode] s ancestors.
216+ bool _isAncestorConstant (List <SourceEdit > edits, Expression targetNode) {
217+ var child = targetNode.parent;
218+ var editsWhichStartWithConst =
219+ edits.where ((e) => e.replacement.startsWith ('const' )).toList ();
220+ if (editsWhichStartWithConst.isEmpty) {
221+ return false ;
222+ }
223+ while (child is Expression ||
224+ child is ArgumentList ||
225+ child is VariableDeclaration ||
226+ child is VariableDeclarationList ) {
227+ if (editsWhichStartWithConst.any ((e) => e.offset == child! .offset)) {
228+ return true ;
229+ }
230+ child = child! .parent;
231+ }
232+ return false ;
233+ }
221234}
222235
223236class _ConstRangeFinder extends RecursiveAstVisitor <void > {
0 commit comments