@@ -6,31 +6,44 @@ import 'package:_fe_analyzer_shared/src/scanner/token.dart';
66import 'package:analysis_server/src/services/completion/dart/utilities.dart' ;
77import 'package:analysis_server/src/services/correction/fix.dart' ;
88import 'package:analysis_server_plugin/edit/dart/correction_producer.dart' ;
9+ import 'package:analysis_server_plugin/src/correction/fix_generators.dart' ;
910import 'package:analyzer/dart/ast/ast.dart' ;
1011import 'package:analyzer/dart/element/element2.dart' ;
12+ import 'package:analyzer/error/error.dart' ;
1113import 'package:analyzer/src/utilities/extensions/flutter.dart' ;
1214import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart' ;
1315import 'package:analyzer_plugin/utilities/fixes/fixes.dart' ;
1416import 'package:collection/collection.dart' ;
1517
1618class AddMissingRequiredArgument extends ResolvedCorrectionProducer {
17- /// The name of the parameter that was missing.
18- String _missingParameterName = '' ;
19+ /// The number of the parameters missing.
20+ late int _missingParameters ;
1921
2022 AddMissingRequiredArgument ({required super .context});
2123
2224 @override
2325 CorrectionApplicability get applicability =>
24- // Not a stand-alone fix; requires follow-up actions.
25- CorrectionApplicability
26- .singleLocation;
26+ // Not a stand-alone fix; requires follow-up actions.
27+ CorrectionApplicability .singleLocation;
2728
2829 @override
29- List <String > get fixArguments => [_missingParameterName ];
30+ List <String > get fixArguments => ['$ _missingParameters ' , _plural ];
3031
3132 @override
3233 FixKind get fixKind => DartFixKind .ADD_MISSING_REQUIRED_ARGUMENT ;
3334
35+ /// All the error codes that this fix can be applied to.
36+ List <ErrorCode > get _errorCodesWhereThisIsValid {
37+ var producerGenerators = [AddMissingRequiredArgument .new ];
38+ var nonLintProducers = registeredFixGenerators.nonLintProducers;
39+ return [
40+ for (var MapEntry (: key, : value) in nonLintProducers.entries)
41+ if (value.containsAny (producerGenerators)) key,
42+ ];
43+ }
44+
45+ String get _plural => _missingParameters == 1 ? '' : 's' ;
46+
3447 @override
3548 Future <void > compute (ChangeBuilder builder) async {
3649 InstanceCreationExpression ? creation;
@@ -54,90 +67,126 @@ class AddMissingRequiredArgument extends ResolvedCorrectionProducer {
5467 }
5568
5669 var diagnostic = this .diagnostic;
57- if (diagnostic == null ) {
70+ if (diagnostic is ! AnalysisError ) {
5871 return ;
5972 }
6073
61- if (targetElement is ExecutableElement2 && argumentList != null ) {
62- // Format: "Missing required argument 'foo'."
63- var messageParts = diagnostic.problemMessage
64- .messageText (includeUrl: false )
65- .split ("'" );
66- if (messageParts.length < 2 ) {
67- return ;
68- }
69- _missingParameterName = messageParts[1 ];
74+ var validErrors = _errorCodesWhereThisIsValid;
75+ var errors = unitResult.errors.where (
76+ (e) => diagnostic.sameRangeAs (e) && validErrors.contains (e.errorCode),
77+ );
7078
71- var missingParameter = targetElement.formalParameters.firstWhereOrNull (
72- (p) => p.name3 == _missingParameterName,
73- );
74- if (missingParameter == null ) {
75- return ;
76- }
79+ // Should not happen since the current diagnostic is in the list of errors
80+ // where this fix is valid.
81+ if (errors.isEmpty) {
82+ errors = [diagnostic];
83+ }
7784
78- int offset;
79- var hasTrailingComma = false ;
80- var insertBetweenParams = false ;
81- var arguments = argumentList.arguments;
82- if (arguments.isEmpty) {
83- offset = argumentList.leftParenthesis.end;
84- } else {
85- var lastArgument = arguments.last;
86- offset = lastArgument.end;
87- hasTrailingComma = lastArgument.endToken.next! .type == TokenType .COMMA ;
88-
89- if (lastArgument is NamedExpression && creation.isWidgetExpression) {
90- if (lastArgument.isChildArgument || lastArgument.isChildrenArgument) {
91- offset = lastArgument.offset;
92- hasTrailingComma = true ;
93- insertBetweenParams = true ;
94- }
85+ // This should only trigger once and the disposition of the valid
86+ // diagnostics in the unit should always be the same.
87+ if (errors.first != diagnostic) {
88+ return ;
89+ }
90+
91+ _missingParameters = errors.length;
92+
93+ for (var (index, diagnostic) in errors.indexed) {
94+ if (targetElement is ExecutableElement2 && argumentList != null ) {
95+ // Format: "Missing required argument 'foo'."
96+ var messageParts = diagnostic.problemMessage
97+ .messageText (includeUrl: false )
98+ .split ("'" );
99+ if (messageParts.length < 2 ) {
100+ return ;
95101 }
96- }
97102
98- var codeStyleOptions = getCodeStyleOptions (unitResult.file);
99- var defaultValue = getDefaultStringParameterValue (
100- missingParameter,
101- codeStyleOptions.preferredQuoteForStrings,
102- );
103+ var parameterName = messageParts[1 ];
104+
105+ var missingParameter = targetElement.formalParameters.firstWhereOrNull (
106+ (p) => p.name3 == parameterName,
107+ );
108+ if (missingParameter == null ) {
109+ return ;
110+ }
103111
104- await builder.addDartFileEdit (file, (builder) {
105- builder.addInsertion (offset, (builder) {
106- if (arguments.isNotEmpty && ! insertBetweenParams) {
107- builder.write (', ' );
112+ int offset;
113+ var hasTrailingComma = false ;
114+ var insertBetweenParams = false ;
115+ var arguments = argumentList.arguments;
116+ if (arguments.isEmpty) {
117+ offset = argumentList.leftParenthesis.end;
118+ } else {
119+ var lastArgument = arguments.last;
120+ offset = lastArgument.end;
121+ hasTrailingComma =
122+ lastArgument.endToken.next! .type == TokenType .COMMA ;
123+
124+ if (lastArgument is NamedExpression && creation.isWidgetExpression) {
125+ if (lastArgument.isChildArgument ||
126+ lastArgument.isChildrenArgument) {
127+ offset = lastArgument.offset;
128+ hasTrailingComma = true ;
129+ insertBetweenParams = true ;
130+ }
108131 }
132+ }
109133
110- builder.write ('$_missingParameterName : ' );
134+ var codeStyleOptions = getCodeStyleOptions (unitResult.file);
135+ var defaultValue = getDefaultStringParameterValue (
136+ missingParameter,
137+ codeStyleOptions.preferredQuoteForStrings,
138+ );
111139
112- // Use defaultValue.cursorPosition if it's not null.
113- if (defaultValue != null ) {
114- var text = defaultValue.text;
115- var cursorPosition = defaultValue.cursorPosition;
116- if (cursorPosition != null ) {
117- builder.write (text.substring (0 , cursorPosition));
118- builder.selectHere ();
119- builder.write (text.substring (cursorPosition));
120- } else {
121- builder.addSimpleLinkedEdit ('VALUE' , text);
140+ await builder.addDartFileEdit (file, (builder) {
141+ builder.addInsertion (offset, (builder) {
142+ if ((arguments.isNotEmpty || index > 0 ) && ! insertBetweenParams) {
143+ builder.write (', ' );
122144 }
123- } else {
124- builder.addSimpleLinkedEdit ('VALUE' , 'null' );
125- }
126145
127- if (creation.isWidgetExpression) {
128- // Insert a trailing comma after Flutter instance creation params.
129- if (! hasTrailingComma) {
130- builder.write (',' );
131- } else if (insertBetweenParams) {
132- builder.writeln (',' );
146+ builder.write ('$parameterName : ' );
147+
148+ // Use defaultValue.cursorPosition if it's not null.
149+ if (defaultValue != null ) {
150+ var text = defaultValue.text;
151+ var cursorPosition = defaultValue.cursorPosition;
152+ if (cursorPosition != null ) {
153+ builder.write (text.substring (0 , cursorPosition));
154+ builder.selectHere ();
155+ builder.write (text.substring (cursorPosition));
156+ } else {
157+ builder.addSimpleLinkedEdit ('VALUE' , text);
158+ }
159+ } else {
160+ builder.addSimpleLinkedEdit ('VALUE' , 'null' );
161+ }
133162
134- // Insert indent before the child: or children: param.
135- var indent = utils.getLinePrefix (offset);
136- builder.write (indent);
163+ if (creation.isWidgetExpression) {
164+ // Insert a trailing comma after Flutter instance creation params.
165+ if (! hasTrailingComma) {
166+ builder.write (',' );
167+ } else if (insertBetweenParams) {
168+ builder.writeln (',' );
169+
170+ // Insert indent before the child: or children: param.
171+ var indent = utils.getLinePrefix (offset);
172+ builder.write (indent);
173+ }
137174 }
138- }
175+ });
139176 });
140- });
177+ }
141178 }
142179 }
143180}
181+
182+ extension < T > on Iterable <T > {
183+ bool containsAny (Iterable <T > values) {
184+ return values.any ((v) => contains (v));
185+ }
186+ }
187+
188+ extension on AnalysisError {
189+ bool sameRangeAs (AnalysisError other) {
190+ return offset == other.offset && length == other.length;
191+ }
192+ }
0 commit comments