From 74f74610986376228692d6ba3fe0f744f878c40d Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Sat, 26 Sep 2026 08:09:48 +0200 Subject: [PATCH 1/5] Represent explicit expression braces in parsetree v1 Signed-off-by: Christoph Knittel --- analysis/src/completion_expressions.ml | 7 +- analysis/src/completion_front_end.ml | 44 ++- analysis/src/utils.ml | 4 +- analysis/src/xform.ml | 42 +- compiler/frontend/bs_builtin_ppx.ml | 1 + compiler/ml/ast_helper.ml | 2 + compiler/ml/ast_helper.mli | 1 + compiler/ml/ast_iterator.ml | 3 + compiler/ml/ast_mapper.ml | 4 + compiler/ml/ast_mapper_from0.ml | 30 +- compiler/ml/ast_mapper_to0.ml | 10 + compiler/ml/depend.ml | 1 + compiler/ml/parsetree.ml | 3 + compiler/ml/pprintast.ml | 3 +- compiler/ml/printast.ml | 4 + compiler/ml/typecore.ml | 13 +- compiler/syntax/src/res_ast_debugger.ml | 12 + compiler/syntax/src/res_comments_table.ml | 16 +- compiler/syntax/src/res_core.ml | 61 +-- compiler/syntax/src/res_parens.ml | 50 +-- compiler/syntax/src/res_parsetree_viewer.ml | 42 +- compiler/syntax/src/res_parsetree_viewer.mli | 6 +- compiler/syntax/src/res_printer.ml | 105 ++--- tests/ounit_tests/ounit_ast_mapper0_tests.ml | 51 +++ .../syntax_tests/data/ast-mapping/Braces.res | 6 + .../data/ast-mapping/expected/Braces.res.txt | 10 + .../expected/ambiguousArrow.res.txt | 3 +- .../errors/expressions/expected/arrow.res.txt | 3 +- .../errors/expressions/expected/block.res.txt | 24 +- .../expressions/expected/consecutive.res.txt | 6 +- .../expected/implementation.res.txt | 4 +- .../expressions/expected/letBinding.res.txt | 4 +- .../expected/typeDefInFunction.res.txt | 2 +- .../expected/unexpectedConstraint.res.txt | 2 +- .../errors/pattern/expected/missing.res.txt | 2 +- .../errors/structure/expected/gh16B.res.txt | 5 +- .../structure/expected/letBinding.res.txt | 4 +- .../expected/angle_operators.res.txt | 5 +- .../expressions/expected/arrow.res.txt | 4 +- .../expressions/expected/async.res.txt | 25 +- .../expressions/expected/await.res.txt | 29 +- .../expressions/expected/binary.res.txt | 4 +- .../expected/binaryNoEs6Arrow.res.txt | 16 +- .../expressions/expected/block.res.txt | 119 +++--- .../expected/bracedOrRecord.res.txt | 42 +- .../expressions/expected/bsObject.res.txt | 21 +- .../expressions/expected/constants.res.txt | 2 +- .../expected/firstClassModule.res.txt | 84 ++-- .../grammar/expressions/expected/jsx.res.txt | 196 ++++------ .../expected/parenthesized.res.txt | 2 +- .../expressions/expected/sideEffects.res.txt | 9 +- .../grammar/expressions/expected/try.res.txt | 5 +- .../expected/tupleVsDivision.res.txt | 3 +- .../expected/unaryOrBinary.res.txt | 6 +- .../expected/underscoreApply.res.txt | 3 +- .../expected/firstClassModules.res.txt | 15 +- .../grammar/pattern/expected/dict.res.txt | 21 +- .../pattern/expected/variantSpreads.res.txt | 16 +- .../structure/expected/letBinding.res.txt | 5 +- .../expected/objectTypeSpreading.res.txt | 7 +- .../expected/equalAfterBinaryExpr.res.txt | 273 +++++++------ .../expected/nonRecTypes.res.txt | 364 +++++++++--------- .../recovery/expression/expected/list.res.txt | 19 +- .../ppx/react/expected/asyncAwait.res.txt | 4 +- .../ppx/react/expected/forwardRef.res.txt | 38 +- .../react/expected/interfaceWithRef.res.txt | 4 +- .../expected/sharedPropsWithProps.res.txt | 4 +- .../data/ppx/react/expected/topLevel.res.txt | 4 +- .../printer/comments/expected/jsx.res.txt | 6 +- .../data/printer/expr/expected/braced.res.txt | 3 +- .../data/printer/expr/expected/if.res.txt | 6 + tests/syntax_tests/data/printer/expr/if.res | 2 + 72 files changed, 1014 insertions(+), 942 deletions(-) create mode 100644 tests/syntax_tests/data/ast-mapping/Braces.res create mode 100644 tests/syntax_tests/data/ast-mapping/expected/Braces.res.txt diff --git a/analysis/src/completion_expressions.ml b/analysis/src/completion_expressions.ml index e4bddf18131..6b71bde4a32 100644 --- a/analysis/src/completion_expressions.ml +++ b/analysis/src/completion_expressions.ml @@ -12,11 +12,12 @@ let rec traverse_expr (exp : Parsetree.expression) ~expr_path ~pos if loc_has_cursor exp.pexp_loc then Some v else None in match exp.pexp_desc with - | Pexp_ident {txt = Lident txt} when Utils.has_braces exp.pexp_attributes -> - (* An ident with braces attribute corresponds to for example `{n}`. - Looks like a record but is parsed as an ident with braces. *) + | Pexp_braces {expr = {pexp_desc = Pexp_ident {txt = Lident txt}}} -> + (* `{n}` looks like a record but contains an identifier. *) some_if_has_cursor (txt, [Completable.NRecordBody {seen_fields = []}] @ expr_path) + | Pexp_braces {expr} -> + traverse_expr expr ~expr_path ~pos ~first_char_before_cursor_no_white | Pexp_ident {txt = Lident txt} -> some_if_has_cursor (txt, expr_path) | Pexp_construct ({txt = Lident "()"}, _) -> some_if_has_cursor ("", expr_path) | Pexp_construct ({txt = Lident txt}, {txt = []}) -> diff --git a/analysis/src/completion_front_end.ml b/analysis/src/completion_front_end.ml index 9d4b52dbaa2..c1a7718976e 100644 --- a/analysis/src/completion_front_end.ml +++ b/analysis/src/completion_front_end.ml @@ -1152,6 +1152,31 @@ let completion_with_parser1 ~debug ~offset ~pos_cursor ~kind_file in typed_completion_expr expr; match expr.pexp_desc with + | Pexp_braces {expr = {pexp_desc = Pexp_ident lid}} -> + if expr.pexp_loc |> Loc.has_pos ~pos:pos_no_white && !result = None then ( + set_found (); + let lid_path = flatten_lid_check_dot lid in + if debug then + Printf.printf "Pexp_ident %s:%s\n" + (lid_path |> String.concat ".") + (Loc.to_string lid.loc); + if lid.loc |> Loc.has_pos ~pos:pos_before_cursor then + let is_likely_module_path = + match lid_path with + | head :: _ when String.length head > 0 -> + head.[0] == Char.uppercase_ascii head.[0] + | _ -> false + in + set_result + (Cpath + (CPId + { + loc = lid.loc; + path = lid_path; + completion_context = + (if is_likely_module_path then ValueOrField else Value); + }))) + | Pexp_braces {expr = inner} -> iterator.expr iterator inner | Pexp_match (expr, cases) when cases <> [] && loc_has_cursor expr.pexp_loc = false @@ -1262,27 +1287,10 @@ let completion_with_parser1 ~debug ~offset ~pos_cursor ~kind_file (lid_path |> String.concat ".") (Loc.to_string lid.loc); if lid.loc |> Loc.has_pos ~pos:pos_before_cursor then - let is_likely_module_path = - match lid_path with - | head :: _ - when String.length head > 0 - && head.[0] == Char.uppercase_ascii head.[0] -> - true - | _ -> false - in set_result (Cpath (CPId - { - loc = lid.loc; - path = lid_path; - completion_context = - (if - is_likely_module_path - && expr |> Res_parsetree_viewer.is_braced_expr - then ValueOrField - else Value); - })) + {loc = lid.loc; path = lid_path; completion_context = Value})) | Pexp_construct (lid, {txt = args; loc = args_loc}) -> ( let lid_path = flatten_lid_check_dot lid in if debug then diff --git a/analysis/src/utils.ml b/analysis/src/utils.ml index be405cb20ed..f5cba22ad3a 100644 --- a/analysis/src/utils.ml +++ b/analysis/src/utils.ml @@ -83,6 +83,7 @@ let flatten_long_ident ?(jsx = false) ?(cut_at_offset = None) lid = let identify_pexp pexp = match pexp with + | Parsetree.Pexp_braces _ -> "Pexp_braces" | Parsetree.Pexp_ident _ -> "Pexp_ident" | Pexp_constant _ -> "Pexp_constant" | Pexp_let _ -> "Pexp_let" @@ -149,9 +150,6 @@ let rec skip_white text i = | ' ' | '\n' | '\r' | '\t' -> skip_white text (i - 1) | _ -> i -let has_braces attributes = - attributes |> List.exists (fun (loc, _) -> loc.Location.txt = "res.braces") - let rec unwrap_if_option (t : Types.type_expr) = match t.desc with | Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> unwrap_if_option t1 diff --git a/analysis/src/xform.ml b/analysis/src/xform.ml index 3ea35b09cf3..b7b44c8132e 100644 --- a/analysis/src/xform.ml +++ b/analysis/src/xform.ml @@ -247,20 +247,6 @@ module Add_braces_to_fn = struct current_structure_item := saved in let expr (iterator : Ast_iterator.iterator) (e : Parsetree.expression) = - let braces_attribute = - let loc = - { - Location.none with - loc_start = Lexing.dummy_pos; - loc_end = - { - Lexing.dummy_pos with - pos_lnum = Lexing.dummy_pos.pos_lnum + 1 (* force line break *); - }; - } - in - (Location.mkloc "res.braces" loc, Parsetree.PStr []) - in let is_function = function | {Parsetree.pexp_desc = Pexp_fun _} -> true | _ -> false @@ -270,9 +256,8 @@ module Add_braces_to_fn = struct when Loc.has_pos ~pos body_expr.pexp_loc && is_braced_expr body_expr = false && is_function body_expr = false -> - body_expr.pexp_attributes <- - braces_attribute :: body_expr.pexp_attributes; - changed := !current_structure_item + changed := + Option.map (fun item -> (item, body_expr)) !current_structure_item | _ -> ()); Ast_iterator.default_iterator.expr iterator e in @@ -285,7 +270,28 @@ module Add_braces_to_fn = struct iterator.structure iterator structure; match !changed with | None -> () - | Some new_structure_item -> + | Some (structure_item, body_expr) -> + let braces_loc = + { + Location.none with + loc_start = Lexing.dummy_pos; + loc_end = + { + Lexing.dummy_pos with + pos_lnum = Lexing.dummy_pos.pos_lnum + 1 (* force line break *); + }; + } + in + let mapper = + { + Ast_mapper.default_mapper with + expr = + (fun mapper expr -> + if expr == body_expr then Ast_helper.Exp.braces ~braces_loc expr + else Ast_mapper.default_mapper.expr mapper expr); + } + in + let new_structure_item = mapper.structure_item mapper structure_item in let range = Loc.range_of_loc new_structure_item.pstr_loc in let new_text = print_structure_item ~range new_structure_item in let code_action = diff --git a/compiler/frontend/bs_builtin_ppx.ml b/compiler/frontend/bs_builtin_ppx.ml index 15cb0243743..6b43f5f868b 100644 --- a/compiler/frontend/bs_builtin_ppx.ml +++ b/compiler/frontend/bs_builtin_ppx.ml @@ -819,6 +819,7 @@ let rec structure_mapper ~await_context (self : mapper) (stru : Ast_structure.t) ~typ:(Mty.typeof_ ~loc me))) :: aux expr) | Pexp_let (_, vbs, expr) -> aux expr @ spelunk_vbs acc vbs + | Pexp_braces {expr} -> aux expr | Pexp_ifthenelse (_, then_expr, Some else_expr) -> aux then_expr @ aux else_expr | Pexp_construct (_, {txt = [expr]}) -> aux expr diff --git a/compiler/ml/ast_helper.ml b/compiler/ml/ast_helper.ml index b08d7bca33f..cdcc952811b 100644 --- a/compiler/ml/ast_helper.ml +++ b/compiler/ml/ast_helper.ml @@ -164,6 +164,8 @@ module Exp = struct {pexp_desc = d; pexp_loc = loc; pexp_attributes = attrs} let attr d a = {d with pexp_attributes = d.pexp_attributes @ [a]} + let braces ?(attrs = []) ~braces_loc e = + mk ~loc:e.pexp_loc ~attrs (Pexp_braces {expr = e; braces_loc}) let ident ?loc ?attrs a = mk ?loc ?attrs (Pexp_ident a) let constant ?loc ?attrs a = mk ?loc ?attrs (Pexp_constant a) let let_ ?loc ?attrs a b c = mk ?loc ?attrs (Pexp_let (a, b, c)) diff --git a/compiler/ml/ast_helper.mli b/compiler/ml/ast_helper.mli index a4947466e4f..72823a77641 100644 --- a/compiler/ml/ast_helper.mli +++ b/compiler/ml/ast_helper.mli @@ -126,6 +126,7 @@ module Exp : sig val mk : ?loc:loc -> ?attrs:attrs -> expression_desc -> expression val attr : expression -> attribute -> expression + val braces : ?attrs:attrs -> braces_loc:loc -> expression -> expression val ident : ?loc:loc -> ?attrs:attrs -> lid -> expression val constant : ?loc:loc -> ?attrs:attrs -> constant -> expression val let_ : diff --git a/compiler/ml/ast_iterator.ml b/compiler/ml/ast_iterator.ml index cbb9fb72199..5090ad46c7c 100644 --- a/compiler/ml/ast_iterator.ml +++ b/compiler/ml/ast_iterator.ml @@ -288,6 +288,9 @@ module E = struct sub.location sub loc; sub.attributes sub attrs; match desc with + | Pexp_braces {expr; braces_loc} -> + sub.location sub braces_loc; + sub.expr sub expr | Pexp_ident x -> iter_loc sub x | Pexp_constant _ -> () | Pexp_let (_r, vbs, e) -> diff --git a/compiler/ml/ast_mapper.ml b/compiler/ml/ast_mapper.ml index 029d95e3004..ff5c85c366b 100644 --- a/compiler/ml/ast_mapper.ml +++ b/compiler/ml/ast_mapper.ml @@ -290,6 +290,10 @@ module E = struct let loc = sub.location sub loc in let attrs = sub.attributes sub attrs in match desc with + | Pexp_braces {expr; braces_loc} -> + braces + ~braces_loc:(sub.location sub braces_loc) + ~attrs (sub.expr sub expr) | Pexp_ident x -> ident ~loc ~attrs (map_loc sub x) | Pexp_constant x -> constant ~loc ~attrs x | Pexp_let (r, vbs, e) -> diff --git a/compiler/ml/ast_mapper_from0.ml b/compiler/ml/ast_mapper_from0.ml index d229e75362b..dddd2c54851 100644 --- a/compiler/ml/ast_mapper_from0.ml +++ b/compiler/ml/ast_mapper_from0.ml @@ -625,8 +625,36 @@ module E = struct let has_jsx_attribute () = attrs |> List.exists (fun ({txt}, _) -> txt = "JSX") in + let first_wrapper = + let rec find = function + | ({txt = "res.braces" | "ns.braces"; loc}, _) :: _ -> + Some (`Braces loc) + | ({txt = "res.await"}, _) :: _ -> Some `Await + | _ :: rest -> find rest + | [] -> None + in + find e.pexp_attributes + in match desc with - | _ when has_await_attribute attrs -> + | _ + when match first_wrapper with + | Some (`Braces _) -> true + | _ -> false -> + let outer_attrs0, braces_loc, inner_attrs0 = + let rec split acc = function + | ({Location.txt = "res.braces" | "ns.braces"; loc}, _) :: rest -> + (List.rev acc, loc, rest) + | a :: rest -> split (a :: acc) rest + | [] -> assert false + in + split [] e.pexp_attributes + in + let inner = sub.expr sub {e with pexp_attributes = inner_attrs0} in + braces + ~braces_loc:(sub.location sub braces_loc) + ~attrs:(sub.attributes sub outer_attrs0) + inner + | _ when first_wrapper = Some `Await -> (* [Ast_mapper_to0] merges the await node's attributes and the inner expression's attributes into the one v0 slot, with [res.await] as the boundary: await-node attributes before it, inner attributes diff --git a/compiler/ml/ast_mapper_to0.ml b/compiler/ml/ast_mapper_to0.ml index 446d7e3ec62..2da609a37cf 100644 --- a/compiler/ml/ast_mapper_to0.ml +++ b/compiler/ml/ast_mapper_to0.ml @@ -443,6 +443,16 @@ module E = struct let is_ppx_context_string = has_ppx_context_string_attr attrs in let attrs = sub.attributes sub (remove_ppx_context_string_attr attrs) in match desc with + | Pexp_braces {expr; braces_loc} -> + let inner = sub.expr sub expr in + { + inner with + pexp_attributes = + attrs + @ ( Location.mkloc "res.braces" (sub.location sub braces_loc), + Pt.PStr [] ) + :: inner.pexp_attributes; + } | Pexp_ident x -> ident ~loc ~attrs (map_loc sub x) | Pexp_constant (Pconst_string payload) when is_ppx_context_string -> (* The PPX protocol predates source-preserving strings. Existing PPXs diff --git a/compiler/ml/depend.ml b/compiler/ml/depend.ml index 28181b57e8e..f979f0ccd29 100644 --- a/compiler/ml/depend.ml +++ b/compiler/ml/depend.ml @@ -208,6 +208,7 @@ let add_pattern bv pat = let rec add_expr bv exp = match exp.pexp_desc with + | Pexp_braces {expr = inner} -> add_expr bv inner | Pexp_ident l -> add bv l | Pexp_constant _ -> () | Pexp_let (rf, pel, e) -> diff --git a/compiler/ml/parsetree.ml b/compiler/ml/parsetree.ml index e736c90b76f..b8d898efeaa 100644 --- a/compiler/ml/parsetree.ml +++ b/compiler/ml/parsetree.ml @@ -271,6 +271,9 @@ and expression = { } and expression_desc = + | Pexp_braces of {expr: expression; braces_loc: Location.t} + (* Explicit braces around an expression. [pexp_loc] stays on the enclosed + expression; [braces_loc] covers the delimiters for printing. *) | Pexp_ident of Longident.t loc (* x M.x *) diff --git a/compiler/ml/pprintast.ml b/compiler/ml/pprintast.ml index 12a8cafaf52..59241b033d3 100644 --- a/compiler/ml/pprintast.ml +++ b/compiler/ml/pprintast.ml @@ -801,6 +801,7 @@ and expression ctxt f x = | _ -> assert false in pp f "%a`%a`" (simple_expr ctxt) tag parts (raw_sources, values) + | Pexp_braces {expr = inner} -> pp f "{%a}" (expression ctxt) inner | _ -> expression1 ctxt f x and expression1 ctxt f x = @@ -846,7 +847,7 @@ and simple_expr ctxt f x = | Pexp_tuple l -> pp f "@[(%a)@]" (list (simple_expr ctxt) ~sep:",@;") l | Pexp_constraint (e, ct) -> - pp f "(%a : %a)" (expression ctxt) e (core_type ctxt) ct + pp f "(%a :@ %a)" (expression ctxt) e (core_type ctxt) ct | Pexp_coerce (e, (), ct) -> pp f "(%a :> %a)" (expression ctxt) e (core_type ctxt) ct | Pexp_variant (l, {txt = []}) -> pp f "`%s" l diff --git a/compiler/ml/printast.ml b/compiler/ml/printast.ml index bee24026932..e46b356ef99 100644 --- a/compiler/ml/printast.ml +++ b/compiler/ml/printast.ml @@ -247,6 +247,10 @@ and expression i ppf x = attributes i ppf x.pexp_attributes; let i = i + 1 in match x.pexp_desc with + | Pexp_braces {expr = inner; braces_loc} -> + line i ppf "Pexp_braces\n"; + line (i + 1) ppf "braces_loc %a\n" fmt_location braces_loc; + expression i ppf inner | Pexp_ident li -> line i ppf "Pexp_ident %a\n" fmt_longident_loc li | Pexp_object_literal fields -> line i ppf "Pexp_object_literal\n"; diff --git a/compiler/ml/typecore.ml b/compiler/ml/typecore.ml index 0f2d516fbe3..c32771bfc57 100644 --- a/compiler/ml/typecore.ml +++ b/compiler/ml/typecore.ml @@ -165,6 +165,7 @@ let iter_expression f e = let rec expr e = f e; match e.pexp_desc with + | Pexp_braces {expr = inner} -> expr inner | Pexp_extension _ (* we don't iterate under extension point *) | Pexp_ident _ | Pexp_constant _ -> () @@ -1868,6 +1869,7 @@ let type_pattern_list env spatl scope expected_tys allow = let rec final_subexpression sexp = match sexp.pexp_desc with + | Pexp_braces {expr = e} -> final_subexpression e | Pexp_let (_, _, e) | Pexp_sequence (_, e) | Pexp_try (e, _) @@ -2049,6 +2051,7 @@ let rec approx_type env sty = let rec type_approx env sexp = match sexp.pexp_desc with + | Pexp_braces {expr = e} -> type_approx env e | Pexp_let (_, _, e) -> type_approx env e | Pexp_fun {params; body} -> newty @@ -2424,12 +2427,8 @@ and type_expect ~context ?deprecated_context ?recarg env sexp ty_expected = (* Special errors for braced identifiers passed to records *) let context = match sexp.pexp_desc with - | Pexp_ident _ -> - if - sexp.pexp_attributes - |> List.exists (fun (attr, _) -> attr.txt = "res.braces") - then Some Error_message_utils.BracedIdent - else context + | Pexp_braces {expr = {pexp_desc = Pexp_ident _}} -> + Some Error_message_utils.BracedIdent | _ -> context in let previous_saved_types = Cmt_format.get_saved_types () in @@ -2461,6 +2460,8 @@ and type_expect_ ?deprecated_context ~context ?(recarg = Rejected) env sexp else (id, ld, e, opt) in match sexp.pexp_desc with + | Pexp_braces {expr = inner} -> + type_expect ~context ?deprecated_context ~recarg env inner ty_expected | Pexp_ident lid -> let path, desc = Typetexp.find_value diff --git a/compiler/syntax/src/res_ast_debugger.ml b/compiler/syntax/src/res_ast_debugger.ml index 7f191c78d41..74c74286b93 100644 --- a/compiler/syntax/src/res_ast_debugger.ml +++ b/compiler/syntax/src/res_ast_debugger.ml @@ -569,6 +569,18 @@ module Sexp_ast = struct and expression expr = let desc = match expr.pexp_desc with + | Pexp_braces {expr = inner; braces_loc} -> + Sexp.list + [ + Sexp.atom "Pexp_braces"; + Sexp.list + [ + Sexp.atom "braces_loc"; + Sexp.atom (string_of_int braces_loc.Location.loc_start.pos_cnum); + Sexp.atom (string_of_int braces_loc.loc_end.pos_cnum); + ]; + expression inner; + ] | Pexp_ident longident_loc -> Sexp.list [Sexp.atom "Pexp_ident"; longident longident_loc.Asttypes.txt] | Pexp_constant c -> Sexp.list [Sexp.atom "Pexp_constant"; constant c] diff --git a/compiler/syntax/src/res_comments_table.ml b/compiler/syntax/src/res_comments_table.ml index c60c3a76021..f7f770cf24a 100644 --- a/compiler/syntax/src/res_comments_table.ml +++ b/compiler/syntax/src/res_comments_table.ml @@ -397,6 +397,7 @@ let fun_expr expr = let rec is_block_expr expr = let open Parsetree in match expr.pexp_desc with + | Pexp_braces {expr = inner} -> is_block_expr inner | Pexp_letmodule _ | Pexp_letexception _ | Pexp_let _ | Pexp_open _ | Pexp_sequence _ -> true @@ -442,15 +443,15 @@ let get_loc node = { case.pc_lhs.ppat_loc with loc_end = - (match Parsetree_viewer.process_braces_attr case.pc_rhs with + (match Parsetree_viewer.process_braces case.pc_rhs with | None, _ -> case.pc_rhs.pexp_loc.loc_end - | Some ({loc}, _), _ -> loc.Location.loc_end); + | Some loc, _ -> loc.Location.loc_end); } | CoreType ct -> ct.ptyp_loc | ExprArgument {loc} -> loc | Expression e -> ( - match e.pexp_attributes with - | ({txt = "res.braces" | "ns.braces"; loc}, _) :: _ -> loc + match Parsetree_viewer.process_braces e with + | Some loc, _ -> loc | _ -> e.pexp_loc) | ExprRecordRow (li, e) -> {li.loc with loc_end = e.pexp_loc.loc_end} | ExtensionConstructor ec -> ec.pext_loc @@ -993,6 +994,13 @@ and walk_expression expr t comments = in match expr.Parsetree.pexp_desc with | _ when comments = [] -> () + | Pexp_braces {expr = inner} when is_block_expr inner -> + walk_expression inner t comments + | Pexp_braces {expr = inner} -> + let before, inside, after = partition_by_loc comments inner.pexp_loc in + attach t.leading inner.pexp_loc before; + walk_expression inner t inside; + attach t.trailing inner.pexp_loc after | Pexp_regexp _ | Pexp_constant _ -> let leading, trailing = partition_leading_trailing comments expr.pexp_loc in attach t.leading expr.pexp_loc leading; diff --git a/compiler/syntax/src/res_core.ml b/compiler/syntax/src/res_core.ml index c1a905c33c4..3be6d163590 100644 --- a/compiler/syntax/src/res_core.ml +++ b/compiler/syntax/src/res_core.ml @@ -275,7 +275,6 @@ let suppress_fragile_match_warning_attr = Ast_helper.Str.eval (Ast_helper.Exp.constant (Ast_helper.Const.string "-4")); ] ) -let make_braces_attr loc = (Location.mkloc "res.braces" loc, Parsetree.PStr []) let make_pat_variant_spread_attr = (Location.mknoloc "res.patVariantSpread", Parsetree.PStr []) @@ -2987,14 +2986,12 @@ and parse_braced_or_record_expr p = let expr = parse_expr_block p in Parser.expect Rbrace p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {expr with pexp_attributes = braces :: expr.pexp_attributes} + Ast_helper.Exp.braces ~braces_loc:loc expr | Continue -> let expr = parse_expr_block p in Parser.expect Rbrace p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {expr with pexp_attributes = braces :: expr.pexp_attributes} + Ast_helper.Exp.braces ~braces_loc:loc expr | token when Token.is_keyword token -> ( match recover_keyword_field_name_if_probably_field p @@ -3013,8 +3010,7 @@ and parse_braced_or_record_expr p = let expr = parse_expr_block p in Parser.expect Rbrace p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {expr with pexp_attributes = braces :: expr.pexp_attributes}) + Ast_helper.Exp.braces ~braces_loc:loc expr) | Rbrace -> Parser.next p; let loc = mk_loc start_pos (Parser.position p) in @@ -3072,22 +3068,16 @@ and parse_braced_or_record_expr p = let expr = parse_expr_block ~first:e p in Parser.expect Rbrace p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - { - expr with - Parsetree.pexp_attributes = braces :: expr.Parsetree.pexp_attributes; - } + Ast_helper.Exp.braces ~braces_loc:loc expr | Rbrace -> Parser.next p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {e with pexp_attributes = braces :: e.pexp_attributes} + Ast_helper.Exp.braces ~braces_loc:loc e | _ -> let expr = parse_expr_block ~first:e p in Parser.expect Rbrace p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {expr with pexp_attributes = braces :: expr.pexp_attributes})) + Ast_helper.Exp.braces ~braces_loc:loc expr)) | Question -> let expr = parse_record_expr ~start_pos [] p in Parser.expect Rbrace p; @@ -3105,8 +3095,7 @@ and parse_braced_or_record_expr p = let expr = parse_expr_block ~first:expr p in Parser.expect Rbrace p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {expr with pexp_attributes = braces :: expr.pexp_attributes} + Ast_helper.Exp.braces ~braces_loc:loc expr | Uident _ | Lident _ -> ( let start_token = Parser.peek p in let value_or_constructor = parse_value_or_constructor p in @@ -3199,14 +3188,12 @@ and parse_braced_or_record_expr p = in Parser.expect Rbrace p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {expr with pexp_attributes = braces :: expr.pexp_attributes} + Ast_helper.Exp.braces ~braces_loc:loc expr | Rbrace -> Parser.next p; let expr = Ast_helper.Exp.ident ~loc:path_ident.loc path_ident in let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {expr with pexp_attributes = braces :: expr.pexp_attributes} + Ast_helper.Exp.braces ~braces_loc:loc expr | EqualGreater -> ( let loc = mk_loc start_pos ident_end_pos in let ident = Location.mkloc (Longident.last path_ident.txt) loc in @@ -3229,19 +3216,16 @@ and parse_braced_or_record_expr p = let expr = parse_expr_block ~first:e p in Parser.expect Rbrace p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {expr with pexp_attributes = braces :: expr.pexp_attributes} + Ast_helper.Exp.braces ~braces_loc:loc expr | Rbrace -> Parser.next p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {e with pexp_attributes = braces :: e.pexp_attributes} + Ast_helper.Exp.braces ~braces_loc:loc e | _ -> let expr = parse_expr_block ~first:e p in Parser.expect Rbrace p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {expr with pexp_attributes = braces :: expr.pexp_attributes}) + Ast_helper.Exp.braces ~braces_loc:loc expr) | _ -> ( Parser.leave_breadcrumb p Grammar.ExprBlock; let a = @@ -3257,19 +3241,16 @@ and parse_braced_or_record_expr p = let expr = parse_expr_block ~first:e p in Parser.expect Rbrace p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {expr with pexp_attributes = braces :: expr.pexp_attributes} + Ast_helper.Exp.braces ~braces_loc:loc expr | Rbrace -> Parser.next p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {e with pexp_attributes = braces :: e.pexp_attributes} + Ast_helper.Exp.braces ~braces_loc:loc e | _ -> let expr = parse_expr_block ~first:e p in Parser.expect Rbrace p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {expr with pexp_attributes = braces :: expr.pexp_attributes})) + Ast_helper.Exp.braces ~braces_loc:loc expr)) | _ -> ( Parser.leave_breadcrumb p Grammar.ExprBlock; let a = parse_primary_expr ~operand:value_or_constructor p in @@ -3281,25 +3262,21 @@ and parse_braced_or_record_expr p = let expr = parse_expr_block ~first:e p in Parser.expect Rbrace p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {expr with pexp_attributes = braces :: expr.pexp_attributes} + Ast_helper.Exp.braces ~braces_loc:loc expr | Rbrace -> Parser.next p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {e with pexp_attributes = braces :: e.pexp_attributes} + Ast_helper.Exp.braces ~braces_loc:loc e | _ -> let expr = parse_expr_block ~first:e p in Parser.expect Rbrace p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {expr with pexp_attributes = braces :: expr.pexp_attributes})) + Ast_helper.Exp.braces ~braces_loc:loc expr)) | _ -> let expr = parse_expr_block p in Parser.expect Rbrace p; let loc = mk_loc start_pos (Parser.position p) in - let braces = make_braces_attr loc in - {expr with pexp_attributes = braces :: expr.pexp_attributes} + Ast_helper.Exp.braces ~braces_loc:loc expr and parse_record_expr_row_with_string_key p : Parsetree.expression Parsetree.record_element option = diff --git a/compiler/syntax/src/res_parens.ml b/compiler/syntax/src/res_parens.ml index b24c5fbede3..2c5a8a23ffc 100644 --- a/compiler/syntax/src/res_parens.ml +++ b/compiler/syntax/src/res_parens.ml @@ -2,9 +2,9 @@ module Parsetree_viewer = Res_parsetree_viewer type kind = Parenthesized | Braced of Location.t | Nothing let expr_with_coercion_kind coercion_kind expr = - let opt_braces, _ = Parsetree_viewer.process_braces_attr expr in + let opt_braces, _ = Parsetree_viewer.process_braces expr in match opt_braces with - | Some ({Location.loc = braces_loc}, _) -> Braced braces_loc + | Some braces_loc -> Braced braces_loc | _ -> ( match expr with | { @@ -30,9 +30,9 @@ let expr_record_row_rhs ~optional e = | _ -> kind let call_expr expr = - let opt_braces, _ = Parsetree_viewer.process_braces_attr expr in + let opt_braces, _ = Parsetree_viewer.process_braces expr in match opt_braces with - | Some ({Location.loc = braces_loc}, _) -> Braced braces_loc + | Some braces_loc -> Braced braces_loc | _ -> ( match expr with | {Parsetree.pexp_attributes = attrs} @@ -63,9 +63,9 @@ let call_expr expr = | _ -> Nothing) let structure_expr expr = - let opt_braces, _ = Parsetree_viewer.process_braces_attr expr in + let opt_braces, _ = Parsetree_viewer.process_braces expr in match opt_braces with - | Some ({Location.loc = braces_loc}, _) -> Braced braces_loc + | Some braces_loc -> Braced braces_loc | None -> ( match expr with | {pexp_desc = Pexp_jsx_element _} -> Nothing @@ -80,9 +80,9 @@ let structure_expr expr = | _ -> Nothing) let unary_expr_operand expr = - let opt_braces, _ = Parsetree_viewer.process_braces_attr expr in + let opt_braces, _ = Parsetree_viewer.process_braces expr in match opt_braces with - | Some ({Location.loc = braces_loc}, _) -> Braced braces_loc + | Some braces_loc -> Braced braces_loc | None -> ( match expr with | {Parsetree.pexp_attributes = attrs} @@ -116,9 +116,9 @@ let unary_expr_operand expr = | _ -> Nothing) let binary_expr_operand ~is_lhs expr = - let opt_braces, _ = Parsetree_viewer.process_braces_attr expr in + let opt_braces, _ = Parsetree_viewer.process_braces expr in match opt_braces with - | Some ({Location.loc = braces_loc}, _) -> Braced braces_loc + | Some braces_loc -> Braced braces_loc | None -> ( match expr with | { @@ -196,9 +196,9 @@ let binary_operator_inside_await_needs_parens operator = < Parsetree_viewer.operator_precedence "->" let assert_or_await_expr_rhs ?(in_await = false) expr = - let opt_braces, _ = Parsetree_viewer.process_braces_attr expr in + let opt_braces, _ = Parsetree_viewer.process_braces expr in match opt_braces with - | Some ({Location.loc = braces_loc}, _) -> Braced braces_loc + | Some braces_loc -> Braced braces_loc | None -> ( match expr with | {Parsetree.pexp_attributes = attrs} @@ -245,9 +245,9 @@ let is_negative_constant constant = | _ -> false let field_expr expr = - let opt_braces, _ = Parsetree_viewer.process_braces_attr expr in + let opt_braces, _ = Parsetree_viewer.process_braces expr in match opt_braces with - | Some ({Location.loc = braces_loc}, _) -> Braced braces_loc + | Some braces_loc -> Braced braces_loc | None -> ( match expr with | {Parsetree.pexp_attributes = attrs} @@ -281,9 +281,9 @@ let field_expr expr = | _ -> Nothing) let ternary_operand expr = - let opt_braces, _ = Parsetree_viewer.process_braces_attr expr in + let opt_braces, _ = Parsetree_viewer.process_braces expr in match opt_braces with - | Some ({Location.loc = braces_loc}, _) -> Braced braces_loc + | Some braces_loc -> Braced braces_loc | None -> ( match expr with | { @@ -311,10 +311,12 @@ let jsx_prop_expr expr = | Parsetree.Pexp_let _ | Pexp_sequence _ | Pexp_letexception _ | Pexp_letmodule _ | Pexp_open _ -> Nothing + | Pexp_braces {expr = inner} when Parsetree_viewer.is_block_expr inner -> + Nothing | _ -> ( - let opt_braces, _ = Parsetree_viewer.process_braces_attr expr in + let opt_braces, _ = Parsetree_viewer.process_braces expr in match opt_braces with - | Some ({Location.loc = braces_loc}, _) -> Braced braces_loc + | Some braces_loc -> Braced braces_loc | None -> ( match expr with | { @@ -349,10 +351,12 @@ let jsx_child_expr expr = | Parsetree.Pexp_let _ | Pexp_sequence _ | Pexp_letexception _ | Pexp_letmodule _ | Pexp_open _ -> Nothing + | Pexp_braces {expr = inner} when Parsetree_viewer.is_block_expr inner -> + Nothing | _ -> ( - let opt_braces, _ = Parsetree_viewer.process_braces_attr expr in + let opt_braces, _ = Parsetree_viewer.process_braces expr in match opt_braces with - | Some ({Location.loc = braces_loc}, _) -> Braced braces_loc + | Some braces_loc -> Braced braces_loc | _ -> ( match expr with | { @@ -384,9 +388,9 @@ let jsx_child_expr expr = | _ -> Parenthesized)) let binary_expr expr = - let opt_braces, _ = Parsetree_viewer.process_braces_attr expr in + let opt_braces, _ = Parsetree_viewer.process_braces expr in match opt_braces with - | Some ({Location.loc = braces_loc}, _) -> Braced braces_loc + | Some braces_loc -> Braced braces_loc | None -> ( match expr with | {Parsetree.pexp_attributes = _ :: _} as expr @@ -415,7 +419,7 @@ let mod_expr_functor_constraint mod_type = | _ -> false let braced_expr expr = - match expr.Parsetree.pexp_desc with + match (Parsetree_viewer.unwrap_braces expr).Parsetree.pexp_desc with | Pexp_constraint ({pexp_desc = Pexp_pack _}, {ptyp_desc = Ptyp_package _}) -> false | Pexp_constraint _ -> true diff --git a/compiler/syntax/src/res_parsetree_viewer.ml b/compiler/syntax/src/res_parsetree_viewer.ml index cef8640566e..52cf4d03ded 100644 --- a/compiler/syntax/src/res_parsetree_viewer.ml +++ b/compiler/syntax/src/res_parsetree_viewer.ml @@ -222,21 +222,25 @@ let fun_expr expr_ = (async, newtype_params newtypes @ params_of_fun params, body) | _ -> (false, [], expr_) -let process_braces_attr expr = - match expr.pexp_attributes with - | (({txt = "res.braces" | "ns.braces"}, _) as attr) :: attrs -> - (Some attr, {expr with pexp_attributes = attrs}) +let process_braces expr = + match expr.pexp_desc with + | Pexp_braces {expr = inner; braces_loc} -> (Some braces_loc, inner) | _ -> (None, expr) +let rec unwrap_braces expr = + match expr.pexp_desc with + | Pexp_braces {expr = inner} -> unwrap_braces inner + | _ -> expr + let filter_parsing_attrs attrs = List.filter (fun attr -> match attr with | ( { Location.txt = - ( "res.braces" | "ns.braces" | "res.iflet" | "res.ternary" - | "res.await" | "res.patVariantSpread" | "res.dictPattern" - | "res.dictSpread" | "res.inlineRecordDefinition" ); + ( "res.iflet" | "res.ternary" | "res.await" + | "res.patVariantSpread" | "res.dictPattern" | "res.dictSpread" + | "res.inlineRecordDefinition" ); }, _ ) -> false @@ -244,15 +248,15 @@ let filter_parsing_attrs attrs = attrs let is_block_expr expr = - match expr.pexp_desc with + match (unwrap_braces expr).pexp_desc with | Pexp_letmodule _ | Pexp_letexception _ | Pexp_let _ | Pexp_open _ | Pexp_sequence _ -> true | _ -> false let is_braced_expr expr = - match process_braces_attr expr with - | Some _, _ -> true + match expr.pexp_desc with + | Pexp_braces _ -> true | _ -> false let is_multiline_text txt = @@ -274,10 +278,9 @@ let is_huggable_expression expr = | Pexp_constant (Pconst_json _ | Pconst_char _) | Pexp_template {values = []} | Pexp_construct ({txt = Longident.Lident ("::" | "[]")}, _) - | Pexp_object_literal _ | Pexp_record _ -> + | Pexp_object_literal _ | Pexp_record _ | Pexp_braces _ -> true | _ when is_block_expr expr -> true - | _ when is_braced_expr expr -> true | Pexp_constant (Pconst_string payload) when is_multiline_text (String_literal.string_source payload) -> true @@ -287,8 +290,9 @@ let is_huggable_expression expr = let is_huggable_rhs expr = match expr.pexp_desc with - | Pexp_array _ | Pexp_tuple _ | Pexp_object_literal _ | Pexp_record _ -> true - | _ when is_braced_expr expr -> true + | Pexp_array _ | Pexp_tuple _ | Pexp_object_literal _ | Pexp_record _ + | Pexp_braces _ -> + true | _ -> false let is_huggable_pattern pattern = @@ -394,8 +398,8 @@ let has_attributes attrs = match attr with | ( { Location.txt = - ( "res.braces" | "ns.braces" | "res.iflet" | "res.ternary" - | "res.await" | "res.inlineRecordDefinition" ); + ( "res.iflet" | "res.ternary" | "res.await" + | "res.inlineRecordDefinition" ); }, _ ) -> false @@ -558,7 +562,7 @@ let should_indent_binary_expr expr = | _ -> false let should_inline_rhs_binary_expr rhs = - match rhs.pexp_desc with + match (unwrap_braces rhs).pexp_desc with | Parsetree.Pexp_constant _ | Pexp_let _ | Pexp_letmodule _ | Pexp_letexception _ | Pexp_sequence _ | Pexp_open _ | Pexp_ifthenelse _ | Pexp_for _ | Pexp_for_of _ | Pexp_for_await_of _ | Pexp_while _ | Pexp_try _ @@ -570,8 +574,8 @@ let is_printable_attribute attr = match attr with | ( { Location.txt = - ( "res.iflet" | "res.braces" | "ns.braces" | "JSX" | "res.await" - | "res.ternary" | "res.inlineRecordDefinition" | "res.dictSpread" ); + ( "res.iflet" | "JSX" | "res.await" | "res.ternary" + | "res.inlineRecordDefinition" | "res.dictSpread" ); }, _ ) -> false diff --git a/compiler/syntax/src/res_parsetree_viewer.mli b/compiler/syntax/src/res_parsetree_viewer.mli index 817d06aaf95..34f65553fe3 100644 --- a/compiler/syntax/src/res_parsetree_viewer.mli +++ b/compiler/syntax/src/res_parsetree_viewer.mli @@ -149,11 +149,13 @@ val collect_spread_dict_expr_parts : val collect_or_pattern_chain : Parsetree.pattern -> Parsetree.pattern list -val process_braces_attr : - Parsetree.expression -> Parsetree.attribute option * Parsetree.expression +val process_braces : + Parsetree.expression -> Location.t option * Parsetree.expression val filter_parsing_attrs : Parsetree.attributes -> Parsetree.attributes +val unwrap_braces : Parsetree.expression -> Parsetree.expression + val is_braced_expr : Parsetree.expression -> bool val is_single_pipe_expr : Parsetree.expression -> bool diff --git a/compiler/syntax/src/res_printer.ml b/compiler/syntax/src/res_printer.ml index 0c265fc518c..d8af802ceaf 100644 --- a/compiler/syntax/src/res_printer.ml +++ b/compiler/syntax/src/res_printer.ml @@ -28,8 +28,8 @@ let add_parens doc = Doc.rparen; ]) -let add_braces doc = - Doc.group +let add_braces ?(force_break = false) doc = + Doc.breakable_group ~force_break (Doc.concat [ Doc.lbrace; @@ -2497,7 +2497,7 @@ and print_value_binding ~state ~rec_flag (vb : Parsetree.value_binding) cmt_tbl ]); ])) | _ -> - let opt_braces, expr = Parsetree_viewer.process_braces_attr vb.pvb_expr in + let opt_braces, expr = Parsetree_viewer.process_braces vb.pvb_expr in let printed_expr = let doc = print_expression_with_comments ~state vb.pvb_expr cmt_tbl in match Parens.expr vb.pvb_expr with @@ -3113,7 +3113,7 @@ and print_if_chain ~state pexp_attributes ifs else_expr cmt_tbl = Doc.group condition; Doc.space; (let then_expr = - match Parsetree_viewer.process_braces_attr then_expr with + match Parsetree_viewer.process_braces then_expr with (* This case only happens when coming from Reason, we strip braces *) | Some _, expr -> expr | _ -> then_expr @@ -3148,6 +3148,7 @@ and print_if_chain ~state pexp_attributes ifs else_expr cmt_tbl = match else_expr with | None -> Doc.nil | Some expr -> + let _, expr = Parsetree_viewer.process_braces expr in Doc.concat [ Doc.text " else "; @@ -3209,8 +3210,16 @@ and print_object_get_doc ~state parent_expr (label : string Location.loc) Doc.group (Doc.concat [parent_doc; Doc.lbracket; member; Doc.rbracket]) and print_expression ~state (e : Parsetree.expression) cmt_tbl = + let force_pipe_breaks = + match e.pexp_desc with + | Pexp_braces {expr = inner; braces_loc} -> + braces_loc.loc_start.pos_lnum + 1 < inner.pexp_loc.loc_start.pos_lnum + | _ -> false + in + let e = Parsetree_viewer.unwrap_braces e in let printed_expression = match e.pexp_desc with + | Pexp_braces {expr = inner} -> print_expression ~state inner cmt_tbl | Pexp_fun { params = @@ -3490,7 +3499,7 @@ and print_expression ~state (e : Parsetree.expression) cmt_tbl = if Parsetree_viewer.is_unary_expression e then print_unary_expression ~state e cmt_tbl else if Parsetree_viewer.is_binary_expression e then - print_binary_expression ~state e cmt_tbl + print_binary_expression ~state ~force_pipe_breaks e cmt_tbl else print_pexp_apply ~state e cmt_tbl | Pexp_field (expr, longident_loc) -> let lhs = @@ -3746,15 +3755,7 @@ and print_expression ~state (e : Parsetree.expression) cmt_tbl = let rhs = match Parens.assert_or_await_expr_rhs ~in_await:true - { - e with - pexp_attributes = - List.filter - (function - | {Location.txt = "res.braces" | "ns.braces"}, _ -> false - | _ -> true) - e.pexp_attributes; - } + (Parsetree_viewer.unwrap_braces e) with | Parens.Parenthesized -> add_parens printed_expression | Braced braces -> print_braces printed_expression e braces @@ -3781,6 +3782,15 @@ and print_pexp_fun ~state ~in_callback e cmt_tbl = let attrs_on_arrow = e.pexp_attributes in let return_expr, typ_constraint = match return_expr.pexp_desc with + | Pexp_braces + { + expr = {pexp_desc = Pexp_constraint (expr, typ)} as constrained; + braces_loc; + } -> + ( Ast_helper.Exp.braces ~braces_loc + ~attrs:(constrained.pexp_attributes @ return_expr.pexp_attributes) + expr, + Some typ ) | Pexp_constraint (expr, typ) -> ( { expr with @@ -3807,7 +3817,7 @@ and print_pexp_fun ~state ~in_callback e cmt_tbl = | _ -> true in let return_expr_doc = - let opt_braces, _ = Parsetree_viewer.process_braces_attr return_expr in + let opt_braces, _ = Parsetree_viewer.process_braces return_expr in let should_inline = match (return_expr.pexp_desc, opt_braces) with | _, Some _ -> true @@ -3972,7 +3982,8 @@ and print_unary_expression ~state expr cmt_tbl = print_comments doc cmt_tbl expr.pexp_loc | _ -> assert false -and print_binary_expression ~state (expr : Parsetree.expression) cmt_tbl = +and print_binary_expression ~state ~force_pipe_breaks + (expr : Parsetree.expression) cmt_tbl = let print_binary_operator ~inline_rhs operator = let spacing_before_operator = if operator = "->" then Doc.soft_line else Doc.space @@ -4045,7 +4056,7 @@ and print_binary_expression ~state (expr : Parsetree.expression) cmt_tbl = ] else match operator with - | "->" when is_multiline -> + | "->" when is_multiline || force_pipe_breaks -> (* If the pipe-chain is written over multiple lines, break automatically * `let x = a->b->c -> same line, break when line-width exceeded * `let x = a-> @@ -4196,7 +4207,8 @@ and print_binary_expression ~state (expr : Parsetree.expression) cmt_tbl = else operator_with_rhs in let doc = - Doc.group + Doc.breakable_group + ~force_break:(force_pipe_breaks && operator = "->") (Doc.concat [ print_operand @@ -4430,7 +4442,7 @@ and print_pexp_apply ~state expr cmt_tbl = in let should_inline = match member_expr.pexp_desc with - | Pexp_constant _ | Pexp_ident _ -> true + | Pexp_constant _ | Pexp_ident _ | Pexp_braces _ -> true | _ -> false in if should_inline then member_doc @@ -4477,7 +4489,7 @@ and print_pexp_apply ~state expr cmt_tbl = in let should_inline = match member_expr.pexp_desc with - | Pexp_constant _ | Pexp_ident _ -> true + | Pexp_constant _ | Pexp_ident _ | Pexp_braces _ -> true | _ -> false in if should_inline then member_doc @@ -4771,16 +4783,9 @@ and get_line_sep_for_jsx_children (children : Parsetree.jsx_children) = and print_jsx_children ~state (children : Parsetree.jsx_children) cmt_tbl = let open Parsetree in let get_loc (expr : Parsetree.expression) = - let braces = - expr.pexp_attributes - |> List.find_map (fun (attr, _) -> - match attr with - | {Location.txt = "res.braces"; loc} -> Some loc - | _ -> None) - in - match braces with - | None -> expr.pexp_loc - | Some loc -> loc + match Parsetree_viewer.process_braces expr with + | None, _ -> expr.pexp_loc + | Some loc, _ -> loc in let sep = get_line_sep_for_jsx_children children in let print_expr (expr : Parsetree.expression) = @@ -4851,9 +4856,12 @@ and print_jsx_prop ~state prop cmt_tbl = in let value_doc = let leading_line_comment_present = - (* If the value expression has braces, these will be representend as an attribute containing the brace range *) - (* comment assignment is a little weird that this point, it will be assigned to a child node of the value expression *) - match (Parens.jsx_prop_expr value, value.pexp_desc) with + (* A leading comment on a braced application may be attached to + the function or first argument rather than the wrapper. *) + match + ( Parens.jsx_prop_expr value, + (Parsetree_viewer.unwrap_braces value).pexp_desc ) + with | ( Braced _, Parsetree.Pexp_apply {funct = fun_expr; args = (_, head_arg) :: _} ) -> @@ -4868,7 +4876,15 @@ and print_jsx_prop ~state prop cmt_tbl = let inner_doc = if Parens.braced_expr value then add_parens doc else doc in - if leading_line_comment_present then add_braces inner_doc + let multiline_before_value = + match value.pexp_desc with + | Pexp_braces {expr = inner; braces_loc} -> + braces_loc.loc_start.pos_lnum + 1 + < inner.pexp_loc.loc_start.pos_lnum + | _ -> false + in + if leading_line_comment_present || multiline_before_value then + add_braces ~force_break:true inner_doc else Doc.concat [Doc.lbrace; inner_doc; Doc.rbrace] | _ -> doc in @@ -5182,7 +5198,7 @@ and print_argument ~state (arg_lbl, arg) cmt_tbl = pexp_attributes = []; pexp_desc = Pexp_ident {txt = Longident.Lident name}; } ) - when lbl = name && not (Parsetree_viewer.is_braced_expr arg) -> + when lbl = name -> let loc = {l0 with loc_end = arg.pexp_loc.loc_end} in let doc = Doc.concat [Doc.tilde; print_ident_like lbl] in print_comments doc cmt_tbl loc @@ -5191,11 +5207,10 @@ and print_argument ~state (arg_lbl, arg) cmt_tbl = { pexp_desc = Pexp_constraint - ( ({pexp_desc = Pexp_ident {txt = Longident.Lident name}} as arg_expr), - typ ); + ({pexp_desc = Pexp_ident {txt = Longident.Lident name}}, typ); pexp_attributes = []; } ) - when lbl = name && not (Parsetree_viewer.is_braced_expr arg_expr) -> + when lbl = name -> let loc = {l0 with loc_end = arg.pexp_loc.loc_end} in let doc = Doc.concat @@ -5261,9 +5276,9 @@ and print_cases ~state (cases : Parsetree.case list) cmt_tbl = { n.Parsetree.pc_lhs.ppat_loc with loc_end = - (match Parsetree_viewer.process_braces_attr n.pc_rhs with + (match Parsetree_viewer.process_braces n.pc_rhs with | None, _ -> n.pc_rhs.pexp_loc.loc_end - | Some ({loc}, _), _ -> loc.Location.loc_end); + | Some loc, _ -> loc.Location.loc_end); }) ~print:(print_case ~state) ~nodes:cases cmt_tbl; ]; @@ -5516,6 +5531,7 @@ and print_exp_fun_parameter ~state parameter cmt_tbl = print_comments doc cmt_tbl cmt_loc and print_expression_block ~state ~braces expr cmt_tbl = + let expr = Parsetree_viewer.unwrap_braces expr in let rec collect_rows acc expr = match expr.Parsetree.pexp_desc with | Parsetree.Pexp_letmodule (mod_name, mod_expr, expr2) -> @@ -5665,7 +5681,7 @@ and print_braces doc expr braces_loc = let open Location in braces_loc.loc_end.pos_lnum > braces_loc.loc_start.pos_lnum in - match expr.Parsetree.pexp_desc with + match (Parsetree_viewer.unwrap_braces expr).Parsetree.pexp_desc with | Pexp_letmodule _ | Pexp_letexception _ | Pexp_let _ | Pexp_open _ | Pexp_sequence _ -> (* already has braces *) @@ -5944,11 +5960,10 @@ and print_mod_expr ~state mod_expr cmt_tbl = ]) | Pmod_unpack expr -> let should_hug = - match expr.pexp_desc with + match (Parsetree_viewer.unwrap_braces expr).pexp_desc with | Pexp_let _ -> true - | Pexp_constraint - ({pexp_desc = Pexp_let _}, {ptyp_desc = Ptyp_package _packageType}) - -> + | Pexp_constraint (inner, {ptyp_desc = Ptyp_package _}) + when Parsetree_viewer.is_block_expr inner -> true | _ -> false in diff --git a/tests/ounit_tests/ounit_ast_mapper0_tests.ml b/tests/ounit_tests/ounit_ast_mapper0_tests.ml index 5bc5a592b43..6f2d0a9049e 100644 --- a/tests/ounit_tests/ounit_ast_mapper0_tests.ml +++ b/tests/ounit_tests/ounit_ast_mapper0_tests.ml @@ -184,6 +184,56 @@ let test_constructor_runtime_tag_reaches_ast0_as_an_attribute _ = let map_expr0 e = Ast_mapper_from0.default_mapper.expr Ast_mapper_from0.default_mapper e +let test_braces_roundtrip_through_ast0 _ = + let inner_loc = source_loc 10 11 in + let inner_braces_loc = source_loc 8 13 in + let outer_braces_loc = source_loc 6 15 in + let inner = + Ast_helper.Exp.ident ~loc:inner_loc + ~attrs:[attr "inner" (Parsetree.PStr [])] + (located_string ~loc:inner_loc (Longident.Lident "x")) + in + let expr = + Ast_helper.Exp.braces ~braces_loc:outer_braces_loc + ~attrs:[attr "outer" (Parsetree.PStr [])] + (Ast_helper.Exp.braces ~braces_loc:inner_braces_loc inner) + in + let expr0 = + Ast_mapper_to0.default_mapper.expr Ast_mapper_to0.default_mapper expr + in + OUnit.assert_equal ~printer:(String.concat ", ") + ["outer"; "res.braces"; "res.braces"; "inner"] + (List.map + (fun (({txt} : string Location.loc), _) -> txt) + expr0.pexp_attributes); + let roundtrip = map_expr0 expr0 in + match roundtrip.pexp_desc with + | Parsetree.Pexp_braces + { + braces_loc = outer_loc; + expr = + { + pexp_desc = + Pexp_braces + { + braces_loc = mapped_inner_braces_loc; + expr = {pexp_desc = Pexp_ident _; pexp_loc; pexp_attributes}; + }; + pexp_attributes = inner_braces_attrs; + }; + } -> + OUnit.assert_equal ~msg:"outer braces location" outer_braces_loc outer_loc; + OUnit.assert_equal ~msg:"inner braces location" inner_braces_loc + mapped_inner_braces_loc; + OUnit.assert_equal ~msg:"expression location" inner_loc pexp_loc; + OUnit.assert_bool "outer attribute retained" + (has_attr "outer" roundtrip.pexp_attributes); + OUnit.assert_equal ~msg:"middle node attributes" [] inner_braces_attrs; + OUnit.assert_bool "inner attribute retained" + (has_attr "inner" pexp_attributes) + | _ -> + assert_failure "Expected two structural brace nodes after ast0 roundtrip" + let map_value_binding0 vb = Ast_mapper_from0.default_mapper.value_binding Ast_mapper_from0.default_mapper vb @@ -1528,6 +1578,7 @@ let suites = >:: test_malformed_internal_record_rest_attr_fails; "record_rest_roundtrips_through_ast0" >:: test_record_rest_roundtrips_through_ast0; + "braces_roundtrip_through_ast0" >:: test_braces_roundtrip_through_ast0; "constructor_args_roundtrip_through_ast0" >:: test_constructor_args_roundtrip_through_ast0; "list_constructor_wire_shape" >:: test_list_constructor_wire_shape; diff --git a/tests/syntax_tests/data/ast-mapping/Braces.res b/tests/syntax_tests/data/ast-mapping/Braces.res new file mode 100644 index 00000000000..daa9f65ad19 --- /dev/null +++ b/tests/syntax_tests/data/ast-mapping/Braces.res @@ -0,0 +1,6 @@ +let value = {{42}} +let branch = if true {1} else {2} +let case = switch value { +| 42 => {value} +| _ => 0 +} diff --git a/tests/syntax_tests/data/ast-mapping/expected/Braces.res.txt b/tests/syntax_tests/data/ast-mapping/expected/Braces.res.txt new file mode 100644 index 00000000000..94ec22a3e38 --- /dev/null +++ b/tests/syntax_tests/data/ast-mapping/expected/Braces.res.txt @@ -0,0 +1,10 @@ +let value = {42} +let branch = if true { + 1 +} else { + 2 +} +let case = switch value { +| 42 => value +| _ => 0 +} diff --git a/tests/syntax_tests/data/parsing/errors/expressions/expected/ambiguousArrow.res.txt b/tests/syntax_tests/data/parsing/errors/expressions/expected/ambiguousArrow.res.txt index 93790c5f7d1..ec0e7aafd32 100644 --- a/tests/syntax_tests/data/parsing/errors/expressions/expected/ambiguousArrow.res.txt +++ b/tests/syntax_tests/data/parsing/errors/expressions/expected/ambiguousArrow.res.txt @@ -26,5 +26,4 @@ let a [arity:1]b = ({js|hi|js} : int) let x = - ((let a = 1 in let b = 2 in fun [arity:1]pattern -> ({js|test|js} : int)) - [@res.braces ]) \ No newline at end of file + {let a = 1 in let b = 2 in fun [arity:1]pattern -> ({js|test|js} : int)} \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/errors/expressions/expected/arrow.res.txt b/tests/syntax_tests/data/parsing/errors/expressions/expected/arrow.res.txt index 2195fb842b7..29f4bf81a63 100644 --- a/tests/syntax_tests/data/parsing/errors/expressions/expected/arrow.res.txt +++ b/tests/syntax_tests/data/parsing/errors/expressions/expected/arrow.res.txt @@ -9,5 +9,4 @@ Did you forget a `,` here? ;;(Object.keys providers).reduce - (fun [arity:2]elements providerId -> ((let x = 1 in let b = 2 in x + b) - [@res.braces ])) \ No newline at end of file + (fun [arity:2]elements providerId -> {let x = 1 in let b = 2 in x + b}) \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/errors/expressions/expected/block.res.txt b/tests/syntax_tests/data/parsing/errors/expressions/expected/block.res.txt index 772ed2be8f3..cde7acc804e 100644 --- a/tests/syntax_tests/data/parsing/errors/expressions/expected/block.res.txt +++ b/tests/syntax_tests/data/parsing/errors/expressions/expected/block.res.txt @@ -64,19 +64,17 @@ Looks like there might be an expression missing here let findThreadByIdLinearScan [arity:2]~threads ~id = - ((Array.findWithIndex ThreadsModel.threads - (fun [arity:2]thread i -> - ((let thisId = - match thread with - | ServerData.OneToOne { otherPersonIDWhichIsAlsoThreadID } -> - otherPersonIDWhichIsAlsoThreadID - | Group { id } -> id - | Unknown { id } -> - (unknown.id -> String.make) -> FBID.ofStringUnsafe in - thisId === id) - [@res.braces ]))) - [@res.braces ]) -let x = ((loop 0 (Nil -> (push doc)))[@res.braces ]) + {Array.findWithIndex ThreadsModel.threads + (fun [arity:2]thread i -> + {let thisId = + match thread with + | ServerData.OneToOne { otherPersonIDWhichIsAlsoThreadID } -> + otherPersonIDWhichIsAlsoThreadID + | Group { id } -> id + | Unknown { id } -> + (unknown.id -> String.make) -> FBID.ofStringUnsafe in + thisId === id})} +let x = {loop 0 (Nil -> (push doc))} ;;match stack with | Empty -> [%rescript.exprhole ] | Cons (doc, rest) -> () diff --git a/tests/syntax_tests/data/parsing/errors/expressions/expected/consecutive.res.txt b/tests/syntax_tests/data/parsing/errors/expressions/expected/consecutive.res.txt index 3d83ff40687..4fd825cc506 100644 --- a/tests/syntax_tests/data/parsing/errors/expressions/expected/consecutive.res.txt +++ b/tests/syntax_tests/data/parsing/errors/expressions/expected/consecutive.res.txt @@ -46,6 +46,6 @@ let f [arity:2]a b = a + 3 ;;b -let f [arity:2]g h = ((a + 3; b)[@res.braces ]) -let () = ((sideEffect1 (); sideEffect2 ())[@res.braces ]) -let () = ((let open Foo in let exception End in x ())[@res.braces ]) \ No newline at end of file +let f [arity:2]g h = {a + 3; b} +let () = {sideEffect1 (); sideEffect2 ()} +let () = {let open Foo in let exception End in x ()} \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/errors/expressions/expected/implementation.res.txt b/tests/syntax_tests/data/parsing/errors/expressions/expected/implementation.res.txt index a22e3718b2b..68efefd8f5a 100644 --- a/tests/syntax_tests/data/parsing/errors/expressions/expected/implementation.res.txt +++ b/tests/syntax_tests/data/parsing/errors/expressions/expected/implementation.res.txt @@ -11,7 +11,5 @@ I'm not sure what to parse here when looking at "}". module InstallerDownload = - struct - let make [arity:1]() = ((
)[@res.braces ])[@@react.component ] - end + struct let make [arity:1]() = {
}[@@react.component ] end module LicenseList = struct end \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/errors/expressions/expected/letBinding.res.txt b/tests/syntax_tests/data/parsing/errors/expressions/expected/letBinding.res.txt index 5f5e78438db..e2cb679c6b4 100644 --- a/tests/syntax_tests/data/parsing/errors/expressions/expected/letBinding.res.txt +++ b/tests/syntax_tests/data/parsing/errors/expressions/expected/letBinding.res.txt @@ -83,6 +83,6 @@ let foo = 2. let foo = true let foo = 2 let foo = f () -let foo = ((2)[@res.braces ]) -let foo = (({js|foo|js})[@res.braces ]) +let foo = {2} +let foo = {{js|foo|js}} let foo as x = () \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/errors/expressions/expected/typeDefInFunction.res.txt b/tests/syntax_tests/data/parsing/errors/expressions/expected/typeDefInFunction.res.txt index 622b8030a9c..45127a17daa 100644 --- a/tests/syntax_tests/data/parsing/errors/expressions/expected/typeDefInFunction.res.txt +++ b/tests/syntax_tests/data/parsing/errors/expressions/expected/typeDefInFunction.res.txt @@ -10,4 +10,4 @@ Type definitions are not allowed inside functions. Move this `type` declaration to the top level or into a module. -let f [arity:1]() = ((1)[@res.braces ]) \ No newline at end of file +let f [arity:1]() = {1} \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/errors/expressions/expected/unexpectedConstraint.res.txt b/tests/syntax_tests/data/parsing/errors/expressions/expected/unexpectedConstraint.res.txt index 1203e3da1da..432d77982dc 100644 --- a/tests/syntax_tests/data/parsing/errors/expressions/expected/unexpectedConstraint.res.txt +++ b/tests/syntax_tests/data/parsing/errors/expressions/expected/unexpectedConstraint.res.txt @@ -23,5 +23,5 @@ Expressions with type constraints need to be wrapped in parens: ("hi": string) -let x = ((let a = 1 in let b = 2 in (a + b : int))[@res.braces ]) +let x = {let a = 1 in let b = 2 in (a + b : int)} let x = ({js|hi|js} : string) \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/errors/pattern/expected/missing.res.txt b/tests/syntax_tests/data/parsing/errors/pattern/expected/missing.res.txt index 79320227adc..494e0fbfe44 100644 --- a/tests/syntax_tests/data/parsing/errors/pattern/expected/missing.res.txt +++ b/tests/syntax_tests/data/parsing/errors/pattern/expected/missing.res.txt @@ -60,5 +60,5 @@ let 4 = [%rescript.exprhole ] ;;0 ;;to ;;10 -;;((Console.log {js|for|js})[@res.braces ]) +;;{Console.log {js|for|js}} ;;match x with | () -> [%rescript.exprhole ] \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/errors/structure/expected/gh16B.res.txt b/tests/syntax_tests/data/parsing/errors/structure/expected/gh16B.res.txt index 9ccd682f928..ea9ee8606fe 100644 --- a/tests/syntax_tests/data/parsing/errors/structure/expected/gh16B.res.txt +++ b/tests/syntax_tests/data/parsing/errors/structure/expected/gh16B.res.txt @@ -24,9 +24,8 @@ module ClientSet = (Belt.Id.MakeComparable)(struct type nonrec t = Client.t let cmp [arity:2]a b = - ((compare (a -> Client.getUniqueId) - (b -> Client.getUniqueId)) - [@res.braces ]) + {compare (a -> Client.getUniqueId) + (b -> Client.getUniqueId)} end) let empty = Belt.Set.make ~id:(module T) end diff --git a/tests/syntax_tests/data/parsing/errors/structure/expected/letBinding.res.txt b/tests/syntax_tests/data/parsing/errors/structure/expected/letBinding.res.txt index f0cf2ceef1a..20efdcf99e3 100644 --- a/tests/syntax_tests/data/parsing/errors/structure/expected/letBinding.res.txt +++ b/tests/syntax_tests/data/parsing/errors/structure/expected/letBinding.res.txt @@ -100,8 +100,8 @@ let rightResource = (ur.resources).find (fun [arity:1]r -> r.account_id === ((connection.left).account).id) -let x = ((let field = p -> parseFieldDeclaration in field)[@res.braces ]) -let t = ((let (_, _, token) = scanner -> scan in token)[@res.braces ]) +let x = {let field = p -> parseFieldDeclaration in field} +let t = {let (_, _, token) = scanner -> scan in token} let (keyTable : int Belt.Map.String.t) = [%rescript.exprhole ] let foo = [%rescript.exprhole ] let (x : int) = string = y diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/angle_operators.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/angle_operators.res.txt index a30403f20d0..6ed3664bdb8 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/angle_operators.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/angle_operators.res.txt @@ -12,9 +12,8 @@ let call = let comment = a >>> b let template = `prefix ${(a >> 2) >= b} suffix` let regex = /[<>]+>>=/g -let jsx =
= b)[@res.braces ]) data-value=((a >> 1) - [@res.braces ])>((a << 1)[@res.braces ]) - <>((`value ${a >>> 1}`)[@res.braces ])
+let jsx =
= b}) data-value=({a >> 1})>({a << 1}) + <>({`value ${a >>> 1}`})
let expressionAfterType = (value : int array) >= other let diamond = {js|💎|js} let located = a >>> b \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/arrow.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/arrow.res.txt index dc435527365..884495cadfa 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/arrow.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/arrow.res.txt @@ -78,5 +78,5 @@ let arr [arity:1]() = ([||] : int nullable array) let fn [arity:1]f = f type nonrec f = int -> unit (a:1) let a = fn (fun [arity:1]_ -> () : f) -let returnsArrayOption [arity:1]() = - (Some [|{js|foo|js}|] : string array option) \ No newline at end of file +let returnsArrayOption [arity:1]() = (Some [|{js|foo|js}|] : + string array option) \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/async.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/async.res.txt index f726e44a170..15285c607aa 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/async.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/async.res.txt @@ -1,21 +1,18 @@ let greetUser async [arity:1]userId = - ((let name = await (getUserName userId) in - ({js|Hello |js} ++ name) ++ {js|!|js}) - [@res.braces ]) + {let name = await (getUserName userId) in + ({js|Hello |js} ++ name) ++ {js|!|js}} ;;async fun [arity:1]() -> 123 -let fetch = ((async fun [arity:1]url -> browserFetch url)[@res.braces ]) +let fetch = {async fun [arity:1]url -> browserFetch url} let fetch2 = - (((async fun [arity:1]url -> browserFetch url); - (async fun [arity:1]url -> browserFetch2 url)) - [@res.braces ]) + {(async fun [arity:1]url -> browserFetch url); + (async fun [arity:1]url -> browserFetch2 url)} let async = - ((let f = async () in - () -> async; - async (); - async.async; - { async = (async.(async)) }; - (result -> async) -> (mapAsync (fun [arity:1]a -> doStuff a))) - [@res.braces ]) + {let f = async () in + () -> async; + async (); + async.async; + { async = (async.(async)) }; + (result -> async) -> (mapAsync (fun [arity:1]a -> doStuff a))} let f = ((if isPositive then async fun [arity:2]a b -> (a + b : int) diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/await.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/await.res.txt index 5637b62c3f9..7363b6d520a 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/await.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/await.res.txt @@ -6,26 +6,21 @@ let maybeSomeValue = let x = (await 1) + 2 let x = (await (wait 1)) + (await (wait 2)) let () = - ((let response = await (fetch {js|/users.json|js}) in - let users = await (response.json ()) in - let comments = (await ((await (fetch {js|comment.json|js})).json ())).(0) in - Console.log2 users comments) - [@res.braces ]) -let () = ((await (delay 10))[@res.braces ]) -let () = ((await (delay 10); await (delay 20))[@res.braces ]) + {let response = await (fetch {js|/users.json|js}) in + let users = await (response.json ()) in + let comments = (await ((await (fetch {js|comment.json|js})).json ())).(0) in + Console.log2 users comments} +let () = {await (delay 10)} +let () = {await (delay 10); await (delay 20)} let forEach = await ((import Belt.List.forEach)[@a ][@b ]) module M = ((Belt.List)[@res.await ][@a ][@b ]) let f [arity:1]() = - ((let module M = ((Belt.List)[@res.await ][@a ][@b ]) in M.forEach) - [@res.braces ]) -let () = ((let module M = ((Belt.List)[@res.await ][@a ][@b ]) in M.forEach) - [@res.braces ]) + {let module M = ((Belt.List)[@res.await ][@a ][@b ]) in M.forEach} +let () = {let module M = ((Belt.List)[@res.await ][@a ][@b ]) in M.forEach} module type BeltList = module type of Belt.List let f [arity:1]() = - ((let module M = (((Belt.List : BeltList))[@res.await ][@a ][@b ]) in - M.forEach) - [@res.braces ]) + {let module M = (((Belt.List : BeltList))[@res.await ][@a ][@b ]) in + M.forEach} let () = - ((let module M = (((Belt.List : BeltList))[@res.await ][@a ][@b ]) in - M.forEach) - [@res.braces ]) \ No newline at end of file + {let module M = (((Belt.List : BeltList))[@res.await ][@a ][@b ]) in + M.forEach} \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/binary.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/binary.res.txt index eb4809b8cb8..6e601802db6 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/binary.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/binary.res.txt @@ -24,6 +24,6 @@ let x = a - b let x = a -. b ;;Constructor (a, b) ;;`Constructor (a, b) -let _ = ((Constructor (a, b); `Constructor (a, b))[@res.braces ]) +let _ = {Constructor (a, b); `Constructor (a, b)} ;;(library.getBalance account) -> - (Promise.catch (fun [arity:1]_ -> ((Promise.resolve None)[@res.braces ]))) \ No newline at end of file + (Promise.catch (fun [arity:1]_ -> {Promise.resolve None})) \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/binaryNoEs6Arrow.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/binaryNoEs6Arrow.res.txt index 260cca36aac..487e1f50b4d 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/binaryNoEs6Arrow.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/binaryNoEs6Arrow.res.txt @@ -21,12 +21,12 @@ ((color === Black) && (color === Red)) && ((sibling === None) || (parent === None)) do () done -;;
- ((match videoContainerRect with - | Some videoContainerRect -> - let newChapter = - ({ startTime = (percent *. duration) } : Video.chapter) in - { a; b } -> onChange - | _ -> ()) - [@res.braces ]))[@res.braces ]) /> +;;
+ {match videoContainerRect with + | Some videoContainerRect -> + let newChapter = + ({ startTime = (percent *. duration) } : + Video.chapter) in + { a; b } -> onChange + | _ -> ()}}) /> ;;if inclusions.(index) <- (uid, url) then onChange inclusions \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/block.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/block.res.txt index 509028d0e0e..dcf25f9fc99 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/block.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/block.res.txt @@ -1,74 +1,61 @@ let b = - ((let module Array = Belt.Array in - ([|1;2|] -> (Array.map (fun [arity:1]x -> x + 1))) -> Console.log) - [@res.braces ]) + {let module Array = Belt.Array in + ([|1;2|] -> (Array.map (fun [arity:1]x -> x + 1))) -> Console.log} let b = - ((let open Belt.Array in - ([|1;2|] -> (map (fun [arity:1]x -> x + 1))) -> Console.log) - [@res.braces ]) -let b = ((let exception QuitEarly in throw QuitEarly)[@res.braces ]) -let b = ((let a = 1 in let b = 2 in a + b)[@res.braces ]) -let b = ((let _ = sideEffect () in ())[@res.braces ]) -let b = ((let _ = sideEffect () in ())[@res.braces ]) -let b = ((a (); b (); c ())[@res.braces ]) -let b = ((a (); b (); (let a = 1 in f a))[@res.braces ]) -let b = ((let a = 1 in let b = 2 in ())[@res.braces ]) + {let open Belt.Array in + ([|1;2|] -> (map (fun [arity:1]x -> x + 1))) -> Console.log} +let b = {let exception QuitEarly in throw QuitEarly} +let b = {let a = 1 in let b = 2 in a + b} +let b = {let _ = sideEffect () in ()} +let b = {let _ = sideEffect () in ()} +let b = {a (); b (); c ()} +let b = {a (); b (); (let a = 1 in f a)} +let b = {let a = 1 in let b = 2 in ()} let b = - ((let module Array = Belt.Array in - let open Array in - let exception Terminate of int in - let a = 1 in - let b = 2 in - sideEffect (); - (let x = (1 + 2) -> (fun [arity:1]x -> x + 1) in - throw (Terminate x))) - [@res.braces ]) -let b = ((f (); g (); h (); (let arr = [|1;2;3|] in ()))[@res.braces ]) + {let module Array = Belt.Array in + let open Array in + let exception Terminate of int in + let a = 1 in + let b = 2 in + sideEffect (); + (let x = (1 + 2) -> (fun [arity:1]x -> x + 1) in throw (Terminate x))} +let b = {f (); g (); h (); (let arr = [|1;2;3|] in ())} let res = - ((let a = {js|a starts out as|js} in - (((print_string a; (let a = 20 in print_int a))) - [@res.braces ]); - print_string a) - [@res.braces ]) + {let a = {js|a starts out as|js} in + {(print_string a; (let a = 20 in print_int a))}; print_string a} let res = - ((let a = {js|first its a string|js} in - let a = 20 in print_int a; print_int a; print_int a) - [@res.braces ]) + {let a = {js|first its a string|js} in + let a = 20 in print_int a; print_int a; print_int a} let res = - ((let a = {js|a is always a string|js} in - print_string a; (let b = 30 in print_int b)) - [@res.braces ]) -let nestedLet = ((let _ = 1 in ())[@res.braces ]) -let nestedLet = ((let _ = 1 in 2)[@res.braces ]) -let init [arity:1]() = ((foo (1 === 1); [%assert 1 === 2])[@res.braces ]) -let init [arity:1]() = (([%assert 1 === 2]; foo (1 === 1); [%assert 1 === 2]) - [@res.braces ]) -let f [arity:1]() = ((let x = 1 in fun [arity:1]_ -> ())[@res.braces ]) + {let a = {js|a is always a string|js} in + print_string a; (let b = 30 in print_int b)} +let nestedLet = {let _ = 1 in ()} +let nestedLet = {let _ = 1 in 2} +let init [arity:1]() = {foo (1 === 1); [%assert 1 === 2]} +let init [arity:1]() = {[%assert 1 === 2]; foo (1 === 1); [%assert 1 === 2]} +let f [arity:1]() = {let x = 1 in fun [arity:1]_ -> ()} let reifyStyle (type a) [arity:1](x : 'a) = - (((let module Internal = - struct - type constructor - external canvasGradient : constructor = "CanvasGradient"[@@val ] - external canvasPattern : constructor = "CanvasPattern"[@@val ] - let instanceOf = - ([%raw `function(x,y) {return +(x instanceof y)}`] : 'a -> - constructor - -> - bool (a:2)) - end in - ((if (typeof x) == {js|string|js} - then Obj.magic String - else - if Internal.instanceOf x Internal.canvasGradient - then Obj.magic Gradient - else - if Internal.instanceOf x Internal.canvasPattern - then Obj.magic Pattern - else - throw - (Invalid_argument - {js|Unknown canvas style kind. Known values are: String, CanvasGradient, CanvasPattern|js})), - (Obj.magic x))) - [@res.braces ]) : (a style * a)) + ({let module Internal = + struct + type constructor + external canvasGradient : constructor = "CanvasGradient"[@@val ] + external canvasPattern : constructor = "CanvasPattern"[@@val ] + let instanceOf = ([%raw `function(x,y) {return +(x instanceof y)}`] : + 'a -> constructor -> bool (a:2)) + end in + ((if (typeof x) == {js|string|js} + then Obj.magic String + else + if Internal.instanceOf x Internal.canvasGradient + then Obj.magic Gradient + else + if Internal.instanceOf x Internal.canvasPattern + then Obj.magic Pattern + else + throw + (Invalid_argument + {js|Unknown canvas style kind. Known values are: String, CanvasGradient, CanvasPattern|js})), + (Obj.magic x))} : + (a style * a)) let calc_fps [arity:2]t0 t1 = - ((let delta = (t1 -. t0) /. 1000. in 1. /. delta)[@res.braces ]) \ No newline at end of file + {let delta = (t1 -. t0) /. 1000. in 1. /. delta} \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/bracedOrRecord.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/bracedOrRecord.res.txt index bd6ca9da36a..dfed8e7d72b 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/bracedOrRecord.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/bracedOrRecord.res.txt @@ -4,26 +4,22 @@ let r = { a = expr } let r = { a = expr } let r = { a = expr; b = expr2 } let r = { f = (fun [arity:1]x -> x + b) } -let e = ((a)[@res.braces ]) -let e = ((a)[@res.braces ]) -let e = ((a; b ())[@res.braces ]) -let e = ((- a)[@res.braces ]) -let e = ((a + b)[@res.braces ]) -let e = ((if a then true else false)[@res.braces ][@res.ternary ]) -let e = ((if a -> computation then true else false) - [@res.braces ][@res.ternary ]) -let e = ((a.(0))[@res.braces ]) -let e = ((f b)[@res.braces ]) -let e = (((a.b).c)[@res.braces ]) -let e = ((arr.(x) <- 20)[@res.braces ]) -let e = ((fun [arity:1]x -> x + (1 -> (doStuff config)))[@res.braces ]) -let e = (((fun [arity:1]x -> x + 1) -> (doStuff config))[@res.braces ]) -let e = ((if fun [arity:1]x -> x + 1 then true else false) - [@res.braces ][@res.ternary ]) -let e = (((fun [arity:1]x -> x + 1) -> sideEffect; logToAnalytics Shady.ml) - [@res.braces ]) -let f = ((fun [arity:1]event -> (event.target).value)[@res.braces ]) -let f = ((fun [arity:1]event -> ((event.target).value : string)) - [@res.braces ]) -let x = ((let a = 1 in let b = 2 in a + b)[@res.braces ]) -;;<>(({js|\n|js} -> React.string)[@res.braces ]) \ No newline at end of file +let e = {a} +let e = {a} +let e = {a; b ()} +let e = {- a} +let e = {a + b} +let e = {((if a then true else false)[@res.ternary ])} +let e = {((if a -> computation then true else false)[@res.ternary ])} +let e = {a.(0)} +let e = {f b} +let e = {(a.b).c} +let e = {arr.(x) <- 20} +let e = {fun [arity:1]x -> x + (1 -> (doStuff config))} +let e = {(fun [arity:1]x -> x + 1) -> (doStuff config)} +let e = {((if fun [arity:1]x -> x + 1 then true else false)[@res.ternary ])} +let e = {(fun [arity:1]x -> x + 1) -> sideEffect; logToAnalytics Shady.ml} +let f = {fun [arity:1]event -> (event.target).value} +let f = {fun [arity:1]event -> ((event.target).value : string)} +let x = {let a = 1 in let b = 2 in a + b} +;;<>({{js|\n|js} -> React.string}) \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/bsObject.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/bsObject.res.txt index ccfacfb92ec..3f5d734f64e 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/bsObject.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/bsObject.res.txt @@ -4,17 +4,14 @@ let y = {"age": 30} let y = {"age": 30, "name": {js|steve|js}} let y = {"age": 30, "name": {js|steve|js}} let z = {"\xff": 1, "\u2212": {js|two|js}, "\0": zero, "\o123": {js|o123|js}} -let x = (({js|age|js})[@res.braces ]) -let x = (({js|age|js}.(0))[@res.braces ]) -let x = (({js|age|js} -> Console.log)[@res.braces ]) -let x = ((if {js|age|js} then true else false)[@res.braces ][@res.ternary ]) +let x = {{js|age|js}} +let x = {{js|age|js}.(0)} +let x = {{js|age|js} -> Console.log} +let x = {((if {js|age|js} then true else false)[@res.ternary ])} let x = - (({js|age|js} -> Console.log; (let foo = 1 in let bar = 2 in foo + bar)) - [@res.braces ]) + {{js|age|js} -> Console.log; (let foo = 1 in let bar = 2 in foo + bar)} let x = - ((((if {js|age|js} then true else false) - [@res.ternary ]); - (let foo = 1 in let bar = 2 in foo + bar)) - [@res.braces ]) -let x = (({js|age|js}.(0); (let foo = 1 in let bar = 2 in foo + bar)) - [@res.braces ]) \ No newline at end of file + {((if {js|age|js} then true else false) + [@res.ternary ]); + (let foo = 1 in let bar = 2 in foo + bar)} +let x = {{js|age|js}.(0); (let foo = 1 in let bar = 2 in foo + bar)} \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/constants.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/constants.res.txt index d42b6cb01fe..714e77fed55 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/constants.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/constants.res.txt @@ -46,7 +46,7 @@ let x = '\b' let x = '\r' let x = ' ' let x = '\xAA' -let () = ((getResult (); (-10))[@res.braces ]) +let () = {getResult (); (-10)} let x = {js|foo\0bar|js} let x = {js|foo\x0Abar|js} let x = {js|\\abc|js} diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/firstClassModule.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/firstClassModule.res.txt index ba6acf0018d..2f017c5862b 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/firstClassModule.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/firstClassModule.res.txt @@ -1,8 +1,7 @@ let makeSet (type s) [arity:1]cmp = - ((let module S = (Set.Make)(struct type nonrec t = s - let compare = cmp end) in ((module - S) : (module Set.S with type elt = s))) - [@res.braces ]) + {let module S = (Set.Make)(struct type nonrec t = s + let compare = cmp end) in ((module S) : + (module Set.S with type elt = s))} let three = ((module Three) : (module X_int)) let numbers = [|three;(module Four)|] let numbers = (three, (module Four)) @@ -10,35 +9,34 @@ let numbers = [three; (module Four)] let numbers = [|three;(module struct let x = 4 end)|] let numbers = (three, (module struct let x = 4 end)) let numbers = [three; (module struct let x = 4 end)] -let plus [arity:2]m1 m2 = ((((module - struct let x = (to_int m1) + (to_int m2) end) : (module X_int))) - [@res.braces ]) +let plus [arity:2]m1 m2 = {((module + struct let x = (to_int m1) + (to_int m2) end) : (module X_int))} let plus [arity:2]m1 m2 = ((module struct let x = (to_int m1) + (to_int m2) end) : (module X_int)) let unique_instance = ((module struct module Query_handler = Unique - let this = Unique.create 0 end) : (module Query_handler_instance)) + let this = Unique.create 0 end) : + (module Query_handler_instance)) let build_instance (type a) [arity:2]((module Q) : (module Query_handler with type config = a)) config - = ((module - struct module Query_handler = Q - let this = Q.create config end) : (module Query_handler_instance)) + = ((module struct module Query_handler = Q + let this = Q.create config end) : + (module Query_handler_instance)) let build_instance (type a) [arity:2]((module Q) : (module Query_handler with type config = a)) config - = ((((module + = {((module struct module Query_handler = Q - let this = Q.create config end) : (module Query_handler_instance))) - [@res.braces ]) + let this = Q.create config end) : + (module Query_handler_instance))} let unique_instance = build_instance (module Unique) 0 let build_dispatch_table [arity:1]handlers = - ((let table = Hashtbl.create (module String) in - List.iter handlers - ~f:(fun - [arity:1](((module I) : (module Query_handler_instance)) as - instance) - -> Hashtbl.set table ~key:I.Query_handler.name ~data:instance) - table) - [@res.braces ]) + {let table = Hashtbl.create (module String) in + List.iter handlers + ~f:(fun + [arity:1](((module I) : (module Query_handler_instance)) as + instance) + -> Hashtbl.set table ~key:I.Query_handler.name ~data:instance) + table} ;;(module Three) ;;((module Three) : (module X_int)) ;;(module Teenager).(0) @@ -50,29 +48,27 @@ let build_dispatch_table [arity:1]handlers = ;;((if ((module Streets).(0)) -> isExpensive then Console.log {js|big money|js} else Console.log {js|affordable|js})[@res.ternary ]) -let () = ((((module Teenager) -> age) -> Console.log)[@res.braces ]) -let () = (((module Teenager).(0))[@res.braces ]) +let () = {((module Teenager) -> age) -> Console.log} +let () = {(module Teenager).(0)} let () = - ((if ((module Teenager) -> age) -> isAdult - then Console.log {js|has responsibilities|js} - else Console.log {js|can play in the playground|js}) - [@res.braces ][@res.ternary ]) + {((if ((module Teenager) -> age) -> isAdult + then Console.log {js|has responsibilities|js} + else Console.log {js|can play in the playground|js}) + [@res.ternary ])} let () = - ((if ((module Streets).(0)) -> isExpensive - then Console.log {js|big money|js} - else Console.log {js|affordable|js}) - [@res.braces ][@res.ternary ]) + {((if ((module Streets).(0)) -> isExpensive + then Console.log {js|big money|js} + else Console.log {js|affordable|js}) + [@res.ternary ])} let () = - ((let a = 1 in - let b = 2 in - (module Teenager).(0); ((module Teenager) -> age) -> Console.log) - [@res.braces ]) + {let a = 1 in + let b = 2 in + (module Teenager).(0); ((module Teenager) -> age) -> Console.log} let () = - ((let a = 1 in - let b = 2 in - ((module Teenager) -> age) -> Console.log; - ((if (((module Teenager).(0)) -> age) -> isAdult - then Console.log {js|has responsibilities|js} - else Console.log {js|can play in the playground|js}) - [@res.ternary ])) - [@res.braces ]) \ No newline at end of file + {let a = 1 in + let b = 2 in + ((module Teenager) -> age) -> Console.log; + ((if (((module Teenager).(0)) -> age) -> isAdult + then Console.log {js|has responsibilities|js} + else Console.log {js|can play in the playground|js}) + [@res.ternary ])} \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/jsx.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/jsx.res.txt index a7fbed342b3..68af38d9155 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/jsx.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/jsx.res.txt @@ -5,13 +5,11 @@ let _ =
let _ =
let _ =
let _ = -
- Console.log {js|click|js}) - [@res.braces ])>
+
+ Console.log {js|click|js}})>
let _ = -
- Console.log {js|click|js}) - [@res.braces ])>
+
+ Console.log {js|click|js}})>
let _ = let _ = let _ = @@ -40,7 +38,7 @@ let _ =
sub1
sub2
let _ =
sub1
sub2
-let _ =
ident [|1;2;3|] ((call a b)[@res.braces ]) ((x.y).z)
+let _ =
ident [|1;2;3|] ({call a b}) ((x.y).z)
let _ = /> let _ =
<>{js|foobar|js}
let _ = @@ -71,20 +69,18 @@ let _ = > let _ = < let _ = > let y = - updater (fun [arity:2]latestComponentBag _ -> - ((let currentActualPath = Routes.hashOfUri newUrl in - let pathFromState = Routes.stateToPath latestComponentBag.state in - ((if currentActualPath == pathFromState - then None - else - dispatchEventless (State.UriNavigated currentActualPath) - latestComponentBag ()) - [@res.ternary ])) - [@res.braces ])) ()) - [@res.braces ]) /> + {let currentActualPath = Routes.hashOfUri newUrl in + let pathFromState = Routes.stateToPath latestComponentBag.state in + ((if currentActualPath == pathFromState + then None + else + dispatchEventless (State.UriNavigated currentActualPath) + latestComponentBag ()) + [@res.ternary ])}) ()}) /> let z =
let icon = - {js|sound-off|js} | v when v < 0.11 -> {js|sound-min|js} | v when v < 0.51 -> {js|sound-med|js} - | _ -> {js|sound-max|js}) - [@res.braces ]) /> + | _ -> {js|sound-max|js}}) /> let _ = ;
|] let y = [| + let _ = - + let _ = - -let _ = -let _ = (Belt.Option.getWithDefault {js||js}))[@res.braces ]) /> -let _ =
((ReasonReact.string {js|BugTest|js})[@res.braces ])
+ let _ = -
((let left = limit -> Int.toString in - (`${left} characters left`) -> React.string) - [@res.braces ])
+ +let _ = (Belt.Option.getWithDefault {js||js})}) /> let _ = - ((let uri = +
({ReasonReact.string {js|BugTest|js}})
+let _ = +
({let left = limit -> Int.toString in + (`${left} characters left`) -> React.string})
+let _ = + ({let uri = {js|/images/header-background.png|js} in ) - [@res.braces ]) -;;
(((((possibleGradeValues -> + styles["backgroundImage"]) uri />}) +;;
({(((possibleGradeValues -> (List.filter (fun [arity:1]g -> g <= state.maxGrade))) -> (List.map (fun [arity:1]possibleGradeValue -> - ))) + ))) -> Array.of_list) - -> ReasonReact.array)[@res.braces ])
-;;
((Console.log (a <= 10))[@res.braces ])
-;;
((Console.log (a <= 10))[@res.braces ])
-;;
Console.log (a <= 10)) - [@res.braces ])>
((Console.log (a <= 10)) - [@res.braces ])
+ -> ReasonReact.array})
+;;
({Console.log (a <= 10)})
+;;
({Console.log (a <= 10)})
+;;
Console.log (a <= 10)})>
({ + Console.log (a <= 10)})
let _ = let _ = let _ = diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/parenthesized.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/parenthesized.res.txt index 58624cdfbb2..5f24f963f4e 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/parenthesized.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/parenthesized.res.txt @@ -11,7 +11,7 @@ let aList = [1; 2] let anArray = [|1;2|] let aTuple = (1, 2) let aRecord = { name = {js|steve|js}; age = 30 } -let blockExpression = ((let a = 1 in let b = 2 in a + b)[@res.braces ]) +let blockExpression = {let a = 1 in let b = 2 in a + b} let assertSmthing = assert true let jsx =
foo
let ifExpr = if true then Console.log true else Console.log false diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/sideEffects.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/sideEffects.res.txt index aaa9b7ee2a7..13cff469e4f 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/sideEffects.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/sideEffects.res.txt @@ -1,11 +1,10 @@ ;;foo () ;;bar () -let () = ((foo (); bar ())[@res.braces ]) +let () = {foo (); bar ()} let () = - ((let x = 1 in - sideEffect (); - (let y = 2 in sideEffect2 (); (let z = 3 in sideEffect3 ()))) - [@res.braces ]) + {let x = 1 in + sideEffect (); + (let y = 2 in sideEffect2 (); (let z = 3 in sideEffect3 ()))} ;;while true do sideEffect1 (); (let x = 1 in sideEffect2 (); (let y = 2 in sideEffect3 ())) done diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/try.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/try.res.txt index 09c94cd879f..02a9ff710bb 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/try.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/try.res.txt @@ -1,6 +1,5 @@ -;;try ((let x = 1 in let y = 2 in dangerousCall (x + y))[@res.braces ]) +;;try {let x = 1 in let y = 2 in dangerousCall (x + y)} with | Foo -> Console.log {js|catched Foo|js} | Exit -> Console.log {js|catched exit|js} ;;try myDangerousFn () with | Foo -> Console.log {js|catched Foo|js}[@@attr ] -let x = ((let y = 1 in try ((apply y)[@res.braces ]) with | _ -> 2) - [@res.braces ]) \ No newline at end of file +let x = {let y = 1 in try {apply y} with | _ -> 2} \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/tupleVsDivision.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/tupleVsDivision.res.txt index e82b11e6729..6036d5771aa 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/tupleVsDivision.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/tupleVsDivision.res.txt @@ -1,5 +1,4 @@ ;;(foo ()) / 1 ;;foo () ;;(1, 2) -> printTuple -let f [arity:1]() = (((foo ()) / 1; foo (); (1, 2) -> printTuple) - [@res.braces ]) \ No newline at end of file +let f [arity:1]() = {(foo ()) / 1; foo (); (1, 2) -> printTuple} \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/unaryOrBinary.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/unaryOrBinary.res.txt index 517a93db880..6a572b24fc6 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/unaryOrBinary.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/unaryOrBinary.res.txt @@ -3,7 +3,5 @@ let width = (((w -. innerLeft) -. imageWidth) -. imageRightGap) -. rowInnerRight let width = (((w - innerLeft) - imageWidth) - imageRightGap) - rowInnerRight let width = - ((w; -. innerLeft; -. imageWidth; -. imageRightGap; -. rowInnerRight) - [@res.braces ]) -let width = ((w; - innerLeft; - imageWidth; - imageRightGap; - rowInnerRight) - [@res.braces ]) \ No newline at end of file + {w; -. innerLeft; -. imageWidth; -. imageRightGap; -. rowInnerRight} +let width = {w; - innerLeft; - imageWidth; - imageRightGap; - rowInnerRight} \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/expressions/expected/underscoreApply.res.txt b/tests/syntax_tests/data/parsing/grammar/expressions/expected/underscoreApply.res.txt index 28c6b0d2b10..46845f87c30 100644 --- a/tests/syntax_tests/data/parsing/grammar/expressions/expected/underscoreApply.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/expressions/expected/underscoreApply.res.txt @@ -20,5 +20,4 @@ let l2 = (List.map (fun [arity:1]__x -> optParam ?v:__x ()))) -> List.length ;;fun [arity:1]__x -> - underscoreWithComments (fun [arity:1]x -> ((something ())[@res.braces ])) - __x \ No newline at end of file + underscoreWithComments (fun [arity:1]x -> {something ()}) __x \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/modexpr/expected/firstClassModules.res.txt b/tests/syntax_tests/data/parsing/grammar/modexpr/expected/firstClassModules.res.txt index 9cd90797b17..2a58911805e 100644 --- a/tests/syntax_tests/data/parsing/grammar/modexpr/expected/firstClassModules.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/modexpr/expected/firstClassModules.res.txt @@ -1,12 +1,9 @@ module Device = (val - (((let deviceName = parseCmdline () in - try Hashtbl.find devices deviceName with | Not_found -> exit 2) - [@res.braces ]) : (module Device))) + ({let deviceName = parseCmdline () in + try Hashtbl.find devices deviceName with | Not_found -> exit 2} : + (module Device))) let draw_using_device [arity:2]device_name picture = - ((let module Device = (val - (Hashtbl.find devices device_name : (module DEVICE))) in - Device.draw picture) - [@res.braces ]) + {let module Device = (val (Hashtbl.find devices device_name : + (module DEVICE))) in Device.draw picture} module New_three = (val (three : (module X_int))) -let to_int [arity:1]m = ((let module M = (val (m : (module X_int))) in M.x) - [@res.braces ]) \ No newline at end of file +let to_int [arity:1]m = {let module M = (val (m : (module X_int))) in M.x} \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/pattern/expected/dict.res.txt b/tests/syntax_tests/data/parsing/grammar/pattern/expected/dict.res.txt index bd1a60053ce..f75a11eed20 100644 --- a/tests/syntax_tests/data/parsing/grammar/pattern/expected/dict.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/pattern/expected/dict.res.txt @@ -15,15 +15,14 @@ type nonrec user = { name: string ; age?: float } let decodeUser [arity:1](json : json) = - (((match json with - | Object (({ name = String name; age = ageJson;_})[@res.dictPattern ]) - -> - Some - { - name; - age = - ?((match ageJson with | Number age -> Some age | _ -> None)) - } - | _ -> (Console.log {js|Not an object.|js}; None)) - [@res.braces ]) : user option) + ({match json with + | Object (({ name = String name; age = ageJson;_})[@res.dictPattern ]) -> + Some + { + name; + age = + ?((match ageJson with | Number age -> Some age | _ -> None)) + } + | _ -> (Console.log {js|Not an object.|js}; None)} : + user option) ;;Console.log (decodeUser (jsonParse (`{"name": "John", "age": 30}`))) \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/pattern/expected/variantSpreads.res.txt b/tests/syntax_tests/data/parsing/grammar/pattern/expected/variantSpreads.res.txt index aa8c0913b64..945eafbc2b0 100644 --- a/tests/syntax_tests/data/parsing/grammar/pattern/expected/variantSpreads.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/pattern/expected/variantSpreads.res.txt @@ -13,16 +13,14 @@ type nonrec d = | ... of b | ... of c let doWithA [arity:1](a : a) = - ((match a with - | One -> Console.log {js|aaa|js} - | Two -> Console.log {js|twwwoooo|js} - | Three -> Console.log {js|threeeee|js}) - [@res.braces ]) + {match a with + | One -> Console.log {js|aaa|js} + | Two -> Console.log {js|twwwoooo|js} + | Three -> Console.log {js|threeeee|js}} let doWithB [arity:1](b : b) = - ((match b with - | One -> Console.log {js|aaa|js} - | _ -> Console.log {js|twwwoooo|js}) - [@res.braces ]) + {match b with + | One -> Console.log {js|aaa|js} + | _ -> Console.log {js|twwwoooo|js}} let lookup [arity:1](b : b) = match b with | ((#a)[@res.patVariantSpread ]) as a -> doWithA a diff --git a/tests/syntax_tests/data/parsing/grammar/structure/expected/letBinding.res.txt b/tests/syntax_tests/data/parsing/grammar/structure/expected/letBinding.res.txt index 16743dcf49e..c4cc10ba3eb 100644 --- a/tests/syntax_tests/data/parsing/grammar/structure/expected/letBinding.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/structure/expected/letBinding.res.txt @@ -4,7 +4,6 @@ let a = 1[@@onFirstBinding ] and b = 2[@@onSecondBinding ] let f : type t. t foo = fun [arity:1]sideEffect -> - ((let module M = struct exception E of t end in - sideEffect (); (fun [arity:1]x -> M.E x)) - [@res.braces ]) + {let module M = struct exception E of t end in + sideEffect (); (fun [arity:1]x -> M.E x)} let f : type t x u. (t * x * y) list = fun [arity:1]l -> f l \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/typexpr/expected/objectTypeSpreading.res.txt b/tests/syntax_tests/data/parsing/grammar/typexpr/expected/objectTypeSpreading.res.txt index aa79fde7a0a..b97fcf206fd 100644 --- a/tests/syntax_tests/data/parsing/grammar/typexpr/expected/objectTypeSpreading.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/typexpr/expected/objectTypeSpreading.res.txt @@ -9,8 +9,7 @@ type nonrec t = < a ;u: int > -> < a ;v: int > -> unit (a:2) type nonrec user = < name: string > let (steve : < user ;age: int > ) = {"name": {js|Steve|js}, "age": 30} let steve = ({"name": {js|Steve|js}, "age": 30} : < user ;age: int > ) -let steve = ((({"name": {js|Steve|js}, "age": 30} : < user ;age: int > )) - [@res.braces ]) +let steve = {({"name": {js|Steve|js}, "age": 30} : < user ;age: int > )} let printFullUser [arity:1](steve : < user ;age: int > ) = Console.log steve let printFullUser [arity:1]~user:(user : < user ;age: int > ) = @@ -20,8 +19,8 @@ let printFullUser [arity:1]~user:(user : < user ;age: int > ) = let printFullUser [arity:1]?(user= (steve : < user ;age: int > )) = Console.log steve external steve : < user ;age: int > = "steve"[@@val ] -let makeCeoOf30yearsOld [arity:1]name = - ({"name": name, "age": 30} : < user ;age: int > ) +let makeCeoOf30yearsOld [arity:1]name = ({"name": name, "age": 30} : + < user ;age: int > ) type nonrec optionalUser = < user ;age: int > option type nonrec optionalTupleUser = (< user ;age: int > * < user ;age: int > ) option diff --git a/tests/syntax_tests/data/parsing/infiniteLoops/expected/equalAfterBinaryExpr.res.txt b/tests/syntax_tests/data/parsing/infiniteLoops/expected/equalAfterBinaryExpr.res.txt index a9275e7059b..9802322f126 100644 --- a/tests/syntax_tests/data/parsing/infiniteLoops/expected/equalAfterBinaryExpr.res.txt +++ b/tests/syntax_tests/data/parsing/infiniteLoops/expected/equalAfterBinaryExpr.res.txt @@ -11,147 +11,142 @@ Did you mean `==` here? let rec _addLoop [arity:2]rbt currentNode = - ((if (Some currentNode) === (rbt -> root) - then currentNode.color <- Black - else - if (currentNode.parent -> castNotOption).color === Black - then () - else - if - (((let uncle = uncleOf currentNode in - ((!==) uncle None) && ((uncle -> castNotOption).color === Red))) - [@res.braces ]) - then - ((currentNode.parent -> castNotOption).color <- Black; - ((uncleOf currentNode) -> castNotOption).color <- Black; - ((grandParentOf currentNode) -> castNotOption).color <- Red; - _addLoop rbt ((grandParentOf currentNode) -> castNotOption)) - else - (let currentNode = - if - (not (isLeft currentNode)) && - (isLeft (currentNode.parent -> castNotOption)) - then - (rotateLeft rbt (currentNode.parent -> castNotOption); - currentNode.left -> castNotOption) - else - if - (isLeft currentNode) && - (not (isLeft (currentNode.parent -> castNotOption))) - then - (rotateRight rbt (currentNode.parent -> castNotOption); - currentNode.right -> castNotOption) - else currentNode in - (currentNode.parent -> castNotOption).color <- Black; - ((grandParentOf currentNode) -> castNotOption).color <- Red; - if isLeft currentNode - then - rotateRight rbt ((grandParentOf currentNode) -> castNotOption) - else rotateLeft rbt ((grandParentOf currentNode) -> castNotOption))) - [@res.braces ]) -let removeNode [arity:2]rbt node = - ((if nodeToRemove.color === Black - then - (if successor.color === Red + {if (Some currentNode) === (rbt -> root) + then currentNode.color <- Black + else + if (currentNode.parent -> castNotOption).color === Black + then () + else + if + {(let uncle = uncleOf currentNode in + ((!==) uncle None) && ((uncle -> castNotOption).color === Red))} then - (successor.color <- Black; - if successor.parent === None then rbt -> (rootSet (Some successor))) + ((currentNode.parent -> castNotOption).color <- Black; + ((uncleOf currentNode) -> castNotOption).color <- Black; + ((grandParentOf currentNode) -> castNotOption).color <- Red; + _addLoop rbt ((grandParentOf currentNode) -> castNotOption)) else - (let break_ = ref false in - let successorRef = ref successor in - while not break_.contents do - let successor = successorRef.contents in - match successor.parent with - | None -> - (rbt -> (rootSet (Some successor)); break_.contents <- true) - | Some successorParent -> - let sibling = siblingOf successor in - (if - ((!==) sibling None) && - ((sibling -> castNotOption).color === Red) + (let currentNode = + if + (not (isLeft currentNode)) && + (isLeft (currentNode.parent -> castNotOption)) + then + (rotateLeft rbt (currentNode.parent -> castNotOption); + currentNode.left -> castNotOption) + else + if + (isLeft currentNode) && + (not (isLeft (currentNode.parent -> castNotOption))) + then + (rotateRight rbt (currentNode.parent -> castNotOption); + currentNode.right -> castNotOption) + else currentNode in + (currentNode.parent -> castNotOption).color <- Black; + ((grandParentOf currentNode) -> castNotOption).color <- Red; + if isLeft currentNode + then rotateRight rbt ((grandParentOf currentNode) -> castNotOption) + else rotateLeft rbt ((grandParentOf currentNode) -> castNotOption))} +let removeNode [arity:2]rbt node = + {if nodeToRemove.color === Black + then + (if successor.color === Red + then + (successor.color <- Black; + if successor.parent === None then rbt -> (rootSet (Some successor))) + else + (let break_ = ref false in + let successorRef = ref successor in + while not break_.contents do + let successor = successorRef.contents in + match successor.parent with + | None -> + (rbt -> (rootSet (Some successor)); break_.contents <- true) + | Some successorParent -> + let sibling = siblingOf successor in + (if + ((!==) sibling None) && + ((sibling -> castNotOption).color === Red) + then + (successorParent.color <- Red; + (sibling -> castNotOption).color <- Black; + if isLeft successor + then rotateLeft rbt successorParent + else rotateRight rbt successorParent); + (let sibling = siblingOf successor in + let siblingNN = sibling -> castNotOption in + if + (successorParent.color === Black) && + ((sibling === None) || + (((siblingNN.color === Black) && + ((siblingNN.left === None) || + ((siblingNN.left -> castNotOption).color === + Black))) + && + ((siblingNN.right === None) || + ((siblingNN.right -> castNotOption).color === + Black)))) then - (successorParent.color <- Red; - (sibling -> castNotOption).color <- Black; - if isLeft successor - then rotateLeft rbt successorParent - else rotateRight rbt successorParent); - (let sibling = siblingOf successor in - let siblingNN = sibling -> castNotOption in - if - (successorParent.color === Black) && - ((sibling === None) || - (((siblingNN.color === Black) && - ((siblingNN.left === None) || - ((siblingNN.left -> castNotOption).color === - Black))) + (if (!==) sibling None then siblingNN.color <- Red; + successorRef.contents <- successorParent) + else + if + (successorParent.color === Red) && + ((sibling === None) || + (((siblingNN.color === Black) && + ((siblingNN.left === None) || + ((siblingNN.left -> castNotOption).color === + Black))) + && + ((siblingNN.right === None) || + ((siblingNN.right -> castNotOption).color === + Black)))) + then + (if (!==) sibling None then siblingNN.color <- Red; + successorParent.color <- Black; + break_.contents <- true) + else + if + ((!==) sibling None) && + ((sibling -> castNotOption).color === Black) + then + (let sibling = sibling -> castNotOption in + if + (((isLeft successor) && + ((sibling.right === None) || + ((sibling.right -> castNotOption).color === + Black))) + && ((!==) sibling.left None)) && - ((siblingNN.right === None) || - ((siblingNN.right -> castNotOption).color === - Black)))) - then - (if (!==) sibling None then siblingNN.color <- Red; - successorRef.contents <- successorParent) - else - if - (successorParent.color === Red) && - ((sibling === None) || - (((siblingNN.color === Black) && - ((siblingNN.left === None) || - ((siblingNN.left -> castNotOption).color - === Black))) + ((sibling.left -> castNotOption).color === Red) + then + (sibling.color <- Red; + (sibling.left -> castNotOption).color <- Black; + rotateRight rbt sibling) + else + if + (((not (isLeft successor)) && + ((sibling.left === None) || + ((sibling.left -> castNotOption).color === + Black))) + && ((!==) sibling.right None)) && - ((siblingNN.right === None) || - ((siblingNN.right -> castNotOption).color - === Black)))) - then - (if (!==) sibling None then siblingNN.color <- Red; - successorParent.color <- Black; - break_.contents <- true) - else - if - ((!==) sibling None) && - ((sibling -> castNotOption).color === Black) - then - (let sibling = sibling -> castNotOption in - if - (((isLeft successor) && - ((sibling.right === None) || - ((sibling.right -> castNotOption).color === - Black))) - && ((!==) sibling.left None)) - && - ((sibling.left -> castNotOption).color === Red) - then - (sibling.color <- Red; - (sibling.left -> castNotOption).color <- Black; - rotateRight rbt sibling) - else - if - (((not (isLeft successor)) && - ((sibling.left === None) || - ((sibling.left -> castNotOption).color - === Black))) - && ((!==) sibling.right None)) - && - ((sibling.right -> castNotOption).color === - Red) - then - (sibling.color <- Red; - (sibling.right -> castNotOption).color <- Black; - rotateLeft rbt sibling); - break_.contents <- true) - else - (let sibling = siblingOf successor in - let sibling = sibling -> castNotOption in - sibling.color <- (successorParent.color); - if isLeft successor - then - ((sibling.right -> castNotOption).color <- Black; - rotateRight rbt successorParent) - else - ((sibling.left -> castNotOption).color <- Black; - rotateLeft rbt successorParent)))) - done)); - if isLeaf successor - then (if (rbt -> root) === (Some successor) then (rbt -> root) = None)) - [@res.braces ]) \ No newline at end of file + ((sibling.right -> castNotOption).color === Red) + then + (sibling.color <- Red; + (sibling.right -> castNotOption).color <- Black; + rotateLeft rbt sibling); + break_.contents <- true) + else + (let sibling = siblingOf successor in + let sibling = sibling -> castNotOption in + sibling.color <- (successorParent.color); + if isLeft successor + then + ((sibling.right -> castNotOption).color <- Black; + rotateRight rbt successorParent) + else + ((sibling.left -> castNotOption).color <- Black; + rotateLeft rbt successorParent)))) + done)); + if isLeaf successor + then (if (rbt -> root) === (Some successor) then (rbt -> root) = None)} \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/infiniteLoops/expected/nonRecTypes.res.txt b/tests/syntax_tests/data/parsing/infiniteLoops/expected/nonRecTypes.res.txt index f2578d1058a..1d9c56f7b7e 100644 --- a/tests/syntax_tests/data/parsing/infiniteLoops/expected/nonRecTypes.res.txt +++ b/tests/syntax_tests/data/parsing/infiniteLoops/expected/nonRecTypes.res.txt @@ -123,154 +123,153 @@ include let rec minNode [arity:1]node = [%rescript.exprhole ] let findMin [arity:1]rbt = [%rescript.exprhole ] let removeNode [arity:2]rbt node = - ((let nodeToRemove = - match ((leftGet node), (rightGet node)) with - | (Some _, Some _) -> - let successor = castNotOption (minNode (rightGet node)) in - (valueSet node (valueGet successor); - heightSet node (heightGet successor); - successor) - | _ -> node in - let successor = - match leftGet nodeToRemove with - | None -> rightGet nodeToRemove - | left -> left in - let (successor, isLeaf) = - match successor with - | None -> - let leaf = - createNode ~value:([%raw {js|0|js}]) ~color:Black ~height:0. in - let isLeaf [arity:1]x = x === leaf in (leaf, isLeaf) - | Some successor -> (successor, ((fun [arity:1]_ -> false))) in - let nodeParent = parentGet nodeToRemove in - parentSet successor nodeParent; - (match nodeParent with - | None -> () - | Some parent -> - leftOrRightSet parent ~node:nodeToRemove (Some successor)); - updateSumRecursive rbt successor; - if (colorGet nodeToRemove) === Black - then - (if (colorGet successor) === Red - then - (colorSet successor Black; - if (parentGet successor) === None - then rootSet rbt (Some successor)) - else - (let break_ = ref false in - let successorRef = ref successor in - while not break_.contents do - let successor = successorRef.contents in - match parentGet successor with - | None -> - (rootSet rbt (Some successor); break_.contents <- true) - | Some successorParent -> - let sibling = siblingOf successor in - (if - ((!==) sibling None) && - ((colorGet (castNotOption sibling)) === Red) + {let nodeToRemove = + match ((leftGet node), (rightGet node)) with + | (Some _, Some _) -> + let successor = castNotOption (minNode (rightGet node)) in + (valueSet node (valueGet successor); + heightSet node (heightGet successor); + successor) + | _ -> node in + let successor = + match leftGet nodeToRemove with + | None -> rightGet nodeToRemove + | left -> left in + let (successor, isLeaf) = + match successor with + | None -> + let leaf = + createNode ~value:([%raw {js|0|js}]) ~color:Black ~height:0. in + let isLeaf [arity:1]x = x === leaf in (leaf, isLeaf) + | Some successor -> (successor, ((fun [arity:1]_ -> false))) in + let nodeParent = parentGet nodeToRemove in + parentSet successor nodeParent; + (match nodeParent with + | None -> () + | Some parent -> + leftOrRightSet parent ~node:nodeToRemove (Some successor)); + updateSumRecursive rbt successor; + if (colorGet nodeToRemove) === Black + then + (if (colorGet successor) === Red + then + (colorSet successor Black; + if (parentGet successor) === None + then rootSet rbt (Some successor)) + else + (let break_ = ref false in + let successorRef = ref successor in + while not break_.contents do + let successor = successorRef.contents in + match parentGet successor with + | None -> + (rootSet rbt (Some successor); break_.contents <- true) + | Some successorParent -> + let sibling = siblingOf successor in + (if + ((!==) sibling None) && + ((colorGet (castNotOption sibling)) === Red) + then + (colorSet successorParent Red; + colorSet (castNotOption sibling) Black; + if isLeft successor + then rotateLeft rbt successorParent + else rotateRight rbt successorParent); + (let sibling = siblingOf successor in + let siblingNN = castNotOption sibling in + if + ((colorGet successorParent) === Black) && + ((sibling === None) || + ((((colorGet siblingNN) === Black) && + (((leftGet siblingNN) === None) || + ((colorGet + (castNotOption (leftGet siblingNN))) + === Black))) + && + (((rightGet siblingNN) === None) || + ((colorGet + (castNotOption (rightGet siblingNN))) + === Black)))) then - (colorSet successorParent Red; - colorSet (castNotOption sibling) Black; - if isLeft successor - then rotateLeft rbt successorParent - else rotateRight rbt successorParent); - (let sibling = siblingOf successor in - let siblingNN = castNotOption sibling in - if - ((colorGet successorParent) === Black) && - ((sibling === None) || - ((((colorGet siblingNN) === Black) && - (((leftGet siblingNN) === None) || + (if (!==) sibling None then colorSet siblingNN Red; + successorRef.contents <- successorParent) + else + if + ((colorGet successorParent) === Red) && + ((sibling === None) || + ((((colorGet siblingNN) === Black) && + (((leftGet siblingNN) === None) || + ((colorGet + (castNotOption (leftGet siblingNN))) + === Black))) + && + (((rightGet siblingNN) === None) || ((colorGet - (castNotOption (leftGet siblingNN))) - === Black))) + (castNotOption (rightGet siblingNN))) + === Black)))) + then + (if (!==) sibling None then colorSet siblingNN Red; + colorSet successorParent Black; + break_.contents <- true) + else + if + ((!==) sibling None) && + ((colorGet (castNotOption sibling)) === Black) + then + (let sibling = castNotOption sibling in + if + (((isLeft successor) && + (((rightGet sibling) === None) || + ((colorGet + (castNotOption (rightGet sibling))) + === Black))) + && ((!==) (leftGet sibling) None)) && - (((rightGet siblingNN) === None) || - ((colorGet - (castNotOption (rightGet siblingNN))) - === Black)))) - then - (if (!==) sibling None then colorSet siblingNN Red; - successorRef.contents <- successorParent) - else - if - ((colorGet successorParent) === Red) && - ((sibling === None) || - ((((colorGet siblingNN) === Black) && - (((leftGet siblingNN) === None) || - ((colorGet - (castNotOption (leftGet siblingNN))) - === Black))) + ((colorGet (castNotOption (leftGet sibling))) + === Red) + then + (colorSet sibling Red; + colorSet (castNotOption (leftGet sibling)) + Black; + rotateRight rbt sibling) + else + if + (((not (isLeft successor)) && + (((leftGet sibling) === None) || + ((colorGet + (castNotOption (leftGet sibling))) + === Black))) + && ((!==) (rightGet sibling) None)) && - (((rightGet siblingNN) === None) || - ((colorGet - (castNotOption (rightGet siblingNN))) - === Black)))) - then - (if (!==) sibling None then colorSet siblingNN Red; - colorSet successorParent Black; - break_.contents <- true) - else - if - ((!==) sibling None) && - ((colorGet (castNotOption sibling)) === Black) - then - (let sibling = castNotOption sibling in - if - (((isLeft successor) && - (((rightGet sibling) === None) || - ((colorGet - (castNotOption (rightGet sibling))) - === Black))) - && ((!==) (leftGet sibling) None)) - && - ((colorGet (castNotOption (leftGet sibling))) - === Red) - then - (colorSet sibling Red; - colorSet (castNotOption (leftGet sibling)) - Black; - rotateRight rbt sibling) - else - if - (((not (isLeft successor)) && - (((leftGet sibling) === None) || - ((colorGet - (castNotOption (leftGet sibling))) - === Black))) - && ((!==) (rightGet sibling) None)) - && - ((colorGet - (castNotOption (rightGet sibling))) - === Red) - then - (colorSet sibling Red; - colorSet (castNotOption (rightGet sibling)) - Black; - rotateLeft rbt sibling); - break_.contents <- true) - else - (let sibling = siblingOf successor in - let sibling = castNotOption sibling in - colorSet sibling (colorGet successorParent); - if isLeft successor - then - (colorSet (castNotOption (rightGet sibling)) - Black; - rotateRight rbt successorParent) - else - (colorSet (castNotOption (leftGet sibling)) - Black; - rotateLeft rbt successorParent)))) - done)); - if isLeaf successor - then - (if (rootGet rbt) === (Some successor) then rootSet rbt None; - (match parentGet successor with - | None -> () - | Some parent -> leftOrRightSet parent ~node:successor None))) - [@res.braces ]) + ((colorGet + (castNotOption (rightGet sibling))) + === Red) + then + (colorSet sibling Red; + colorSet (castNotOption (rightGet sibling)) + Black; + rotateLeft rbt sibling); + break_.contents <- true) + else + (let sibling = siblingOf successor in + let sibling = castNotOption sibling in + colorSet sibling (colorGet successorParent); + if isLeft successor + then + (colorSet (castNotOption (rightGet sibling)) + Black; + rotateRight rbt successorParent) + else + (colorSet (castNotOption (leftGet sibling)) + Black; + rotateLeft rbt successorParent)))) + done)); + if isLeaf successor + then + (if (rootGet rbt) === (Some successor) then rootSet rbt None; + (match parentGet successor with + | None -> () + | Some parent -> leftOrRightSet parent ~node:successor None))} let remove [arity:2]rbt value = match _findNode rbt (rootGet rbt) value with | Some node -> @@ -279,21 +278,20 @@ include Some (heightGet node)) | None -> None let findThroughCallback [arity:2]rbt cb = - ((let rec findThroughCallback [arity:3]rbt node cb = - match node with - | None -> None - | Some node -> - let cmp = cb (valueGet node) in - if cmp === 0 - then Some node - else - if cmp < 0 - then findThroughCallback rbt (leftGet node) cb - else findThroughCallback rbt (rightGet node) cb in - match findThroughCallback rbt (rootGet rbt) cb with - | None -> None - | Some node -> Some (valueGet node)) - [@res.braces ]) + {let rec findThroughCallback [arity:3]rbt node cb = + match node with + | None -> None + | Some node -> + let cmp = cb (valueGet node) in + if cmp === 0 + then Some node + else + if cmp < 0 + then findThroughCallback rbt (leftGet node) cb + else findThroughCallback rbt (rightGet node) cb in + match findThroughCallback rbt (rootGet rbt) cb with + | None -> None + | Some node -> Some (valueGet node)} let make [arity:1]~compare = t ~size:0 ~root:None ~compare let rec heightOfInterval [arity:4]rbt node lhs rhs = match node with @@ -356,34 +354,32 @@ include | None -> firstRightParent node | Some right -> Some (leftmost right) let rec sumLeftSpine [arity:2]node ~fromRightChild = - ((let leftSpine = - match leftGet node with - | None -> heightGet node - | Some left -> - if fromRightChild - then (heightGet node) +. (sumGet left) - else 0.0 in - match parentGet node with - | None -> leftSpine - | Some parent -> - leftSpine +. - (sumLeftSpine parent - ~fromRightChild:((rightGet parent) === (Some node)))) - [@res.braces ]) + {let leftSpine = + match leftGet node with + | None -> heightGet node + | Some left -> + if fromRightChild + then (heightGet node) +. (sumGet left) + else 0.0 in + match parentGet node with + | None -> leftSpine + | Some parent -> + leftSpine +. + (sumLeftSpine parent + ~fromRightChild:((rightGet parent) === (Some node)))} let getY [arity:1]node = (sumLeftSpine node ~fromRightChild:true) -. (heightGet node) let linearSearch [arity:2]rbt callback = - ((let rec find [arity:2]node callback = - if callback (valueGet node) - then Some (valueGet node) - else - (match nextNode node with - | None -> None - | Some node -> find node callback) in - match minNode (rootGet rbt) with - | None -> None - | Some node -> find node callback) - [@res.braces ]) + {let rec find [arity:2]node callback = + if callback (valueGet node) + then Some (valueGet node) + else + (match nextNode node with + | None -> None + | Some node -> find node callback) in + match minNode (rootGet rbt) with + | None -> None + | Some node -> find node callback} let rec iterate [arity:4]~inclusive firstNode lastNode ~callback = match firstNode with | None -> () diff --git a/tests/syntax_tests/data/parsing/recovery/expression/expected/list.res.txt b/tests/syntax_tests/data/parsing/recovery/expression/expected/list.res.txt index efdedd7aa6c..096e985280b 100644 --- a/tests/syntax_tests/data/parsing/recovery/expression/expected/list.res.txt +++ b/tests/syntax_tests/data/parsing/recovery/expression/expected/list.res.txt @@ -37,16 +37,13 @@ Possible solutions: let flags = ((if reasonFormat then - ((let parts = Utils.split_on_char ' ' flags in - let rec loop [arity:1]items = - ((match items with - | [|{js|-pp|js};_ppFlag;rest|] -> loop rest - | [|x;rest|] -> - ((Primitive_array.spread)[@res.spread ]) - [|[|x|];(loop rest)|] - | [||] -> [||]) - [@res.braces ]) in - (loop parts) -> (String.concat {js| |js})) - [@res.braces ]) + {let parts = Utils.split_on_char ' ' flags in + let rec loop [arity:1]items = + {match items with + | [|{js|-pp|js};_ppFlag;rest|] -> loop rest + | [|x;rest|] -> + ((Primitive_array.spread)[@res.spread ]) [|[|x|];(loop rest)|] + | [||] -> [||]} in + (loop parts) -> (String.concat {js| |js})} else flags) [@res.ternary ]) \ No newline at end of file diff --git a/tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt b/tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt index 8deba04ab52..9ff739a4c78 100644 --- a/tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt @@ -6,9 +6,9 @@ module C0 = { a: 'a, } - let make = async ({a, _}: props<_>) => { + let make = async ({a, _}: props<_>): React.element => { let a = await f(a) - (ReactDOM.jsx("div", {children: ?ReactDOM.someElement({React.int(a)})}): React.element) + ReactDOM.jsx("div", {children: ?ReactDOM.someElement({React.int(a)})}) } let make = React.component({ let \"AsyncAwait$C0" = (props: props<_>) => Jsx.promise(make(props)) diff --git a/tests/syntax_tests/data/ppx/react/expected/forwardRef.res.txt b/tests/syntax_tests/data/ppx/react/expected/forwardRef.res.txt index e468d8328bd..f23f859040f 100644 --- a/tests/syntax_tests/data/ppx/react/expected/forwardRef.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/forwardRef.res.txt @@ -38,17 +38,16 @@ module V4A = { @res.jsxComponentProps type props = {} - let make = (_: props) => { + let make = (_: props): React.element => { let input = React.useRef(Nullable.null) - ( - ReactDOM.jsx( - "div", - { - children: ?ReactDOM.someElement( - React.jsx(FancyInput.make, {ref: input, children: {React.string("Click to focus")}}), - ), - }, - ): React.element + + ReactDOM.jsx( + "div", + { + children: ?ReactDOM.someElement( + React.jsx(FancyInput.make, {ref: input, children: {React.string("Click to focus")}}), + ), + }, ) } let make = React.component({ @@ -96,17 +95,16 @@ module V4AUncurried = { @res.jsxComponentProps type props = {} - let make = (_: props) => { + let make = (_: props): React.element => { let input = React.useRef(Nullable.null) - ( - ReactDOM.jsx( - "div", - { - children: ?ReactDOM.someElement( - React.jsx(FancyInput.make, {ref: input, children: {React.string("Click to focus")}}), - ), - }, - ): React.element + + ReactDOM.jsx( + "div", + { + children: ?ReactDOM.someElement( + React.jsx(FancyInput.make, {ref: input, children: {React.string("Click to focus")}}), + ), + }, ) } let make = React.component({ diff --git a/tests/syntax_tests/data/ppx/react/expected/interfaceWithRef.res.txt b/tests/syntax_tests/data/ppx/react/expected/interfaceWithRef.res.txt index d2f4f629c51..266f97d3281 100644 --- a/tests/syntax_tests/data/ppx/react/expected/interfaceWithRef.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/interfaceWithRef.res.txt @@ -6,9 +6,9 @@ type props<'x, 'ref> = { let make = ( {x, _}: props, ref: nullable, -) => { +): React.element => { let _ = ref->Nullable.toOption->Belt.Option.map(ReactDOM.Ref.domRef) - (React.string(x): React.element) + React.string(x) } let make = React.forwardRef({ let \"InterfaceWithRef" = (props: props<_>, ref) => make(props, ref) diff --git a/tests/syntax_tests/data/ppx/react/expected/sharedPropsWithProps.res.txt b/tests/syntax_tests/data/ppx/react/expected/sharedPropsWithProps.res.txt index 2a55a2a6ee1..1818ef31f27 100644 --- a/tests/syntax_tests/data/ppx/react/expected/sharedPropsWithProps.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/sharedPropsWithProps.res.txt @@ -40,9 +40,9 @@ module V4A4 = { module V4A5 = { type props<'a> = {a: 'a} - let make = async ({a}: props<_>) => { + let make = async ({a}: props<_>): React.element => { let a = await f(a) - (ReactDOM.jsx("div", {children: ?ReactDOM.someElement({React.int(a)})}): React.element) + ReactDOM.jsx("div", {children: ?ReactDOM.someElement({React.int(a)})}) } let make = React.component({ let \"SharedPropsWithProps$V4A5" = (props: props<_>): React.element => Jsx.promise(make(props)) diff --git a/tests/syntax_tests/data/ppx/react/expected/topLevel.res.txt b/tests/syntax_tests/data/ppx/react/expected/topLevel.res.txt index 4446110077d..bc712ece06e 100644 --- a/tests/syntax_tests/data/ppx/react/expected/topLevel.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/topLevel.res.txt @@ -7,9 +7,9 @@ module V4A = { b: 'b, } - let make = ({a, b, _}: props<_, _>) => { + let make = ({a, b, _}: props<_, _>): React.element => { Console.log("This function should be named 'TopLevel.react'") - (ReactDOM.jsx("div", {}): React.element) + ReactDOM.jsx("div", {}) } let make = React.component({ let \"TopLevel$V4A" = (props: props<_>) => make(props) diff --git a/tests/syntax_tests/data/printer/comments/expected/jsx.res.txt b/tests/syntax_tests/data/printer/comments/expected/jsx.res.txt index 664018ce3b0..236769adef4 100644 --- a/tests/syntax_tests/data/printer/comments/expected/jsx.res.txt +++ b/tests/syntax_tests/data/printer/comments/expected/jsx.res.txt @@ -61,8 +61,10 @@ module Cite = {
// Must not jump inside braces - {// But this one is inside - React.string("Hello, World!")} + { + // But this one is inside + React.string("Hello, World!") + }
diff --git a/tests/syntax_tests/data/printer/expr/expected/braced.res.txt b/tests/syntax_tests/data/printer/expr/expected/braced.res.txt index a3ca220decf..5deab6270c3 100644 --- a/tests/syntax_tests/data/printer/expr/expected/braced.res.txt +++ b/tests/syntax_tests/data/printer/expr/expected/braced.res.txt @@ -305,8 +305,7 @@ let x = { { // comment - a + - b + a + b } {a} + {b} diff --git a/tests/syntax_tests/data/printer/expr/expected/if.res.txt b/tests/syntax_tests/data/printer/expr/expected/if.res.txt index d7ed0b6a58b..c438c1f8e6d 100644 --- a/tests/syntax_tests/data/printer/expr/expected/if.res.txt +++ b/tests/syntax_tests/data/printer/expr/expected/if.res.txt @@ -8,6 +8,12 @@ let name = if true { "steve" } +let sameBraces = if flag { + value +} else { + fallback +} + let name = if true { user.name } else if false { diff --git a/tests/syntax_tests/data/printer/expr/if.res b/tests/syntax_tests/data/printer/expr/if.res index 05b594bdc7d..14214eec8bf 100644 --- a/tests/syntax_tests/data/printer/expr/if.res +++ b/tests/syntax_tests/data/printer/expr/if.res @@ -8,6 +8,8 @@ let name = if true { "steve" } +let sameBraces = if flag {{value}} else {{fallback}} + let name = if true { user.name } else if false { From bf824a281c1013e752e92d4f378b45f43b3c2752 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Sat, 26 Sep 2026 08:12:16 +0200 Subject: [PATCH 2/5] Document parsetree braces change Signed-off-by: Christoph Knittel --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 958cc846283..9501a96cd34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ #### :nail_care: Polish +- Represent explicit expression braces as `Pexp_braces` in parsetree v1 and format `else` branches consistently with `if` branches. https://github.com/rescript-lang/rescript/pull/8678 - Omit redundant braces around multi-statement switch case bodies when formatting. https://github.com/rescript-lang/rescript/pull/8677 - Avoid running `rescript-schema-ppx` and `sury-ppx` on source files without an `@schema` annotation. https://github.com/rescript-lang/rescript/pull/8662 From 6710020aa77fd92694acb25485dc002aa370c0d0 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Sat, 26 Sep 2026 08:27:15 +0200 Subject: [PATCH 3/5] Preserve braces when flattening open tuples Signed-off-by: Christoph Knittel --- compiler/frontend/ast_open_cxt.ml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/compiler/frontend/ast_open_cxt.ml b/compiler/frontend/ast_open_cxt.ml index e9ebd7747b5..0ce8cc9330a 100644 --- a/compiler/frontend/ast_open_cxt.ml +++ b/compiler/frontend/ast_open_cxt.ml @@ -30,6 +30,7 @@ type whole = * Longident.t Asttypes.loc * loc * Parsetree.attributes) + | Braces of loc * loc * Parsetree.attributes type t = whole list type exp = Parsetree.expression @@ -42,6 +43,9 @@ type destruct_output = exp list let rec destruct_open_tuple (e : Parsetree.expression) (acc : t) : (t * destruct_output * _) option = match e.pexp_desc with + | Pexp_braces {expr; braces_loc} -> + destruct_open_tuple expr + (Braces (e.pexp_loc, braces_loc, e.pexp_attributes) :: acc) | Pexp_open (flag, lid, cont) -> destruct_open_tuple cont (Let_open (flag, lid, e.pexp_loc, e.pexp_attributes) :: acc) @@ -57,5 +61,12 @@ let restore_exp (xs : Parsetree.expression) (qualifiers : t) : pexp_desc = Pexp_open (flag, lid, x); pexp_attributes = attrs; pexp_loc = loc; + } + : Parsetree.expression) + | Braces (loc, braces_loc, attrs) -> + ({ + pexp_desc = Pexp_braces {expr = x; braces_loc}; + pexp_attributes = attrs; + pexp_loc = loc; } : Parsetree.expression)) From eaa2818c1e4e8e166d56ee2a6892bba0a528c926 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Sat, 26 Sep 2026 09:28:57 +0200 Subject: [PATCH 4/5] Preserve attributes on braced expressions Signed-off-by: Christoph Knittel --- compiler/ml/typecore.ml | 5 ++++- compiler/syntax/src/res_printer.ml | 9 ++++++++- tests/syntax_tests/data/printer/expr/braced.res | 2 ++ .../data/printer/expr/expected/braced.res.txt | 2 ++ tests/tests/src/inline_regression_test.mjs | 12 ++++++++++++ tests/tests/src/inline_regression_test.res | 9 +++++++++ 6 files changed, 37 insertions(+), 2 deletions(-) diff --git a/compiler/ml/typecore.ml b/compiler/ml/typecore.ml index c32771bfc57..56ae64840ed 100644 --- a/compiler/ml/typecore.ml +++ b/compiler/ml/typecore.ml @@ -2461,7 +2461,10 @@ and type_expect_ ?deprecated_context ~context ?(recarg = Rejected) env sexp in match sexp.pexp_desc with | Pexp_braces {expr = inner} -> - type_expect ~context ?deprecated_context ~recarg env inner ty_expected + let exp = + type_expect ~context ?deprecated_context ~recarg env inner ty_expected + in + {exp with exp_attributes = sexp.pexp_attributes @ exp.exp_attributes} | Pexp_ident lid -> let path, desc = Typetexp.find_value diff --git a/compiler/syntax/src/res_printer.ml b/compiler/syntax/src/res_printer.ml index d8af802ceaf..a86c8b19778 100644 --- a/compiler/syntax/src/res_printer.ml +++ b/compiler/syntax/src/res_printer.ml @@ -3216,7 +3216,14 @@ and print_expression ~state (e : Parsetree.expression) cmt_tbl = braces_loc.loc_start.pos_lnum + 1 < inner.pexp_loc.loc_start.pos_lnum | _ -> false in - let e = Parsetree_viewer.unwrap_braces e in + let rec unwrap_braces_with_attributes attrs (e : Parsetree.expression) = + match e.pexp_desc with + | Pexp_braces {expr = inner} -> + unwrap_braces_with_attributes (attrs @ e.pexp_attributes) inner + | _ when attrs = [] -> e + | _ -> {e with pexp_attributes = attrs @ e.pexp_attributes} + in + let e = unwrap_braces_with_attributes [] e in let printed_expression = match e.pexp_desc with | Pexp_braces {expr = inner} -> print_expression ~state inner cmt_tbl diff --git a/tests/syntax_tests/data/printer/expr/braced.res b/tests/syntax_tests/data/printer/expr/braced.res index 50b6bae8877..28d6ee8fa2a 100644 --- a/tests/syntax_tests/data/printer/expr/braced.res +++ b/tests/syntax_tests/data/printer/expr/braced.res @@ -12,6 +12,8 @@ let f = (a, b) => { let x = { a } let x = { a + b } +let attributed = @inline(never) {x => x + 1} + let x = { // here a diff --git a/tests/syntax_tests/data/printer/expr/expected/braced.res.txt b/tests/syntax_tests/data/printer/expr/expected/braced.res.txt index 5deab6270c3..ed9502ba6a0 100644 --- a/tests/syntax_tests/data/printer/expr/expected/braced.res.txt +++ b/tests/syntax_tests/data/printer/expr/expected/braced.res.txt @@ -12,6 +12,8 @@ let f = (a, b) => { let x = {a} let x = {a + b} +let attributed = {@inline(never) x => x + 1} + let x = { // here a diff --git a/tests/tests/src/inline_regression_test.mjs b/tests/tests/src/inline_regression_test.mjs index 4ba1a84c8bf..418f859ba70 100644 --- a/tests/tests/src/inline_regression_test.mjs +++ b/tests/tests/src/inline_regression_test.mjs @@ -42,8 +42,20 @@ Mocha.describe("Inline_regression_test", () => { Mocha.test("basename", () => Test_utils.eq("File \"inline_regression_test.res\", line 33, characters 7-14", basename("b/c/a.b"), "a.b")); }); +function bracedNoInline(x) { + return x + 1 | 0; +} + +let bracedNoInlineResult = bracedNoInline(1); + +Mocha.describe("braced expression attributes", () => { + Mocha.test("inline never is retained", () => Test_utils.eq("File \"inline_regression_test.res\", line 42, characters 7-14", bracedNoInlineResult, 2)); +}); + export { generic_basename, basename, + bracedNoInline, + bracedNoInlineResult, } /* Not a pure module */ diff --git a/tests/tests/src/inline_regression_test.res b/tests/tests/src/inline_regression_test.res index 34878cc84a1..ceae7973f6c 100644 --- a/tests/tests/src/inline_regression_test.res +++ b/tests/tests/src/inline_regression_test.res @@ -33,3 +33,12 @@ describe(__MODULE__, () => { eq(__LOC__, basename("b/c/a.b"), "a.b") }) }) + +let bracedNoInline = {@inline(never) x => x + 1} +let bracedNoInlineResult = bracedNoInline(1) + +describe("braced expression attributes", () => { + test("inline never is retained", () => { + eq(__LOC__, bracedNoInlineResult, 2) + }) +}) From 792a5491dc608bfb32745ba30492a954c99c152c Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Sat, 26 Sep 2026 11:06:44 +0200 Subject: [PATCH 5/5] Handle braced expressions in JSX and completion Signed-off-by: Christoph Knittel --- analysis/src/completion_front_end.ml | 8 +- compiler/syntax/src/jsx_v4.ml | 4 + .../tests/src/CompletionBracedReceiver.res | 12 +++ .../expected/CompletionBracedReceiver.res.txt | 84 +++++++++++++++++++ .../data/ppx/react/expected/v4.res.txt | 14 ++++ tests/syntax_tests/data/ppx/react/v4.res | 5 ++ 6 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 tests/analysis_tests/tests/src/CompletionBracedReceiver.res create mode 100644 tests/analysis_tests/tests/src/expected/CompletionBracedReceiver.res.txt diff --git a/analysis/src/completion_front_end.ml b/analysis/src/completion_front_end.ml index c1a7718976e..9ab176db825 100644 --- a/analysis/src/completion_front_end.ml +++ b/analysis/src/completion_front_end.ml @@ -209,6 +209,7 @@ let find_arg_completables ~(args : arg list) ~end_pos ~pos_before_cursor let rec expr_to_context_path_inner ~(in_jsx_context : bool) (e : Parsetree.expression) = match e.pexp_desc with + | Pexp_braces {expr} -> expr_to_context_path_inner ~in_jsx_context expr | Pexp_constant (Pconst_string _ | Pconst_json _ | Pconst_raw_source _) -> Some Completable.CPString | Pexp_template _ -> Some Completable.CPString @@ -1375,7 +1376,12 @@ let completion_with_parser1 ~debug ~offset ~pos_cursor ~kind_file } in set_result (Cpath context_path) - else if Loc.end_ e.pexp_loc = pos_before_cursor then + else if + (match e.pexp_desc with + | Pexp_braces {braces_loc} -> Loc.end_ braces_loc + | _ -> Loc.end_ e.pexp_loc) + = pos_before_cursor + then match expr_to_context_path ~in_jsx_context:!in_jsx_context e with | Some context_path -> set_result diff --git a/compiler/syntax/src/jsx_v4.ml b/compiler/syntax/src/jsx_v4.ml index 4a7775fdb60..279b6918162 100644 --- a/compiler/syntax/src/jsx_v4.ml +++ b/compiler/syntax/src/jsx_v4.ml @@ -393,6 +393,8 @@ let modified_binding_old binding = (* TODO: there is a long-tail of unsupported features inside of blocks - Pexp_letmodule , Pexp_letexception , Pexp_ifthenelse *) let rec spelunk_for_fun_expression expression = match expression with + | {pexp_desc = Pexp_braces {expr = inner}} -> + spelunk_for_fun_expression inner (* let make = (~prop) => ... *) | {pexp_desc = Pexp_fun _} -> expression (* let make = {let foo = bar in (~prop) => ...} *) @@ -427,6 +429,8 @@ let modified_binding ~binding_loc ~binding_pat_loc ~fn_name binding = (* TODO: there is a long-tail of unsupported features inside of blocks - Pexp_letmodule , Pexp_letexception , Pexp_ifthenelse *) let rec spelunk_for_fun_expression expression = match expression with + | {pexp_desc = Pexp_braces {expr = inner}} -> + spelunk_for_fun_expression inner (* let make = (()) => ... *) (* let make = (_) => ... *) | { diff --git a/tests/analysis_tests/tests/src/CompletionBracedReceiver.res b/tests/analysis_tests/tests/src/CompletionBracedReceiver.res new file mode 100644 index 00000000000..869eb1cd86f --- /dev/null +++ b/tests/analysis_tests/tests/src/CompletionBracedReceiver.res @@ -0,0 +1,12 @@ +type value = {test: bool} + +let value: value = {test: true} + +// {value}.t +// ^com + +// {value}. +// ^com + +// value. +// ^com diff --git a/tests/analysis_tests/tests/src/expected/CompletionBracedReceiver.res.txt b/tests/analysis_tests/tests/src/expected/CompletionBracedReceiver.res.txt new file mode 100644 index 00000000000..43087c1b69f --- /dev/null +++ b/tests/analysis_tests/tests/src/expected/CompletionBracedReceiver.res.txt @@ -0,0 +1,84 @@ +Complete src/CompletionBracedReceiver.res 4:12 +posCursor:[4:12] posNoWhite:[4:11] Found expr:[4:4->4:12] +Pexp_field [4:4->4:9] t:[4:11->4:12] +Completable: Cpath Value[value].t +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath Value[value].t +ContextPath Value[value] +Path value +ContextPath Value[value]->t +ContextPath Value[value] +Path value +CPPipe pathFromEnv: found:true +Path CompletionBracedReceiver.t +Path t +[ + { + "detail": "bool", + "documentation": { + "kind": "markdown", + "value": "```rescript\ntest: bool\n```\n\n```rescript\ntype value = {test: bool}\n```" + }, + "kind": 5, + "label": "test", + "tags": [] + } +] + +Complete src/CompletionBracedReceiver.res 7:11 +posCursor:[7:11] posNoWhite:[7:10] Found expr:[7:4->7:11] +Pexp_field [7:4->7:9] _:[13:0->7:11] +Completable: Cpath Value[value]."" +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath Value[value]."" +ContextPath Value[value] +Path value +ContextPath Value[value]-> +ContextPath Value[value] +Path value +CPPipe pathFromEnv: found:true +Path CompletionBracedReceiver. +Path +[ + { + "detail": "bool", + "documentation": { + "kind": "markdown", + "value": "```rescript\ntest: bool\n```\n\n```rescript\ntype value = {test: bool}\n```" + }, + "kind": 5, + "label": "test", + "tags": [] + } +] + +Complete src/CompletionBracedReceiver.res 10:9 +posCursor:[10:9] posNoWhite:[10:8] Found expr:[10:3->10:9] +Pexp_field [10:3->10:8] _:[13:0->10:9] +Completable: Cpath Value[value]."" +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath Value[value]."" +ContextPath Value[value] +Path value +ContextPath Value[value]-> +ContextPath Value[value] +Path value +CPPipe pathFromEnv: found:true +Path CompletionBracedReceiver. +Path +[ + { + "detail": "bool", + "documentation": { + "kind": "markdown", + "value": "```rescript\ntest: bool\n```\n\n```rescript\ntype value = {test: bool}\n```" + }, + "kind": 5, + "label": "test", + "tags": [] + } +] + diff --git a/tests/syntax_tests/data/ppx/react/expected/v4.res.txt b/tests/syntax_tests/data/ppx/react/expected/v4.res.txt index a9c093b3475..a23517ad7dd 100644 --- a/tests/syntax_tests/data/ppx/react/expected/v4.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/v4.res.txt @@ -9,6 +9,20 @@ let make = React.component({ \"V4" }) +module Braced = { + @res.jsxComponentProps + type props<'name> = { + name: 'name, + } + + let make = ({name, _}: props<_>): React.element => React.string(name) + let make = React.component({ + let \"V4$Braced" = (props: props<_>) => make(props) + + \"V4$Braced" + }) +} + module AnotherName = { @res.jsxComponentProps type // Component with another name than "make" diff --git a/tests/syntax_tests/data/ppx/react/v4.res b/tests/syntax_tests/data/ppx/react/v4.res index ff8a2129ca5..9463314cf4d 100644 --- a/tests/syntax_tests/data/ppx/react/v4.res +++ b/tests/syntax_tests/data/ppx/react/v4.res @@ -2,6 +2,11 @@ @react.component let make = (~x: string, ~y: string) => React.string(x ++ y) +module Braced = { + @react.component + let make = {(~name) => React.string(name)} +} + module AnotherName = { // Component with another name than "make" @react.component