Skip to content

Commit 5f0433a

Browse files
chloestefantsovaCommit Bot
authored and
Commit Bot
committed
[cfe] Report more errors on forbidden enum constructor calls
This CL also adds support for redirecting enum factories. Part of #47453 Change-Id: Idd16598316a52ac6122a47f4c12f82ab5750d8f3 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/230661 Auto-Submit: Chloe Stefantsova <[email protected]> Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Johnni Winther <[email protected]>
1 parent a066e2c commit 5f0433a

23 files changed

+1001
-37
lines changed

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

+18
Original file line numberDiff line numberDiff line change
@@ -2778,6 +2778,14 @@ const MessageCode messageEnumConstructorSuperInitializer = const MessageCode(
27782778
"EnumConstructorSuperInitializer",
27792779
problemMessage: r"""Enum constructors can't contain super-initializers.""");
27802780

2781+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
2782+
const Code<Null> codeEnumConstructorTearoff = messageEnumConstructorTearoff;
2783+
2784+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
2785+
const MessageCode messageEnumConstructorTearoff = const MessageCode(
2786+
"EnumConstructorTearoff",
2787+
problemMessage: r"""Enum constructors can't be torn off.""");
2788+
27812789
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
27822790
const Code<Null> codeEnumDeclarationEmpty = messageEnumDeclarationEmpty;
27832791

@@ -2807,6 +2815,16 @@ const MessageCode messageEnumEntryWithTypeArgumentsWithoutArguments =
28072815
problemMessage:
28082816
r"""Missing arguments in enum constructor invocation.""");
28092817

2818+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
2819+
const Code<Null> codeEnumFactoryRedirectsToConstructor =
2820+
messageEnumFactoryRedirectsToConstructor;
2821+
2822+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
2823+
const MessageCode messageEnumFactoryRedirectsToConstructor = const MessageCode(
2824+
"EnumFactoryRedirectsToConstructor",
2825+
problemMessage:
2826+
r"""Enum factory constructors can't redirect to generative constructors.""");
2827+
28102828
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
28112829
const Code<Null> codeEnumInClass = messageEnumInClass;
28122830

pkg/front_end/lib/src/fasta/kernel/expression_generator.dart

+3
Original file line numberDiff line numberDiff line change
@@ -3219,6 +3219,9 @@ class TypeUseGenerator extends AbstractReadOnlyAccessGenerator {
32193219
messageAbstractClassConstructorTearOff,
32203220
nameOffset,
32213221
name.text.length);
3222+
} else if (declarationBuilder.cls.isEnum) {
3223+
return _helper.buildProblem(messageEnumConstructorTearoff,
3224+
nameOffset, name.text.length);
32223225
}
32233226
tearOffExpression = _helper.forest
32243227
.createConstructorTearOff(token.charOffset, tearOff);

pkg/front_end/lib/src/fasta/source/source_class_builder.dart

+27-12
Original file line numberDiff line numberDiff line change
@@ -952,24 +952,29 @@ class SourceClassBuilder extends ClassBuilderImpl
952952
return;
953953
}
954954

