Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

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

#### :house: Internal

Expand Down
148 changes: 141 additions & 7 deletions compiler/syntax/src/res_core.ml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,24 @@ module ErrorMessages = struct

let dict_expr_spread = "Dict literals do not support spread (`...`) yet."

let record_field_missing_colon =
"Records use `:` when assigning fields. Example: `{field: value}`"

let record_pattern_field_missing_colon =
"Record patterns use `:` when matching fields. Example: `{field: value}`"

let record_type_field_missing_colon =
"Record fields in type declarations use `:`. Example: `{field: string}`"

let dict_field_missing_colon =
"Dict entries use `:` to separate keys from values. Example: `{\"k\": v}`"

let labelled_argument_missing_equal =
"Use `=` to pass a labelled argument. Example: `~label=value`"

let optional_labelled_argument_missing_equal =
"Optional labelled arguments use `=?`. Example: `~label=?value`"

let variant_ident =
"A polymorphic variant (e.g. #id) must start with an alphabetical letter \
or be a number (e.g. #742)"
Expand Down Expand Up @@ -1414,6 +1432,13 @@ and parse_record_pattern_row_field ~attrs p =
let optional = parse_optional_label p in
let pat = parse_pattern p in
(pat, optional)
| Equal ->
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
(Diagnostics.message ErrorMessages.record_pattern_field_missing_colon);
Parser.next p;
let optional = parse_optional_label p in
let pat = parse_pattern p in
(pat, optional)
| _ ->
( Ast_helper.Pat.var ~loc:label.loc ~attrs
(Location.mkloc (Longident.last label.txt) label.loc),
Expand Down Expand Up @@ -3062,6 +3087,19 @@ and parse_braced_or_record_expr p =
in
Parser.expect Rbrace p;
expr
| Equal ->
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
(Diagnostics.message ErrorMessages.record_field_missing_colon);
Parser.next p;
let field_expr = parse_expr p in
Parser.optional p Comma |> ignore;
let expr =
parse_record_expr_with_string_keys ~start_pos
{Parsetree.lid = field; x = field_expr; opt = false}
p
in
Parser.expect Rbrace p;
expr
| _ -> (
let tag = if p.mode = ParseForTypeChecker then Some "js" else None in
let constant =
Expand Down Expand Up @@ -3155,6 +3193,28 @@ and parse_braced_or_record_expr p =
in
Parser.expect Rbrace p;
expr)
| Equal -> (
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
(Diagnostics.message ErrorMessages.record_field_missing_colon);
Parser.next p;
let optional = parse_optional_label p in
let field_expr = parse_expr p in
match p.Parser.token with
| Rbrace ->
Parser.next p;
let loc = mk_loc start_pos p.prev_end_pos in
Ast_helper.Exp.record ~loc
[{lid = path_ident; x = field_expr; opt = optional}]
None
| _ ->
Parser.expect Comma p;
let expr =
parse_record_expr ~start_pos
[{lid = path_ident; x = field_expr; opt = optional}]
p
in
Parser.expect Rbrace p;
expr)
(* error case *)
| Lident _ ->
if p.prev_end_pos.pos_lnum < p.start_pos.pos_lnum then (
Expand Down Expand Up @@ -3297,6 +3357,12 @@ and parse_record_expr_row_with_string_key p :
Parser.next p;
let field_expr = parse_expr p in
Some {lid = field; x = field_expr; opt = false}
| Equal ->
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
(Diagnostics.message ErrorMessages.record_field_missing_colon);
Parser.next p;
let field_expr = parse_expr p in
Some {lid = field; x = field_expr; opt = false}
| _ ->
Some
{
Expand Down Expand Up @@ -3326,6 +3392,13 @@ and parse_record_expr_row p :
let optional = parse_optional_label p in
let field_expr = parse_expr p in
Some {lid = field; x = field_expr; opt = optional}
| Equal ->
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
(Diagnostics.message ErrorMessages.record_field_missing_colon);
Parser.next p;
let optional = parse_optional_label p in
let field_expr = parse_expr p in
Some {lid = field; x = field_expr; opt = optional}
| _ ->
let value = Ast_helper.Exp.ident ~loc:field.loc ~attrs field in
let value =
Expand Down Expand Up @@ -3385,6 +3458,12 @@ and parse_dict_expr_row p =
Parser.next p;
let fieldExpr = parse_expr p in
Some (field, fieldExpr)
| Equal ->
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
(Diagnostics.message ErrorMessages.dict_field_missing_colon);
Parser.next p;
let fieldExpr = parse_expr p in
Some (field, fieldExpr)
| _ -> Some (field, Ast_helper.Exp.ident ~loc:field.loc field))
| _ -> None

Expand Down Expand Up @@ -3889,12 +3968,42 @@ and parse_argument2 p : argument option =
in
Some {label; expr}
| Colon ->
let colon_start = p.start_pos in
Parser.next p;
let typ = parse_typ_expr p in
let loc = mk_loc start_pos p.prev_end_pos in
let expr = Ast_helper.Exp.constraint_ ~loc ident_expr typ in
Some
{label = Asttypes.Labelled {txt = ident; loc = named_arg_loc}; expr}
let colon_end = p.prev_end_pos in
if Grammar.is_typ_expr_start p.Parser.token then
let typ = parse_typ_expr p in
let loc = mk_loc start_pos p.prev_end_pos in
let expr = Ast_helper.Exp.constraint_ ~loc ident_expr typ in
Some
{label = Asttypes.Labelled {txt = ident; loc = named_arg_loc}; expr}
else
let label, expr =
match p.Parser.token with
| Question ->
Parser.err ~start_pos:colon_start ~end_pos:colon_end p
(Diagnostics.message
ErrorMessages.optional_labelled_argument_missing_equal);
Parser.next p;
let expr = parse_constrained_or_coerced_expr p in
(Asttypes.Optional {txt = ident; loc = named_arg_loc}, expr)
| _ ->
Parser.err ~start_pos:colon_start ~end_pos:colon_end p
(Diagnostics.message
ErrorMessages.labelled_argument_missing_equal);
let expr =
match p.Parser.token with
| Underscore
when not (is_es6_arrow_expression ~in_ternary:false p) ->
let loc = mk_loc p.start_pos p.end_pos in
Parser.next p;
Ast_helper.Exp.ident ~loc
(Location.mkloc (Longident.Lident "_") loc)
| _ -> parse_constrained_or_coerced_expr p
in
(Asttypes.Labelled {txt = ident; loc = named_arg_loc}, expr)
in
Some {label; expr}
| _ ->
Some
{
Expand Down Expand Up @@ -4791,7 +4900,13 @@ and parse_string_field_declaration p =
let name_end_pos = p.end_pos in
Parser.next p;
let field_name = Location.mkloc name (mk_loc name_start_pos name_end_pos) in
Parser.expect ~grammar:Grammar.TypeExpression Colon p;
(match p.Parser.token with
| Colon -> Parser.next p
| Equal ->
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
(Diagnostics.message ErrorMessages.record_type_field_missing_colon);
Parser.next p
| _ -> Parser.expect ~grammar:Grammar.TypeExpression Colon p);
let typ = parse_poly_type_expr p in
Some (Parsetree.Otag (field_name, attrs, typ))
| DotDotDot ->
Expand All @@ -4804,7 +4919,13 @@ and parse_string_field_declaration p =
(Diagnostics.message (ErrorMessages.object_quoted_field_name name));
Parser.next p;
let field_name = Location.mkloc name name_loc in
Parser.expect ~grammar:Grammar.TypeExpression Colon p;
(match p.Parser.token with
| Colon -> Parser.next p
| Equal ->
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
(Diagnostics.message ErrorMessages.record_type_field_missing_colon);
Parser.next p
| _ -> Parser.expect ~grammar:Grammar.TypeExpression Colon p);
let typ = parse_poly_type_expr p in
Some (Parsetree.Otag (field_name, attrs, typ))
| _token -> None
Expand Down Expand Up @@ -4833,6 +4954,14 @@ and parse_field_declaration ?current_type_name_path ?inline_types_context p =
extend_current_type_name_path current_type_name_path name.txt
in
parse_poly_type_expr ?current_type_name_path ?inline_types_context p
| Equal ->
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
(Diagnostics.message ErrorMessages.record_type_field_missing_colon);
Parser.next p;
let current_type_name_path =
extend_current_type_name_path current_type_name_path name.txt
in
parse_poly_type_expr ?current_type_name_path ?inline_types_context p
| _ ->
Ast_helper.Typ.constr ~loc:name.loc {name with txt = Lident name.txt} []
in
Expand Down Expand Up @@ -4874,6 +5003,11 @@ and parse_field_declaration_region ?current_type_name_path ?inline_types_context
| Colon ->
Parser.next p;
parse_poly_type_expr ?current_type_name_path ?inline_types_context p
| Equal ->
Parser.err ~start_pos:p.start_pos ~end_pos:p.end_pos p
(Diagnostics.message ErrorMessages.record_type_field_missing_colon);
Parser.next p;
parse_poly_type_expr ?current_type_name_path ?inline_types_context p
| _ ->
Ast_helper.Typ.constr ~loc:name.loc ~attrs
{name with txt = Lident name.txt}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

Syntax error!
syntax_tests/data/parsing/errors/expressions/labelledArgumentMissingEqual.res:1:16

1 │ let _ = fn(~foo:1)
2 │ let _ = fn(~bar:?value)
3 │

Use `=` to pass a labelled argument. Example: `~label=value`


Syntax error!
syntax_tests/data/parsing/errors/expressions/labelledArgumentMissingEqual.res:2:16

1 │ let _ = fn(~foo:1)
2 │ let _ = fn(~bar:?value)
3 │

Optional labelled arguments use `=?`. Example: `~label=?value`

let _ = fn ~foo:1
let _ = fn ?bar:value
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
let _ = fn(~foo:1)
let _ = fn(~bar:?value)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

Syntax error!
syntax_tests/data/parsing/errors/pattern/recordFieldWrongAssignment.res:1:9

1 │ let {foo=bar} = record
2 │

Record patterns use `:` when matching fields. Example: `{field: value}`

let { foo = bar } = record
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let {foo=bar} = record
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

Syntax error!
syntax_tests/data/parsing/errors/structure/recordFieldWrongAssignment.res:1:13

1 │ let r = {foo=1}
2 │

Records use `:` when assigning fields. Example: `{field: value}`

let r = { foo = 1 }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let r = {foo=1}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

Syntax error!
syntax_tests/data/parsing/errors/typeDef/recordFieldWrongAssignment.res:1:14

1 │ type t = {foo=string}
2 │

Record fields in type declarations use `:`. Example: `{field: string}`

type nonrec t = {
foo: string }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
type t = {foo=string}
Loading