Skip to content

Commit 4800d2c

Browse files
srawlinsCommit Queue
authored andcommitted
Analyzer warnings: add support for @Deprecation.mixin
Work towards #60504 * The annotation causes certain usage to generate a warning (+ tests). * Possible fixes for this new warning are offered (+ tests). * Placing the annotation on an invalid element generates a different warning (+ tests). Change-Id: I70dc502aa7c26feab9b8e3de8bb7b59bdce8fb66 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/448220 Reviewed-by: Konstantin Shcheglov <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent ced0ef4 commit 4800d2c

File tree

10 files changed

+311
-1
lines changed

10 files changed

+311
-1
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3485,6 +3485,8 @@ WarningCode.DEPRECATED_INSTANTIATE:
34853485
notes: |-
34863486
The only fix would be to delete the whole instance creation expression, or
34873487
follow the directions in the deprecation message.
3488+
WarningCode.DEPRECATED_MIXIN:
3489+
status: needsFix
34883490
WarningCode.DEPRECATED_MIXIN_FUNCTION:
34893491
status: needsFix
34903492
notes: |-
@@ -3579,6 +3581,9 @@ WarningCode.INVALID_DEPRECATED_IMPLEMENT_ANNOTATION:
35793581
WarningCode.INVALID_DEPRECATED_INSTANTIATE_ANNOTATION:
35803582
status: needsFix
35813583
notes: The fix is to remove the annotation.
3584+
WarningCode.INVALID_DEPRECATED_MIXIN_ANNOTATION:
3585+
status: needsFix
3586+
notes: The fix is to remove the annotation.
35823587
WarningCode.INVALID_DEPRECATED_SUBCLASS_ANNOTATION:
35833588
status: needsFix
35843589
notes: The fix is to remove the annotation.

