@@ -398,6 +398,27 @@ let build_longident words =
398398 | [] -> assert false
399399 | hd :: tl -> List. fold_left (fun p s -> Longident. Ldot (p, s)) (Lident hd) tl
400400
401+ (* Recovers a keyword used as field name if it's probable that it's a full
402+ field name (not punning etc), by checking if there's a colon after it. *)
403+ let recover_keyword_field_name_if_probably_field p ~mk_message :
404+ (string * Location. t ) option =
405+ if
406+ Token. is_keyword p.Parser. token
407+ && Parser. lookahead p (fun st ->
408+ Parser. next st;
409+ st.Parser. token = Colon )
410+ then (
411+ let keyword_txt = Token. to_string p.token in
412+ let keyword_start = p.Parser. start_pos in
413+ let keyword_end = p.Parser. end_pos in
414+ Parser. err ~start_pos: keyword_start ~end_pos: keyword_end p
415+ (Diagnostics. message (mk_message keyword_txt));
416+ let loc = mk_loc keyword_start keyword_end in
417+ let recovered_field_name = keyword_txt ^ " _" in
418+ Parser. next p;
419+ Some (recovered_field_name, loc))
420+ else None
421+
401422let make_infix_operator (p : Parser.t ) token start_pos end_pos =
402423 let stringified_token =
403424 if token = Token. Equal then (
@@ -1379,16 +1400,31 @@ and parse_record_pattern_row p =
13791400 Some (false , PatUnderscore )
13801401 | _ ->
13811402 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 )
1403+ match
1404+ recover_keyword_field_name_if_probably_field p
1405+ ~mk_message: (fun keyword_txt ->
1406+ " Cannot use keyword `" ^ keyword_txt
1407+ ^ " ` here. Keywords are not allowed as record field names." )
1408+ with
1409+ | Some (recovered_field_name , loc ) ->
1410+ Parser. expect Colon p;
1411+ let optional = parse_optional_label p in
1412+ let pat = parse_pattern p in
1413+ let field =
1414+ Location. mkloc (Longident. Lident recovered_field_name) loc
1415+ in
1416+ Some (false , PatField {lid = field; x = pat; opt = optional})
1417+ | None ->
1418+ let keyword_txt = Token. to_string p.token in
1419+ let keyword_start = p.Parser. start_pos in
1420+ let keyword_end = p.Parser. end_pos in
1421+ let message =
1422+ " Cannot use keyword `" ^ keyword_txt
1423+ ^ " ` here. Keywords are not allowed as record field names."
1424+ in
1425+ Parser. err ~start_pos: keyword_start ~end_pos: keyword_end p
1426+ (Diagnostics. message message);
1427+ None )
13921428 else None
13931429
13941430and parse_record_pattern ~attrs p =
@@ -2935,25 +2971,29 @@ and parse_braced_or_record_expr p =
29352971 let start_pos = p.Parser. start_pos in
29362972 Parser. expect Lbrace p;
29372973 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}
2974+ | token when Token. is_keyword token -> (
2975+ match
2976+ recover_keyword_field_name_if_probably_field p
2977+ ~mk_message: (fun keyword_txt ->
2978+ " Cannot use keyword `" ^ keyword_txt
2979+ ^ " ` as a record field name. Suggestion: rename it (e.g. `"
2980+ ^ keyword_txt ^ " _`)" )
2981+ with
2982+ | Some (recovered_field_name , loc ) ->
2983+ Parser. expect Colon p;
2984+ let optional = parse_optional_label p in
2985+ let field_expr = parse_expr p in
2986+ let field = Location. mkloc (Longident. Lident recovered_field_name) loc in
2987+ let first_row = {Parsetree. lid = field; x = field_expr; opt = optional} in
2988+ let expr = parse_record_expr ~start_pos [first_row] p in
2989+ Parser. expect Rbrace p;
2990+ expr
2991+ | None ->
2992+ let expr = parse_expr_block p in
2993+ Parser. expect Rbrace p;
2994+ let loc = mk_loc start_pos p.prev_end_pos in
2995+ let braces = make_braces_attr loc in
2996+ {expr with pexp_attributes = braces :: expr .pexp_attributes})
29572997 | Rbrace ->
29582998 Parser. next p;
29592999 let loc = mk_loc start_pos p.prev_end_pos in
@@ -3272,17 +3312,33 @@ and parse_record_expr_row p :
32723312 | _ -> None )
32733313 | _ ->
32743314 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 )
3315+ match
3316+ recover_keyword_field_name_if_probably_field p
3317+ ~mk_message: (fun keyword_txt ->
3318+ " Cannot use keyword `" ^ keyword_txt
3319+ ^ " ` as a record field name. Suggestion: rename it (e.g. `"
3320+ ^ keyword_txt ^ " _`)" )
3321+ with
3322+ | Some (recovered_field_name , loc ) ->
3323+ Parser. expect Colon p;
3324+ let optional = parse_optional_label p in
3325+ let field_expr = parse_expr p in
3326+ let field =
3327+ Location. mkloc (Longident. Lident recovered_field_name) loc
3328+ in
3329+ Some {lid = field; x = field_expr; opt = optional}
3330+ | None ->
3331+ let keyword_txt = Token. to_string p.token in
3332+ let keyword_start = p.Parser. start_pos in
3333+ let keyword_end = p.Parser. end_pos in
3334+ let message =
3335+ " Cannot use keyword `" ^ keyword_txt
3336+ ^ " ` as a record field name. Suggestion: rename it (e.g. `"
3337+ ^ keyword_txt ^ " _`)"
3338+ in
3339+ Parser. err ~start_pos: keyword_start ~end_pos: keyword_end p
3340+ (Diagnostics. message message);
3341+ None )
32863342 else None
32873343
32883344and parse_dict_expr_row p =
@@ -4781,19 +4837,38 @@ and parse_field_declaration_region ?current_type_name_path ?inline_types_context
47814837 Some (Ast_helper.Type. field ~attrs ~loc ~mut ~optional name typ)
47824838 | _ ->
47834839 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 )
4840+ match
4841+ recover_keyword_field_name_if_probably_field p
4842+ ~mk_message: (fun keyword_txt ->
4843+ " Cannot use keyword `" ^ keyword_txt
4844+ ^ " ` as a record field name. Suggestion: rename it (e.g. `"
4845+ ^ keyword_txt ^ " _`)\n " ^ " If you need the field to be \" "
4846+ ^ keyword_txt ^ " \" at runtime, annotate the field: `@as(\" "
4847+ ^ keyword_txt ^ " \" ) " ^ keyword_txt ^ " _ : ...`" )
4848+ with
4849+ | Some (recovered_field_name , name_loc ) ->
4850+ let optional = parse_optional_label p in
4851+ Parser. expect Colon p;
4852+ let typ =
4853+ parse_poly_type_expr ?current_type_name_path ?inline_types_context p
4854+ in
4855+ let loc = mk_loc start_pos typ.ptyp_loc.loc_end in
4856+ let name = Location. mkloc recovered_field_name name_loc in
4857+ Some (Ast_helper.Type. field ~attrs ~loc ~mut ~optional name typ)
4858+ | None ->
4859+ let keyword_txt = Token. to_string p.token in
4860+ let keyword_start = p.Parser. start_pos in
4861+ let keyword_end = p.Parser. end_pos in
4862+ let message =
4863+ " Cannot use keyword `" ^ keyword_txt
4864+ ^ " ` as a record field name. Suggestion: rename it (e.g. `"
4865+ ^ keyword_txt ^ " _`)\n " ^ " If you need the field to be \" "
4866+ ^ keyword_txt ^ " \" at runtime, annotate the field: `@as(\" "
4867+ ^ keyword_txt ^ " \" ) " ^ keyword_txt ^ " _ : ...`"
4868+ in
4869+ Parser. err ~start_pos: keyword_start ~end_pos: keyword_end p
4870+ (Diagnostics. message message);
4871+ None )
47974872 else (
47984873 if attrs <> [] then
47994874 Parser. err ~start_pos p
0 commit comments