Skip to content

Commit 8b3f0d0

Browse files
committed
better error when trying to use a keyword as a record field
1 parent 9efa7e2 commit 8b3f0d0

File tree

7 files changed

+163
-13
lines changed

7 files changed

+163
-13
lines changed

compiler/syntax/src/res_core.ml

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,7 +1377,19 @@ and parse_record_pattern_row p =
13771377
| Underscore ->
13781378
Parser.next p;
13791379
Some (false, PatUnderscore)
1380-
| _ -> None
1380+
| _ ->
1381+
if Token.is_keyword p.token then (
1382+
let keyword_txt = Token.to_string p.token in
1383+
let keyword_start = p.Parser.start_pos in
1384+
let keyword_end = p.Parser.end_pos in
1385+
let message =
1386+
"Cannot use keyword `" ^ keyword_txt
1387+
^ "` here. Keywords are not allowed as record field names."
1388+
in
1389+
Parser.err ~start_pos:keyword_start ~end_pos:keyword_end p
1390+
(Diagnostics.message message);
1391+
None)
1392+
else None
13811393

13821394
and parse_record_pattern ~attrs p =
13831395
let start_pos = p.start_pos in
@@ -2923,6 +2935,25 @@ and parse_braced_or_record_expr p =
29232935
let start_pos = p.Parser.start_pos in
29242936
Parser.expect Lbrace p;
29252937
match p.Parser.token with
2938+
| token when Token.is_keyword token ->
2939+
let colon_follows =
2940+
Parser.lookahead p (fun st ->
2941+
Parser.next st;
2942+
st.Parser.token = Colon)
2943+
in
2944+
(* If a colon follows then this is likely to be a record field. *)
2945+
(if colon_follows then
2946+
let keyword_txt = Token.to_string token in
2947+
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
2948+
(Diagnostics.message
2949+
("Cannot use keyword `" ^ keyword_txt
2950+
^ "` as a record field name. Suggestion: rename it (e.g. `"
2951+
^ keyword_txt ^ "_`)")));
2952+
let expr = parse_expr_block p in
2953+
Parser.expect Rbrace p;
2954+
let loc = mk_loc start_pos p.prev_end_pos in
2955+
let braces = make_braces_attr loc in
2956+
{expr with pexp_attributes = braces :: expr.pexp_attributes}
29262957
| Rbrace ->
29272958
Parser.next p;
29282959
let loc = mk_loc start_pos p.prev_end_pos in
@@ -3239,7 +3270,20 @@ and parse_record_expr_row p :
32393270
in
32403271
Some {lid = field; x = value; opt = true}
32413272
| _ -> None)
3242-
| _ -> None
3273+
| _ ->
3274+
if Token.is_keyword p.token then (
3275+
let keyword_txt = Token.to_string p.token in
3276+
let keyword_start = p.Parser.start_pos in
3277+
let keyword_end = p.Parser.end_pos in
3278+
let message =
3279+
"Cannot use keyword `" ^ keyword_txt
3280+
^ "` as a record field name. Suggestion: rename it (e.g. `"
3281+
^ keyword_txt ^ "_`)"
3282+
in
3283+
Parser.err ~start_pos:keyword_start ~end_pos:keyword_end p
3284+
(Diagnostics.message message);
3285+
None)
3286+
else None
32433287

