Skip to content

Commit 582e303

Browse files
scheglovCommit Queue
authored andcommitted
Augment. Report when a class/mixin augmentation has extra/missing modifier.
Change-Id: I351f8edec54a2b7459756e7622c0f4e9d728584c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/365203 Commit-Queue: Konstantin Shcheglov <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent 02efc14 commit 582e303

File tree

8 files changed

+475
-0
lines changed

8 files changed

+475
-0
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,10 @@ CompileTimeErrorCode.ASSIGNMENT_TO_TYPE:
209209
status: noFix
210210
CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT:
211211
status: hasFix
212+
CompileTimeErrorCode.AUGMENTATION_MODIFIER_EXTRA:
213+
status: needsFix
214+
CompileTimeErrorCode.AUGMENTATION_MODIFIER_MISSING:
215+
status: needsFix
212216
CompileTimeErrorCode.AUGMENTATION_OF_DIFFERENT_DECLARATION_KIND:
213217
status: noFix
214218
CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION:

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,27 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
214214
hasPublishedDocs: true,
215215
);
216216

217+
/// Parameters:
218+
/// 0: the lexeme of the modifier.
219+
static const CompileTimeErrorCode AUGMENTATION_MODIFIER_EXTRA =
220+
CompileTimeErrorCode(
221+
'AUGMENTATION_MODIFIER_EXTRA',
222+
"The augmentation has the '{0}' modifier that the declaration doesn't "
223+
"have.",
224+
correctionMessage:
225+
"Try removing the '{0}' modifier, or adding it to the declaration.",
226+
);
227+
228+
/// Parameters:
229+
/// 0: the lexeme of the modifier.
230+
static const CompileTimeErrorCode AUGMENTATION_MODIFIER_MISSING =
231+
CompileTimeErrorCode(
232+
'AUGMENTATION_MODIFIER_MISSING',
233+
"The augmentation is missing the '{0}' modifier that the declaration has.",
234+
correctionMessage:
235+
"Try adding the '{0}' modifier, or removing it from the declaration.",
236+
);
237+
217238
/// Parameters:
218239
/// 0: the name of the declaration kind.
219240
/// 1: the name of the augmentation kind.

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ const List<ErrorCode> errorCodeValues = [
6464
CompileTimeErrorCode.ASSIGNMENT_TO_METHOD,
6565
CompileTimeErrorCode.ASSIGNMENT_TO_TYPE,
6666
CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT,
67+
CompileTimeErrorCode.AUGMENTATION_MODIFIER_EXTRA,
68+
CompileTimeErrorCode.AUGMENTATION_MODIFIER_MISSING,
6769
CompileTimeErrorCode.AUGMENTATION_OF_DIFFERENT_DECLARATION_KIND,
6870
CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION,
6971
CompileTimeErrorCode.AUGMENTATION_WITHOUT_IMPORT,

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

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,12 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
453453
element: element,
454454
);
455455

456+
_checkClassAugmentationModifiers(
457+
augmentKeyword: node.augmentKeyword,
458+
augmentationNode: node,
459+
augmentationElement: element,
460+
);
461+
456462
_isInNativeClass = node.nativeClause != null;
457463

458464
var augmented = element.augmented;
@@ -1166,6 +1172,12 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
11661172
element: element,
11671173
);
11681174

1175+
_checkMixinAugmentationModifiers(
1176+
augmentKeyword: node.augmentKeyword,
1177+
augmentationNode: node,
1178+
augmentationElement: element,
1179+
);
1180+
11691181
var augmented = element.augmented;
11701182
var declarationElement = augmented.declaration;
11711183
_enclosingClass = declarationElement;
@@ -1610,6 +1622,86 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
16101622
);
16111623
}
16121624

