Skip to content

Commit 4b14012

Browse files
authored
#1401. [Patterns] Switch expression tests (#1633)
* #1401. [Patterns] Switch expression tests added * #1401. [Patterns] More switch expression tests added * #1401. Implement review notes * #1401. Fix spec_parser failures
1 parent 15ee0c8 commit 4b14012

22 files changed

+1589
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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+
/// @assertion
6+
/// primary ::= // Existing productions...
7+
/// | switchExpression
8+
///
9+
/// switchExpression ::= 'switch' '(' expression ')' '{'
10+
/// switchExpressionCase ( ',' switchExpressionCase )*
11+
/// ','? '}'
12+
/// switchExpressionCase ::= guardedPattern '=>' expression
13+
///
14+
/// The body is a series of cases. Each case has a pattern, optional guard, and
15+
/// a single expression body. As with other expression forms containing a list
16+
/// of subelements (argument lists, collection literals), the cases are
17+
/// separated by commas with an optional trailing comma. Since the body of each
18+
/// case is a single expression with a known terminator, it's easy to tell when
19+
/// one case ends and the next begins. That lets us do away with the case
20+
/// keyword.
21+
///
22+
/// To keep the syntax small and light, we also disallow a default clause.
23+
/// Instead, you can use a shorter _ wildcard pattern to catch any remaining
24+
/// values.
25+
///
26+
/// @description Check that it is a compile-time error if a switch expression
27+
/// contains `case`, `default` or uses `;` as a separator
28+
/// @author [email protected]
29+
30+
// SharedOptions=--enable-experiment=patterns
31+
32+
main () {
33+
int i = 42;
34+
var x = switch(i) {
35+
case 1 => "one"
36+
// ^^^^
37+
// [analyzer] unspecified
38+
// [cfe] unspecified
39+
_ => "any"
40+
};
41+
42+
var y = switch(i) {
43+
1 => "one",
44+
default => "default"
45+
// ^^^^^^^
46+
// [analyzer] unspecified
47+
// [cfe] unspecified
48+
};
49+
50+
var z = switch(i) {
51+
1 => "one";
52+
// ^
53+
// [analyzer] unspecified
54+
// [cfe] unspecified
55+
_ => "any"
56+
};
57+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
/// @assertion
6+
/// primary ::= // Existing productions...
7+
/// | switchExpression
8+
///
9+
/// switchExpression ::= 'switch' '(' expression ')' '{'
10+
/// switchExpressionCase ( ',' switchExpressionCase )*
11+
/// ','? '}'
12+
/// switchExpressionCase ::= guardedPattern '=>' expression
13+
///
14+
/// The body is a series of cases. Each case has a pattern, optional guard, and
15+
/// a single expression body. As with other expression forms containing a list
16+
/// of subelements (argument lists, collection literals), the cases are
17+
/// separated by commas with an optional trailing comma. Since the body of each
18+
/// case is a single expression with a known terminator, it's easy to tell when
19+
/// one case ends and the next begins. That lets us do away with the case
20+
/// keyword.
21+
///
22+
/// To keep the syntax small and light, we also disallow a default clause.
23+
/// Instead, you can use a shorter _ wildcard pattern to catch any remaining
24+
/// values.
25+
///
26+
/// @description Check that it is a compile-time error if a switch expression
27+
/// uses `:` instead of '=>'
28+
/// @author [email protected]
29+
30+
// SharedOptions=--enable-experiment=patterns
31+
32+
main () {
33+
int i = 42;
34+
var x = switch(i) {
35+
1: "one"
36+
// ^
37+
// [analyzer] unspecified
38+
// [cfe] unspecified
39+
_ => "any"
40+
};
41+
42+
var y = switch(i) {
43+
1 => "one",
44+
_: "zero"
45+
// ^
46+
// [analyzer] unspecified
47+
// [cfe] unspecified
48+
};
49+
50+
var z = switch(i) {
51+
1: "one",
52+
// ^
53+
// [analyzer] unspecified
54+
// [cfe] unspecified
55+
_: "zero"
56+
// ^
57+
// [analyzer] unspecified
58+
// [cfe] unspecified
59+
};
60+
}
Lines changed: 45 additions & 0 deletions
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+
/// @assertion
6+
/// primary ::= // Existing productions...
7+
/// | switchExpression
8+
///
9+
/// switchExpression ::= 'switch' '(' expression ')' '{'
10+
/// switchExpressionCase ( ',' switchExpressionCase )*
11+
/// ','? '}'
12+
/// switchExpressionCase ::= guardedPattern '=>' expression
13+
///
14+
/// The body is a series of cases. Each case has a pattern, optional guard, and
15+
/// a single expression body. As with other expression forms containing a list
16+
/// of subelements (argument lists, collection literals), the cases are
17+
/// separated by commas with an optional trailing comma. Since the body of each
18+
/// case is a single expression with a known terminator, it's easy to tell when
19+
/// one case ends and the next begins. That lets us do away with the case
20+
/// keyword.
21+
///
22+
/// To keep the syntax small and light, we also disallow a default clause.
23+
/// Instead, you can use a shorter _ wildcard pattern to catch any remaining
24+
/// values.
25+
///
26+
/// @description Check that it is a compile-time error if a case expressions in
27+
/// a switch expression are not separated by comma
28+
/// @author [email protected]
29+
30+
// SharedOptions=--enable-experiment=patterns
31+
32+
main () {
33+
int i = 42;
34+
var x = switch(i) {
35+
1 => "one"
36+
// ^
37+
// [analyzer] unspecified
38+
// [cfe] unspecified
39+
2 => "two"
40+
// ^
41+
// [analyzer] unspecified
42+
// [cfe] unspecified
43+
_ => "any"
44+
};
45+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
/// @assertion
6+
/// primary ::= // Existing productions...
7+
/// | switchExpression
8+
///
9+
/// switchExpression ::= 'switch' '(' expression ')' '{'
10+
/// switchExpressionCase ( ',' switchExpressionCase )*
11+
/// ','? '}'
12+
/// switchExpressionCase ::= guardedPattern '=>' expression
13+
///
14+
/// The body is a series of cases. Each case has a pattern, optional guard, and
15+
/// a single expression body. As with other expression forms containing a list
16+
/// of subelements (argument lists, collection literals), the cases are
17+
/// separated by commas with an optional trailing comma. Since the body of each
18+
/// case is a single expression with a known terminator, it's easy to tell when
19+
/// one case ends and the next begins. That lets us do away with the case
20+
/// keyword.
21+
///
22+
/// To keep the syntax small and light, we also disallow a default clause.
23+
/// Instead, you can use a shorter _ wildcard pattern to catch any remaining
24+
/// values.
25+
///
26+
/// @description Check that it is not an error if a trailing comma is not
27+
/// omitted
28+
/// @author [email protected]
29+
30+
// SharedOptions=--enable-experiment=patterns
31+
32+
import "../../Utils/expect.dart";
33+
34+
main () {
35+
int i = 2;
36+
var x = switch(i) {
37+
1 => "one",
38+
2 => "two",
39+
_ => "any",
40+
};
41+
Expect.equals("two", x);
42+
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
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+
/// @assertion
6+
/// primary ::= // Existing productions...
7+
/// | switchExpression
8+
///
9+
/// switchExpression ::= 'switch' '(' expression ')' '{'
10+
/// switchExpressionCase ( ',' switchExpressionCase )*
11+
/// ','? '}'
12+
/// switchExpressionCase ::= guardedPattern '=>' expression
13+
///
14+
/// The body is a series of cases. Each case has a pattern, optional guard, and
15+
/// a single expression body. As with other expression forms containing a list
16+
/// of subelements (argument lists, collection literals), the cases are
17+
/// separated by commas with an optional trailing comma. Since the body of each
18+
/// case is a single expression with a known terminator, it's easy to tell when
19+
/// one case ends and the next begins. That lets us do away with the case
20+
/// keyword.
21+
///
22+
/// To keep the syntax small and light, we also disallow a default clause.
23+
/// Instead, you can use a shorter _ wildcard pattern to catch any remaining
24+
/// values.
25+
///
26+
/// Slotting into primary means it can be used anywhere any expression can
27+
/// appear, even as operands to unary and binary operators. Many of these uses
28+
/// are ugly, but not any more problematic than using a collection literal in
29+
/// the same context since a switch expression is always delimited by a switch
30+
/// and }.
31+
///
32+
/// @description Checks a switch expression with different subpatterns and a
33+
/// guard expression
34+
/// @author [email protected]
35+
36+
// SharedOptions=--enable-experiment=patterns,records
37+
38+
import "../../Utils/expect.dart";
39+
import "patterns_lib.dart";
40+
41+
String test(int value) =>
42+
switch (value) {
43+
var a1 && 0 || var a1 && 1 when a1 == 0 => "logical-or-1",
44+
1 || 2 => "logical-or-2",
45+
var a2 && > 10 && var b2 && < 15 when a2 == 14 => "logical-and-1",
46+
> 12 && < 17 => "logical-and-2",
47+
< 25 when false => "relational-1",
48+
<= 25 when true => "relational-2",
49+
30 when 1 > 2 => "constant-1",
50+
30 when 1 < 2 => "constant-2",
51+
(var a3 && 40) when a3 == 42 => "parenthesized-1",
52+
(40) => "parenthesized-2",
53+
_ => "default"
54+
};
55+
56+
String testCast(num value) =>
57+
switch (value) {
58+
var c1 as int when c1 == 42 => "cast-1 =$c1",
59+
var c2 as double when c2 > 4 => "cast-2 =${c2.toStringAsFixed(2)}",
60+
_ => "default"
61+
};
62+
63+
String testNullCheck(int? value) =>
64+
switch (value) {
65+
var a1? when a1 > 0 => "null-check-1",
66+
var a2? => "null-check-2",
67+
_ => "default"
68+
};
69+
70+
String testNullAssert(int? value) {
71+
switch (value) {
72+
var a1! when a1 > 0 => "null-assert-1",
73+
var a2! => "null-assert-2",
74+
_ => "default"
75+
};
76+
77+
String testVariable(int value) =>
78+
switch (value) {
79+
var a1 when a1 > 0 => "variable-1",
80+
var a2 when a2 < 0 => "variable-2",
81+
_ => "default"
82+
};
83+
84+
String testList(List<int> list) =>
85+
switch (list) {
86+
[1, var a] when a > 0 => "list-1",
87+
[1, final b, ...] when b < 0 => "list-2",
88+
[1, _, ...r] when !r.isEmpty => "list-3",
89+
_ => "default"
90+
};
91+
92+
String testMap(Map<String, int> map) {
93+
switch (map) {
94+
{"key1": 1, "key2": var a} when a > 0 => "map-1",
95+
{"key1": 1, "key2": final b} when b < 0 => "map-2",
96+
_ => "default"
97+
};
98+
99+
String testRecord(Record record) {
100+
switch (record) {
101+
(1, var a) when a > 0 => "record-1",
102+
(1, final b) when b < 0 => "record-2",
103+
(1, 2, n: var c) when c > 0 => "record-3",
104+
(1, 2, n: final d) when d < 0 => "record-4",
105+
_ => "default"
106+
};
107+
108+
String testObject(Shape shape) =>
109+
switch (shape) {
110+
Square(sizeAsInt: var a) when a > 2 => "object-1",
111+
Square(areaAsInt: final b) when b < 4 => "object-2",
112+
Circle(sizeAsInt: var c) when c > 1 => "object-3",
113+
Circle(sizeAsInt: final d) when d < 1 => "object-4",
114+
_ => "default"
115+
};
116+
117+
main() {
118+
Expect.equals("logical-or-1", test(0));
119+
Expect.equals("logical-or-2", test(1));
120+
Expect.equals("logical-and-1", test(14));
121+
Expect.equals("logical-and-2", test(13));
122+
Expect.equals("default", test(24));
123+
Expect.equals("relational-2", test(23));
124+
Expect.equals("constant-2", test(30));
125+
Expect.equals("parenthesized-2", test(40));
126+
Expect.equals("default", test(100));
127+
128+
Expect.equals("cast-1 =42", testCast(42));
129+
Expect.equals("cast-2 =41.00", testCast(41));
130+
Expect.equals("default", testCast(3));
131+
132+
Expect.equals("null-check-2", testNullCheck(0));
133+
Expect.equals("null-check-1", testNullCheck(1));
134+
Expect.equals("default", testNullCheck(null));
135+
136+
Expect.equals("null-assert-1", testNullAssert(1));
137+
Expect.equals("null-assert-2", testNullAssert(0));
138+
Expect.throws(() {testNullAssert(null);});
139+
140+
Expect.equals("variable-1", testVariable(1));
141+
Expect.equals("variable-2", testVariable(-1));
142+
Expect.equals("default", testVariable(0));
143+
144+
Expect.equals("list-1", testList([1, 2]));
145+
Expect.equals("list-2", testList([1, -1]));
146+
Expect.equals("list-3", testList([1, 0, 3]));
147+
Expect.equals("default", testList([1, 0]));
148+
149+
Expect.equals("map-1", testMap({"key1": 1, "key2": 2}));
150+
Expect.equals("map-2", testMap({"key1": 1, "key2": -3}));
151+
Expect.equals("default", testMap({"key1": 1, "key2": 0}));
152+
153+
Expect.equals("record-1", testRecord((1, 2)));
154+
Expect.equals("record-2", testRecord((1, -3)));
155+
Expect.equals("record-3", testRecord((1, 2, n: 3)));
156+
Expect.equals("record-4", testRecord((1, 2, n: -4)));
157+
Expect.equals("default", testRecord((1, 0)));
158+
Expect.equals("default", testRecord((1, 2, n: 0)));
159+
160+
Expect.equals("object-1", testObject(Square(3)));
161+
Expect.equals("object-2", testObject(Square(1)));
162+
Expect.equals("default", testObject(Square(0)));
163+
Expect.equals("object-3", testObject(Circle(2)));
164+
Expect.equals("object-4", testObject(Circle(0.5)));
165+
Expect.equals("default", testObject(Circle(1)));
166+
}

0 commit comments

Comments
 (0)