diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f16d7bd44..0ebd00baaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ #### :bug: Bug fix +- Fix the formatter dropping or moving attributes and `await` on module expressions, such as `module M = @attr F(X)`, and printing invalid code for module constraints and functors in some positions, such as `module M: T = (X: S)`. https://github.com/rescript-lang/rescript/pull/8735 - Make rewatch compile independent modules after an unrelated failure and recompile blocked dependents when a changed interface survives a failed implementation, including across full watcher rebuilds. https://github.com/rescript-lang/rescript/pull/8667 - Fix `RescriptTools.binaryPath` failing with `ERR_PACKAGE_IMPORT_NOT_DEFINED` when used from a user project. https://github.com/rescript-lang/rescript/pull/8694 - GenType: type inline-record cases of untagged variants as objects instead of their single field's type. https://github.com/rescript-lang/rescript/pull/8693 diff --git a/compiler/syntax/src/res_parens.ml b/compiler/syntax/src/res_parens.ml index 4b8e671027..cf76c63f1b 100644 --- a/compiler/syntax/src/res_parens.ml +++ b/compiler/syntax/src/res_parens.ml @@ -428,11 +428,23 @@ let braced_expr expr = | Pexp_constraint _ -> true | _ -> false -let include_mod_expr mod_expr = +(* A constraint that needs parens where [M: S] would not parse or would mean + something else, e.g. after [include] or [module type of]. With attributes + or [await] it prints its own. *) +let mod_constraint mod_expr = match mod_expr.Parsetree.pmod_desc with - | Parsetree.Pmod_constraint _ -> true + | Parsetree.Pmod_constraint _ -> + not (Parsetree_viewer.mod_expr_has_attributes mod_expr) | _ -> false +(* An applied module expression that needs parens: [(M: S)(X)], + [((Y) => M)(X)], [(%ext)(X)], and with attributes or [await], which + would otherwise apply to the whole application *) +let mod_apply_callee callee = + match callee.Parsetree.pmod_desc with + | Pmod_constraint _ | Pmod_functor _ | Pmod_extension (_, PStr []) -> true + | _ -> Parsetree_viewer.mod_expr_has_attributes callee + let mod_expr_parens mod_expr = match mod_expr with | { diff --git a/compiler/syntax/src/res_parens.mli b/compiler/syntax/src/res_parens.mli index 8d304823f4..593fa40986 100644 --- a/compiler/syntax/src/res_parens.mli +++ b/compiler/syntax/src/res_parens.mli @@ -33,7 +33,8 @@ val mod_expr_functor_constraint : Parsetree.module_type -> bool val braced_expr : Parsetree.expression -> bool val call_expr : Parsetree.expression -> kind -val include_mod_expr : Parsetree.module_expr -> bool +val mod_constraint : Parsetree.module_expr -> bool +val mod_apply_callee : Parsetree.module_expr -> bool val mod_expr_parens : Parsetree.module_expr -> bool diff --git a/compiler/syntax/src/res_parsetree_viewer.ml b/compiler/syntax/src/res_parsetree_viewer.ml index 7604b96b40..8d0bd99fde 100644 --- a/compiler/syntax/src/res_parsetree_viewer.ml +++ b/compiler/syntax/src/res_parsetree_viewer.ml @@ -225,19 +225,20 @@ let rec unwrap_braces expr = | Pexp_braces {expr = inner} -> unwrap_braces inner | _ -> expr +(* Attributes the parser adds to encode syntax; they are never printed *) +let is_parsing_attr (attr : Parsetree.attribute) = + match attr with + | ( { + Location.txt = + ( "res.iflet" | "res.ternary" | "res.await" | "res.patVariantSpread" + | "res.dictPattern" | "res.dictSpread" ); + }, + _ ) -> + true + | _ -> false + let filter_parsing_attrs attrs = - List.filter - (fun attr -> - match attr with - | ( { - Location.txt = - ( "res.iflet" | "res.ternary" | "res.await" - | "res.patVariantSpread" | "res.dictPattern" | "res.dictSpread" ); - }, - _ ) -> - false - | _ -> true) - attrs + List.filter (fun attr -> not (is_parsing_attr attr)) attrs let is_block_expr expr = match (unwrap_braces expr).pexp_desc with @@ -548,26 +549,37 @@ let is_printable_attribute attr = let has_printable_attributes attrs = List.exists is_printable_attribute attrs +(* Attributes or [await] on a module expression: either one prints before it + and binds less tightly than an application or a constraint. The attributes + are the ones [Res_printer.print_attributes] prints. *) +let mod_expr_has_attributes (mod_expr : Parsetree.module_expr) = + List.exists + (fun attr -> + match attr with + | {Location.txt = "res.await"}, _ -> true + | _ -> not (is_parsing_attr attr)) + mod_expr.pmod_attributes + let filter_printable_attributes attrs = List.filter is_printable_attribute attrs let partition_printable_attributes attrs = List.partition is_printable_attribute attrs +let is_doc_comment_attribute ((id, payload) : Parsetree.attribute) = + match (id, payload) with + | ( {txt = "res.doc"}, + PStr + [ + { + pstr_desc = + Pstr_eval ({pexp_desc = Pexp_constant (Pconst_string _)}, _); + }; + ] ) -> + true + | _ -> false + let partition_doc_comment_attributes attrs = - List.partition - (fun ((id, payload) : Parsetree.attribute) -> - match (id, payload) with - | ( {txt = "res.doc"}, - PStr - [ - { - pstr_desc = - Pstr_eval ({pexp_desc = Pexp_constant (Pconst_string _)}, _); - }; - ] ) -> - true - | _ -> false) - attrs + List.partition is_doc_comment_attribute attrs let rec is_fun_expr expr = match expr.pexp_desc with @@ -600,7 +612,11 @@ let requires_special_callback_printing_first_arg args = let mod_expr_apply mod_expr = let rec loop acc mod_expr = match mod_expr with - | {pmod_desc = Pmod_apply (next, arg)} -> loop (arg :: acc) next + (* An inner application with attributes is kept as the callee, so the + printer can parenthesize it with its attributes *) + | {pmod_desc = Pmod_apply (next, arg)} as apply + when acc = [] || not (mod_expr_has_attributes apply) -> + loop (arg :: acc) next | _ -> (acc, mod_expr) in loop [] mod_expr @@ -608,10 +624,12 @@ let mod_expr_apply mod_expr = let mod_expr_functor mod_expr = let rec loop acc mod_expr = match mod_expr with + (* An awaited inner functor is kept as the result, so [await] is printed *) | { pmod_desc = Pmod_functor (lbl, mod_type, return_mod_expr); pmod_attributes = attrs; - } -> + } + when acc = [] || not (has_await_attribute attrs) -> let param = (attrs, lbl, mod_type) in loop (param :: acc) return_mod_expr | return_mod_expr -> (List.rev acc, return_mod_expr) diff --git a/compiler/syntax/src/res_parsetree_viewer.mli b/compiler/syntax/src/res_parsetree_viewer.mli index 509a334557..deb4f1f250 100644 --- a/compiler/syntax/src/res_parsetree_viewer.mli +++ b/compiler/syntax/src/res_parsetree_viewer.mli @@ -96,9 +96,11 @@ val filter_fragile_match_attributes : val should_indent_binary_expr : Parsetree.expression -> bool val should_inline_rhs_binary_expr : Parsetree.expression -> bool val has_printable_attributes : Parsetree.attributes -> bool +val mod_expr_has_attributes : Parsetree.module_expr -> bool val filter_printable_attributes : Parsetree.attributes -> Parsetree.attributes val partition_printable_attributes : Parsetree.attributes -> Parsetree.attributes * Parsetree.attributes +val is_doc_comment_attribute : Parsetree.attribute -> bool val partition_doc_comment_attributes : Parsetree.attributes -> Parsetree.attributes * Parsetree.attributes diff --git a/compiler/syntax/src/res_printer.ml b/compiler/syntax/src/res_printer.ml index 1701bd2ef5..7eb4fc48e5 100644 --- a/compiler/syntax/src/res_printer.ml +++ b/compiler/syntax/src/res_printer.ml @@ -28,6 +28,31 @@ let add_parens doc = Doc.rparen; ]) +(* Module expressions hug their parens, so a multi-line structure or + signature keeps its braces next to them: [(M: {...})] *) +let add_mod_expr_parens doc = Doc.concat [Doc.lparen; doc; Doc.rparen] + +(* Whether a module expression's printed form starts with a doc comment: + doc comments print before other attributes, and an unparenthesized + constraint or application starts with its leftmost module *) +let rec mod_expr_starts_with_doc_comment (mod_expr : Parsetree.module_expr) = + match mod_expr.pmod_desc with + (* [await] and a functor's parameter list come first *) + | _ when Parsetree_viewer.has_await_attribute mod_expr.pmod_attributes -> + false + | Pmod_functor _ -> false + | _ + when List.exists Parsetree_viewer.is_doc_comment_attribute + mod_expr.pmod_attributes -> + true + | _ when Parsetree_viewer.mod_expr_has_attributes mod_expr -> false + | Pmod_constraint (inner, _) -> mod_expr_starts_with_doc_comment inner + | Pmod_apply _ -> + let _, call_expr = Parsetree_viewer.mod_expr_apply mod_expr in + (not (Parens.mod_apply_callee call_expr)) + && mod_expr_starts_with_doc_comment call_expr + | _ -> false + let add_braces ?(force_break = false) doc = Doc.breakable_group ~force_break (Doc.concat @@ -798,19 +823,18 @@ and print_module_binding ~state ~is_rec module_binding cmt_tbl i = in let mod_expr_doc, mod_constraint_doc = match module_binding.pmb_expr with - | {pmod_desc = Pmod_constraint (mod_expr, mod_type)} - when not - (Parsetree_viewer.has_await_attribute - module_binding.pmb_expr.pmod_attributes) -> - ( print_mod_expr ~state mod_expr cmt_tbl, + | {pmod_desc = Pmod_constraint (mod_expr, mod_type)} as constrained + (* [module M: S = E] has no node for the constraint's attributes *) + when not (Parsetree_viewer.mod_expr_has_attributes constrained) -> + let mod_expr_doc = + if Parens.mod_expr_parens constrained then + add_mod_expr_parens (print_mod_expr ~state mod_expr cmt_tbl) + else print_mod_expr_constraint_parens ~state mod_expr cmt_tbl + in + ( mod_expr_doc, Doc.concat [Doc.text ": "; print_mod_type ~state mod_type cmt_tbl] ) | mod_expr -> (print_mod_expr ~state mod_expr cmt_tbl, Doc.nil) in - let mod_expr_doc_parens = - if Parens.mod_expr_parens module_binding.pmb_expr then - Doc.concat [Doc.lparen; mod_expr_doc; Doc.rparen] - else mod_expr_doc - in let mod_name = let doc = Doc.text module_binding.pmb_name.Location.txt in print_comments doc cmt_tbl module_binding.pmb_name.loc @@ -824,7 +848,7 @@ and print_module_binding ~state ~is_rec module_binding cmt_tbl i = mod_name; mod_constraint_doc; Doc.text " = "; - mod_expr_doc_parens; + mod_expr_doc; ] in print_comments doc cmt_tbl module_binding.pmb_loc @@ -983,7 +1007,10 @@ and print_mod_type ~state mod_type cmt_tbl = ]) | Pmty_typeof mod_expr -> Doc.concat - [Doc.text "module type of "; print_mod_expr ~state mod_expr cmt_tbl] + [ + Doc.text "module type of "; + print_mod_expr_constraint_parens ~state mod_expr cmt_tbl; + ] | Pmty_extension extension -> print_extension ~state ~at_module_lvl:false extension cmt_tbl | Pmty_alias longident -> @@ -1199,30 +1226,35 @@ and print_include_declaration ~state print_attributes ~state include_declaration.pincl_attributes cmt_tbl; Doc.text "include "; (let include_doc = - match include_declaration.pincl_mod.pmod_desc with + match include_declaration.pincl_mod with (* include Module.Name({ type t = t }) try as oneliner if there is a single type alias declaration *) - | Pmod_apply - ( {pmod_desc = Pmod_ident longident_loc}, - { - pmod_desc = - Pmod_structure - [ - ({ - pstr_desc = - Pstr_type - ( _, - [ - { - ptype_kind = Ptype_abstract; - ptype_manifest = Some _; - }; - ] ); - } as structure_item); - ]; - } ) -> + | { + pmod_attributes = []; + pmod_desc = + Pmod_apply + ( {pmod_desc = Pmod_ident longident_loc; pmod_attributes = []}, + { + pmod_attributes = []; + pmod_desc = + Pmod_structure + [ + ({ + pstr_desc = + Pstr_type + ( _, + [ + { + ptype_kind = Ptype_abstract; + ptype_manifest = Some _; + }; + ] ); + } as structure_item); + ]; + } ); + } -> Doc.concat [ print_longident_location longident_loc cmt_tbl; @@ -1244,7 +1276,7 @@ and print_include_declaration ~state ] | _ -> print_mod_expr ~state include_declaration.pincl_mod cmt_tbl in - if Parens.include_mod_expr include_declaration.pincl_mod then + if Parens.mod_constraint include_declaration.pincl_mod then add_parens include_doc else include_doc); ] @@ -3659,6 +3691,23 @@ and print_expression ~state (e : Parsetree.expression) cmt_tbl = Doc.space; print_expression_block ~state ~braces:true body cmt_tbl; ]) + | Pexp_constraint + ( {pexp_desc = Pexp_pack ({pmod_desc = Pmod_constraint _} as mod_expr)}, + {ptyp_desc = Ptyp_package package_type; ptyp_loc} ) -> + (* [module((M: S1): S2)] would parse as a functor, so this keeps the + general form [(module((M: S1)): module(S2))] *) + Doc.concat + [ + Doc.lparen; + Doc.text "module("; + print_mod_expr_constraint_parens ~state mod_expr cmt_tbl; + Doc.text "): "; + print_comments + (print_package_type ~state ~print_module_keyword_and_parens:true + package_type cmt_tbl) + cmt_tbl ptyp_loc; + Doc.rparen; + ] | Pexp_constraint ( {pexp_desc = Pexp_pack mod_expr}, {ptyp_desc = Ptyp_package package_type; ptyp_loc} ) -> @@ -3670,7 +3719,7 @@ and print_expression ~state (e : Parsetree.expression) cmt_tbl = (Doc.concat [ Doc.soft_line; - print_mod_expr ~state mod_expr cmt_tbl; + print_mod_expr_constraint_parens ~state mod_expr cmt_tbl; Doc.text ": "; print_comments (print_package_type ~state @@ -3706,7 +3755,10 @@ and print_expression ~state (e : Parsetree.expression) cmt_tbl = Doc.text "module("; Doc.indent (Doc.concat - [Doc.soft_line; print_mod_expr ~state mod_expr cmt_tbl]); + [ + Doc.soft_line; + print_mod_expr_constraint_parens ~state mod_expr cmt_tbl; + ]); Doc.soft_line; Doc.rparen; ]) @@ -5554,9 +5606,7 @@ and print_expression_block ~state ~braces expr cmt_tbl = let name, mod_expr = match mod_expr.pmod_desc with | Pmod_constraint (mod_expr2, mod_type) - when not - (Parsetree_viewer.has_await_attribute mod_expr.pmod_attributes) - -> + when not (Parsetree_viewer.mod_expr_has_attributes mod_expr) -> let name = Doc.concat [name; Doc.text ": "; print_mod_type ~state mod_type cmt_tbl] @@ -5570,7 +5620,7 @@ and print_expression_block ~state ~braces expr cmt_tbl = Doc.text "module "; name; Doc.text " = "; - print_mod_expr ~state mod_expr cmt_tbl; + print_mod_expr_constraint_parens ~state mod_expr cmt_tbl; ] in let loc = {expr.pexp_loc with loc_end = mod_expr.pmod_loc.loc_end} in @@ -6025,7 +6075,7 @@ and print_mod_expr ~state mod_expr cmt_tbl = let args, call_expr = Parsetree_viewer.mod_expr_apply mod_expr in let is_unit_sugar = match args with - | [{pmod_desc = Pmod_structure []}] -> true + | [{pmod_desc = Pmod_structure []; pmod_attributes = []}] -> true | _ -> false in let should_hug = @@ -6033,10 +6083,15 @@ and print_mod_expr ~state mod_expr cmt_tbl = | [{pmod_desc = Pmod_structure _}] -> true | _ -> false in + let call_expr_doc = + let doc = print_mod_expr ~state call_expr cmt_tbl in + if Parens.mod_apply_callee call_expr then add_mod_expr_parens doc + else doc + in Doc.group (Doc.concat [ - print_mod_expr ~state call_expr cmt_tbl; + call_expr_doc; (if is_unit_sugar then print_mod_apply_arg ~state (List.hd args [@doesNotRaise]) @@ -6067,21 +6122,33 @@ and print_mod_expr ~state mod_expr cmt_tbl = Doc.rparen; ]); ]) - | Pmod_constraint (mod_expr, mod_type) -> - Doc.concat - [ - print_mod_expr ~state mod_expr cmt_tbl; - Doc.text ": "; - print_mod_type ~state mod_type cmt_tbl; - ] + | Pmod_constraint (inner, mod_type) -> + let doc = + Doc.concat + [ + print_mod_expr ~state inner cmt_tbl; + Doc.text ": "; + print_mod_type ~state mod_type cmt_tbl; + ] + in + (* [@attr (M: S)], [await (M: S)]: without parens, attributes and + [await] would apply to [M] *) + if Parsetree_viewer.mod_expr_has_attributes mod_expr then + add_mod_expr_parens doc + else doc | Pmod_functor _ -> print_mod_functor ~state mod_expr cmt_tbl in + let doc = + match mod_expr.pmod_desc with + (* A functor's attributes belong to its first parameter, see + [print_mod_functor] *) + | Pmod_functor _ -> doc + | _ -> Doc.concat [print_mod_expr_attributes ~state mod_expr cmt_tbl; doc] + in + (* [await] comes first: [@attr await M] does not parse *) let doc = if Parsetree_viewer.has_await_attribute mod_expr.pmod_attributes then - match mod_expr.pmod_desc with - | Pmod_constraint _ -> - Doc.concat [Doc.text "await "; Doc.lparen; doc; Doc.rparen] - | _ -> Doc.concat [Doc.text "await "; doc] + Doc.concat [Doc.text "await "; doc] else doc in print_comments doc cmt_tbl mod_expr.pmod_loc @@ -6092,14 +6159,15 @@ and print_mod_functor ~state mod_expr cmt_tbl = in let return_constraint, return_mod_expr = match return_mod_expr.pmod_desc with - | Pmod_constraint (mod_expr, mod_type) -> + | Pmod_constraint (mod_expr, mod_type) + when not (Parsetree_viewer.mod_expr_has_attributes return_mod_expr) -> let constraint_doc = let doc = print_mod_type ~state mod_type cmt_tbl in if Parens.mod_expr_functor_constraint mod_type then add_parens doc else doc in let mod_constraint = Doc.concat [Doc.text ": "; constraint_doc] in - (mod_constraint, print_mod_expr ~state mod_expr cmt_tbl) + (mod_constraint, print_mod_expr_constraint_parens ~state mod_expr cmt_tbl) | _ -> (Doc.nil, print_mod_expr ~state return_mod_expr cmt_tbl) in let parameters_doc = @@ -6159,10 +6227,39 @@ and print_mod_functor_param ~state (attrs, lbl, opt_mod_type) cmt_tbl = in print_comments doc cmt_tbl cmt_loc +(* A module expression's attributes, doc comments first, on one line with + it: unlike before a declaration, a line break here would leave the module + at the start of the next line *) +and print_mod_expr_attributes ~state (mod_expr : Parsetree.module_expr) cmt_tbl + = + match Parsetree_viewer.filter_parsing_attrs mod_expr.pmod_attributes with + | [] -> Doc.nil + | attrs -> + let doc_comments, attrs = + Parsetree_viewer.partition_doc_comment_attributes attrs + in + Doc.concat + (List.map + (fun attr -> + Doc.concat [fst (print_attribute ~state attr cmt_tbl); Doc.space]) + (doc_comments @ attrs)) + and print_mod_apply_arg ~state mod_expr cmt_tbl = - match mod_expr.pmod_desc with - | Pmod_structure [] -> Doc.text "()" - | _ -> print_mod_expr ~state mod_expr cmt_tbl + match mod_expr with + | {pmod_desc = Pmod_structure []; pmod_attributes = []} -> Doc.text "()" + | _ -> + let doc = print_mod_expr ~state mod_expr cmt_tbl in + (* The parser doesn't accept a doc comment at the start of an argument *) + if mod_expr_starts_with_doc_comment mod_expr then add_mod_expr_parens doc + else doc + +(* A constraint without attributes needs parens where [M: S] would not parse + or would mean something else: after [module M: T =], in a functor's + result, after [module type of] and inside [module(...)]. With attributes + it prints its own. *) +and print_mod_expr_constraint_parens ~state mod_expr cmt_tbl = + let doc = print_mod_expr ~state mod_expr cmt_tbl in + if Parens.mod_constraint mod_expr then add_mod_expr_parens doc else doc and print_exception_def ~state (constr : Parsetree.extension_constructor) cmt_tbl = diff --git a/tests/syntax_tests/data/printer/modExpr/attributes.res b/tests/syntax_tests/data/printer/modExpr/attributes.res new file mode 100644 index 0000000000..c39dfa18f4 --- /dev/null +++ b/tests/syntax_tests/data/printer/modExpr/attributes.res @@ -0,0 +1,57 @@ +module A = @attr F +module B = @attr {} +module C = @attr { + let x = 1 +} +module D = @a @b F + +// on a functor application, its functor, or an argument +module E = @attr F({}) +module G = (@attr F)({}) +module H = @attr F(A, B) +module I = (@attr F(A))(B) +module J = F(@attr X) +module Q = F(@attr {}) +include @attr F({type t = int}) +include (@attr F)({type t = int}) +include F(@attr {type t = int}) + +module K: T = @attr X +include @attr F({}) + +// on a module constraint, or on the module it constrains +module N = @attr (X: S) +module O = F(@attr (X: S)) +module P = F((@attr X: S)) +include @attr (X: S) +include (@attr X: S) + +// on a functor's result +module R = (X) => @attr (Y: S) +module S = (X): S => @attr Y + +// @JSX is printed like any other attribute +module T = (@JSX F)(A) +module U = (@JSX F(A))(B) +module V = @JSX (X: S) + +// a functor argument can't start with a doc comment +module W = H(@w /** doc */ X) +module Y = H((/** doc */ X: S)) +module Z = /** doc */ X +include /** doc */ X + +module L = @attr unpack(x) +module M = @attr %ext + +let f = async () => { + module A = await @attr X + module B = await (@attr X: S) + module C = await @attr (X: S) + module D = @attr (X: S) + module E = (await F)(A) + module G = (await F(A))(B) + module H = (X: T) => await (Y: S) + module I = (X: T) => await (Y: S) => {} + () +} diff --git a/tests/syntax_tests/data/printer/modExpr/expected/attributes.res.txt b/tests/syntax_tests/data/printer/modExpr/expected/attributes.res.txt new file mode 100644 index 0000000000..ccc639f7bc --- /dev/null +++ b/tests/syntax_tests/data/printer/modExpr/expected/attributes.res.txt @@ -0,0 +1,63 @@ +module A = @attr F +module B = @attr {} +module C = @attr { + let x = 1 +} +module D = @a @b F + +// on a functor application, its functor, or an argument +module E = @attr F() +module G = (@attr F)() +module H = @attr F(A, B) +module I = (@attr F(A))(B) +module J = F(@attr X) +module Q = F(@attr {}) +include @attr F({ + type t = int +}) +include (@attr F)({ + type t = int +}) +include F(@attr { + type t = int +}) + +module K: T = @attr X +include @attr F() + +// on a module constraint, or on the module it constrains +module N = @attr (X: S) +module O = F(@attr (X: S)) +module P = F(@attr X: S) +include @attr (X: S) +include (@attr X: S) + +// on a functor's result +module R = (_: X) => @attr (Y: S) +module S = (_: X): S => @attr Y + +// @JSX is printed like any other attribute +module T = (@JSX F)(A) +module U = (@JSX F(A))(B) +module V = @JSX (X: S) + +// a functor argument can't start with a doc comment +module W = H((/** doc */ @w X)) +module Y = H((/** doc */ X: S)) +module Z = /** doc */ X +include /** doc */ X + +module L = @attr unpack(x) +module M = @attr %ext + +let f = async () => { + module A = await @attr X + module B = await (@attr X: S) + module C = await @attr (X: S) + module D = @attr (X: S) + module E = (await F)(A) + module G = (await F(A))(B) + module H = (X: T) => await (Y: S) + module I = (X: T) => await (Y: S) => {} + () +} diff --git a/tests/syntax_tests/data/printer/modExpr/expected/parens.res.txt b/tests/syntax_tests/data/printer/modExpr/expected/parens.res.txt new file mode 100644 index 0000000000..4a8d821af6 --- /dev/null +++ b/tests/syntax_tests/data/printer/modExpr/expected/parens.res.txt @@ -0,0 +1,17 @@ +// module constraints where `M: S` would not parse or would mean something else +module A: T = (X: S) +module rec B: T = (X: S) +module type C = module type of (X: S) +let d = module((X: S)) +let e = (module((X: S1)): module(S2)) +let f = () => { + module M: T = (X: S) + () +} + +// functors that are applied +module G = (X: S)(Z) +module H = ((Y: S) => {})(Z) +module I = ((_: Y): S => W)(Z) +module J = (%ext)(Z) +module K = %ext(A)(Z) diff --git a/tests/syntax_tests/data/printer/modExpr/parens.res b/tests/syntax_tests/data/printer/modExpr/parens.res new file mode 100644 index 0000000000..61f0c4c4cb --- /dev/null +++ b/tests/syntax_tests/data/printer/modExpr/parens.res @@ -0,0 +1,17 @@ +// module constraints where `M: S` would not parse or would mean something else +module A: T = (X: S) +module rec B: T = (X: S) +module type C = module type of (X: S) +let d = module((X: S)) +let e = (module((X: S1)): module(S2)) +let f = () => { + module M: T = (X: S) + () +} + +// functors that are applied +module G = ((X: S))(Z) +module H = ((Y: S) => {})(Z) +module I = ((Y): S => W)(Z) +module J = (%ext)(Z) +module K = %ext(A)(Z)