4
4
5
5
import 'package:analysis_server/src/services/correction/fix.dart' ;
6
6
import 'package:analysis_server_plugin/edit/dart/correction_producer.dart' ;
7
+ import 'package:analyzer/dart/ast/token.dart' ;
7
8
import 'package:analyzer/dart/element/element.dart' ;
8
9
import 'package:analyzer/src/dart/ast/ast.dart' ;
9
10
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart' ;
@@ -36,52 +37,104 @@ class ConvertNullCheckToNullAwareElementOrEntry
36
37
)) {
37
38
if (node.caseClause == null ) {
38
39
// An element or entry of the form `if (x != null) ...`.
39
- if (thenElement is ! MapLiteralEntry ) {
40
- // In case of a list or set element, we simply replace the entire
41
- // element with the then-element prefixed by '?'.
40
+ if (thenElement is SimpleIdentifier ) {
41
+ // In case of a list or set element with a promotable target, we
42
+ // simply replace the entire element with the then-element prefixed by
43
+ // '?'.
42
44
//
43
- // `[if (x != null) x]` ==> `[?x]`
44
- // `{if (x != null) x]` ==> `{?x}`
45
+ // `if (x != null) x` is rewritten as `?x`
45
46
await builder.addDartFileEdit (file, (builder) {
46
47
builder.addSimpleReplacement (
47
- range.startOffsetEndOffset (
48
- node.ifKeyword.offset,
49
- thenElement.offset,
50
- ),
48
+ range.startStart (node, thenElement),
51
49
'?' ,
52
50
);
53
51
});
54
- } else {
52
+ } else if (thenElement is PostfixExpression ) {
53
+ // In case of a list or set element with a getter target, we replace
54
+ // the entire element with the then-element target identifier prefixed
55
+ // by '?'. Note that in the case of a getter target, the null-check
56
+ // operator '!' is always present in [thenElement].
57
+ //
58
+ // `if (x != null) x!` is rewritten as `?x`
59
+ await builder.addDartFileEdit (file, (builder) {
60
+ builder.addSimpleReplacement (
61
+ range.startStart (node, thenElement),
62
+ '?' ,
63
+ );
64
+ builder.addDeletion (range.endEnd (thenElement.operand, thenElement));
65
+ });
66
+ } else if (thenElement is MapLiteralEntry ) {
55
67
// In case of a map entry we need to check if it's the key that's
56
68
// promoted to non-nullable or the value.
69
+ var thenElementKey = thenElement.key;
70
+ var keyCanonicalElement = switch (thenElementKey) {
71
+ SimpleIdentifier () => thenElementKey.canonicalElement,
72
+ PostfixExpression (: var operand, operator : Token (lexeme: '!' )) =>
73
+ operand.canonicalElement,
74
+ _ => null ,
75
+ };
76
+
57
77
var binaryCondition = condition as BinaryExpression ;
58
- var keyCanonicalElement = thenElement.key.canonicalElement;
59
78
if (keyCanonicalElement != null &&
60
79
(binaryCondition.leftOperand.canonicalElement ==
61
80
keyCanonicalElement ||
62
81
binaryCondition.rightOperand.canonicalElement ==
63
82
keyCanonicalElement)) {
64
- // In case the key is promoted, we simply replace everything before
65
- // the key with '?'.
66
- //
67
- // `{if (x != null) x: "value"}` ==> `{?x: "value"}`
68
- await builder.addDartFileEdit (file, (builder) {
69
- builder.addSimpleReplacement (
70
- range.startOffsetEndOffset (node.offset, thenElement.key.offset),
71
- '?' ,
72
- );
73
- });
83
+ if (thenElementKey is SimpleIdentifier ) {
84
+ // In case the key is null-aware and is promotable, we simply
85
+ // replace everything before the key with '?'.
86
+ //
87
+ // `if (x != null) x: "v"` is rewritten as `?x: "v"`
88
+ await builder.addDartFileEdit (file, (builder) {
89
+ builder.addSimpleReplacement (
90
+ range.startStart (node, thenElement.key),
91
+ '?' ,
92
+ );
93
+ });
94
+ } else if (thenElementKey is PostfixExpression ) {
95
+ // In case the key is null-aware and is a getter, we replace
96
+ // everything before the key with '?' and remove '!' afterwards.
97
+ // Note that in the case of a getter, the null-check operator '!'
98
+ // is always present in [thenElementKey].
99
+ //
100
+ // `if (x != null) x!: "v"` is rewritten as `?x: "v"`
101
+ await builder.addDartFileEdit (file, (builder) {
102
+ builder.addSimpleReplacement (
103
+ range.startStart (node, thenElementKey),
104
+ '?' ,
105
+ );
106
+ builder.addDeletion (
107
+ range.endStart (thenElementKey.operand, thenElement.separator),
108
+ );
109
+ });
110
+ }
74
111
} else {
75
- // In case the value is promoted, we remove everything before the
76
- // key and insert '?' before the value.
77
- //
78
- // `{if (x != null) "key": x}` ==> `{"key": ?x}`
79
- await builder.addDartFileEdit (file, (builder) {
80
- builder.addDeletion (
81
- range.startOffsetEndOffset (node.offset, thenElement.key.offset),
82
- );
83
- builder.addSimpleInsertion (thenElement.value.offset, '?' );
84
- });
112
+ var thenElementValue = thenElement.value;
113
+ if (thenElementValue is SimpleIdentifier ) {
114
+ // In case the value is null-aware and is promotable, we remove
115
+ // everything before the key and insert '?' before the value.
116
+ //
117
+ // `if (x != null) "k": x` is rewritten as `"k": ?x`
118
+ await builder.addDartFileEdit (file, (builder) {
119
+ builder.addDeletion (range.startStart (node, thenElement.key));
120
+ builder.addSimpleInsertion (thenElement.value.offset, '?' );
121
+ });
122
+ } else if (thenElementValue is PostfixExpression ) {
123
+ // In case the value is null-aware and is a getter, we remove
124
+ // everything before the key, insert '?' before the value, and
125
+ // delete '!' after it. Note that in the case of a getter, the
126
+ // null-check operator '!' is always present in
127
+ // [thenElementValue].
128
+ //
129
+ // `if (x != null) "k": x!` is rewritten as `"k": ?x`
130
+ await builder.addDartFileEdit (file, (builder) {
131
+ builder.addDeletion (range.startStart (node, thenElementKey));
132
+ builder.addSimpleInsertion (thenElementValue.offset, '?' );
133
+ builder.addDeletion (
134
+ range.endEnd (thenElementValue.operand, thenElementValue),
135
+ );
136
+ });
137
+ }
85
138
}
86
139
}
87
140
} else {
@@ -90,13 +143,13 @@ class ConvertNullCheckToNullAwareElementOrEntry
90
143
// In case of a list or set element, we replace the entire element
91
144
// with the expression to the left of 'case', prefixed by '?'.
92
145
//
93
- // `[if (x case var y?) y]` ==> `[?x]`
94
- // `{if (x case var y?) y]` ==> `{?x}`
146
+ // `if (x case var y?) y` is rewritten as `?x`
95
147
await builder.addDartFileEdit (file, (builder) {
96
148
builder.addSimpleReplacement (
97
- range.startOffsetEndOffset (node.offset, node.end ),
98
- '?${ condition . toSource ()} ' ,
149
+ range.startStart (node, condition ),
150
+ '?' ,
99
151
);
152
+ builder.addDeletion (range.endEnd (condition, node));
100
153
});
101
154
} else {
102
155
// In case of a map entry we need to check if it's the key that's
@@ -111,28 +164,24 @@ class ConvertNullCheckToNullAwareElementOrEntry
111
164
// In case the key is promoted, replace everything before ':' with
112
165
// the expression before 'case', prefixed by '?'.
113
166
//
114
- // `{ if (x case var y?) y: "value"}` ==> `{ ?x: "value"} `
167
+ // `if (x case var y?) y: "v"` is rewritten as ` ?x: "v" `
115
168
await builder.addDartFileEdit (file, (builder) {
116
169
builder.addSimpleReplacement (
117
- range.startOffsetEndOffset (node.offset, thenElement.key.end ),
118
- '?${ condition . toSource ()} ' ,
170
+ range.startStart (node, condition ),
171
+ '?' ,
119
172
);
173
+ builder.addDeletion (range.endEnd (condition, thenElement.key));
120
174
});
121
175
} else {
122
176
// In case the value is promoted, delete everything before the key
123
177
// and replace the value with the expression to the left of 'case',
124
178
// prefixed by '?'.
125
179
//
126
- // `{ if (x case var y?) "key ": y}` ==> `{"key ": ?x} `
180
+ // `if (x case var y?) "k ": y` is rewritten as `"k ": ?x`
127
181
await builder.addDartFileEdit (file, (builder) {
128
- builder.addDeletion (
129
- range.startOffsetEndOffset (node.offset, thenElement.key.offset),
130
- );
182
+ builder.addDeletion (range.startStart (node, thenElement.key));
131
183
builder.addSimpleReplacement (
132
- range.startOffsetEndOffset (
133
- thenElement.value.offset,
134
- thenElement.value.end,
135
- ),
184
+ range.startEnd (thenElement.value, thenElement.value),
136
185
'?${condition .toSource ()}' ,
137
186
);
138
187
});
0 commit comments