pkg/analyzer/lib/src/diagnostic/diagnostic_code_values.g.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,7 @@ const List<DiagnosticCode> diagnosticCodeValues = [
987987
WarningCode.deprecatedImplement,
988988
WarningCode.deprecatedImplementsFunction,
989989
WarningCode.deprecatedInstantiate,
990+
WarningCode.deprecatedMixin,
990991
WarningCode.deprecatedMixinFunction,
991992
WarningCode.deprecatedNewInCommentReference,
992993
WarningCode.deprecatedSubclass,
@@ -1023,6 +1024,7 @@ const List<DiagnosticCode> diagnosticCodeValues = [
10231024
WarningCode.invalidDeprecatedExtendAnnotation,
10241025
WarningCode.invalidDeprecatedImplementAnnotation,
10251026
WarningCode.invalidDeprecatedInstantiateAnnotation,
1027+
WarningCode.invalidDeprecatedMixinAnnotation,
10261028
WarningCode.invalidDeprecatedSubclassAnnotation,
10271029
WarningCode.invalidExportOfInternalElement,
10281030
WarningCode.invalidExportOfInternalElementIndirectly,

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ class AnnotationVerifier {
136136
return;
137137
}
138138

139+
if (kind == 'mixin') {
140+
_checkDeprecatedMixin(node, node.parent);
141+
return;
142+
}
143+
139144
if (kind == 'subclass') {
140145
_checkDeprecatedSubclass(node, node.parent);
141146
return;
@@ -219,6 +224,15 @@ class AnnotationVerifier {
219224
);
220225
}
221226

227+
void _checkDeprecatedMixin(Annotation node, AstNode parent) {
228+
if (parent is ClassDeclaration && parent.mixinKeyword != null) return;
229+
230+
_diagnosticReporter.atNode(
231+
node.name,
232+
WarningCode.invalidDeprecatedMixinAnnotation,
233+
);
234+
}
235+
222236
void _checkDeprecatedSubclass(Annotation node, AstNode parent) {
223237
Element? declaredElement;
224238
if (parent

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10872,6 +10872,19 @@ class WarningCode extends DiagnosticCodeWithExpectedTypes {
1087210872
expectedTypes: [ExpectedType.object],
1087310873
);
1087410874

10875+
/// Parameters:
10876+
/// Object typeName: the name of the type
10877+
static const WarningTemplate<
10878+
LocatableDiagnostic Function({required Object typeName})
10879+
>
10880+
deprecatedMixin = WarningTemplate(
10881+
'DEPRECATED_MIXIN',
10882+
"Mixing in '{0}' is deprecated.",
10883+
correctionMessage: "Try removing '{0}' from the 'with' clause.",
10884+
withArguments: _withArgumentsDeprecatedMixin,
10885+
expectedTypes: [ExpectedType.object],
10886+
);
10887+
1087510888
/// No parameters.
1087610889
static const WarningWithoutArguments deprecatedMixinFunction =
1087710890
WarningWithoutArguments(
@@ -11371,6 +11384,18 @@ class WarningCode extends DiagnosticCodeWithExpectedTypes {
1137111384
expectedTypes: [],
1137211385
);
1137311386

11387+
/// This warning is generated anywhere where `@Deprecated.mixin` annotates
11388+
/// something other than a mixin class.
11389+
///
11390+
/// No parameters.
11391+
static const WarningWithoutArguments invalidDeprecatedMixinAnnotation =
11392+
WarningWithoutArguments(
11393+
'INVALID_DEPRECATED_MIXIN_ANNOTATION',
11394+
"The annotation '@Deprecated.mixin' can only be applied to classes.",
11395+
correctionMessage: "Try removing the '@Deprecated.mixin' annotation.",
11396+
expectedTypes: [],
11397+
);
11398+
1137411399
/// No parameters.
1137511400
static const WarningWithoutArguments
1137611401
invalidDeprecatedSubclassAnnotation = WarningWithoutArguments(
@@ -12958,6 +12983,12 @@ class WarningCode extends DiagnosticCodeWithExpectedTypes {
1295812983
return LocatableDiagnosticImpl(deprecatedInstantiate, [p0]);
1295912984
}
1296012985

12986+
static LocatableDiagnostic _withArgumentsDeprecatedMixin({
12987+
required Object typeName,
12988+
}) {
12989+
return LocatableDiagnosticImpl(deprecatedMixin, [typeName]);
12990+
}
12991+
1296112992
static LocatableDiagnostic _withArgumentsDeprecatedSubclass({
1296212993
required Object p0,
1296312994
}) {

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ class DeprecatedFunctionalityVerifier {
2121
void classDeclaration(ClassDeclaration node) {
2222
_checkForDeprecatedExtend(node.extendsClause?.superclass);
2323
_checkForDeprecatedImplement(node.implementsClause?.interfaces);
24+
_checkForDeprecatedMixin(node.withClause);
2425
_checkForDeprecatedSubclass(node.withClause?.mixinTypes);
2526
}
2627

2728
void classTypeAlias(ClassTypeAlias node) {
2829
_checkForDeprecatedExtend(node.superclass);
2930
_checkForDeprecatedImplement(node.implementsClause?.interfaces);
31+
_checkForDeprecatedMixin(node.withClause);
3032
}
3133

3234
void constructorName(ConstructorName node) {
@@ -43,6 +45,7 @@ class DeprecatedFunctionalityVerifier {
4345

4446
void enumDeclaration(EnumDeclaration node) {
4547
_checkForDeprecatedImplement(node.implementsClause?.interfaces);
48+
_checkForDeprecatedMixin(node.withClause);
4649
}
4750

4851
void mixinDeclaration(MixinDeclaration node) {
@@ -98,6 +101,22 @@ class DeprecatedFunctionalityVerifier {
98101
}
99102
}
100103

104+
void _checkForDeprecatedMixin(WithClause? node) {
105+
if (node == null) return;
106+
for (var mixin in node.mixinTypes) {
107+
var element = mixin.type?.element;
108+
if (element is! InterfaceElement) continue;
109+
if (element.library == _currentLibrary) continue;
110+
if (element.isDeprecatedWithKind('mixin')) {
111+
_diagnosticReporter.atNode(
112+
mixin,
113+
WarningCode.deprecatedMixin,
114+
arguments: [element.name!],
115+
);
116+
}
117+
}
118+
}
119+
101120
void _checkForDeprecatedSubclass(List<NamedType>? namedTypes) {
102121
if (namedTypes == null) return;
103122
for (var namedType in namedTypes) {

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,10 +325,12 @@ class Deprecated extends Object {
325325
: _kind = _DeprecationKind.subclass;
326326
const Deprecated.instantiate([this.message = "next release"])
327327
: _kind = _DeprecationKind.instantiate;
328+
const Deprecated.mixin([this.message = "next release"])
329+
: _kind = _DeprecationKind.mixin;
328330
}
329331
330332
enum _DeprecationKind {
331-
use, implement, extend, subclass, instantiate;
333+
use, implement, extend, subclass, instantiate, mixin;
332334
}
333335
334336
class pragma {

pkg/analyzer/messages.yaml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24088,6 +24088,48 @@ WarningCode:
2408824088
#### Common fixes
2408924089

2409024090
Follow any directions found in the `Deprecation.instantiate` annotation.
24091+
DEPRECATED_MIXIN:
24092+
parameters:
24093+
Object typeName: the name of the type
24094+
problemMessage: "Mixing in '#typeName' is deprecated."
24095+
correctionMessage: Try removing '#typeName' from the 'with' clause.
24096+
hasPublishedDocs: false
24097+
documentation: |-
24098+
#### Description
24099+
24100+
The analyzer produces this diagnostic when a mixin class annotated with
24101+
`@Deprecated.mixin` is used in the `with` clause of a class or enum
24102+
declaration. This annotation indicates that the ability for classes
24103+
to mixin the annotated mixin class is deprecated, and will soon be
24104+
removed, perhaps by removing the `mixin` class modifier.
24105+
24106+
#### Example
24107+
24108+
If the library `p` defines a class annotated with `@Deprecated.mixin`:
24109+
24110+
```dart
24111+
%uri="package:p/p.dart"
24112+
@Deprecated.mixin()
24113+
mixin class C {}
24114+
```
24115+
24116+
Then, the following code, when in a library other than `p`, produces this
24117+
diagnostic:
24118+
24119+
```dart
24120+
import 'package:p/p.dart';
24121+
24122+
class D with [!C!] {}
24123+
```
24124+
24125+
#### Common fixes
24126+
24127+
Follow any directions found in the `Deprecation.mixin` annotation, or
24128+
just remove the mixin class name from the `with` clause.
24129+
24130+
```dart
24131+
class D {}
24132+
```
2409124133
DEPRECATED_MIXIN_FUNCTION:
2409224134
parameters: none
2409324135
sharedName: DEPRECATED_SUBTYPE_OF_FUNCTION
@@ -25093,6 +25135,37 @@ WarningCode:
2509325135
```dart
2509425136
sealed class C {}
2509525137
```
25138+
INVALID_DEPRECATED_MIXIN_ANNOTATION:
25139+
parameters: none
25140+
problemMessage: "The annotation '@Deprecated.mixin' can only be applied to classes."
25141+
correctionMessage: Try removing the '@Deprecated.mixin' annotation.
25142+
hasPublishedDocs: false
25143+
comment: |-
25144+
This warning is generated anywhere where `@Deprecated.mixin` annotates
25145+
something other than a mixin class.
25146+
documentation: |-
25147+
#### Description
25148+
25149+
The analyzer produces this diagnostic when anything other than a
25150+
mixin class is annotated with Deprecated.mixin.
25151+
25152+
#### Example
25153+
25154+
The following code produces this diagnostic because the annotation is on a
25155+
non-mixin class:
25156+
25157+
```dart
25158+
@[!Deprecated.mixin!]()
25159+
class C {}
25160+
```
25161+
25162+
#### Common fixes
25163+
25164+
Remove the annotation:
25165+
25166+
```dart
25167+
class C {}
25168+
```
2509625169
INVALID_DEPRECATED_SUBCLASS_ANNOTATION:
2509725170
parameters: none
2509825171
problemMessage: "The annotation '@Deprecated.subclass' can only be applied to subclassable classes and mixins."
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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.dart';
6+
import 'package:test_reflective_loader/test_reflective_loader.dart';
7+
8+
import '../dart/resolution/context_collection_resolution.dart';
9+
10+
main() {
11+
defineReflectiveSuite(() {
12+
defineReflectiveTests(DeprecatedMixinTest);
13+
});
14+
}
15+
16+
@reflectiveTest
17+
class DeprecatedMixinTest extends PubPackageResolutionTest {
18+
test_annotatedClass_typedef() async {
19+
newFile('$testPackageLibPath/foo.dart', r'''
20+
@Deprecated.mixin()
21+
mixin class Foo {}
22+
typedef Foo2 = Foo;
23+
''');
24+
25+
await assertErrorsInCode(
26+
r'''
27+
import 'foo.dart';
28+
class Bar with Foo {}
29+
''',
30+
[error(WarningCode.deprecatedMixin, 34, 3)],
31+
);
32+
}
33+
34+
test_annotatedClassTypeAlias() async {
35+
newFile('$testPackageLibPath/foo.dart', r'''
36+
mixin M {}
37+
@Deprecated.mixin()
38+
mixin class Foo = Object with M;
39+
''');
40+
41+
await assertErrorsInCode(
42+
r'''
43+
import 'foo.dart';
44+
class Bar with Foo {}
45+
''',
46+
[error(WarningCode.deprecatedMixin, 34, 3)],
47+
);
48+
}
49+
50+
test_class() async {
51+
newFile('$testPackageLibPath/foo.dart', r'''
52+
@Deprecated.mixin()
53+
mixin class Foo {}
54+
''');
55+
56+
await assertErrorsInCode(
57+
r'''
58+
import 'foo.dart';
59+
class Bar with Foo {}
60+
''',
61+
[error(WarningCode.deprecatedMixin, 34, 3)],
62+
);
63+
}
64+
65+
test_classTypeAlias() async {
66+
newFile('$testPackageLibPath/foo.dart', r'''
67+
@Deprecated.mixin()
68+
mixin class Foo {}
69+
''');
70+
71+
await assertErrorsInCode(
72+
r'''
73+
import 'foo.dart';
74+
class Bar = Object with Foo;
75+
''',
76+
[error(WarningCode.deprecatedMixin, 43, 3)],
77+
);
78+
}
79+
80+
test_enum() async {
81+
newFile('$testPackageLibPath/foo.dart', r'''
82+
@Deprecated.mixin()
83+
mixin class Foo {}
84+
''');
85+
86+
await assertErrorsInCode(
87+
r'''
88+
import 'foo.dart';
89+
enum Bar with Foo {
90+
one, two;
91+
}
92+
''',
93+
[error(WarningCode.deprecatedMixin, 33, 3)],
94+
);
95+
}
96+
97+
test_insideLibrary() async {
98+
await assertNoErrorsInCode(r'''
99+
@Deprecated.mixin()
100+
mixin class Foo {}
101+
class Bar with Foo {}
102+
''');
103+
}
104+
105+
test_noAnnotation() async {
106+
newFile('$testPackageLibPath/foo.dart', r'''
107+
mixin class Foo {}
108+
''');
109+
110+
await assertNoErrorsInCode(r'''
111+
import 'foo.dart';
112+
class Bar with Foo {}
113+
''');
114+
}
115+
}

0 commit comments

Comments
 (0)