Skip to content

Commit 1dd93dd

Browse files
srawlinsCommit Queue
authored and
Commit Queue
committed
analyzer: implement invalid_await_not_required_annotation warning
Work towards #46218 Change-Id: I41391cc317dec625f0b34afbf5f5a3402d8b08b4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/424261 Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent 8fb9578 commit 1dd93dd

File tree

7 files changed

+273
-2
lines changed

7 files changed

+273
-2
lines changed

pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3542,6 +3542,8 @@ WarningCode.INFERENCE_FAILURE_ON_UNTYPED_PARAMETER:
35423542
status: noFix
35433543
WarningCode.INVALID_ANNOTATION_TARGET:
35443544
status: hasFix
3545+
WarningCode.INVALID_AWAIT_NOT_REQUIRED_ANNOTATION:
3546+
status: needsFix
35453547
WarningCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT:
35463548
status: needsFix
35473549
notes: |-

pkg/analyzer/lib/src/error/annotation_verifier.dart

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ class AnnotationVerifier {
4444
}
4545
var parent = node.parent;
4646
if (element.isAwaitNotRequired) {
47-
// TODO(srawlins): Check that functions return Future and fields and
48-
// getters have a static type of Future.
47+
_checkAwaitNotRequired(node);
4948
} else if (element.isFactory) {
5049
_checkFactory(node);
5150
} else if (element.isInternal) {
@@ -72,6 +71,48 @@ class AnnotationVerifier {
7271
_checkKinds(node, parent, element);
7372
}
7473

74+
void _checkAwaitNotRequired(Annotation node) {
75+
void checkType(DartType? type, {AstNode? errorNode}) {
76+
if (type == null || type.isDartAsyncFuture || type.isDartAsyncFutureOr) {
77+
return;
78+
}
79+
80+
var element = type.element3;
81+
if (element is InterfaceElement2 &&
82+
element.allSupertypes.any((t) => t.isDartAsyncFuture)) {
83+
return;
84+
}
85+
86+
_errorReporter.atNode(
87+
errorNode ?? node.name,
88+
WarningCode.INVALID_AWAIT_NOT_REQUIRED_ANNOTATION,
89+
);
90+
}
91+
92+
var parent = node.parent;
93+
if (parent
94+
case MethodDeclaration(:var declaredFragment) ||
95+
FunctionDeclaration(:var declaredFragment)) {
96+
var type = declaredFragment?.element.type;
97+
if (type is FunctionType) {
98+
checkType(type.returnType);
99+
}
100+
} else if (parent case FieldDeclaration(:var fields)) {
101+
for (var field in fields.variables) {
102+
checkType(field.declaredFieldElement.type, errorNode: field);
103+
}
104+
} else if (parent case TopLevelVariableDeclaration(:var variables)) {
105+
for (var variable in variables.variables) {
106+
checkType(
107+
variable.declaredTopLevelVariableElement.type,
108+
errorNode: variable,
109+
);
110+
}
111+
} else {
112+
// Warning reported by `_checkKinds`.
113+
}
114+
}
115+
75116
/// Reports a warning at [node] if its parent is not a valid target for a
76117
/// `@factory` annotation.
77118
void _checkFactory(Annotation node) {

pkg/analyzer/lib/src/error/codes.g.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6640,6 +6640,13 @@ class WarningCode extends ErrorCode {
66406640
hasPublishedDocs: true,
66416641
);
66426642

6643+
/// No parameters.
6644+
static const WarningCode INVALID_AWAIT_NOT_REQUIRED_ANNOTATION = WarningCode(
6645+
'INVALID_AWAIT_NOT_REQUIRED_ANNOTATION',
6646+
"The annotation 'awaitNotRequired' can only be applied to a "
6647+
"Future-returning function, or a Future-typed field.",
6648+
);
6649+
66436650
/// Parameters:
66446651
/// 0: the name of the element
66456652
static const WarningCode INVALID_EXPORT_OF_INTERNAL_ELEMENT = WarningCode(

pkg/analyzer/lib/src/error/error_code_values.g.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,7 @@ const List<ErrorCode> errorCodeValues = [
10171017
WarningCode.INFERENCE_FAILURE_ON_UNINITIALIZED_VARIABLE,
10181018
WarningCode.INFERENCE_FAILURE_ON_UNTYPED_PARAMETER,
10191019
WarningCode.INVALID_ANNOTATION_TARGET,
1020+
WarningCode.INVALID_AWAIT_NOT_REQUIRED_ANNOTATION,
10201021
WarningCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT,
10211022
WarningCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT_INDIRECTLY,
10221023
WarningCode.INVALID_FACTORY_METHOD_DECL,

pkg/analyzer/messages.yaml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24156,6 +24156,36 @@ WarningCode:
2415624156
#### Common fixes
2415724157

2415824158
Remove the annotation from the declaration.
24159+
INVALID_AWAIT_NOT_REQUIRED_ANNOTATION:
24160+
problemMessage: "The annotation 'awaitNotRequired' can only be applied to a Future-returning function, or a Future-typed field."
24161+
hasPublishedDocs: false
24162+
comment: No parameters.
24163+
documentation: |-
24164+
#### Description
24165+
24166+
The analyzer produces this diagnostic when anything other than a
24167+
`Future`-returning function or a `Future`-typed field or top-level
24168+
variable is annotated with [`awaitNotRequired`][meta-awaitNotRequired].
24169+
24170+
#### Example
24171+
24172+
The following code produces this diagnostic because the annotation is on a
24173+
`void`-returning function:
24174+
24175+
```dart
24176+
import 'package:meta/meta.dart';
24177+
24178+
@[!awaitNotRequired!]
24179+
void f() {}
24180+
```
24181+
24182+
#### Common fixes
24183+
24184+
Remove the annotation:
24185+
24186+
```dart
24187+
void f() {}
24188+
```
2415924189
INVALID_EXPORT_OF_INTERNAL_ELEMENT:
2416024190
problemMessage: "The member '{0}' can't be exported as a part of a package's public API."
2416124191
correctionMessage: "Try using a hide clause to hide '{0}'."
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/src/error/codes.g.dart';
6+
import 'package:test_reflective_loader/test_reflective_loader.dart';
7+
8+
import '../dart/resolution/context_collection_resolution.dart';
9+
10+
void main() {
11+
defineReflectiveSuite(() {
12+
defineReflectiveTests(InvalidAwaitNotRequiredAnnotationTest);
13+
});
14+
}
15+
16+
@reflectiveTest
17+
class InvalidAwaitNotRequiredAnnotationTest extends PubPackageResolutionTest {
18+
@override
19+
void setUp() {
20+
super.setUp();
21+
writeTestPackageConfigWithMeta();
22+
}
23+
24+
test_invalid_field_inferredReturnType() async {
25+
await assertErrorsInCode(
26+
'''
27+
import 'package:meta/meta.dart';
28+
class C {
29+
@awaitNotRequired
30+
var x = 0;
31+
}
32+
''',
33+
[error(WarningCode.INVALID_AWAIT_NOT_REQUIRED_ANNOTATION, 69, 5)],
34+
);
35+
}
36+
37+
test_invalid_field_intReturnType() async {
38+
await assertErrorsInCode(
39+
'''
40+
import 'package:meta/meta.dart';
41+
class C {
42+
@awaitNotRequired
43+
int x = 0;
44+
}
45+
''',
46+
[error(WarningCode.INVALID_AWAIT_NOT_REQUIRED_ANNOTATION, 69, 5)],
47+
);
48+
}
49+
50+
test_invalid_function_voidReturnType() async {
51+
await assertErrorsInCode(
52+
'''
53+
import 'package:meta/meta.dart';
54+
@awaitNotRequired
55+
void f() {}
56+
''',
57+
[error(WarningCode.INVALID_AWAIT_NOT_REQUIRED_ANNOTATION, 34, 16)],
58+
);
59+
}
60+
61+
test_invalid_method_voidReturnType() async {
62+
await assertErrorsInCode(
63+
'''
64+
import 'package:meta/meta.dart';
65+
class C {
66+
@awaitNotRequired
67+
void f() {}
68+
}
69+
''',
70+
[error(WarningCode.INVALID_AWAIT_NOT_REQUIRED_ANNOTATION, 46, 16)],
71+
);
72+
}
73+
74+
test_invalid_method_voidReturnType_inheritedReturnType() async {
75+
await assertErrorsInCode(
76+
'''
77+
import 'package:meta/meta.dart';
78+
class C {
79+
void f() {}
80+
}
81+
class D extends C {
82+
@awaitNotRequired
83+
@override
84+
f() {}
85+
}
86+
''',
87+
[error(WarningCode.INVALID_AWAIT_NOT_REQUIRED_ANNOTATION, 82, 16)],
88+
);
89+
}
90+
91+
test_invalid_topLevelVariable_intReturnType() async {
92+
await assertErrorsInCode(
93+
'''
94+
import 'package:meta/meta.dart';
95+
@awaitNotRequired
96+
int x = 0;
97+
''',
98+
[error(WarningCode.INVALID_AWAIT_NOT_REQUIRED_ANNOTATION, 55, 5)],
99+
);
100+
}
101+
102+
test_valid_field_futureReturnType() async {
103+
await assertNoErrorsInCode('''
104+
import 'package:meta/meta.dart';
105+
class C {
106+
@awaitNotRequired
107+
Future<int> x = Future.value(7);
108+
}
109+
''');
110+
}
111+
112+
test_valid_field_futureReturnType_inferred() async {
113+
await assertNoErrorsInCode('''
114+
import 'package:meta/meta.dart';
115+
class C {
116+
@awaitNotRequired
117+
var x = Future.value(7);
118+
}
119+
''');
120+
}
121+
122+
test_valid_function_futureOrReturnType() async {
123+
await assertNoErrorsInCode('''
124+
import 'dart:async';
125+
import 'package:meta/meta.dart';
126+
@awaitNotRequired
127+
FutureOr<int> f() => 7;
128+
''');
129+
}
130+
131+
test_valid_function_futureReturnType() async {
132+
await assertNoErrorsInCode('''
133+
import 'package:meta/meta.dart';
134+
@awaitNotRequired
135+
Future<int> f() => Future.value(7);
136+
''');
137+
}
138+
139+
test_valid_function_futureReturnType_nullable() async {
140+
await assertNoErrorsInCode('''
141+
import 'package:meta/meta.dart';
142+
@awaitNotRequired
143+
Future<int>? f() => null;
144+
''');
145+
}
146+
147+
test_valid_function_futureSubtypeReturnType() async {
148+
await assertNoErrorsInCode('''
149+
import 'package:meta/meta.dart';
150+
@awaitNotRequired
151+
external Future2<int> f();
152+
abstract class Future2<T> implements Future<T> {}
153+
''');
154+
}
155+
156+
test_valid_method_futureReturnType() async {
157+
await assertNoErrorsInCode('''
158+
import 'package:meta/meta.dart';
159+
class C {
160+
@awaitNotRequired
161+
Future<int> f() => Future.value(7);
162+
}
163+
''');
164+
}
165+
166+
test_valid_method_futureReturnType_inheritedReturnType() async {
167+
await assertNoErrorsInCode('''
168+
import 'package:meta/meta.dart';
169+
class C {
170+
Future<int> f() => Future.value(0);
171+
}
172+
class D extends C {
173+
@awaitNotRequired
174+
@override
175+
f() => Future.value(7);
176+
}
177+
''');
178+
}
179+
180+
test_valid_topLevelVariable_futureReturnType() async {
181+
await assertNoErrorsInCode('''
182+
import 'package:meta/meta.dart';
183+
@awaitNotRequired
184+
Future<int> x = Future.value(7);
185+
''');
186+
}
187+
}

pkg/analyzer/test/src/diagnostics/test_all.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,8 @@ import 'invalid_annotation_from_deferred_library_test.dart'
432432
import 'invalid_annotation_target_test.dart' as invalid_annotation_target;
433433
import 'invalid_annotation_test.dart' as invalid_annotation;
434434
import 'invalid_assignment_test.dart' as invalid_assignment;
435+
import 'invalid_await_not_required_annotation_test.dart'
436+
as await_not_required_annotation;
435437
import 'invalid_constant_test.dart' as invalid_constant;
436438
import 'invalid_constructor_name_test.dart' as invalid_constructor_name;
437439
import 'invalid_do_not_submit_test.dart' as invalid_do_not_submit;
@@ -1213,6 +1215,7 @@ main() {
12131215
invalid_annotation_from_deferred_library.main();
12141216
invalid_annotation_target.main();
12151217
invalid_assignment.main();
1218+
await_not_required_annotation.main();
12161219
invalid_constant.main();
12171220
invalid_constructor_name.main();
12181221
invalid_do_not_submit.main();

0 commit comments

Comments
 (0)