Skip to content

Commit 88113a2

Browse files
FMorschelCommit Queue
authored andcommitted
Fix errors when type is future of required type
[email protected] Fixes #50461 Change-Id: I03664df4f3fe3464344079f37a2ea00fa881fffc Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/388040 Reviewed-by: Brian Wilkerson <[email protected]> Auto-Submit: Felipe Morschel <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]> Reviewed-by: Samuel Rawlins <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent de8aa39 commit 88113a2

File tree

3 files changed

+217
-15
lines changed

3 files changed

+217
-15
lines changed

pkg/analysis_server/lib/src/services/correction/dart/add_await.dart

Lines changed: 81 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,21 @@
44

55
import 'package:analysis_server/src/services/correction/fix.dart';
66
import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
7-
import 'package:analyzer/dart/ast/ast.dart';
87
import 'package:analyzer/dart/element/type.dart';
8+
import 'package:analyzer/src/dart/ast/ast.dart';
99
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
1010
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
1111

1212
class AddAwait extends ResolvedCorrectionProducer {
1313
/// The kind of correction to be made.
1414
final _CorrectionKind _correctionKind;
1515

16+
AddAwait.argumentType({required super.context})
17+
: _correctionKind = _CorrectionKind.argumentType;
18+
19+
AddAwait.assignment({required super.context})
20+
: _correctionKind = _CorrectionKind.invalidAssignment;
21+
1622
AddAwait.nonBool({required super.context})
1723
: _correctionKind = _CorrectionKind.nonBool;
1824

@@ -33,13 +39,23 @@ class AddAwait extends ResolvedCorrectionProducer {
3339

3440
@override
3541
Future<void> compute(ChangeBuilder builder) async {
36-
if (_correctionKind == _CorrectionKind.unawaited) {
37-
if (node.parent is CascadeExpression) {
38-
return;
39-
}
40-
await _addAwait(builder);
41-
} else if (_correctionKind == _CorrectionKind.nonBool) {
42-
await _computeNonBool(builder);
42+
switch (_correctionKind) {
43+
case _CorrectionKind.argumentType:
44+
if (_isValidType(expectedTypeFromExp: (exp) => exp.argumentType)) {
45+
await _addAwait(builder);
46+
}
47+
case _CorrectionKind.invalidAssignment:
48+
if (_isValidType(expectedTypeFromExp: (exp) => exp.assignmentType)) {
49+
await _addAwait(builder);
50+
}
51+
case _CorrectionKind.nonBool:
52+
if (_isValidType(isValid: (type) => type.isDartCoreBool)) {
53+
await _addAwait(builder);
54+
}
55+
case _CorrectionKind.unawaited:
56+
if (node.parent is! CascadeExpression) {
57+
await _addAwait(builder);
58+
}
4359
}
4460
}
4561

@@ -49,21 +65,71 @@ class AddAwait extends ResolvedCorrectionProducer {
4965
});
5066
}
5167

52-
Future<void> _computeNonBool(ChangeBuilder builder) async {
68+
/// If the expression is not a future it is not valid.
69+
/// If the expression is a future, we check if the type of the future is
70+
/// assignable to the expected type using either [isValid] or
71+
/// [expectedTypeFromExp].
72+
/// If [isValid] is provided, it is used to check if the future's type is
73+
/// valid.
74+
/// If [expectedTypeFromExp] is provided the return type should be the
75+
/// underlying expected type of the expression. The future type will then be
76+
/// checked if it is assignable to it.
77+
bool _isValidType(
78+
{DartType? Function(Expression expr)? expectedTypeFromExp,
79+
bool Function(DartType type)? isValid}) {
80+
assert((isValid != null) ^ (expectedTypeFromExp != null),
81+
'Use either isValid or expectedTypeFromExp, but not both');
5382
var expr = node;
54-
if (expr is! Expression) return;
83+
if (expr is! Expression) {
84+
return false;
85+
}
5586
var staticType = expr.staticType;
56-
if (staticType is! ParameterizedType) return;
87+
if (staticType is! InterfaceType) {
88+
return false;
89+
}
90+
if (!staticType.isDartAsyncFuture) {
91+
return false;
92+
}
93+
94+
var typeArg = expectedTypeFromExp?.call(expr);
95+
if (typeArg == null && isValid == null) {
96+
return false;
97+
}
5798

58-
if (staticType.isDartAsyncFuture &&
59-
staticType.typeArguments.firstOrNull?.isDartCoreBool == true) {
60-
await _addAwait(builder);
99+
var type = staticType.typeArguments.first;
100+
if (typeArg == null) {
101+
return isValid!(type);
102+
} else {
103+
return typeSystem.isAssignableTo(type, typeArg);
61104
}
62105
}
63106
}
64107

65108
/// The kinds of corrections supported by [AddAwait].
66109
enum _CorrectionKind {
67-
unawaited,
110+
argumentType,
111+
invalidAssignment,
68112
nonBool,
113+
unawaited,
114+
}
115+
116+
extension on Expression {
117+
DartType? get argumentType {
118+
var expr = this;
119+
if (parent case NamedExpression named) {
120+
expr = named;
121+
}
122+
if (expr.parent is ArgumentList) {
123+
return expr.correspondingParameter?.type;
124+
}
125+
return null;
126+
}
127+
128+
DartType? get assignmentType {
129+
return switch (parent) {
130+
VariableDeclarationImpl variableDeclaration => variableDeclaration.type,
131+
AssignmentExpression assignment => assignment.writeType,
132+
_ => null
133+
};
134+
}
69135
}

pkg/analysis_server/lib/src/services/correction/fix_internal.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,7 @@ final _builtInNonLintProducers = <ErrorCode, List<ProducerGenerator>>{
941941
AddExplicitCast.new,
942942
AddNullCheck.new,
943943
WrapInText.new,
944+
AddAwait.argumentType,
944945
],
945946
CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT: [
946947
AddAsync.new,
@@ -1104,6 +1105,7 @@ final _builtInNonLintProducers = <ErrorCode, List<ProducerGenerator>>{
11041105
AddNullCheck.new,
11051106
ChangeTypeAnnotation.new,
11061107
MakeVariableNullable.new,
1108+
AddAwait.assignment,
11071109
],
11081110
CompileTimeErrorCode.INVALID_CONSTANT: [
11091111
RemoveConst.new,

pkg/analysis_server/test/src/services/correction/fix/add_await_test.dart

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:analysis_server/src/services/correction/fix.dart';
66
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
7+
import 'package:analyzer/src/error/codes.dart';
78
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
89
import 'package:linter/src/lint_names.dart';
910
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -13,6 +14,7 @@ import 'fix_processor.dart';
1314
void main() {
1415
defineReflectiveSuite(() {
1516
defineReflectiveTests(AddAwaitTest);
17+
defineReflectiveTests(AddAwaitTestArgumentAndAssignment);
1618
});
1719
}
1820

@@ -104,3 +106,135 @@ Future<void> f() async {
104106
await assertNoFix();
105107
}
106108
}
109+
110+
@reflectiveTest
111+
class AddAwaitTestArgumentAndAssignment extends FixProcessorTest {
112+
@override
113+
FixKind get kind => DartFixKind.ADD_AWAIT;
114+
115+
Future<void> test_stringNamedParameter_futureInt() async {
116+
await resolveTestCode('''
117+
void foo({required String s}) {}
118+
119+
Future<int> bar() async => 0;
120+
121+
void baz() {
122+
foo(s: bar());
123+
}
124+
''');
125+
await assertNoFix();
126+
}
127+
128+
Future<void> test_stringNamedParameter_futureString() async {
129+
await resolveTestCode('''
130+
void foo({required String s}) {}
131+
132+
Future<String> bar() async => '';
133+
134+
void baz() {
135+
foo(s: bar());
136+
}
137+
''');
138+
await assertHasFix('''
139+
void foo({required String s}) {}
140+
141+
Future<String> bar() async => '';
142+
143+
void baz() {
144+
foo(s: await bar());
145+
}
146+
''');
147+
}
148+
149+
Future<void> test_stringParameter_futureInt() async {
150+
await resolveTestCode('''
151+
void foo(String s) {}
152+
153+
Future<int> bar() async => 0;
154+
155+
void baz() {
156+
foo(bar());
157+
}
158+
''');
159+
await assertNoFix();
160+
}
161+
162+
Future<void> test_stringParameter_futureString() async {
163+
await resolveTestCode('''
164+
void foo(String s) {}
165+
166+
Future<String> bar() async => '';
167+
168+
void baz() {
169+
foo(bar());
170+
}
171+
''');
172+
await assertHasFix('''
173+
void foo(String s) {}
174+
175+
Future<String> bar() async => '';
176+
177+
void baz() {
178+
foo(await bar());
179+
}
180+
''');
181+
}
182+
183+
Future<void> test_stringVariable_assignment_futureString() async {
184+
await resolveTestCode('''
185+
Future<String> bar() async => '';
186+
187+
void baz() {
188+
String? variable;
189+
variable = bar();
190+
}
191+
''');
192+
await assertHasFix(
193+
'''
194+
Future<String> bar() async => '';
195+
196+
void baz() {
197+
String? variable;
198+
variable = await bar();
199+
}
200+
''',
201+
errorFilter: (error) =>
202+
error.errorCode == CompileTimeErrorCode.INVALID_ASSIGNMENT,
203+
);
204+
}
205+
206+
Future<void> test_stringVariable_futureInt() async {
207+
await resolveTestCode('''
208+
Future<int> bar() async => 0;
209+
210+
void baz() {
211+
String variable = bar();
212+
}
213+
''');
214+
await assertNoFix(
215+
errorFilter: (error) =>
216+
error.errorCode == CompileTimeErrorCode.INVALID_ASSIGNMENT,
217+
);
218+
}
219+
220+
Future<void> test_stringVariable_futureString() async {
221+
await resolveTestCode('''
222+
Future<String> bar() async => '';
223+
224+
void baz() {
225+
String variable = bar();
226+
}
227+
''');
228+
await assertHasFix(
229+
'''
230+
Future<String> bar() async => '';
231+
232+
void baz() {
233+
String variable = await bar();
234+
}
235+
''',
236+
errorFilter: (error) =>
237+
error.errorCode == CompileTimeErrorCode.INVALID_ASSIGNMENT,
238+
);
239+
}
240+
}

0 commit comments

Comments
 (0)