1625+
void _checkClassAugmentationModifiers({
1626+
required Token? augmentKeyword,
1627+
required ClassDeclarationImpl augmentationNode,
1628+
required ClassElementImpl augmentationElement,
1629+
}) {
1630+
if (augmentKeyword == null) {
1631+
return;
1632+
}
1633+
1634+
var target = augmentationElement.augmentationTarget;
1635+
if (target == null) {
1636+
return;
1637+
}
1638+
1639+
var declaration = target.augmented.declaration;
1640+
1641+
void singleModifier({
1642+
required String modifierName,
1643+
required bool declarationFlag,
1644+
required Token? augmentationModifier,
1645+
}) {
1646+
if (declarationFlag) {
1647+
if (augmentationModifier == null) {
1648+
errorReporter.atToken(
1649+
augmentKeyword,
1650+
CompileTimeErrorCode.AUGMENTATION_MODIFIER_MISSING,
1651+
arguments: [modifierName],
1652+
);
1653+
}
1654+
} else {
1655+
if (augmentationModifier != null) {
1656+
errorReporter.atToken(
1657+
augmentationModifier,
1658+
CompileTimeErrorCode.AUGMENTATION_MODIFIER_EXTRA,
1659+
arguments: [modifierName],
1660+
);
1661+
}
1662+
}
1663+
}
1664+
1665+
// Sealed classes are also abstract, report just `sealed` mismatch.
1666+
if (!declaration.isSealed) {
1667+
singleModifier(
1668+
modifierName: 'abstract',
1669+
declarationFlag: declaration.isAbstract,
1670+
augmentationModifier: augmentationNode.abstractKeyword,
1671+
);
1672+
}
1673+
1674+
singleModifier(
1675+
modifierName: 'base',
1676+
declarationFlag: declaration.isBase,
1677+
augmentationModifier: augmentationNode.baseKeyword,
1678+
);
1679+
1680+
singleModifier(
1681+
modifierName: 'final',
1682+
declarationFlag: declaration.isFinal,
1683+
augmentationModifier: augmentationNode.finalKeyword,
1684+
);
1685+
1686+
singleModifier(
1687+
modifierName: 'interface',
1688+
declarationFlag: declaration.isInterface,
1689+
augmentationModifier: augmentationNode.interfaceKeyword,
1690+
);
1691+
1692+
singleModifier(
1693+
modifierName: 'mixin',
1694+
declarationFlag: declaration.isMixinClass,
1695+
augmentationModifier: augmentationNode.mixinKeyword,
1696+
);
1697+
1698+
singleModifier(
1699+
modifierName: 'sealed',
1700+
declarationFlag: declaration.isSealed,
1701+
augmentationModifier: augmentationNode.sealedKeyword,
1702+
);
1703+
}
1704+
16131705
/// Checks the class for problems with the superclass, mixins, or implemented
16141706
/// interfaces.
16151707
///
@@ -5768,6 +5860,53 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
57685860
}
57695861
}
57705862

