@@ -6,10 +6,24 @@ import 'package:analysis_server/src/services/correction/fix.dart';
66import 'package:analysis_server_plugin/edit/dart/correction_producer.dart' ;
77import 'package:analyzer/dart/ast/ast.dart' ;
88import 'package:analyzer/dart/ast/precedence.dart' ;
9+ import 'package:analyzer/dart/ast/token.dart' ;
10+ import 'package:analyzer/source/source_range.dart' ;
911import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart' ;
1012import 'package:analyzer_plugin/utilities/fixes/fixes.dart' ;
1113import 'package:analyzer_plugin/utilities/range_factory.dart' ;
1214
15+ /// This correction producer can act on a variety of code, and various edits
16+ /// might be applied.
17+ typedef _ParametersFixData =
18+ ({
19+ SourceRange parensRange,
20+ List <SourceRange > deleteRanges,
21+ bool negated,
22+ bool needsParens,
23+ (SourceRange , String )? replace,
24+ bool bangBeforeParens,
25+ });
26+
1327class ConvertToBooleanExpression extends ResolvedCorrectionProducer {
1428 ConvertToBooleanExpression ({required super .context});
1529
@@ -27,49 +41,202 @@ class ConvertToBooleanExpression extends ResolvedCorrectionProducer {
2741 Future <void > compute (ChangeBuilder builder) async {
2842 AstNode ? node = this .node;
2943 if (node is BooleanLiteral ) node = node.parent;
30- if (node is ! BinaryExpression ) return ;
31-
32- var rightOperand = node.rightOperand;
33- var leftOperand = node.leftOperand;
34-
35- Expression expression;
36- BooleanLiteral literal;
37-
38- var deleteRange = range.endEnd (leftOperand, rightOperand);
44+ _ParametersFixData parameters;
3945
40- if (rightOperand is BooleanLiteral ) {
41- literal = rightOperand;
42- expression = node.leftOperand;
43- } else if (leftOperand is BooleanLiteral ) {
44- literal = leftOperand;
45- expression = node.rightOperand;
46- deleteRange = range.startStart (leftOperand, rightOperand);
46+ if (node case BinaryExpression (
47+ : var rightOperand,
48+ : var leftOperand,
49+ operator : Token (type: var operator ),
50+ )) {
51+ if (rightOperand is BooleanLiteral ) {
52+ parameters = _processBinaryExp (
53+ node,
54+ operator ,
55+ rightOperand,
56+ leftOperand,
57+ currentIsLeft: false ,
58+ );
59+ } else if (leftOperand is BooleanLiteral ) {
60+ parameters = _processBinaryExp (
61+ node,
62+ operator ,
63+ leftOperand,
64+ rightOperand,
65+ currentIsLeft: true ,
66+ );
67+ } else {
68+ return ;
69+ }
70+ } else if (node
71+ case ConditionalExpression (
72+ : var condition,
73+ : var thenExpression,
74+ : var elseExpression,
75+ ) &&
76+ var conditionalExp) {
77+ _ParametersFixData ? result = (switch ((thenExpression, elseExpression)) {
78+ (BooleanLiteral then, BooleanLiteral elseExp) => () {
79+ var equalValues = then.value == elseExp.value;
80+ var rangeStart =
81+ equalValues
82+ // keep `then`
83+ ? range.startStart (condition, then)
84+ // keep `condition`
85+ : range.endEnd (condition, then);
86+ // remove ` : elseExp`
87+ var rangeEnd = range.endEnd (then, elseExp);
88+ return (
89+ parensRange: range.node (equalValues ? then : condition),
90+ deleteRanges: [rangeStart, rangeEnd],
91+ negated: ! then.value && elseExp.value,
92+ replace: null ,
93+ needsParens: ! equalValues && condition.needsParens,
94+ bangBeforeParens: false ,
95+ );
96+ }(),
97+ (BooleanLiteral then, Expression elseExp) => () {
98+ var replaceRange = range.endStart (condition, elseExp);
99+ var operator = then.ifBarElseAmpersand;
100+ return (
101+ parensRange: range.node (conditionalExp),
102+ deleteRanges: const < SourceRange > [],
103+ negated: ! then.value,
104+ replace: (replaceRange, ' ${operator .lexeme } ' ),
105+ // conditional expressions always need parens so there will
106+ // be no need to add them
107+ needsParens: false ,
108+ bangBeforeParens: false ,
109+ );
110+ }(),
111+ (Expression then, BooleanLiteral elseExp) => () {
112+ var rangeStart = range.endStart (condition, then);
113+ var rangeEnd = range.endEnd (then, elseExp);
114+ var operator = elseExp.ifBarElseAmpersand;
115+ return (
116+ parensRange: range.node (conditionalExp),
117+ deleteRanges: [rangeEnd],
118+ negated: elseExp.value,
119+ replace: (rangeStart, ' ${operator .lexeme } ' ),
120+ // conditional expressions always need parens so there will
121+ // be no need to add them
122+ needsParens: false ,
123+ bangBeforeParens: false ,
124+ );
125+ }(),
126+ (_, _) => null ,
127+ });
128+ if (result == null ) {
129+ return ;
130+ }
131+ parameters = result;
47132 } else {
48133 return ;
49134 }
135+ await _addEdit (builder, parameters);
136+ }
50137
51- var negated = ! isPositiveCase (node, literal);
138+ Future <void > _addEdit (
139+ ChangeBuilder builder,
140+ _ParametersFixData parameters,
141+ ) async {
142+ var (
143+ : parensRange,
144+ : deleteRanges,
145+ : negated,
146+ : replace,
147+ : needsParens,
148+ : bangBeforeParens,
149+ ) = parameters;
52150 await builder.addDartFileEdit (file, (builder) {
53- if (negated) {
54- builder.addSimpleInsertion (expression.offset, '!' );
55- if (expression.needsParens) {
56- builder.addSimpleInsertion (expression.offset, '(' );
57- builder.addSimpleInsertion (expression.end, ')' );
151+ if (bangBeforeParens) {
152+ if (negated) {
153+ builder.addSimpleInsertion (parensRange.offset, TokenType .BANG .lexeme);
154+ if (needsParens) {
155+ builder.addSimpleInsertion (parensRange.offset, '(' );
156+ builder.addSimpleInsertion (parensRange.end, ')' );
157+ }
158+ }
159+ } else {
160+ if (needsParens) {
161+ builder.addSimpleInsertion (parensRange.offset, '(' );
58162 }
163+ if (negated) {
164+ builder.addSimpleInsertion (parensRange.offset, TokenType .BANG .lexeme);
165+ }
166+ if (needsParens) {
167+ builder.addSimpleInsertion (parensRange.end, ')' );
168+ }
169+ }
170+ if (replace != null ) {
171+ builder.addSimpleReplacement (replace.$1, replace.$2);
172+ }
173+ for (var range in deleteRanges) {
174+ builder.addDeletion (range);
59175 }
60- builder.addDeletion (deleteRange);
61176 });
62177 }
63178
179+ _ParametersFixData _processBinaryExp (
180+ BinaryExpression binaryExp,
181+ TokenType operator ,
182+ BooleanLiteral current,
183+ Expression other, {
184+ required bool currentIsLeft,
185+ }) {
186+ List <SourceRange > deleteRanges;
187+ SourceRange parensRange;
188+ bool needsParens;
189+ switch (operator ) {
190+ case TokenType .BAR || TokenType .BAR_BAR when current.value:
191+ case TokenType .AMPERSAND || TokenType .AMPERSAND_AMPERSAND
192+ when ! current.value:
193+ deleteRanges = [
194+ currentIsLeft
195+ ? range.endEnd (current, other)
196+ : range.startStart (other, current),
197+ ];
198+ parensRange = range.node (current);
199+ needsParens = current.needsParens;
200+ default :
201+ deleteRanges = [
202+ currentIsLeft
203+ ? range.startStart (current, other)
204+ : range.endEnd (other, current),
205+ ];
206+ parensRange = range.node (other);
207+ needsParens = other.needsParens;
208+ }
209+ return (
210+ negated: ! isPositiveCase (binaryExp, current),
211+ parensRange: parensRange,
212+ deleteRanges: deleteRanges,
213+ needsParens: needsParens,
214+ replace: null ,
215+ bangBeforeParens: true ,
216+ );
217+ }
218+
64219 static bool isPositiveCase (
65220 BinaryExpression expression,
66221 BooleanLiteral literal,
67222 ) {
68- if (expression.operator .lexeme == '==' ) return literal.value;
69- return ! literal.value;
223+ return switch (expression.operator .type) {
224+ TokenType .BAR ||
225+ TokenType .BAR_BAR ||
226+ TokenType .AMPERSAND ||
227+ TokenType .AMPERSAND_AMPERSAND => true ,
228+ TokenType .EQ_EQ => literal.value,
229+ TokenType .BANG_EQ || TokenType .CARET => ! literal.value,
230+ _ => throw StateError ('Unexpected operator ${expression .operator .type }' ),
231+ };
70232 }
71233}
72234
235+ extension on BooleanLiteral {
236+ TokenType get ifBarElseAmpersand =>
237+ value ? TokenType .BAR_BAR : TokenType .AMPERSAND_AMPERSAND ;
238+ }
239+
73240extension on Expression {
74241 bool get needsParens => precedence <= Precedence .prefix;
75242}
0 commit comments