955-
// Check whether [redirecteeType] <: [factoryType].
956-
if (!typeEnvironment.isSubtypeOf(
957-
redirecteeType, factoryType, SubtypeCheckMode.ignoringNullabilities)) {
958-
_addProblemForRedirectingFactory(
959-
factory,
960-
templateIncompatibleRedirecteeFunctionType.withArguments(
961-
redirecteeType, factoryType, library.isNonNullableByDefault),
962-
factory.redirectionTarget.charOffset,
963-
noLength);
964-
} else if (library.isNonNullableByDefault) {
965-
if (!typeEnvironment.isSubtypeOf(
966-
redirecteeType, factoryType, SubtypeCheckMode.withNullabilities)) {
955+
// Redirection to generative enum constructors is forbidden and is reported
956+
// as an error elsewhere.
957+
if (!(cls.isEnum &&
958+
(factory.redirectionTarget.target?.isConstructor ?? false))) {
959+
// Check whether [redirecteeType] <: [factoryType].
960+
if (!typeEnvironment.isSubtypeOf(redirecteeType, factoryType,
961+
SubtypeCheckMode.ignoringNullabilities)) {
967962
_addProblemForRedirectingFactory(
968963
factory,
969964
templateIncompatibleRedirecteeFunctionType.withArguments(
970965
redirecteeType, factoryType, library.isNonNullableByDefault),
971966
factory.redirectionTarget.charOffset,
972967
noLength);
968+
} else if (library.isNonNullableByDefault) {
969+
if (!typeEnvironment.isSubtypeOf(
970+
redirecteeType, factoryType, SubtypeCheckMode.withNullabilities)) {
971+
_addProblemForRedirectingFactory(
972+
factory,
973+
templateIncompatibleRedirecteeFunctionType.withArguments(
974+
redirecteeType, factoryType, library.isNonNullableByDefault),
975+
factory.redirectionTarget.charOffset,
976+
noLength);
977+
}
973978
}
974979
}
975980
}
@@ -1690,6 +1695,16 @@ class SourceClassBuilder extends ClassBuilderImpl
16901695
noLength);
16911696
targetNode = null;
16921697
}
1698+
if (targetNode != null &&
1699+
targetNode is Constructor &&
1700+
targetNode.enclosingClass.isEnum) {
1701+
_addProblemForRedirectingFactory(
1702+
declaration,
1703+
messageEnumFactoryRedirectsToConstructor,
1704+
redirectionTarget.charOffset,
1705+
noLength);
1706+
targetNode = null;
1707+
}
16931708
if (targetNode != null) {
16941709
List<DartType> typeArguments = declaration.typeArguments ??
16951710
new List<DartType>.filled(

pkg/front_end/lib/src/fasta/source/source_enum_builder.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class SourceEnumBuilder extends SourceClassBuilder {
8585
this.objectType,
8686
this.stringType,
8787
SourceLibraryBuilder parent,
88+
List<ConstructorReferenceBuilder> constructorReferences,
8889
int startCharOffset,
8990
int charOffset,
9091
int charEndOffset,
@@ -100,7 +101,7 @@ class SourceEnumBuilder extends SourceClassBuilder {
100101
scope,
101102
constructors,
102103
parent,
103-
/* constructorReferences = */ null,
104+
constructorReferences,
104105
startCharOffset,
105106
charOffset,
106107
charEndOffset,
@@ -115,6 +116,7 @@ class SourceEnumBuilder extends SourceClassBuilder {
115116
List<TypeBuilder>? interfaceBuilders,
116117
List<EnumConstantInfo?>? enumConstantInfos,
117118
SourceLibraryBuilder parent,
119+
List<ConstructorReferenceBuilder> constructorReferences,
118120
int startCharOffset,
119121
int charOffset,
120122
int charEndOffset,
@@ -422,6 +424,7 @@ class SourceEnumBuilder extends SourceClassBuilder {
422424
objectType,
423425
stringType,
424426
parent,
427+
constructorReferences,
425428
startCharOffsetComputed,
426429
charOffset,
427430
charEndOffset,

pkg/front_end/lib/src/fasta/source/source_library_builder.dart

+2
Original file line numberDiff line numberDiff line change
@@ -2820,6 +2820,7 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
28202820
interfaceBuilders,
28212821
enumConstantInfos,
28222822
this,
2823+
new List<ConstructorReferenceBuilder>.of(constructorReferences),
28232824
startCharOffset,
28242825
charOffset,
28252826
charEndOffset,
@@ -2831,6 +2832,7 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
28312832
debugName: "enum $name",
28322833
isModifiable: false),
28332834
new ConstructorScope(name, constructors));
2835+
constructorReferences.clear();
28342836

28352837
Map<String, TypeVariableBuilder>? typeVariablesByName =
28362838
checkTypeVariables(typeVariables, enumBuilder);

pkg/front_end/messages.status

+4
Original file line numberDiff line numberDiff line change
@@ -215,12 +215,16 @@ Encoding/analyzerCode: Fail
215215
EnumConstantSameNameAsEnclosing/example: Fail
216216
EnumConstructorSuperInitializer/analyzerCode: Fail
217217
EnumConstructorSuperInitializer/example: Fail
218+
EnumConstructorTearoff/analyzerCode: Fail
219+
EnumConstructorTearoff/example: Fail
218220
EnumDeclaresConstFactory/analyzerCode: Fail
219221
EnumDeclaresConstFactory/example: Fail
220222
EnumDeclaresFactory/analyzerCode: Fail
221223
EnumDeclaresFactory/example: Fail
222224
EnumEntryWithTypeArgumentsWithoutArguments/analyzerCode: Fail
223225
EnumEntryWithTypeArgumentsWithoutArguments/example: Fail
226+
EnumFactoryRedirectsToConstructor/analyzerCode: Fail
227+
EnumFactoryRedirectsToConstructor/example: Fail
224228
EnumInstantiation/example: Fail
225229
EnumNonConstConstructor/analyzerCode: Fail
226230
EnumNonConstConstructor/example: Fail

pkg/front_end/messages.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -5467,3 +5467,9 @@ EnumNonConstConstructor:
54675467

54685468
EnumConstructorSuperInitializer:
54695469
problemMessage: "Enum constructors can't contain super-initializers."
5470+
5471+
EnumConstructorTearoff:
5472+
problemMessage: "Enum constructors can't be torn off."
5473+
5474+
EnumFactoryRedirectsToConstructor:
5475+
problemMessage: "Enum factory constructors can't redirect to generative constructors."

pkg/front_end/test/spell_checking_list_common.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,7 @@ following
12491249
follows
12501250
food
12511251
for
1252+
forbidden
12521253
force
12531254
forced
12541255
forest
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) 2022, 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+
enum E {
6+
one, // Ok.
7+
two.named(), // Ok.
8+
three.f(), // Error.
9+
four.f2(); // Error.
10+
11+
const E();
12+
13+
const E.named()
14+
: this(); // Ok.
15+
16+
factory E.f() => values.first;
17+
18+
factory E.f2() {
19+
return const E(); // Error.
20+
}
21+
22+
const factory E.f3() = E; // Error.
23+
24+
factory E.f4() = E; // Error.
25+
26+
factory E.f5() = E.f; // Ok.
27+
28+
factory E.f6(int value) = E.f; // Error.
29+
}
30+
31+
test() {
32+
new E(); // Error.
33+
const E(); // Error.
34+
E.new; // Error.
35+
36+
new E.named(); // Error.
37+
const E().named(); // Error.
38+
E.named; // Error.
39+
40+
new E.f(); // Ok.
41+
const E.f(); // Error.
42+
E.f; // Ok.
43+
}
44+
45+
main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
library /*isNonNullableByDefault*/;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:22:26: Error: Enum factory constructors can't redirect to generative constructors.
6+
// const factory E.f3() = E; // Error.
7+
// ^
8+
//
9+
// pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:24:20: Error: Enum factory constructors can't redirect to generative constructors.
10+
// factory E.f4() = E; // Error.
11+
// ^
12+
//
13+
// pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:8:8: Error: Couldn't find constructor 'E.f'.
14+
// three.f(), // Error.
15+
// ^
16+
//
17+
// pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:9:7: Error: Couldn't find constructor 'E.f2'.
18+
// four.f2(); // Error.
19+
// ^^
20+
//
21+
// pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:28:29: Error: The constructor function type 'E Function()' isn't a subtype of 'E Function(int)'.
22+
// - 'E' is from 'pkg/front_end/testcases/enhanced_enums/constructor_calls.dart'.
23+
// factory E.f6(int value) = E.f; // Error.
24+
// ^
25+
//
26+
// pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:19:18: Error: Enums can't be instantiated.
27+
// return const E(); // Error.
28+
// ^
29+
//
30+
// pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:32:7: Error: Enums can't be instantiated.
31+
// new E(); // Error.
32+
// ^
33+
//
34+
// pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:33:9: Error: Enums can't be instantiated.
35+
// const E(); // Error.
36+
// ^
37+
//
38+
// pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:34:5: Error: Enum constructors can't be torn off.
39+
// E.new; // Error.
40+
// ^^^
41+
//
42+
// pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:36:7: Error: Enums can't be instantiated.
43+
// new E.named(); // Error.
44+
// ^
45+
//
46+
// pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:37:9: Error: Enums can't be instantiated.
47+
// const E().named(); // Error.
48+
// ^
49+
//
50+
// pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:38:5: Error: Enum constructors can't be torn off.
51+
// E.named; // Error.
52+
// ^^^^^
53+
//
54+
// pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:41:9: Error: Cannot invoke a non-'const' factory where a const expression is expected.
55+
// Try using a constructor or factory that is 'const'.
56+
// const E.f(); // Error.
57+
// ^
58+
//
59+
import self as self;
60+
import "dart:core" as core;
61+
62+
class E extends core::_Enum /*isEnum*/ {
63+
static const field core::List<self::E> values = invalid-expression "pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:8:8: Error: Couldn't find constructor 'E.f'.
64+
three.f(), // Error.
65+
^";
66+
static const field self::E one = #C3;
67+
static const field self::E two = #C6;
68+
static const field self::E three = invalid-expression "pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:8:8: Error: Couldn't find constructor 'E.f'.
69+
three.f(), // Error.
70+
^";
71+
static const field self::E four = invalid-expression "pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:9:7: Error: Couldn't find constructor 'E.f2'.
72+
four.f2(); // Error.
73+
^^";
74+
static final field dynamic _redirecting# = <dynamic>[#C7, #C8, #C9, #C10]/*isLegacy*/;
75+
const constructor •(core::int index, core::String name) → self::E
76+
: super core::_Enum::•(index, name)
77+
;
78+
const constructor named(core::int index, core::String name) → self::E
79+
: this self::E::•(index, name)
80+
;
81+
method toString() → core::String
82+
return "E.${this.{core::_Enum::_name}{core::String}}";
83+
static factory f() → self::E
84+
return invalid-expression "pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:8:8: Error: Couldn't find constructor 'E.f'.
85+
three.f(), // Error.
86+
^".{core::Iterable::first}{self::E};
87+
static factory f2() → self::E {
88+
return invalid-expression "pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:19:18: Error: Enums can't be instantiated.
89+
return const E(); // Error.
90+
^";
91+
}
92+
static factory f3() → self::E
93+
return invalid-expression "pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:22:26: Error: Enum factory constructors can't redirect to generative constructors.
94+
const factory E.f3() = E; // Error.
95+
^";
96+
static factory f4() → self::E
97+
return invalid-expression "pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:24:20: Error: Enum factory constructors can't redirect to generative constructors.
98+
factory E.f4() = E; // Error.
99+
^";
100+
static factory f5() → self::E
101+
return self::E::f();
102+
static factory f6(core::int value) → self::E
103+
return invalid-expression "pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:28:29: Error: The constructor function type 'E Function()' isn't a subtype of 'E Function(int)'.
104+
- 'E' is from 'pkg/front_end/testcases/enhanced_enums/constructor_calls.dart'.
105+
factory E.f6(int value) = E.f; // Error.
106+
^";
107+
}
108+
static method test() → dynamic {
109+
invalid-expression "pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:32:7: Error: Enums can't be instantiated.
110+
new E(); // Error.
111+
^";
112+
invalid-expression "pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:33:9: Error: Enums can't be instantiated.
113+
const E(); // Error.
114+
^";
115+
invalid-expression "pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:34:5: Error: Enum constructors can't be torn off.
116+
E.new; // Error.
117+
^^^";
118+
invalid-expression "pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:36:7: Error: Enums can't be instantiated.
119+
new E.named(); // Error.
120+
^";
121+
invalid-expression "pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:37:9: Error: Enums can't be instantiated.
122+
const E().named(); // Error.
123+
^"{dynamic}.named();
124+
invalid-expression "pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:38:5: Error: Enum constructors can't be torn off.
125+
E.named; // Error.
126+
^^^^^";
127+
self::E::f();
128+
invalid-expression "pkg/front_end/testcases/enhanced_enums/constructor_calls.dart:41:9: Error: Cannot invoke a non-'const' factory where a const expression is expected.
129+
Try using a constructor or factory that is 'const'.
130+
const E.f(); // Error.
131+
^";
132+
#C11;
133+
}
134+
static method main() → dynamic {}
135+
136+
constants {
137+
#C1 = 0
138+
#C2 = "one"
139+
#C3 = self::E {index:#C1, _name:#C2}
140+
#C4 = 1
141+
#C5 = "two"
142+
#C6 = self::E {index:#C4, _name:#C5}
143+
#C7 = constructor-tearoff self::E::f3
144+
#C8 = constructor-tearoff self::E::f4
145+
#C9 = constructor-tearoff self::E::f5
146+
#C10 = constructor-tearoff self::E::f6
147+
#C11 = constructor-tearoff self::E::f
148+
}
149+
150+
151+
Constructor coverage from constants:
152+
org-dartlang-testcase:///constructor_calls.dart:
153+
- E. (from org-dartlang-testcase:///constructor_calls.dart:11:9)
154+
- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart:76:9)
155+
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
156+
- E.named (from org-dartlang-testcase:///constructor_calls.dart:13:9)

0 commit comments

Comments
 (0)