5863+
void _checkMixinAugmentationModifiers({
5864+
required Token? augmentKeyword,
5865+
required MixinDeclarationImpl augmentationNode,
5866+
required MixinElementImpl augmentationElement,
5867+
}) {
5868+
if (augmentKeyword == null) {
5869+
return;
5870+
}
5871+
5872+
var target = augmentationElement.augmentationTarget;
5873+
if (target == null) {
5874+
return;
5875+
}
5876+
5877+
var declaration = target.augmented.declaration;
5878+
5879+
void singleModifier({
5880+
required String modifierName,
5881+
required bool declarationFlag,
5882+
required Token? augmentationModifier,
5883+
}) {
5884+
if (declarationFlag) {
5885+
if (augmentationModifier == null) {
5886+
errorReporter.atToken(
5887+
augmentKeyword,
5888+
CompileTimeErrorCode.AUGMENTATION_MODIFIER_MISSING,
5889+
arguments: [modifierName],
5890+
);
5891+
}
5892+
} else {
5893+
if (augmentationModifier != null) {
5894+
errorReporter.atToken(
5895+
augmentationModifier,
5896+
CompileTimeErrorCode.AUGMENTATION_MODIFIER_EXTRA,
5897+
arguments: [modifierName],
5898+
);
5899+
}
5900+
}
5901+
}
5902+
5903+
singleModifier(
5904+
modifierName: 'base',
5905+
declarationFlag: declaration.isBase,
5906+
augmentationModifier: augmentationNode.baseKeyword,
5907+
);
5908+
}
5909+
57715910
/// Checks the class for problems with the superclass, mixins, or implemented
57725911
/// interfaces.
57735912
void _checkMixinInheritance(MixinDeclaration node, MixinOnClause? onClause,

pkg/analyzer/messages.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,18 @@ CompileTimeErrorCode:
11091109
}
11101110
}
11111111
```
1112+
AUGMENTATION_MODIFIER_EXTRA:
1113+
problemMessage: The augmentation has the '{0}' modifier that the declaration doesn't have.
1114+
correctionMessage: Try removing the '{0}' modifier, or adding it to the declaration.
1115+
comment: |-
1116+
Parameters:
1117+
0: the lexeme of the modifier.
1118+
AUGMENTATION_MODIFIER_MISSING:
1119+
problemMessage: The augmentation is missing the '{0}' modifier that the declaration has.
1120+
correctionMessage: Try adding the '{0}' modifier, or removing it from the declaration.
1121+
comment: |-
1122+
Parameters:
1123+
0: the lexeme of the modifier.
11121124
AUGMENTATION_OF_DIFFERENT_DECLARATION_KIND:
11131125
problemMessage: Can't augment a {0} with a {1}.
11141126
correctionMessage: Try changing the augmentation to match the declaration kind.
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// Copyright (c) 2024, 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(AugmentationModifierExtraTest);
13+
});
14+
}
15+
16+
@reflectiveTest
17+
class AugmentationModifierExtraTest extends PubPackageResolutionTest {
18+
test_class_abstract() async {
19+
newFile('$testPackageLibPath/a.dart', r'''
20+
import augment 'test.dart';
21+
22+
class A {}
23+
''');
24+
25+
await assertErrorsInCode(r'''
26+
augment library 'a.dart';
27+
28+
augment abstract class A {}
29+
''', [
30+
error(CompileTimeErrorCode.AUGMENTATION_MODIFIER_EXTRA, 35, 8),
31+
]);
32+
}
33+
34+
test_class_abstract_base() async {
35+
newFile('$testPackageLibPath/a.dart', r'''
36+
import augment 'test.dart';
37+
38+
class A {}
39+
''');
40+
41+
await assertErrorsInCode(r'''
42+
augment library 'a.dart';
43+
44+
augment abstract base class A {}
45+
''', [
46+
error(CompileTimeErrorCode.AUGMENTATION_MODIFIER_EXTRA, 35, 8),
47+
error(CompileTimeErrorCode.AUGMENTATION_MODIFIER_EXTRA, 44, 4),
48+
]);
49+
}
50+
51+
test_class_base() async {
52+
newFile('$testPackageLibPath/a.dart', r'''
53+
import augment 'test.dart';
54+
55+
class A {}
56+
''');
57+
58+
await assertErrorsInCode(r'''
59+
augment library 'a.dart';
60+
61+
augment base class A {}
62+
''', [
63+
error(CompileTimeErrorCode.AUGMENTATION_MODIFIER_EXTRA, 35, 4),
64+
]);
65+
}
66+
67+
test_class_final() async {
68+
newFile('$testPackageLibPath/a.dart', r'''
69+
import augment 'test.dart';
70+
71+
class A {}
72+
''');
73+
74+
await assertErrorsInCode(r'''
75+
augment library 'a.dart';
76+
77+
augment final class A {}
78+
''', [
79+
error(CompileTimeErrorCode.AUGMENTATION_MODIFIER_EXTRA, 35, 5),
80+
]);
81+
}
82+
83+
test_class_interface() async {
84+
newFile('$testPackageLibPath/a.dart', r'''
85+
import augment 'test.dart';
86+
87+
class A {}
88+
''');
89+
90+
await assertErrorsInCode(r'''
91+
augment library 'a.dart';
92+
93+
augment interface class A {}
94+
''', [
95+
error(CompileTimeErrorCode.AUGMENTATION_MODIFIER_EXTRA, 35, 9),
96+
]);
97+
}
98+
99+
test_class_mixin() async {
100+
newFile('$testPackageLibPath/a.dart', r'''
101+
import augment 'test.dart';
102+
103+
class A {}
104+
''');
105+
106+
await assertErrorsInCode(r'''
107+
augment library 'a.dart';
108+
109+
augment mixin class A {}
110+
''', [
111+
error(CompileTimeErrorCode.AUGMENTATION_MODIFIER_EXTRA, 35, 5),
112+
]);
113+
}
114+
115+
test_class_sealed() async {
116+
newFile('$testPackageLibPath/a.dart', r'''
117+
import augment 'test.dart';
118+
119+
class A {}
120+
''');
121+
122+
await assertErrorsInCode(r'''
123+
augment library 'a.dart';
124+
125+
augment sealed class A {}
126+
''', [
127+
error(CompileTimeErrorCode.AUGMENTATION_MODIFIER_EXTRA, 35, 6),
128+
]);
129+
}
130+
131+
test_mixin_base() async {
132+
newFile('$testPackageLibPath/a.dart', r'''
133+
import augment 'test.dart';
134+
135+
mixin A {}
136+
''');
137+
138+
await assertErrorsInCode(r'''
139+
augment library 'a.dart';
140+
141+
augment base mixin A {}
142+
''', [
143+
error(CompileTimeErrorCode.AUGMENTATION_MODIFIER_EXTRA, 35, 4),
144+
]);
145+
}
146+
}

0 commit comments

Comments
 (0)