32443288
and parse_dict_expr_row p =
32453289
match p.Parser.token with
@@ -4736,17 +4780,32 @@ and parse_field_declaration_region ?current_type_name_path ?inline_types_context
47364780
let loc = mk_loc start_pos typ.ptyp_loc.loc_end in
47374781
Some (Ast_helper.Type.field ~attrs ~loc ~mut ~optional name typ)
47384782
| _ ->
4739-
if attrs <> [] then
4740-
Parser.err ~start_pos p
4741-
(Diagnostics.message
4742-
"Attributes and doc comments can only be used at the beginning of a \
4743-
field declaration");
4744-
if mut = Mutable then
4745-
Parser.err ~start_pos p
4746-
(Diagnostics.message
4747-
"The `mutable` qualifier can only be used at the beginning of a \
4748-
field declaration");
4749-
None
4783+
if Token.is_keyword p.token then (
4784+
let keyword_txt = Token.to_string p.token in
4785+
let keyword_start = p.Parser.start_pos in
4786+
let keyword_end = p.Parser.end_pos in
4787+
let message =
4788+
"Cannot use keyword `" ^ keyword_txt
4789+
^ "` as a record field name. Suggestion: rename it (e.g. `"
4790+
^ keyword_txt ^ "_`)\n" ^ " If you need the field to be \""
4791+
^ keyword_txt ^ "\" at runtime, annotate the field: `@as(\""
4792+
^ keyword_txt ^ "\") " ^ keyword_txt ^ "_ : ...`"
4793+
in
4794+
Parser.err ~start_pos:keyword_start ~end_pos:keyword_end p
4795+
(Diagnostics.message message);
4796+
None)
4797+
else (
4798+
if attrs <> [] then
4799+
Parser.err ~start_pos p
4800+
(Diagnostics.message
4801+
"Attributes and doc comments can only be used at the beginning of \
4802+
a field declaration");
4803+
if mut = Mutable then
4804+
Parser.err ~start_pos p
4805+
(Diagnostics.message
4806+
"The `mutable` qualifier can only be used at the beginning of a \
4807+
field declaration");
4808+
None)
47504809

47514810
(* record-decl ::=
47524811
* | { field-decl }
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
Syntax error!
3+
syntax_tests/data/parsing/errors/structure/recordFieldKeywordInExpr.res:1:10-13
4+
5+
1 │ let r = {type: 1}
6+
2 │
7+
8+
Cannot use keyword `type` as a record field name. Suggestion: rename it (e.g. `type_`)
9+
10+
11+
Syntax error!
12+
syntax_tests/data/parsing/errors/structure/recordFieldKeywordInExpr.res:1:15-16
13+
14+
1 │ let r = {type: 1}
15+
2 │
16+
17+
consecutive statements on a line must be separated by ';' or a newline
18+
19+
let r = ((([%rescript.exprhole ] : [%rescript.typehole ]))[@res.braces ])
20+
;;1
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
Syntax error!
3+
syntax_tests/data/parsing/errors/structure/recordFieldKeywordInPattern.res:1:6-9
4+
5+
1 │ let {type} = r
6+
2 │
7+
3 │
8+
9+
Cannot use keyword `type` here. Keywords are not allowed as record field names.
10+
11+
12+
Syntax error!
13+
syntax_tests/data/parsing/errors/structure/recordFieldKeywordInPattern.res:1:10
14+
15+
1 │ let {type} = r
16+
2 │
17+
3 │
18+
19+
I'm not sure what to parse here when looking at "}".
20+
21+
let { } = [%rescript.exprhole ]
22+
;;r
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
Syntax error!
3+
syntax_tests/data/parsing/errors/structure/recordFieldKeywordInType.res:3:3-6
4+
5+
1 │ type r = {
6+
2 │ id: string,
7+
3 │ type: int,
8+
4 │ }
9+
5 │
10+
11+
Cannot use keyword `type` as a record field name. Suggestion: rename it (e.g. `type_`)
12+
If you need the field to be "type" at runtime, annotate the field: `@as("type") type_ : ...`
13+
14+
15+
Syntax error!
16+
syntax_tests/data/parsing/errors/structure/recordFieldKeywordInType.res:3:7
17+
18+
1 │ type r = {
19+
2 │ id: string,
20+
3 │ type: int,
21+
4 │ }
22+
5 │
23+
24+
I'm not sure what to parse here when looking at ":".
25+
26+
27+
Syntax error!
28+
syntax_tests/data/parsing/errors/structure/recordFieldKeywordInType.res:3:12
29+
30+
1 │ type r = {
31+
2 │ id: string,
32+
3 │ type: int,
33+
4 │ }
34+
5 │
35+
36+
I'm not sure what to parse here when looking at ",".
37+
38+
type nonrec r = {
39+
id: string }
40+
type nonrec _
41+
;;int
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
let r = {type: 1}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
let {type} = r
2+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
type r = {
2+
id: string,
3+
type: int,
4+
}
5+

0 commit comments

Comments
 (0)