Skip to content

Commit 6e7c2de

Browse files
zthcknitt
authored andcommitted
Avoid warning 56 blowup for dict patterns (#8403)
* avoid warning 56 blowup for dict patterns * changelog # Conflicts: # CHANGELOG.md
1 parent 58f3f22 commit 6e7c2de

6 files changed

Lines changed: 256 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
- Allow builds while watchers are running. https://github.com/rescript-lang/rescript/pull/8349
3434
- Rewatch: preserve warnings after atomic-save full rebuilds. https://github.com/rescript-lang/rescript/pull/8358
3535
- Fix type lowering for `dict{}` and `async`, so you don't need to annotate one extra time when the type is known. https://github.com/rescript-lang/rescript/pull/8359
36+
- Fix issue where warning 56 would blow up with `dict{}` patterns. https://github.com/rescript-lang/rescript/pull/8403
3637

3738
#### :memo: Documentation
3839

compiler/ml/parmatch.ml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2017,6 +2017,25 @@ let contains_extension pat =
20172017
loop pat;
20182018
!r
20192019

2020+
let contains_dict_pattern pat =
2021+
let r = ref false in
2022+
let rec loop p =
2023+
if Dict_type_helpers.has_dict_pattern_attribute p.pat_attributes then
2024+
r := true
2025+
else Typedtree.iter_pattern_desc loop p.pat_desc
2026+
in
2027+
loop pat;
2028+
!r
2029+
2030+
let rec opaque_dict_patterns pat =
2031+
if Dict_type_helpers.has_dict_pattern_attribute pat.pat_attributes then
2032+
{pat with pat_desc = Tpat_any; pat_extra = []; pat_attributes = []}
2033+
else
2034+
{
2035+
pat with
2036+
pat_desc = Typedtree.map_pattern_desc opaque_dict_patterns pat.pat_desc;
2037+
}
2038+
20202039
(* Build an untyped or-pattern from its expected type *)
20212040
let ppat_of_type env ty =
20222041
match pats_of_type env ty with
@@ -2192,6 +2211,19 @@ let check_unused pred casel =
21922211
if skip then r
21932212
else
21942213
(* Then look for empty patterns *)
2214+
let pss, qs =
2215+
if
2216+
contains_dict_pattern q
2217+
|| List.exists
2218+
(fun ps -> List.exists contains_dict_pattern ps)
2219+
pss
2220+
then
2221+
( List.filter
2222+
(fun ps -> not (List.exists contains_dict_pattern ps))
2223+
pss,
2224+
List.map opaque_dict_patterns qs )
2225+
else (pss, qs)
2226+
in
21952227
let sfs = satisfiables pss qs in
21962228
if sfs = [] then Unused
21972229
else
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
2+
Warning number 4
3+
/.../fixtures/dict_pattern_unreachable_case.res:6:3-11:3
4+
5+
4 │
6+
5 │ let f = (type a, x: kind<a>, y: kind<a>, payload: JSON.t) =>
7+
6 │ switch (x, y, payload) {
8+
7 │  | (Int, Int, _) => 0
9+
. │ ...
10+
10 │  | _ => 3
11+
11 │  }
12+
12 │
13+
14+
this pattern-matching is fragile.
15+
It will remain exhaustive when constructors are added to type kind.
16+
17+
18+
Warning number 4
19+
/.../fixtures/dict_pattern_unreachable_case.res:6:3-11:3
20+
21+
4 │
22+
5 │ let f = (type a, x: kind<a>, y: kind<a>, payload: JSON.t) =>
23+
6 │ switch (x, y, payload) {
24+
7 │  | (Int, Int, _) => 0
25+
. │ ...
26+
10 │  | _ => 3
27+
11 │  }
28+
12 │
29+
30+
this pattern-matching is fragile.
31+
It will remain exhaustive when constructors are added to type Stdlib_JSON.t.
32+
33+
34+
Warning number 56
35+
/.../fixtures/dict_pattern_unreachable_case.res:9:5-62
36+
37+
7 ┆ | (Int, Int, _) => 0
38+
8 ┆ | (String, String, _) => 1
39+
9 ┆ | (_, _, JSON.Object(dict{"operationName": JSON.String(_)})) => 2
40+
10 ┆ | _ => 3
41+
11 ┆ }
42+
43+
this match case is unreachable.
44+
Consider replacing it with a refutation case '<pat> -> .'
45+
46+
47+
Warning number 56
48+
/.../fixtures/dict_pattern_unreachable_case.res:10:5
49+
50+
8 ┆ | (String, String, _) => 1
51+
9 ┆ | (_, _, JSON.Object(dict{"operationName": JSON.String(_)})) => 2
52+
10 ┆ | _ => 3
53+
11 ┆ }
54+
12 ┆
55+
56+
this match case is unreachable.
57+
Consider replacing it with a refutation case '<pat> -> .'
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
type rec kind<_> =
2+
| Int: kind<int>
3+
| String: kind<string>
4+
5+
let f = (type a, x: kind<a>, y: kind<a>, payload: JSON.t) =>
6+
switch (x, y, payload) {
7+
| (Int, Int, _) => 0
8+
| (String, String, _) => 1
9+
| (_, _, JSON.Object(dict{"operationName": JSON.String(_)})) => 2
10+
| _ => 3
11+
}

tests/tests/src/DictPatternMatchingOptimization.mjs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,129 @@ function decode(data) {
7878
}
7979
}
8080

