@@ -16,9 +16,12 @@ import 'package:analyzer/src/generated/java_core.dart';
16
16
import 'package:analyzer/src/generated/source.dart' ;
17
17
import 'package:analyzer/src/lint/options_rule_validator.dart' ;
18
18
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart' ;
19
+ import 'package:analyzer_plugin/utilities/change_builder/change_builder_yaml.dart' ;
19
20
import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart' ;
20
21
import 'package:analyzer_plugin/utilities/fixes/fixes.dart' ;
22
+ import 'package:collection/collection.dart' ;
21
23
import 'package:yaml/yaml.dart' ;
24
+ import 'package:yaml_edit/yaml_edit.dart' ;
22
25
23
26
/// The generator used to generate fixes in analysis options files.
24
27
class AnalysisOptionsFixGenerator {
@@ -64,6 +67,25 @@ class AnalysisOptionsFixGenerator {
64
67
// } else
65
68
if (errorCode == AnalysisOptionsHintCode .STRONG_MODE_SETTING_DEPRECATED ) {
66
69
await _addFix_removeSetting (coveringNodePath);
70
+ } else if (errorCode ==
71
+ AnalysisOptionsWarningCode
72
+ .ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT ) {
73
+ var analyzerMap = options['analyzer' ];
74
+ if (analyzerMap is ! YamlMap ) {
75
+ return fixes;
76
+ }
77
+ var strongModeMap = analyzerMap['strong-mode' ];
78
+
79
+ if (strongModeMap is ! YamlMap ) {
80
+ return fixes;
81
+ }
82
+ if (_isErrorAtMapKey (strongModeMap, 'implicit-casts' )) {
83
+ await _addFix_replaceWithStrictCasts (
84
+ coveringNodePath, analyzerMap, strongModeMap);
85
+ } else if (_isErrorAtMapKey (strongModeMap, 'implicit-dynamic' )) {
86
+ await _addFix_replaceWithStrictRawTypes (
87
+ coveringNodePath, analyzerMap, strongModeMap);
88
+ }
67
89
} else if (errorCode == DEPRECATED_LINT_HINT ) {
68
90
await _addFix_removeLint (coveringNodePath);
69
91
// } else if (errorCode == AnalysisOptionsWarningCode.INCLUDED_FILE_WARNING) {
@@ -101,6 +123,52 @@ class AnalysisOptionsFixGenerator {
101
123
}
102
124
}
103
125
126
+ /// Replaces `analyzer: strong-mode: implicit-casts: false` with
127
+ /// `analyzer: language: strict-casts: true` .
128
+ Future <void > _addFix_replaceWithStrictCasts (List <YamlNode > coveringNodePath,
129
+ YamlMap analyzerMap, YamlMap strongModeMap) async {
130
+ var builder =
131
+ ChangeBuilder (workspace: _NonDartChangeWorkspace (resourceProvider));
132
+ await builder.addYamlFileEdit (file, (builder) {
133
+ _replaceStrongModeEntryWithLanguageEntry (
134
+ builder,
135
+ coveringNodePath,
136
+ analyzerMap,
137
+ strongModeMap,
138
+ strongModeKey: 'implicit-casts' ,
139
+ languageKey: 'strict-casts' ,
140
+ languageValue: true ,
141
+ );
142
+ });
143
+ _addFixFromBuilder (
144
+ builder, AnalysisOptionsFixKind .REPLACE_WITH_STRICT_CASTS ,
145
+ args: [coveringNodePath[0 ].toString ()]);
146
+ }
147
+
148
+ /// Replaces `analyzer: strong-mode: implicit-dynamic: false` with
149
+ /// `analyzer: language: strict-raw-types: true` .
150
+ Future <void > _addFix_replaceWithStrictRawTypes (
151
+ List <YamlNode > coveringNodePath,
152
+ YamlMap analyzerMap,
153
+ YamlMap strongModeMap) async {
154
+ var builder =
155
+ ChangeBuilder (workspace: _NonDartChangeWorkspace (resourceProvider));
156
+ await builder.addYamlFileEdit (file, (builder) {
157
+ _replaceStrongModeEntryWithLanguageEntry (
158
+ builder,
159
+ coveringNodePath,
160
+ analyzerMap,
161
+ strongModeMap,
162
+ strongModeKey: 'implicit-dynamic' ,
163
+ languageKey: 'strict-raw-types' ,
164
+ languageValue: true ,
165
+ );
166
+ });
167
+ _addFixFromBuilder (
168
+ builder, AnalysisOptionsFixKind .REPLACE_WITH_STRICT_RAW_TYPES ,
169
+ args: [coveringNodePath[0 ].toString ()]);
170
+ }
171
+
104
172
/// Add a fix whose edits were built by the [builder] that has the given
105
173
/// [kind] . If [args] are provided, they will be used to fill in the message
106
174
/// for the fix.
@@ -184,6 +252,20 @@ class AnalysisOptionsFixGenerator {
184
252
return offset;
185
253
}
186
254
255
+ /// Returns whether the error is located within [map] , covering the
256
+ /// [YamlScalar] node for [key] .
257
+ bool _isErrorAtMapKey (YamlMap map, String key) {
258
+ var keyNode = map.nodes.keys
259
+ .whereType <YamlScalar >()
260
+ .firstWhereOrNull ((k) => k.value == key);
261
+ if (keyNode == null ) {
262
+ return false ;
263
+ }
264
+ var keyOffset = keyNode.span.start.offset;
265
+ var keyLength = keyNode.span.end.offset - keyOffset;
266
+ return keyOffset == errorOffset && keyLength == errorLength;
267
+ }
268
+
187
269
SourceRange _lines (int start, int end) {
188
270
var startLocation = lineInfo.getLocation (start);
189
271
var startOffset = lineInfo.getOffsetOfLine (startLocation.lineNumber - 1 );
@@ -192,6 +274,53 @@ class AnalysisOptionsFixGenerator {
192
274
math.min (endLocation.lineNumber, lineInfo.lineCount - 1 ));
193
275
return SourceRange (startOffset, endOffset - startOffset);
194
276
}
277
+
278
+ /// Replaces a 'strong-mode' entry keyed to [strongModeKey] with a 'language'
279
+ /// entry with [languageKey] and [languageValue] .
280
+ ///
281
+ /// 'strong-mode' and 'language' are each maps which can be found under the
282
+ /// top-level 'analyzer' map. 'strong-mode' (given as [strongModeMap] ) must
283
+ /// already be present under the 'analyzer' map (given as [analyzerMap] ).
284
+ void _replaceStrongModeEntryWithLanguageEntry (
285
+ YamlFileEditBuilder builder,
286
+ List <YamlNode > coveringNodePath,
287
+ YamlMap analyzerMap,
288
+ YamlMap strongModeMap, {
289
+ required String strongModeKey,
290
+ required String languageKey,
291
+ required Object ? languageValue,
292
+ }) {
293
+ var yamlEditor = YamlEditor (content);
294
+ // If 'language' does not exist yet under 'analyzer', create it.
295
+ if (analyzerMap['language' ] == null ) {
296
+ yamlEditor.update (['analyzer' , 'language' ], {languageKey: languageValue});
297
+ } else {
298
+ yamlEditor.update (['analyzer' , 'language' , languageKey], languageValue);
299
+ }
300
+ var languageEdit = yamlEditor.edits.single;
301
+ builder.addSimpleReplacement (
302
+ SourceRange (languageEdit.offset, languageEdit.length),
303
+ languageEdit.replacement);
304
+
305
+ // If `strongModeKey` is the only entry under 'strong-mode', then remove
306
+ // the entire 'strong-mode' entry.
307
+ if (strongModeMap.length == 1 ) {
308
+ yamlEditor.remove (['analyzer' , 'strong-mode' ]);
309
+ } else {
310
+ yamlEditor.remove (['analyzer' , 'strong-mode' , strongModeKey]);
311
+ }
312
+ var strongModeEdit = yamlEditor.edits[1 ];
313
+ int strongModeEditOffset;
314
+ if (strongModeEdit.offset > languageEdit.offset) {
315
+ strongModeEditOffset = strongModeEdit.offset -
316
+ (languageEdit.replacement.length - languageEdit.length);
317
+ } else {
318
+ strongModeEditOffset = strongModeEdit.offset;
319
+ }
320
+ builder.addSimpleReplacement (
321
+ SourceRange (strongModeEditOffset, strongModeEdit.length),
322
+ strongModeEdit.replacement);
323
+ }
195
324
}
196
325
197
326
class _NonDartChangeWorkspace implements ChangeWorkspace {
0 commit comments