Skip to content

Commit 69bc0d0

Browse files
authored
Merge branch 'master' into complete-throw
2 parents 678220d + f53e36d commit 69bc0d0

File tree

12 files changed

+222
-7
lines changed

12 files changed

+222
-7
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#### :nail_care: Polish
3232

3333
- Add (dev-)dependencies to build schema. https://github.com/rescript-lang/rescript/pull/7892
34+
- Dedicated error for dict literal spreads. https://github.com/rescript-lang/rescript/pull/7901
35+
- Dedicated error message for when mixing up `:` and `=` in various positions. https://github.com/rescript-lang/rescript/pull/7900
3436
- Add completions for `throw`. https://github.com/rescript-lang/rescript/pull/7905
3537

3638
#### :house: Internal

compiler/syntax/src/res_core.ml

Lines changed: 149 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,26 @@ module ErrorMessages = struct
9797
...b}` wouldn't make sense, as `b` would override every field of `a` \
9898
anyway."
9999

100+
let dict_expr_spread = "Dict literals do not support spread (`...`) yet."
101+
102+
let record_field_missing_colon =
103+
"Records use `:` when assigning fields. Example: `{field: value}`"
104+
105+
let record_pattern_field_missing_colon =
106+
"Record patterns use `:` when matching fields. Example: `{field: value}`"
107+
108+
let record_type_field_missing_colon =
109+
"Record fields in type declarations use `:`. Example: `{field: string}`"
110+
111+
let dict_field_missing_colon =
112+
"Dict entries use `:` to separate keys from values. Example: `{\"k\": v}`"
113+
114+
let labelled_argument_missing_equal =
115+
"Use `=` to pass a labelled argument. Example: `~label=value`"
116+
117+
let optional_labelled_argument_missing_equal =
118+
"Optional labelled arguments use `=?`. Example: `~label=?value`"
119+
100120
let variant_ident =
101121
"A polymorphic variant (e.g. #id) must start with an alphabetical letter \
102122
or be a number (e.g. #742)"
@@ -1412,6 +1432,13 @@ and parse_record_pattern_row_field ~attrs p =
14121432
let optional = parse_optional_label p in
14131433
let pat = parse_pattern p in
14141434
(pat, optional)
1435+
| Equal ->
1436+
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
1437+
(Diagnostics.message ErrorMessages.record_pattern_field_missing_colon);
1438+
Parser.next p;
1439+
let optional = parse_optional_label p in
1440+
let pat = parse_pattern p in
1441+
(pat, optional)
14151442
| _ ->
14161443
( Ast_helper.Pat.var ~loc:label.loc ~attrs
14171444
(Location.mkloc (Longident.last label.txt) label.loc),
@@ -3060,6 +3087,19 @@ and parse_braced_or_record_expr p =
30603087
in
30613088
Parser.expect Rbrace p;
30623089
expr
3090+
| Equal ->
3091+
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
3092+
(Diagnostics.message ErrorMessages.record_field_missing_colon);
3093+
Parser.next p;
3094+
let field_expr = parse_expr p in
3095+
Parser.optional p Comma |> ignore;
3096+
let expr =
3097+
parse_record_expr_with_string_keys ~start_pos
3098+
{Parsetree.lid = field; x = field_expr; opt = false}
3099+
p
3100+
in
3101+
Parser.expect Rbrace p;
3102+
expr
30633103
| _ -> (
30643104
let tag = if p.mode = ParseForTypeChecker then Some "js" else None in
30653105
let constant =
@@ -3153,6 +3193,28 @@ and parse_braced_or_record_expr p =
31533193
in
31543194
Parser.expect Rbrace p;
31553195
expr)
3196+
| Equal -> (
3197+
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
3198+
(Diagnostics.message ErrorMessages.record_field_missing_colon);
3199+
Parser.next p;
3200+
let optional = parse_optional_label p in
3201+
let field_expr = parse_expr p in
3202+
match p.Parser.token with
3203+
| Rbrace ->
3204+
Parser.next p;
3205+
let loc = mk_loc start_pos p.prev_end_pos in
3206+
Ast_helper.Exp.record ~loc
3207+
[{lid = path_ident; x = field_expr; opt = optional}]
3208+
None
3209+
| _ ->
3210+
Parser.expect Comma p;
3211+
let expr =
3212+
parse_record_expr ~start_pos
3213+
[{lid = path_ident; x = field_expr; opt = optional}]
3214+
p
3215+
in
3216+
Parser.expect Rbrace p;
3217+
expr)
31563218
(* error case *)
31573219
| Lident _ ->
31583220
if p.prev_end_pos.pos_lnum < p.start_pos.pos_lnum then (
@@ -3295,6 +3357,12 @@ and parse_record_expr_row_with_string_key p :
32953357
Parser.next p;
32963358
let field_expr = parse_expr p in
32973359
Some {lid = field; x = field_expr; opt = false}
3360+
| Equal ->
3361+
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
3362+
(Diagnostics.message ErrorMessages.record_field_missing_colon);
3363+
Parser.next p;
3364+
let field_expr = parse_expr p in
3365+
Some {lid = field; x = field_expr; opt = false}
32983366
| _ ->
32993367
Some
33003368
{
@@ -3324,6 +3392,13 @@ and parse_record_expr_row p :
33243392
let optional = parse_optional_label p in
33253393
let field_expr = parse_expr p in
33263394
Some {lid = field; x = field_expr; opt = optional}
3395+
| Equal ->
3396+
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
3397+
(Diagnostics.message ErrorMessages.record_field_missing_colon);
3398+
Parser.next p;
3399+
let optional = parse_optional_label p in
3400+
let field_expr = parse_expr p in
3401+
Some {lid = field; x = field_expr; opt = optional}
33273402
| _ ->
33283403
let value = Ast_helper.Exp.ident ~loc:field.loc ~attrs field in
33293404
let value =
@@ -3368,6 +3443,12 @@ and parse_record_expr_row p :
33683443

33693444
and parse_dict_expr_row p =
33703445
match p.Parser.token with
3446+
| DotDotDot ->
3447+
Parser.err p (Diagnostics.message ErrorMessages.dict_expr_spread);
3448+
Parser.next p;
3449+
(* Parse the expr so it's consumed *)
3450+
let _spread_expr = parse_constrained_or_coerced_expr p in
3451+
None
33713452
| String s -> (
33723453
let loc = mk_loc p.start_pos p.end_pos in
33733454
Parser.next p;
@@ -3377,6 +3458,12 @@ and parse_dict_expr_row p =
33773458
Parser.next p;
33783459
let fieldExpr = parse_expr p in
33793460
Some (field, fieldExpr)
3461+
| Equal ->
3462+
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
3463+
(Diagnostics.message ErrorMessages.dict_field_missing_colon);
3464+
Parser.next p;
3465+
let fieldExpr = parse_expr p in
3466+
Some (field, fieldExpr)
33803467
| _ -> Some (field, Ast_helper.Exp.ident ~loc:field.loc field))
33813468
| _ -> None
33823469

@@ -3881,12 +3968,42 @@ and parse_argument2 p : argument option =
38813968
in
38823969
Some {label; expr}
38833970
| Colon ->
3971+
let colon_start = p.start_pos in
38843972
Parser.next p;
3885-
let typ = parse_typ_expr p in
3886-
let loc = mk_loc start_pos p.prev_end_pos in
3887-
let expr = Ast_helper.Exp.constraint_ ~loc ident_expr typ in
3888-
Some
3889-
{label = Asttypes.Labelled {txt = ident; loc = named_arg_loc}; expr}
3973+
let colon_end = p.prev_end_pos in
3974+
if Grammar.is_typ_expr_start p.Parser.token then
3975+
let typ = parse_typ_expr p in
3976+
let loc = mk_loc start_pos p.prev_end_pos in
3977+
let expr = Ast_helper.Exp.constraint_ ~loc ident_expr typ in
3978+
Some
3979+
{label = Asttypes.Labelled {txt = ident; loc = named_arg_loc}; expr}
3980+
else
3981+
let label, expr =
3982+
match p.Parser.token with
3983+
| Question ->
3984+
Parser.err ~start_pos:colon_start ~end_pos:colon_end p
3985+
(Diagnostics.message
3986+
ErrorMessages.optional_labelled_argument_missing_equal);
3987+
Parser.next p;
3988+
let expr = parse_constrained_or_coerced_expr p in
3989+
(Asttypes.Optional {txt = ident; loc = named_arg_loc}, expr)
3990+
| _ ->
3991+
Parser.err ~start_pos:colon_start ~end_pos:colon_end p
3992+
(Diagnostics.message
3993+
ErrorMessages.labelled_argument_missing_equal);
3994+
let expr =
3995+
match p.Parser.token with
3996+
| Underscore
3997+
when not (is_es6_arrow_expression ~in_ternary:false p) ->
3998+
let loc = mk_loc p.start_pos p.end_pos in
3999+
Parser.next p;
4000+
Ast_helper.Exp.ident ~loc
4001+
(Location.mkloc (Longident.Lident "_") loc)
4002+
| _ -> parse_constrained_or_coerced_expr p
4003+
in
4004+
(Asttypes.Labelled {txt = ident; loc = named_arg_loc}, expr)
4005+
in
4006+
Some {label; expr}
38904007
| _ ->
38914008
Some
38924009
{
@@ -4783,7 +4900,13 @@ and parse_string_field_declaration p =
47834900
let name_end_pos = p.end_pos in
47844901
Parser.next p;
47854902
let field_name = Location.mkloc name (mk_loc name_start_pos name_end_pos) in
4786-
Parser.expect ~grammar:Grammar.TypeExpression Colon p;
4903+
(match p.Parser.token with
4904+
| Colon -> Parser.next p
4905+
| Equal ->
4906+
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
4907+
(Diagnostics.message ErrorMessages.record_type_field_missing_colon);
4908+
Parser.next p
4909+
| _ -> Parser.expect ~grammar:Grammar.TypeExpression Colon p);
47874910
let typ = parse_poly_type_expr p in
47884911
Some (Parsetree.Otag (field_name, attrs, typ))
47894912
| DotDotDot ->
@@ -4796,7 +4919,13 @@ and parse_string_field_declaration p =
47964919
(Diagnostics.message (ErrorMessages.object_quoted_field_name name));
47974920
Parser.next p;
47984921
let field_name = Location.mkloc name name_loc in
4799-
Parser.expect ~grammar:Grammar.TypeExpression Colon p;
4922+
(match p.Parser.token with
4923+
| Colon -> Parser.next p
4924+
| Equal ->
4925+
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
4926+
(Diagnostics.message ErrorMessages.record_type_field_missing_colon);
4927+
Parser.next p
4928+
| _ -> Parser.expect ~grammar:Grammar.TypeExpression Colon p);
48004929
let typ = parse_poly_type_expr p in
48014930
Some (Parsetree.Otag (field_name, attrs, typ))
48024931
| _token -> None
@@ -4825,6 +4954,14 @@ and parse_field_declaration ?current_type_name_path ?inline_types_context p =
48254954
extend_current_type_name_path current_type_name_path name.txt
48264955
in
48274956
parse_poly_type_expr ?current_type_name_path ?inline_types_context p
4957+
| Equal ->
4958+
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
4959+
(Diagnostics.message ErrorMessages.record_type_field_missing_colon);
4960+
Parser.next p;
4961+
let current_type_name_path =
4962+
extend_current_type_name_path current_type_name_path name.txt
4963+
in
4964+
parse_poly_type_expr ?current_type_name_path ?inline_types_context p
48284965
| _ ->
48294966
Ast_helper.Typ.constr ~loc:name.loc {name with txt = Lident name.txt} []
48304967
in
@@ -4866,6 +5003,11 @@ and parse_field_declaration_region ?current_type_name_path ?inline_types_context
48665003
| Colon ->
48675004
Parser.next p;
48685005
parse_poly_type_expr ?current_type_name_path ?inline_types_context p
5006+
| Equal ->
5007+
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
5008+
(Diagnostics.message ErrorMessages.record_type_field_missing_colon);
5009+
Parser.next p;
5010+
parse_poly_type_expr ?current_type_name_path ?inline_types_context p
48695011
| _ ->
48705012
Ast_helper.Typ.constr ~loc:name.loc ~attrs
48715013
{name with txt = Lident name.txt}
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/expressions/labelledArgumentMissingEqual.res:1:16
4+
5+
1 │ let _ = fn(~foo:1)
6+
2 │ let _ = fn(~bar:?value)
7+
3 │
8+
9+
Use `=` to pass a labelled argument. Example: `~label=value`
10+
11+
12+
Syntax error!
13+
syntax_tests/data/parsing/errors/expressions/labelledArgumentMissingEqual.res:2:16
14+
15+
1 │ let _ = fn(~foo:1)
16+
2 │ let _ = fn(~bar:?value)
17+
3 │
18+
19+
Optional labelled arguments use `=?`. Example: `~label=?value`
20+
21+
let _ = fn ~foo:1
22+
let _ = fn ?bar:value
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
let _ = fn(~foo:1)
2+
let _ = fn(~bar:?value)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
let x = dict{...foo, "bar": 3}
2+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
Syntax error!
3+
syntax_tests/data/parsing/errors/other/dict_spread.res:1:14-16
4+
5+
1 │ let x = dict{...foo, "bar": 3}
6+
2 │
7+
3 │
8+
9+
Dict literals do not support spread (`...`) yet.
10+
11+
let x = Primitive_dict.make [|("bar", 3)|]
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
Syntax error!
3+
syntax_tests/data/parsing/errors/pattern/recordFieldWrongAssignment.res:1:9
4+
5+
1 │ let {foo=bar} = record
6+
2 │
7+
8+
Record patterns use `:` when matching fields. Example: `{field: value}`
9+
10+
let { foo = bar } = record
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
let {foo=bar} = record
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
Syntax error!
3+
syntax_tests/data/parsing/errors/structure/recordFieldWrongAssignment.res:1:13
4+
5+
1 │ let r = {foo=1}
6+
2 │
7+
8+
Records use `:` when assigning fields. Example: `{field: value}`
9+
10+
let r = { foo = 1 }
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
let r = {foo=1}

0 commit comments

Comments
 (0)