Skip to content

Commit 0f2787c

Browse files
pqcommit-bot@chromium.org
authored andcommitted
validation of @nonvirtual use
see: #34378 Change-Id: I0fe67c56bf7c08f211b6bf89d84c5495e32601a7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/122742 Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Phil Quitslund <[email protected]>
1 parent 4d1bc04 commit 0f2787c

File tree

8 files changed

+337
-0
lines changed

8 files changed

+337
-0
lines changed

pkg/analyzer/lib/dart/element/element.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,10 @@ abstract class ElementAnnotation implements ConstantEvaluationTarget {
706706
/// overriding methods to call super.
707707
bool get isMustCallSuper;
708708

709+
/// Return `true` if this annotation marks the associated member as being
710+
/// non-virtual.
711+
bool get isNonVirtual;
712+
709713
/// Return `true` if this annotation marks the associated type as
710714
/// having "optional" type arguments.
711715
bool get isOptionalTypeArgs;

pkg/analyzer/lib/error/error.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ const List<ErrorCode> errorCodeValues = const [
370370
HintCode.INVALID_FACTORY_METHOD_IMPL,
371371
HintCode.INVALID_IMMUTABLE_ANNOTATION,
372372
HintCode.INVALID_LITERAL_ANNOTATION,
373+
HintCode.INVALID_NON_VIRTUAL_ANNOTATION,
373374
HintCode.INVALID_REQUIRED_NAMED_PARAM,
374375
HintCode.INVALID_REQUIRED_OPTIONAL_POSITIONAL_PARAM,
375376
// ignore: deprecated_member_use_from_same_package

pkg/analyzer/lib/src/dart/element/element.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2431,6 +2431,9 @@ class ElementAnnotationImpl implements ElementAnnotation {
24312431
/// annotations.
24322432
static String _NG_META_LIB_NAME = "angular.meta";
24332433

2434+
/// The name of the top-level variable used to mark a member as being nonVirtual.
2435+
static String _NON_VIRTUAL_VARIABLE_NAME = "nonVirtual";
2436+
24342437
/// The name of the top-level variable used to mark a method as being expected
24352438
/// to override an inherited method.
24362439
static String _OVERRIDE_VARIABLE_NAME = "override";
@@ -2554,6 +2557,12 @@ class ElementAnnotationImpl implements ElementAnnotation {
25542557
element.name == _MUST_CALL_SUPER_VARIABLE_NAME &&
25552558
element.library?.name == _META_LIB_NAME;
25562559

2560+
@override
2561+
bool get isNonVirtual =>
2562+
element is PropertyAccessorElement &&
2563+
element.name == _NON_VIRTUAL_VARIABLE_NAME &&
2564+
element.library?.name == _META_LIB_NAME;
2565+
25572566
@override
25582567
bool get isOptionalTypeArgs =>
25592568
element is PropertyAccessorElement &&

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,19 @@ class HintCode extends AnalyzerErrorCode {
356356
"Only const constructors can have the `@literal` annotation.",
357357
hasPublishedDocs: true);
358358

359+
/**
360+
* This hint is generated anywhere where `@nonVirtual` annotates something
361+
* other than a non-abstract instance member in a class or mixin.
362+
*
363+
* Parameters:
364+
* 0: the name of the member
365+
*/
366+
static const HintCode INVALID_NON_VIRTUAL_ANNOTATION = const HintCode(
367+
'INVALID_NON_VIRTUAL_ANNOTATION',
368+
"The member '{0}' is annotated with '@nonVirtual' but only concrete "
369+
"instance members of classes or mixins can be annotated with it.",
370+
correction: "Remove @nonVirtual.");
371+
359372
/**
360373
* This hint is generated anywhere where `@required` annotates a named
361374
* parameter with a default value.

pkg/analyzer/lib/src/generated/resolver.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,27 @@ class BestPracticesVerifier extends RecursiveAstVisitor<void> {
155155
_errorReporter
156156
.reportErrorForNode(HintCode.INVALID_LITERAL_ANNOTATION, node, []);
157157
}
158+
} else if (element?.isNonVirtual == true) {
159+
if (parent is FieldDeclaration) {
160+
if (parent.isStatic) {
161+
_errorReporter.reportErrorForNode(
162+
HintCode.INVALID_NON_VIRTUAL_ANNOTATION,
163+
node,
164+
[node.element.name]);
165+
}
166+
} else if (parent is MethodDeclaration) {
167+
if (parent.parent is ExtensionDeclaration ||
168+
parent.isStatic ||
169+
parent.isAbstract) {
170+
_errorReporter.reportErrorForNode(
171+
HintCode.INVALID_NON_VIRTUAL_ANNOTATION,
172+
node,
173+
[node.element.name]);
174+
}
175+
} else {
176+
_errorReporter.reportErrorForNode(
177+
HintCode.INVALID_NON_VIRTUAL_ANNOTATION, node, [node.element.name]);
178+
}
158179
} else if (element?.isSealed == true) {
159180
if (!(parent is ClassDeclaration || parent is ClassTypeAlias)) {
160181
_errorReporter.reportErrorForNode(

pkg/analyzer/lib/src/test_utilities/package_mixin.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const _Factory factory = const _Factory();
3333
const Immutable immutable = const Immutable();
3434
const _Literal literal = const _Literal();
3535
const _MustCallSuper mustCallSuper = const _MustCallSuper();
36+
const _NonVirtual nonVirtual = const _NonVirtual();
3637
const _OptionalTypeArgs optionalTypeArgs = const _OptionalTypeArgs();
3738
const _Protected protected = const _Protected();
3839
const Required required = const Required();
@@ -55,6 +56,9 @@ class _Literal {
5556
class _MustCallSuper {
5657
const _MustCallSuper();
5758
}
59+
class _NonVirtual {
60+
const _NonVirtual();
61+
}
5862
class _OptionalTypeArgs {
5963
const _OptionalTypeArgs();
6064
}
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
// Copyright (c) 2019, 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.dart';
6+
import 'package:analyzer/src/test_utilities/package_mixin.dart';
7+
import 'package:test_reflective_loader/test_reflective_loader.dart';
8+
9+
import '../dart/resolution/driver_resolution.dart';
10+
11+
main() {
12+
defineReflectiveSuite(() {
13+
defineReflectiveTests(InvalidNonVirtualAnnotationTest);
14+
});
15+
}
16+
17+
@reflectiveTest
18+
class InvalidNonVirtualAnnotationTest extends DriverResolutionTest
19+
with PackageMixin {
20+
setUp() {
21+
super.setUp();
22+
addMetaPackage();
23+
}
24+
25+
test_class() async {
26+
await assertErrorsInCode(r'''
27+
import 'package:meta/meta.dart';
28+
29+
@nonVirtual
30+
class C {}
31+
''', [
32+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 34, 11),
33+
]);
34+
}
35+
36+
test_class_abstract_member() async {
37+
await assertErrorsInCode(r'''
38+
import 'package:meta/meta.dart';
39+
40+
abstract class C {
41+
@nonVirtual
42+
void m();
43+
}
44+
''', [
45+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 55, 11),
46+
]);
47+
}
48+
49+
test_class_getter() async {
50+
await assertNoErrorsInCode(r'''
51+
import 'package:meta/meta.dart';
52+
53+
class C {
54+
@nonVirtual
55+
int get g => 0;
56+
}
57+
''');
58+
}
59+
60+
test_class_instance_field() async {
61+
await assertNoErrorsInCode(r'''
62+
import 'package:meta/meta.dart';
63+
64+
class C {
65+
@nonVirtual
66+
int f;
67+
}
68+
''');
69+
}
70+
71+
test_class_instance_member() async {
72+
await assertNoErrorsInCode(r'''
73+
import 'package:meta/meta.dart';
74+
75+
class C {
76+
@nonVirtual
77+
void m() {
78+
}
79+
}
80+
''');
81+
}
82+
83+
test_class_setter() async {
84+
await assertNoErrorsInCode(r'''
85+
import 'package:meta/meta.dart';
86+
87+
class C {
88+
@nonVirtual
89+
set s(int v) {}
90+
}
91+
''');
92+
}
93+
94+
test_class_static_field() async {
95+
await assertErrorsInCode(r'''
96+
import 'package:meta/meta.dart';
97+
98+
class C {
99+
@nonVirtual
100+
static int f;
101+
}
102+
''', [
103+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 47, 11),
104+
]);
105+
}
106+
107+
test_class_static_method() async {
108+
await assertErrorsInCode(r'''
109+
import 'package:meta/meta.dart';
110+
111+
class C {
112+
@nonVirtual
113+
static void m() {}
114+
}
115+
''', [
116+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 47, 11),
117+
]);
118+
}
119+
120+
test_enum() async {
121+
await assertErrorsInCode(r'''
122+
import 'package:meta/meta.dart';
123+
124+
@nonVirtual
125+
enum E {
126+
a, b, c
127+
}
128+
''', [
129+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 34, 11),
130+
]);
131+
}
132+
133+
test_enum_constant() async {
134+
await assertErrorsInCode(r'''
135+
import 'package:meta/meta.dart';
136+
137+
enum E {
138+
@nonVirtual
139+
a,
140+
b, c
141+
}
142+
''', [
143+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 45, 11),
144+
]);
145+
}
146+
147+
test_extension() async {
148+
await assertErrorsInCode(r'''
149+
import 'package:meta/meta.dart';
150+
151+
@nonVirtual
152+
extension E on Object {}
153+
''', [
154+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 34, 11),
155+
]);
156+
}
157+
158+
test_extension_member() async {
159+
await assertErrorsInCode(r'''
160+
import 'package:meta/meta.dart';
161+
162+
extension E on Object {
163+
@nonVirtual
164+
void m() {}
165+
}
166+
''', [
167+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 61, 11),
168+
]);
169+
}
170+
171+
test_import() async {
172+
await assertErrorsInCode(r'''
173+
@nonVirtual
174+
import 'package:meta/meta.dart';
175+
''', [
176+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 0, 11),
177+
]);
178+
}
179+
180+
test_mixin() async {
181+
await assertErrorsInCode(r'''
182+
import 'package:meta/meta.dart';
183+
184+
@nonVirtual
185+
mixin M {}
186+
''', [
187+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 34, 11),
188+
]);
189+
}
190+
191+
test_mixin_instance_member() async {
192+
await assertNoErrorsInCode(r'''
193+
import 'package:meta/meta.dart';
194+
195+
mixin M {
196+
@nonVirtual
197+
void m() {}
198+
}
199+
''');
200+
}
201+
202+
test_mixin_static_field() async {
203+
await assertErrorsInCode(r'''
204+
import 'package:meta/meta.dart';
205+
206+
mixin M {
207+
@nonVirtual
208+
static int f;
209+
}
210+
''', [
211+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 46, 11),
212+
]);
213+
}
214+
215+
test_mixin_static_method() async {
216+
await assertErrorsInCode(r'''
217+
import 'package:meta/meta.dart';
218+
219+
mixin M {
220+
@nonVirtual
221+
static void m() {}
222+
}
223+
''', [
224+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 46, 11),
225+
]);
226+
}
227+
228+
test_top_level_function() async {
229+
await assertErrorsInCode(r'''
230+
import 'package:meta/meta.dart';
231+
232+
@nonVirtual
233+
m() {}
234+
''', [
235+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 34, 11),
236+
]);
237+
}
238+
239+
test_top_level_getter() async {
240+
await assertErrorsInCode(r'''
241+
import 'package:meta/meta.dart';
242+
243+
@nonVirtual
244+
int get g => 0;
245+
''', [
246+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 34, 11),
247+
]);
248+
}
249+
250+
test_top_level_setter() async {
251+
await assertErrorsInCode(r'''
252+
import 'package:meta/meta.dart';
253+
254+
@nonVirtual
255+
set s(int v) {}
256+
''', [
257+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 34, 11),
258+
]);
259+
}
260+
261+
test_top_level_var() async {
262+
await assertErrorsInCode(r'''
263+
import 'package:meta/meta.dart';
264+
265+
@nonVirtual
266+
int x;
267+
''', [
268+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 34, 11),
269+
]);
270+
}
271+
272+
test_typedef() async {
273+
await assertErrorsInCode(r'''
274+
import 'package:meta/meta.dart';
275+
276+
@nonVirtual
277+
typedef bool predicate(Object o);
278+
''', [
279+
error(HintCode.INVALID_NON_VIRTUAL_ANNOTATION, 34, 11),
280+
]);
281+
}
282+
}

0 commit comments

Comments
 (0)