@@ -155,6 +155,22 @@ module ErrorMessages = struct
155
155
let multiple_inline_record_definitions_at_same_path =
156
156
" Only one inline record definition is allowed per record field. This \
157
157
defines more than one inline record."
158
+
159
+ let keyword_field_in_expr keyword_txt =
160
+ " Cannot use keyword `" ^ keyword_txt
161
+ ^ " ` as a record field name. Suggestion: rename it (e.g. `" ^ keyword_txt
162
+ ^ " _`)"
163
+
164
+ let keyword_field_in_pattern keyword_txt =
165
+ " Cannot use keyword `" ^ keyword_txt
166
+ ^ " ` here. Keywords are not allowed as record field names."
167
+
168
+ let keyword_field_in_type keyword_txt =
169
+ " Cannot use keyword `" ^ keyword_txt
170
+ ^ " ` as a record field name. Suggestion: rename it (e.g. `" ^ keyword_txt
171
+ ^ " _`)\n If you need the field to be \" " ^ keyword_txt
172
+ ^ " \" at runtime, annotate the field: `@as(\" " ^ keyword_txt ^ " \" ) "
173
+ ^ keyword_txt ^ " _ : ...`"
158
174
end
159
175
160
176
module InExternal = struct
@@ -403,6 +419,30 @@ let build_longident words =
403
419
| [] -> assert false
404
420
| hd :: tl -> List. fold_left (fun p s -> Longident. Ldot (p, s)) (Lident hd) tl
405
421
422
+ let emit_keyword_field_error (p : Parser.t ) ~mk_message =
423
+ let keyword_txt = Token. to_string p.token in
424
+ let keyword_start = p.Parser. start_pos in
425
+ let keyword_end = p.Parser. end_pos in
426
+ Parser. err ~start_pos: keyword_start ~end_pos: keyword_end p
427
+ (Diagnostics. message (mk_message keyword_txt))
428
+
429
+ (* Recovers a keyword used as field name if it's probable that it's a full
430
+ field name (not punning etc), by checking if there's a colon after it. *)
431
+ let recover_keyword_field_name_if_probably_field p ~mk_message :
432
+ (string * Location. t ) option =
433
+ if
434
+ Token. is_keyword p.Parser. token
435
+ && Parser. lookahead p (fun st ->
436
+ Parser. next st;
437
+ st.Parser. token = Colon )
438
+ then (
439
+ emit_keyword_field_error p ~mk_message ;
440
+ let loc = mk_loc p.Parser. start_pos p.Parser. end_pos in
441
+ let recovered_field_name = Token. to_string p.token ^ " _" in
442
+ Parser. next p;
443
+ Some (recovered_field_name, loc))
444
+ else None
445
+
406
446
let make_infix_operator (p : Parser.t ) token start_pos end_pos =
407
447
let stringified_token =
408
448
if token = Token. Equal then (
@@ -1382,7 +1422,25 @@ and parse_record_pattern_row p =
1382
1422
| Underscore ->
1383
1423
Parser. next p;
1384
1424
Some (false , PatUnderscore )
1385
- | _ -> None
1425
+ | _ ->
1426
+ if Token. is_keyword p.token then (
1427
+ match
1428
+ recover_keyword_field_name_if_probably_field p
1429
+ ~mk_message: ErrorMessages. keyword_field_in_pattern
1430
+ with
1431
+ | Some (recovered_field_name , loc ) ->
1432
+ Parser. expect Colon p;
1433
+ let optional = parse_optional_label p in
1434
+ let pat = parse_pattern p in
1435
+ let field =
1436
+ Location. mkloc (Longident. Lident recovered_field_name) loc
1437
+ in
1438
+ Some (false , PatField {lid = field; x = pat; opt = optional})
1439
+ | None ->
1440
+ emit_keyword_field_error p
1441
+ ~mk_message: ErrorMessages. keyword_field_in_pattern;
1442
+ None )
1443
+ else None
1386
1444
1387
1445
and parse_record_pattern ~attrs p =
1388
1446
let start_pos = p.start_pos in
@@ -2928,6 +2986,26 @@ and parse_braced_or_record_expr p =
2928
2986
let start_pos = p.Parser. start_pos in
2929
2987
Parser. expect Lbrace p;
2930
2988
match p.Parser. token with
2989
+ | token when Token. is_keyword token -> (
2990
+ match
2991
+ recover_keyword_field_name_if_probably_field p
2992
+ ~mk_message: ErrorMessages. keyword_field_in_expr
2993
+ with
2994
+ | Some (recovered_field_name , loc ) ->
2995
+ Parser. expect Colon p;
2996
+ let optional = parse_optional_label p in
2997
+ let field_expr = parse_expr p in
2998
+ let field = Location. mkloc (Longident. Lident recovered_field_name) loc in
2999
+ let first_row = {Parsetree. lid = field; x = field_expr; opt = optional} in
3000
+ let expr = parse_record_expr ~start_pos [first_row] p in
3001
+ Parser. expect Rbrace p;
3002
+ expr
3003
+ | None ->
3004
+ let expr = parse_expr_block p in
3005
+ Parser. expect Rbrace p;
3006
+ let loc = mk_loc start_pos p.prev_end_pos in
3007
+ let braces = make_braces_attr loc in
3008
+ {expr with pexp_attributes = braces :: expr .pexp_attributes})
2931
3009
| Rbrace ->
2932
3010
Parser. next p;
2933
3011
let loc = mk_loc start_pos p.prev_end_pos in
@@ -3244,7 +3322,25 @@ and parse_record_expr_row p :
3244
3322
in
3245
3323
Some {lid = field; x = value; opt = true }
3246
3324
| _ -> None )
3247
- | _ -> None
3325
+ | _ ->
3326
+ if Token. is_keyword p.token then (
3327
+ match
3328
+ recover_keyword_field_name_if_probably_field p
3329
+ ~mk_message: ErrorMessages. keyword_field_in_expr
3330
+ with
3331
+ | Some (recovered_field_name , loc ) ->
3332
+ Parser. expect Colon p;
3333
+ let optional = parse_optional_label p in
3334
+ let field_expr = parse_expr p in
3335
+ let field =
3336
+ Location. mkloc (Longident. Lident recovered_field_name) loc
3337
+ in
3338
+ Some {lid = field; x = field_expr; opt = optional}
3339
+ | None ->
3340
+ emit_keyword_field_error p
3341
+ ~mk_message: ErrorMessages. keyword_field_in_expr;
3342
+ None )
3343
+ else None
3248
3344
3249
3345
and parse_dict_expr_row p =
3250
3346
match p.Parser. token with
@@ -4742,17 +4838,36 @@ and parse_field_declaration_region ?current_type_name_path ?inline_types_context
4742
4838
let loc = mk_loc start_pos typ.ptyp_loc.loc_end in
4743
4839
Some (Ast_helper.Type. field ~attrs ~loc ~mut ~optional name typ)
4744
4840
| _ ->
4745
- if attrs <> [] then
4746
- Parser. err ~start_pos p
4747
- (Diagnostics. message
4748
- " Attributes and doc comments can only be used at the beginning of a \
4749
- field declaration" );
4750
- if mut = Mutable then
4751
- Parser. err ~start_pos p
4752
- (Diagnostics. message
4753
- " The `mutable` qualifier can only be used at the beginning of a \
4754
- field declaration" );
4755
- None
4841
+ if Token. is_keyword p.token then (
4842
+ match
4843
+ recover_keyword_field_name_if_probably_field p
4844
+ ~mk_message: ErrorMessages. keyword_field_in_type
4845
+ with
4846
+ | Some (recovered_field_name , name_loc ) ->
4847
+ let optional = parse_optional_label p in
4848
+ Parser. expect Colon p;
4849
+ let typ =
4850
+ parse_poly_type_expr ?current_type_name_path ?inline_types_context p
4851
+ in
4852
+ let loc = mk_loc start_pos typ.ptyp_loc.loc_end in
4853
+ let name = Location. mkloc recovered_field_name name_loc in
4854
+ Some (Ast_helper.Type. field ~attrs ~loc ~mut ~optional name typ)
4855
+ | None ->
4856
+ emit_keyword_field_error p
4857
+ ~mk_message: ErrorMessages. keyword_field_in_type;
4858
+ None )
4859
+ else (
4860
+ if attrs <> [] then
4861
+ Parser. err ~start_pos p
4862
+ (Diagnostics. message
4863
+ " Attributes and doc comments can only be used at the beginning of \
4864
+ a field declaration" );
4865
+ if mut = Mutable then
4866
+ Parser. err ~start_pos p
4867
+ (Diagnostics. message
4868
+ " The `mutable` qualifier can only be used at the beginning of a \
4869
+ field declaration" );
4870
+ None )
4756
4871
4757
4872
(* record-decl ::=
4758
4873
* | { field-decl }
0 commit comments