Skip to content

Commit f4dd96c

Browse files
johnniwinthercommit-bot@chromium.org
authored andcommitted
[cfe] Handle nullable enum in switch case
Closes #43348 Change-Id: Id2bb94f2019e85021c43eb7b2a5d12a651159369 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/162184 Reviewed-by: Dmitry Stefantsov <[email protected]> Commit-Queue: Johnni Winther <[email protected]>
1 parent 3d77741 commit f4dd96c

9 files changed

+571
-2
lines changed

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5332,6 +5332,9 @@ class InferenceVisitor
53325332
enumFields = expressionType.classNode.fields
53335333
.where((Field field) => field.isConst && field.type == expressionType)
53345334
.toSet();
5335+
if (expressionType.isPotentiallyNullable) {
5336+
enumFields.add(null);
5337+
}
53355338
}
53365339

53375340
inferrer.flowAnalysis.switchStatement_expressionEnd(node);
@@ -5350,8 +5353,12 @@ class InferenceVisitor
53505353
Expression caseExpression = caseExpressionResult.expression;
53515354
switchCase.expressions[index] = caseExpression..parent = switchCase;
53525355
DartType caseExpressionType = caseExpressionResult.inferredType;
5353-
if (enumFields != null && caseExpression is StaticGet) {
5354-
enumFields.remove(caseExpression.target);
5356+
if (enumFields != null) {
5357+
if (caseExpression is StaticGet) {
5358+
enumFields.remove(caseExpression.target);
5359+
} else if (caseExpression is NullLiteral) {
5360+
enumFields.remove(null);
5361+
}
53555362
}
53565363

53575364
if (!inferrer.isTopLevel) {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright (c) 2020, 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 Enum { e1, e2 }
6+
7+
int method1(Enum? e) {
8+
switch (e) {
9+
case Enum.e1:
10+
case Enum.e2:
11+
return 0;
12+
}
13+
}
14+
15+
int method2(Enum? e) {
16+
switch (e) {
17+
case Enum.e1:
18+
case Enum.e2:
19+
return 0;
20+
case null:
21+
return 1;
22+
}
23+
}
24+
25+
int method3(Enum? e) {
26+
switch (e) {
27+
case Enum.e1:
28+
case Enum.e2:
29+
return 0;
30+
default:
31+
return 1;
32+
}
33+
}
34+
35+
int method4(Enum? e) {
36+
switch (e) {
37+
case Enum.e1:
38+
case Enum.e2:
39+
return 0;
40+
case null:
41+
default:
42+
return 1;
43+
}
44+
}
45+
46+
test() {
47+
method1(Enum.e1);
48+
}
49+
50+
main() {
51+
expect(0, method2(Enum.e1));
52+
expect(0, method2(Enum.e2));
53+
expect(1, method2(null));
54+
55+
expect(0, method3(Enum.e1));
56+
expect(0, method3(Enum.e2));
57+
expect(1, method3(null));
58+
59+
expect(0, method4(Enum.e1));
60+
expect(0, method4(Enum.e2));
61+
expect(1, method4(null));
62+
}
63+
64+
expect(expected, actual) {
65+
if (expected != actual) {
66+
throw 'Expected $expected, actual $actual.';
67+
}
68+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
class Enum extends core::Object /*isEnum*/ {
6+
final field core::int index;
7+
final field core::String _name;
8+
static const field core::List<self::Enum> values = const <self::Enum>[self::Enum::e1, self::Enum::e2];
9+
static const field self::Enum e1 = const self::Enum::•(0, "Enum.e1");
10+
static const field self::Enum e2 = const self::Enum::•(1, "Enum.e2");
11+
const constructor •(core::int index, core::String _name) → self::Enum
12+
: self::Enum::index = index, self::Enum::_name = _name, super core::Object::•()
13+
;
14+
method toString() → core::String
15+
return this.{=self::Enum::_name};
16+
}
17+
static method method1(self::Enum? e) → core::int
18+
;
19+
static method method2(self::Enum? e) → core::int
20+
;
21+
static method method3(self::Enum? e) → core::int
22+
;
23+
static method method4(self::Enum? e) → core::int
24+
;
25+
static method test() → dynamic
26+
;
27+
static method main() → dynamic
28+
;
29+
static method expect(dynamic expected, dynamic actual) → dynamic
30+
;
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
library /*isNonNullableByDefault*/;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/nnbd/switch_nullable_enum.dart:7:5: Error: A non-null value must be returned since the return type 'int' doesn't allow null.
6+
// int method1(Enum? e) {
7+
// ^
8+
//
9+
import self as self;
10+
import "dart:core" as core;
11+
12+
class Enum extends core::Object /*isEnum*/ {
13+
final field core::int index;
14+
final field core::String _name;
15+
static const field core::List<self::Enum> values = #C7;
16+
static const field self::Enum e1 = #C3;
17+
static const field self::Enum e2 = #C6;
18+
const constructor •(core::int index, core::String _name) → self::Enum
19+
: self::Enum::index = index, self::Enum::_name = _name, super core::Object::•()
20+
;
21+
method toString() → core::String
22+
return this.{=self::Enum::_name};
23+
}
24+
static method method1(self::Enum? e) → core::int {
25+
switch(e) {
26+
#L1:
27+
case #C3:
28+
case #C6:
29+
{
30+
return 0;
31+
}
32+
}
33+
return let final<BottomType> #t1 = invalid-expression "pkg/front_end/testcases/nnbd/switch_nullable_enum.dart:7:5: Error: A non-null value must be returned since the return type 'int' doesn't allow null.
34+
int method1(Enum? e) {
35+
^" in null;
36+
}
37+
static method method2(self::Enum? e) → core::int {
38+
switch(e) {
39+
#L2:
40+
case #C3:
41+
case #C6:
42+
{
43+
return 0;
44+
}
45+
#L3:
46+
case #C8:
47+
{
48+
return 1;
49+
}
50+
}
51+
}
52+
static method method3(self::Enum? e) → core::int {
53+
switch(e) {
54+
#L4:
55+
case #C3:
56+
case #C6:
57+
{
58+
return 0;
59+
}
60+
#L5:
61+
default:
62+
{
63+
return 1;
64+
}
65+
}
66+
}
67+
static method method4(self::Enum? e) → core::int {
68+
switch(e) {
69+
#L6:
70+
case #C3:
71+
case #C6:
72+
{
73+
return 0;
74+
}
75+
#L7:
76+
case #C8:
77+
default:
78+
{
79+
return 1;
80+
}
81+
}
82+
}
83+
static method test() → dynamic {
84+
self::method1(#C3);
85+
}
86+
static method main() → dynamic {
87+
self::expect(0, self::method2(#C3));
88+
self::expect(0, self::method2(#C6));
89+
self::expect(1, self::method2(null));
90+
self::expect(0, self::method3(#C3));
91+
self::expect(0, self::method3(#C6));
92+
self::expect(1, self::method3(null));
93+
self::expect(0, self::method4(#C3));
94+
self::expect(0, self::method4(#C6));
95+
self::expect(1, self::method4(null));
96+
}
97+
static method expect(dynamic expected, dynamic actual) → dynamic {
98+
if(!expected.{core::Object::==}(actual)) {
99+
throw "Expected ${expected}, actual ${actual}.";
100+
}
101+
}
102+
103+
constants {
104+
#C1 = 0
105+
#C2 = "Enum.e1"
106+
#C3 = self::Enum {index:#C1, _name:#C2}
107+
#C4 = 1
108+
#C5 = "Enum.e2"
109+
#C6 = self::Enum {index:#C4, _name:#C5}
110+
#C7 = <self::Enum>[#C3, #C6]
111+
#C8 = null
112+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
library /*isNonNullableByDefault*/;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/nnbd/switch_nullable_enum.dart:7:5: Error: A non-null value must be returned since the return type 'int' doesn't allow null.
6+
// int method1(Enum? e) {
7+
// ^
8+
//
9+
import self as self;
10+
import "dart:core" as core;
11+
12+
class Enum extends core::Object /*isEnum*/ {
13+
final field core::int index;
14+
final field core::String _name;
15+
static const field core::List<self::Enum> values = #C7;
16+
static const field self::Enum e1 = #C3;
17+
static const field self::Enum e2 = #C6;
18+
const constructor •(core::int index, core::String _name) → self::Enum
19+
: self::Enum::index = index, self::Enum::_name = _name, super core::Object::•()
20+
;
21+
method toString() → core::String
22+
return this.{=self::Enum::_name};
23+
}
24+
static method method1(self::Enum? e) → core::int {
25+
switch(e) {
26+
#L1:
27+
case #C3:
28+
case #C6:
29+
{
30+
return 0;
31+
}
32+
}
33+
return let final<BottomType> #t1 = invalid-expression "pkg/front_end/testcases/nnbd/switch_nullable_enum.dart:7:5: Error: A non-null value must be returned since the return type 'int' doesn't allow null.
34+
int method1(Enum? e) {
35+
^" in null;
36+
}
37+
static method method2(self::Enum? e) → core::int {
38+
switch(e) {
39+
#L2:
40+
case #C3:
41+
case #C6:
42+
{
43+
return 0;
44+
}
45+
#L3:
46+
case #C8:
47+
{
48+
return 1;
49+
}
50+
}
51+
}
52+
static method method3(self::Enum? e) → core::int {
53+
switch(e) {
54+
#L4:
55+
case #C3:
56+
case #C6:
57+
{
58+
return 0;
59+
}
60+
#L5:
61+
default:
62+
{
63+
return 1;
64+
}
65+
}
66+
}
67+
static method method4(self::Enum? e) → core::int {
68+
switch(e) {
69+
#L6:
70+
case #C3:
71+
case #C6:
72+
{
73+
return 0;
74+
}
75+
#L7:
76+
case #C8:
77+
default:
78+
{
79+
return 1;
80+
}
81+
}
82+
}
83+
static method test() → dynamic {
84+
self::method1(#C3);
85+
}
86+
static method main() → dynamic {
87+
self::expect(0, self::method2(#C3));
88+
self::expect(0, self::method2(#C6));
89+
self::expect(1, self::method2(null));
90+
self::expect(0, self::method3(#C3));
91+
self::expect(0, self::method3(#C6));
92+
self::expect(1, self::method3(null));
93+
self::expect(0, self::method4(#C3));
94+
self::expect(0, self::method4(#C6));
95+
self::expect(1, self::method4(null));
96+
}
97+
static method expect(dynamic expected, dynamic actual) → dynamic {
98+
if(!expected.{core::Object::==}(actual)) {
99+
throw "Expected ${expected}, actual ${actual}.";
100+
}
101+
}
102+
103+
constants {
104+
#C1 = 0
105+
#C2 = "Enum.e1"
106+
#C3 = self::Enum {index:#C1, _name:#C2}
107+
#C4 = 1
108+
#C5 = "Enum.e2"
109+
#C6 = self::Enum {index:#C4, _name:#C5}
110+
#C7 = <self::Enum>[#C3, #C6]
111+
#C8 = null
112+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
enum Enum { e1, e2 }
2+
int method1(Enum? e) {}
3+
int method2(Enum? e) {}
4+
int method3(Enum? e) {}
5+
int method4(Enum? e) {}
6+
test() {}
7+
main() {}
8+
expect(expected, actual) {}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
enum Enum { e1, e2 }
2+
expect(expected, actual) {}
3+
int method1(Enum? e) {}
4+
int method2(Enum? e) {}
5+
int method3(Enum? e) {}
6+
int method4(Enum? e) {}
7+
main() {}
8+
test() {}

0 commit comments

Comments
 (0)