81+
function decodePayload(data) {
82+
let match = JSON.parse(data);
83+
if (typeof match !== "object" || match === null || Array.isArray(match)) {
84+
return 0;
85+
}
86+
let exit = 0;
87+
let tmp = match.operationName;
88+
if (typeof tmp === "string") {
89+
let tmp$1 = match.query;
90+
if (typeof tmp$1 === "string") {
91+
let tmp$2 = match.documentId;
92+
if (typeof tmp$2 === "string") {
93+
let tmp$3 = match.variables;
94+
if (typeof tmp$3 === "object" && tmp$3 !== null && !Array.isArray(tmp$3)) {
95+
return 4;
96+
}
97+
exit = 1;
98+
} else {
99+
exit = 1;
100+
}
101+
} else {
102+
exit = 1;
103+
}
104+
} else {
105+
exit = 1;
106+
}
107+
if (exit === 1) {
108+
let exit$1 = 0;
109+
let tmp$4 = match.operationName;
110+
if (typeof tmp$4 === "string") {
111+
let tmp$5 = match.query;
112+
if (typeof tmp$5 === "string") {
113+
let tmp$6 = match.documentId;
114+
if (typeof tmp$6 === "string") {
115+
return 3;
116+
}
117+
exit$1 = 2;
118+
} else {
119+
exit$1 = 2;
120+
}
121+
} else {
122+
exit$1 = 2;
123+
}
124+
if (exit$1 === 2) {
125+
let exit$2 = 0;
126+
let tmp$7 = match.operationName;
127+
if (typeof tmp$7 === "string") {
128+
let tmp$8 = match.query;
129+
if (typeof tmp$8 === "string") {
130+
let tmp$9 = match.variables;
131+
if (typeof tmp$9 === "object" && tmp$9 !== null && !Array.isArray(tmp$9)) {
132+
return 3;
133+
}
134+
exit$2 = 3;
135+
} else {
136+
exit$2 = 3;
137+
}
138+
} else {
139+
exit$2 = 3;
140+
}
141+
if (exit$2 === 3) {
142+
let exit$3 = 0;
143+
let tmp$10 = match.operationName;
144+
if (typeof tmp$10 === "string") {
145+
let tmp$11 = match.documentId;
146+
if (typeof tmp$11 === "string") {
147+
let tmp$12 = match.variables;
148+
if (typeof tmp$12 === "object" && tmp$12 !== null && !Array.isArray(tmp$12)) {
149+
return 3;
150+
}
151+
exit$3 = 4;
152+
} else {
153+
exit$3 = 4;
154+
}
155+
} else {
156+
exit$3 = 4;
157+
}
158+
if (exit$3 === 4) {
159+
let exit$4 = 0;
160+
let tmp$13 = match.query;
161+
if (typeof tmp$13 === "string") {
162+
let tmp$14 = match.documentId;
163+
if (typeof tmp$14 === "string") {
164+
let tmp$15 = match.variables;
165+
if (typeof tmp$15 === "object" && tmp$15 !== null && !Array.isArray(tmp$15)) {
166+
return 3;
167+
}
168+
exit$4 = 5;
169+
} else {
170+
exit$4 = 5;
171+
}
172+
} else {
173+
exit$4 = 5;
174+
}
175+
if (exit$4 === 5) {
176+
let exit$5 = 0;
177+
let tmp$16 = match.operationName;
178+
if (typeof tmp$16 === "string") {
179+
let tmp$17 = match.query;
180+
if (typeof tmp$17 === "string") {
181+
return 2;
182+
}
183+
exit$5 = 6;
184+
} else {
185+
exit$5 = 6;
186+
}
187+
if (exit$5 === 6) {
188+
let tmp$18 = match.operationName;
189+
if (typeof tmp$18 === "string") {
190+
return 1;
191+
} else {
192+
return 0;
193+
}
194+
}
195+
}
196+
}
197+
}
198+
}
199+
}
200+
}
201+
81202
export {
82203
decode,
204+
decodePayload,
83205
}
84206
/* No side effect */

tests/tests/src/DictPatternMatchingOptimization.res

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,36 @@ let decode = (~data: string): array<inbound> =>
1313
| JSON.Object(dict{"type": JSON.String("f")}) => [F]
1414
| _ => []
1515
}
16+
17+
let decodePayload = data =>
18+
switch JSON.parseOrThrow(data) {
19+
| JSON.Object(dict{
20+
"operationName": JSON.String(_),
21+
"query": JSON.String(_),
22+
"documentId": JSON.String(_),
23+
"variables": JSON.Object(_),
24+
}) => 4
25+
| JSON.Object(dict{
26+
"operationName": JSON.String(_),
27+
"query": JSON.String(_),
28+
"documentId": JSON.String(_),
29+
}) => 3
30+
| JSON.Object(dict{
31+
"operationName": JSON.String(_),
32+
"query": JSON.String(_),
33+
"variables": JSON.Object(_),
34+
}) => 3
35+
| JSON.Object(dict{
36+
"operationName": JSON.String(_),
37+
"documentId": JSON.String(_),
38+
"variables": JSON.Object(_),
39+
}) => 3
40+
| JSON.Object(dict{
41+
"query": JSON.String(_),
42+
"documentId": JSON.String(_),
43+
"variables": JSON.Object(_),
44+
}) => 3
45+
| JSON.Object(dict{"operationName": JSON.String(_), "query": JSON.String(_)}) => 2
46+
| JSON.Object(dict{"operationName": JSON.String(_)}) => 1
47+
| _ => 0
48+
}

0 commit comments

Comments
 (0)