Skip to content

Commit f49c786

Browse files
parloughCommit Queue
authored and
Commit Queue
committed
[cfe/analyzer] Report error on invalid abstract final class modifier combinations
Fixes #54143 Bug: #54143 Change-Id: I5710a8524dfea94b8e42595c8907b1ebf738405c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/342240 Reviewed-by: Johnni Winther <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Johnni Winther <[email protected]>
1 parent 338fd65 commit f49c786

16 files changed

+299
-2
lines changed

pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,31 @@ const MessageCode messageAbstractFieldInitializer = const MessageCode(
9898
correctionMessage:
9999
r"""Try removing the initializer or the 'abstract' keyword.""");
100100

101+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
102+
const Code<Null> codeAbstractFinalBaseClass = messageAbstractFinalBaseClass;
103+
104+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
105+
const MessageCode messageAbstractFinalBaseClass = const MessageCode(
106+
"AbstractFinalBaseClass",
107+
index: 176,
108+
problemMessage:
109+
r"""An 'abstract' class can't be declared as both 'final' and 'base'.""",
110+
correctionMessage:
111+
r"""Try removing either the 'final' or 'base' keyword.""");
112+
113+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
114+
const Code<Null> codeAbstractFinalInterfaceClass =
115+
messageAbstractFinalInterfaceClass;
116+
117+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
118+
const MessageCode messageAbstractFinalInterfaceClass = const MessageCode(
119+
"AbstractFinalInterfaceClass",
120+
index: 177,
121+
problemMessage:
122+
r"""An 'abstract' class can't be declared as both 'final' and 'interface'.""",
123+
correctionMessage:
124+
r"""Try removing either the 'final' or 'interface' keyword.""");
125+
101126
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
102127
const Code<Null> codeAbstractLateField = messageAbstractLateField;
103128

pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2622,9 +2622,20 @@ class Parser {
26222622
Token token = computeTypeParamOrArg(
26232623
name, /* inDeclaration = */ true, /* allowsVariance = */ true)
26242624
.parseVariables(name, this);
2625-
if (abstractToken != null && sealedToken != null) {
2626-
reportRecoverableError(sealedToken, codes.messageAbstractSealedClass);
2625+
if (abstractToken != null) {
2626+
if (sealedToken != null) {
2627+
reportRecoverableError(sealedToken, codes.messageAbstractSealedClass);
2628+
} else if (finalToken != null) {
2629+
if (baseToken != null) {
2630+
reportRecoverableErrorWithEnd(
2631+
finalToken, baseToken, codes.messageAbstractFinalBaseClass);
2632+
} else if (interfaceToken != null) {
2633+
reportRecoverableErrorWithEnd(finalToken, interfaceToken,
2634+
codes.messageAbstractFinalInterfaceClass);
2635+
}
2636+
}
26272637
}
2638+
26282639
if (optional('=', token.next!)) {
26292640
listener.beginNamedMixinApplication(
26302641
beginToken,

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2507,6 +2507,14 @@ ParserErrorCode.ABSTRACT_EXTERNAL_FIELD:
25072507
status: needsFix
25082508
notes: |-
25092509
Remove either the `abstract` or the `external` keyword.
2510+
ParserErrorCode.ABSTRACT_FINAL_BASE_CLASS:
2511+
status: needsFix
2512+
notes: |-
2513+
Remove either the `final` or `base` keyword.
2514+
ParserErrorCode.ABSTRACT_FINAL_INTERFACE_CLASS:
2515+
status: needsFix
2516+
notes: |-
2517+
Remove either the `final` or `interface` keyword.
25102518
ParserErrorCode.ABSTRACT_LATE_FIELD:
25112519
status: needsFix
25122520
notes: |-

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ final fastaAnalyzerErrorCodes = <ErrorCode?>[
197197
ParserErrorCode.EXPECTED_EXTENSION_BODY,
198198
ParserErrorCode.EXTRANEOUS_MODIFIER_IN_EXTENSION_TYPE,
199199
ParserErrorCode.EXTRANEOUS_MODIFIER_IN_PRIMARY_CONSTRUCTOR,
200+
ParserErrorCode.ABSTRACT_FINAL_BASE_CLASS,
201+
ParserErrorCode.ABSTRACT_FINAL_INTERFACE_CLASS,
200202
];
201203

202204
class ParserErrorCode extends ErrorCode {
@@ -220,6 +222,19 @@ class ParserErrorCode extends ErrorCode {
220222
correctionMessage: "Try removing the 'abstract' or 'external' keyword.",
221223
);
222224

225+
static const ParserErrorCode ABSTRACT_FINAL_BASE_CLASS = ParserErrorCode(
226+
'ABSTRACT_FINAL_BASE_CLASS',
227+
"An 'abstract' class can't be declared as both 'final' and 'base'.",
228+
correctionMessage: "Try removing either the 'final' or 'base' keyword.",
229+
);
230+
231+
static const ParserErrorCode ABSTRACT_FINAL_INTERFACE_CLASS = ParserErrorCode(
232+
'ABSTRACT_FINAL_INTERFACE_CLASS',
233+
"An 'abstract' class can't be declared as both 'final' and 'interface'.",
234+
correctionMessage:
235+
"Try removing either the 'final' or 'interface' keyword.",
236+
);
237+
223238
static const ParserErrorCode ABSTRACT_LATE_FIELD = ParserErrorCode(
224239
'ABSTRACT_LATE_FIELD',
225240
"Abstract fields cannot be late.",

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,8 @@ const List<ErrorCode> errorCodeValues = [
649649
ParserErrorCode.ABSTRACT_CLASS_MEMBER,
650650
ParserErrorCode.ABSTRACT_ENUM,
651651
ParserErrorCode.ABSTRACT_EXTERNAL_FIELD,
652+
ParserErrorCode.ABSTRACT_FINAL_BASE_CLASS,
653+
ParserErrorCode.ABSTRACT_FINAL_INTERFACE_CLASS,
652654
ParserErrorCode.ABSTRACT_LATE_FIELD,
653655
ParserErrorCode.ABSTRACT_SEALED_CLASS,
654656
ParserErrorCode.ABSTRACT_STATIC_FIELD,

pkg/analyzer/test/src/fasta/ast_builder_test.dart

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,56 @@ main() {
1717

1818
@reflectiveTest
1919
class AstBuilderTest extends ParserDiagnosticsTest {
20+
void test_class_abstract_final_base() {
21+
var parseResult = parseStringWithErrors(r'''
22+
/// text
23+
abstract final base class A {}
24+
''');
25+
parseResult.assertErrors([
26+
error(ParserErrorCode.ABSTRACT_FINAL_BASE_CLASS, 18, 10),
27+
]);
28+
29+
var node = parseResult.findNode.classDeclaration('class A {}');
30+
assertParsedNodeText(node, r'''
31+
ClassDeclaration
32+
documentationComment: Comment
33+
tokens
34+
/// text
35+
abstractKeyword: abstract
36+
baseKeyword: base
37+
finalKeyword: final
38+
classKeyword: class
39+
name: A
40+
leftBracket: {
41+
rightBracket: }
42+
''');
43+
}
44+
45+
void test_class_abstract_final_interface() {
46+
var parseResult = parseStringWithErrors(r'''
47+
/// text
48+
abstract final interface class A {}
49+
''');
50+
parseResult.assertErrors([
51+
error(ParserErrorCode.ABSTRACT_FINAL_INTERFACE_CLASS, 18, 15),
52+
]);
53+
54+
var node = parseResult.findNode.classDeclaration('class A {}');
55+
assertParsedNodeText(node, r'''
56+
ClassDeclaration
57+
documentationComment: Comment
58+
tokens
59+
/// text
60+
abstractKeyword: abstract
61+
interfaceKeyword: interface
62+
finalKeyword: final
63+
classKeyword: class
64+
name: A
65+
leftBracket: {
66+
rightBracket: }
67+
''');
68+
}
69+
2070
void test_class_abstract_sealed() {
2171
var parseResult = parseStringWithErrors(r'''
2272
/// text

pkg/front_end/messages.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,22 @@ AbstractLateField:
10401040
script:
10411041
- "abstract class C {abstract late var f;}"
10421042

1043+
AbstractFinalBaseClass:
1044+
problemMessage: "An 'abstract' class can't be declared as both 'final' and 'base'."
1045+
correctionMessage: "Try removing either the 'final' or 'base' keyword."
1046+
analyzerCode: ParserErrorCode.ABSTRACT_FINAL_BASE_CLASS
1047+
index: 176
1048+
script:
1049+
- "abstract final base class C {}"
1050+
1051+
AbstractFinalInterfaceClass:
1052+
problemMessage: "An 'abstract' class can't be declared as both 'final' and 'interface'."
1053+
correctionMessage: "Try removing either the 'final' or 'interface' keyword."
1054+
analyzerCode: ParserErrorCode.ABSTRACT_FINAL_INTERFACE_CLASS
1055+
index: 177
1056+
script:
1057+
- "abstract final interface class C {}"
1058+
10431059
AbstractSealedClass:
10441060
problemMessage: "A 'sealed' class can't be marked 'abstract' because it's already implicitly abstract."
10451061
correctionMessage: "Try removing the 'abstract' keyword."
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright (c) 2023, 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+
abstract final base class FinalBase {}
6+
abstract final interface class FinalInterface {}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/class_modifiers/invalid_abstract_modifier_combinations.dart:5:10: Error: An 'abstract' class can't be declared as both 'final' and 'base'.
6+
// Try removing either the 'final' or 'base' keyword.
7+
// abstract final base class FinalBase {}
8+
// ^^^^^^^^^^
9+
//
10+
// pkg/front_end/testcases/class_modifiers/invalid_abstract_modifier_combinations.dart:6:10: Error: An 'abstract' class can't be declared as both 'final' and 'interface'.
11+
// Try removing either the 'final' or 'interface' keyword.
12+
// abstract final interface class FinalInterface {}
13+
// ^^^^^^^^^^^^^^^
14+
//
15+
import self as self;
16+
import "dart:core" as core;
17+
18+
abstract base final class FinalBase extends core::Object {
19+
synthetic constructor •() → self::FinalBase
20+
: super core::Object::•()
21+
;
22+
}
23+
abstract interface final class FinalInterface extends core::Object {
24+
synthetic constructor •() → self::FinalInterface
25+
: super core::Object::•()
26+
;
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/class_modifiers/invalid_abstract_modifier_combinations.dart:5:10: Error: An 'abstract' class can't be declared as both 'final' and 'base'.
6+
// Try removing either the 'final' or 'base' keyword.
7+
// abstract final base class FinalBase {}
8+
// ^^^^^^^^^^
9+
//
10+
// pkg/front_end/testcases/class_modifiers/invalid_abstract_modifier_combinations.dart:6:10: Error: An 'abstract' class can't be declared as both 'final' and 'interface'.
11+
// Try removing either the 'final' or 'interface' keyword.
12+
// abstract final interface class FinalInterface {}
13+
// ^^^^^^^^^^^^^^^
14+
//
15+
import self as self;
16+
import "dart:core" as core;
17+
18+
abstract base final class FinalBase extends core::Object {
19+
synthetic constructor •() → self::FinalBase
20+
: super core::Object::•()
21+
;
22+
}
23+
abstract interface final class FinalInterface extends core::Object {
24+
synthetic constructor •() → self::FinalInterface
25+
: super core::Object::•()
26+
;
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
abstract final base class FinalBase {}
2+
abstract final interface class FinalInterface {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
abstract final base class FinalBase {}
2+
abstract final interface class FinalInterface {}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/class_modifiers/invalid_abstract_modifier_combinations.dart:5:10: Error: An 'abstract' class can't be declared as both 'final' and 'base'.
6+
// Try removing either the 'final' or 'base' keyword.
7+
// abstract final base class FinalBase {}
8+
// ^^^^^^^^^^
9+
//
10+
// pkg/front_end/testcases/class_modifiers/invalid_abstract_modifier_combinations.dart:6:10: Error: An 'abstract' class can't be declared as both 'final' and 'interface'.
11+
// Try removing either the 'final' or 'interface' keyword.
12+
// abstract final interface class FinalInterface {}
13+
// ^^^^^^^^^^^^^^^
14+
//
15+
import self as self;
16+
import "dart:core" as core;
17+
18+
abstract base final class FinalBase extends core::Object {
19+
synthetic constructor •() → self::FinalBase
20+
: super core::Object::•()
21+
;
22+
}
23+
abstract interface final class FinalInterface extends core::Object {
24+
synthetic constructor •() → self::FinalInterface
25+
: super core::Object::•()
26+
;
27+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/class_modifiers/invalid_abstract_modifier_combinations.dart:5:10: Error: An 'abstract' class can't be declared as both 'final' and 'base'.
6+
// Try removing either the 'final' or 'base' keyword.
7+
// abstract final base class FinalBase {}
8+
// ^^^^^^^^^^
9+
//
10+
// pkg/front_end/testcases/class_modifiers/invalid_abstract_modifier_combinations.dart:6:10: Error: An 'abstract' class can't be declared as both 'final' and 'interface'.
11+
// Try removing either the 'final' or 'interface' keyword.
12+
// abstract final interface class FinalInterface {}
13+
// ^^^^^^^^^^^^^^^
14+
//
15+
import self as self;
16+
import "dart:core" as core;
17+
18+
abstract base final class FinalBase extends core::Object {
19+
synthetic constructor •() → self::FinalBase
20+
: super core::Object::•()
21+
;
22+
}
23+
abstract interface final class FinalInterface extends core::Object {
24+
synthetic constructor •() → self::FinalInterface
25+
: super core::Object::•()
26+
;
27+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/class_modifiers/invalid_abstract_modifier_combinations.dart:5:10: Error: An 'abstract' class can't be declared as both 'final' and 'base'.
6+
// Try removing either the 'final' or 'base' keyword.
7+
// abstract final base class FinalBase {}
8+
// ^^^^^^^^^^
9+
//
10+
// pkg/front_end/testcases/class_modifiers/invalid_abstract_modifier_combinations.dart:6:10: Error: An 'abstract' class can't be declared as both 'final' and 'interface'.
11+
// Try removing either the 'final' or 'interface' keyword.
12+
// abstract final interface class FinalInterface {}
13+
// ^^^^^^^^^^^^^^^
14+
//
15+
import self as self;
16+
import "dart:core" as core;
17+
18+
abstract base final class FinalBase extends core::Object {
19+
synthetic constructor •() → self::FinalBase
20+
;
21+
}
22+
abstract interface final class FinalInterface extends core::Object {
23+
synthetic constructor •() → self::FinalInterface
24+
;
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/class_modifiers/invalid_abstract_modifier_combinations.dart:5:10: Error: An 'abstract' class can't be declared as both 'final' and 'base'.
6+
// Try removing either the 'final' or 'base' keyword.
7+
// abstract final base class FinalBase {}
8+
// ^^^^^^^^^^
9+
//
10+
// pkg/front_end/testcases/class_modifiers/invalid_abstract_modifier_combinations.dart:6:10: Error: An 'abstract' class can't be declared as both 'final' and 'interface'.
11+
// Try removing either the 'final' or 'interface' keyword.
12+
// abstract final interface class FinalInterface {}
13+
// ^^^^^^^^^^^^^^^
14+
//
15+
import self as self;
16+
import "dart:core" as core;
17+
18+
abstract base final class FinalBase extends core::Object {
19+
synthetic constructor •() → self::FinalBase
20+
: super core::Object::•()
21+
;
22+
}
23+
abstract interface final class FinalInterface extends core::Object {
24+
synthetic constructor •() → self::FinalInterface
25+
: super core::Object::•()
26+
;
27+
}

0 commit comments

Comments
 (0)