diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dcfb0ee52..2047caef9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ #### :bug: Bug fix +- Fix excessive parentheses and indentation in function assignments to refs, align record and array assignment formatting across refs and fields, and preserve function return-type parentheses and consistent JSX fragment layout in callbacks. https://github.com/rescript-lang/rescript/pull/8611 - Fix a recursive module with an empty signature discarding its right-hand side. Lambda-to-Lam conversion rewrote `Pupdate_mod` to unit when the module's shape had no fields, dropping the primitive's arguments - one of which is the right-hand side - so `module rec M: {} = { let () = Console.log("effect") }` emitted nothing for `M`. The elision now happens where the bindings are produced, with the right-hand side still in hand. https://github.com/rescript-lang/rescript/pull/8608 - Fix `Int.Ref.increment` and `Int.Ref.decrement` evaluating their argument twice: `Int.Ref.increment(mkRef())` emitted `mkRef().contents = mkRef().contents + 1 | 0`. The `%incr` and `%decr` builtins lowered to an assignment that repeated the argument expression; they now bind the reference before the read-modify-write. Inlining decisions around an increment are taken on the code it stands for rather than on a single primitive node. https://github.com/rescript-lang/rescript/pull/8608 - Fix a compiler crash on a polymorphic variant whose numeric name exceeds the `int32` range. `#99999999999("a")` and the same name in a pattern failed with `Failure("Int32.of_string")` and no location, because the range check ran in the frontend AST pass and matched only payload-free expressions. It now runs in `Typecore`, next to the integer literal decoding whose overflow error it mirrors, and covers both label positions. A bare `type t = [#99999999999]` still compiles, since nothing decodes a row field name. https://github.com/rescript-lang/rescript/pull/8608 diff --git a/compiler/syntax/src/res_parens.ml b/compiler/syntax/src/res_parens.ml index 2f311dcfce..0c25e46853 100644 --- a/compiler/syntax/src/res_parens.ml +++ b/compiler/syntax/src/res_parens.ml @@ -275,20 +275,6 @@ let field_expr expr = | _ when Parsetree_viewer.expr_is_await expr -> Parenthesized | _ -> Nothing) -let set_field_expr_rhs expr = - let opt_braces, _ = Parsetree_viewer.process_braces_attr expr in - match opt_braces with - | Some ({Location.loc = braces_loc}, _) -> Braced braces_loc - | None -> ( - match expr with - | { - Parsetree.pexp_desc = - Pexp_constraint ({pexp_desc = Pexp_pack _}, {ptyp_desc = Ptyp_package _}); - } -> - Nothing - | {pexp_desc = Pexp_constraint _} -> Parenthesized - | _ -> Nothing) - let ternary_operand expr = let opt_braces, _ = Parsetree_viewer.process_braces_attr expr in match opt_braces with diff --git a/compiler/syntax/src/res_parens.mli b/compiler/syntax/src/res_parens.mli index 02642646dd..3ce0218c84 100644 --- a/compiler/syntax/src/res_parens.mli +++ b/compiler/syntax/src/res_parens.mli @@ -15,8 +15,6 @@ val assert_or_await_expr_rhs : ?in_await:bool -> Parsetree.expression -> kind val field_expr : Parsetree.expression -> kind -val set_field_expr_rhs : Parsetree.expression -> kind - val ternary_operand : Parsetree.expression -> kind val jsx_prop_expr : Parsetree.expression -> kind diff --git a/compiler/syntax/src/res_parsetree_viewer.ml b/compiler/syntax/src/res_parsetree_viewer.ml index 0a2aeeb21b..87e2a4d803 100644 --- a/compiler/syntax/src/res_parsetree_viewer.ml +++ b/compiler/syntax/src/res_parsetree_viewer.ml @@ -547,8 +547,7 @@ let should_indent_binary_expr expr = } when is_binary_operator operator -> is_equality_operator operator - || (not (same_precedence_sub_expression operator lhs)) - || operator = ":=" + || not (same_precedence_sub_expression operator lhs) | _ -> false let should_inline_rhs_binary_expr rhs = diff --git a/compiler/syntax/src/res_printer.ml b/compiler/syntax/src/res_printer.ml index 7893e4f320..35b5087f2a 100644 --- a/compiler/syntax/src/res_printer.ml +++ b/compiler/syntax/src/res_printer.ml @@ -3133,8 +3133,26 @@ and print_if_chain ~state pexp_attributes ifs else_expr cmt_tbl = in Doc.concat [print_attributes ~state attrs cmt_tbl; if_docs; else_doc] -and print_object_set_expr ~state (expr : Parsetree.expression) obj member rhs - cmt_tbl = +and print_assignment_rhs ~operator rhs rhs_doc = + (* Delimited expressions handle their own indentation. Binary expressions + and switches can break after the assignment operator. *) + let should_indent = + (not (Parsetree_viewer.is_braced_expr rhs)) + && (Parsetree_viewer.is_binary_expression rhs + || + match rhs.pexp_desc with + | Pexp_match _ -> not (Parsetree_viewer.is_if_let_expr rhs) + | _ -> false) + in + Doc.concat + [ + Doc.text operator; + (if should_indent then + Doc.group (Doc.indent (Doc.concat [Doc.line; rhs_doc])) + else Doc.concat [Doc.space; rhs_doc]); + ] + +and print_object_set_expr ~state obj member rhs cmt_tbl = let rhs_doc = let doc = print_expression_with_comments ~state rhs cmt_tbl in match Parens.expr rhs with @@ -3142,27 +3160,15 @@ and print_object_set_expr ~state (expr : Parsetree.expression) obj member rhs | Braced braces -> print_braces doc rhs braces | Nothing -> doc in - (* TODO: unify indentation of "=" *) - let should_indent = - (not (Parsetree_viewer.is_braced_expr rhs)) - && Parsetree_viewer.is_binary_expression rhs - in - let doc = - Doc.group - (Doc.concat - [ - print_object_get_doc ~state ~expr_loc:expr.pexp_loc obj member cmt_tbl; - Doc.text " ="; - (if should_indent then - Doc.group (Doc.indent (Doc.concat [Doc.line; rhs_doc])) - else Doc.concat [Doc.space; rhs_doc]); - ]) - in - ignore expr; - doc + Doc.group + (Doc.concat + [ + print_object_get_doc ~state obj member cmt_tbl; + print_assignment_rhs ~operator:" =" rhs rhs_doc; + ]) -and print_object_get_doc ~state ~expr_loc parent_expr - (label : string Location.loc) cmt_tbl = +and print_object_get_doc ~state parent_expr (label : string Location.loc) + cmt_tbl = let parent_doc = let doc = print_expression_with_comments ~state parent_expr cmt_tbl in match Parens.unary_expr_operand parent_expr with @@ -3170,7 +3176,6 @@ and print_object_get_doc ~state ~expr_loc parent_expr | Braced braces -> print_braces doc parent_expr braces | Nothing -> doc in - ignore expr_loc; let member = let member_doc = print_comments (Doc.text label.txt) cmt_tbl label.loc in Doc.concat [Doc.text "\""; member_doc; Doc.text "\""] @@ -3178,83 +3183,6 @@ and print_object_get_doc ~state ~expr_loc parent_expr Doc.group (Doc.concat [parent_doc; Doc.lbracket; member; Doc.rbracket]) and print_expression ~state (e : Parsetree.expression) cmt_tbl = - let print_arrow e = - let async, parameters, return_expr = Parsetree_viewer.fun_expr e in - let attrs_on_arrow = e.pexp_attributes in - let return_expr, typ_constraint = - match return_expr.pexp_desc with - | Pexp_constraint (expr, typ) -> - ( { - expr with - pexp_attributes = - List.concat [expr.pexp_attributes; return_expr.pexp_attributes]; - }, - Some typ ) - | _ -> (return_expr, None) - in - let has_constraint = - match typ_constraint with - | Some _ -> true - | None -> false - in - let parameters_doc = - print_expr_fun_parameters ~state ~in_callback:NoCallback ~async - ~has_constraint parameters cmt_tbl - in - let return_expr_doc = - let opt_braces, _ = Parsetree_viewer.process_braces_attr return_expr in - let should_inline = - match (return_expr.pexp_desc, opt_braces) with - | _, Some _ -> true - | ( ( Pexp_array _ | Pexp_tuple _ - | Pexp_construct (_, Some _) - | Pexp_record _ ), - _ ) -> - true - | _ -> false - in - let should_indent = - match return_expr.pexp_desc with - | Pexp_sequence _ | Pexp_let _ | Pexp_letmodule _ | Pexp_letexception _ - | Pexp_open _ - | Pexp_jsx_element (Jsx_fragment _) -> - false - | _ -> true - in - let return_doc = - let doc = print_expression_with_comments ~state return_expr cmt_tbl in - match Parens.expr return_expr with - | Parens.Parenthesized -> add_parens doc - | Braced braces -> print_braces doc return_expr braces - | Nothing -> doc - in - if should_inline then Doc.concat [Doc.space; return_doc] - else - Doc.group - (if should_indent then Doc.indent (Doc.concat [Doc.line; return_doc]) - else Doc.concat [Doc.space; return_doc]) - in - let typ_constraint_doc = - match typ_constraint with - | Some typ -> - let typ_doc = - let doc = print_typ_expr ~state typ cmt_tbl in - if Parens.arrow_return_typ_expr typ then add_parens doc else doc - in - Doc.concat [Doc.text ": "; typ_doc] - | _ -> Doc.nil - in - let attrs = print_attributes ~state attrs_on_arrow cmt_tbl in - Doc.group - (Doc.concat - [ - attrs; - parameters_doc; - typ_constraint_doc; - Doc.text " =>"; - return_expr_doc; - ]) - in let printed_expression = match e.pexp_desc with | Pexp_fun @@ -3273,7 +3201,7 @@ and print_expression ~state (e : Parsetree.expression) cmt_tbl = print_expression_with_comments ~state (Parsetree_viewer.rewrite_underscore_apply e) cmt_tbl - | Pexp_fun _ -> print_arrow e + | Pexp_fun _ -> print_pexp_fun ~state ~in_callback:NoCallback e cmt_tbl | Parsetree.Pexp_constant c -> print_constant c | Pexp_jsx_element (Jsx_fragment @@ -3917,9 +3845,9 @@ and print_expression ~state (e : Parsetree.expression) cmt_tbl = let doc_typ = print_typ_expr ~state typ cmt_tbl in Doc.concat [Doc.lparen; doc_expr; Doc.text " :> "; doc_typ; Doc.rparen] | Pexp_object_get (parent_expr, label) -> - print_object_get_doc ~state ~expr_loc:e.pexp_loc parent_expr label cmt_tbl + print_object_get_doc ~state parent_expr label cmt_tbl | Pexp_object_set (obj, member, rhs) -> - print_object_set_expr ~state e obj member rhs cmt_tbl + print_object_set_expr ~state obj member rhs cmt_tbl | Pexp_await e -> let printed_expression = print_expression_with_comments ~state e cmt_tbl @@ -3982,7 +3910,8 @@ and print_pexp_fun ~state ~in_callback e cmt_tbl = let return_should_indent = match return_expr.pexp_desc with | Pexp_sequence _ | Pexp_let _ | Pexp_letmodule _ | Pexp_letexception _ - | Pexp_open _ -> + | Pexp_open _ + | Pexp_jsx_element (Jsx_fragment _) -> false | _ -> true in @@ -4020,17 +3949,27 @@ and print_pexp_fun ~state ~in_callback e cmt_tbl = in let typ_constraint_doc = match typ_constraint with - | Some typ -> Doc.concat [Doc.text ": "; print_typ_expr ~state typ cmt_tbl] + | Some typ -> + let typ_doc = + let doc = print_typ_expr ~state typ cmt_tbl in + if Parens.arrow_return_typ_expr typ then add_parens doc else doc + in + Doc.concat [Doc.text ": "; typ_doc] | _ -> Doc.nil in - Doc.concat - [ - print_attributes ~state attrs_on_arrow cmt_tbl; - parameters_doc; - typ_constraint_doc; - Doc.text " =>"; - return_expr_doc; - ] + let doc = + Doc.concat + [ + print_attributes ~state ~inline:true attrs_on_arrow cmt_tbl; + parameters_doc; + typ_constraint_doc; + Doc.text " =>"; + return_expr_doc; + ] + in + match in_callback with + | NoCallback -> Doc.group doc + | FitsOnOneLine | ArgumentsFitOnOneLine -> doc and print_ternary_operand ~state expr cmt_tbl = let doc = print_expression_with_comments ~state expr cmt_tbl in @@ -4042,7 +3981,7 @@ and print_ternary_operand ~state expr cmt_tbl = and print_set_field_expr ~state attrs lhs longident_loc rhs loc cmt_tbl = let rhs_doc = let doc = print_expression_with_comments ~state rhs cmt_tbl in - match Parens.set_field_expr_rhs rhs with + match Parens.expr rhs with | Parens.Parenthesized -> add_parens doc | Braced braces -> print_braces doc rhs braces | Nothing -> doc @@ -4054,7 +3993,6 @@ and print_set_field_expr ~state attrs lhs longident_loc rhs loc cmt_tbl = | Braced braces -> print_braces doc lhs braces | Nothing -> doc in - let should_indent = Parsetree_viewer.is_binary_expression rhs in let doc = Doc.group (Doc.concat @@ -4062,10 +4000,7 @@ and print_set_field_expr ~state attrs lhs longident_loc rhs loc cmt_tbl = lhs_doc; Doc.dot; print_lident_path longident_loc cmt_tbl; - Doc.text " ="; - (if should_indent then - Doc.group (Doc.indent (Doc.concat [Doc.line; rhs_doc])) - else Doc.concat [Doc.space; rhs_doc]); + print_assignment_rhs ~operator:" =" rhs rhs_doc; ]) in let doc = @@ -4276,24 +4211,7 @@ and print_binary_expression ~state (expr : Parsetree.expression) cmt_tbl = in if is_lhs then add_parens doc else doc | Pexp_object_set (obj, member, rhs) -> - let rhs_doc = print_expression_with_comments ~state rhs cmt_tbl in - let lhs_doc = - print_object_get_doc ~state ~expr_loc:expr.pexp_loc obj member - cmt_tbl - in - (* TODO: unify indentation of "=" *) - let should_indent = Parsetree_viewer.is_binary_expression rhs in - let doc = - Doc.group - (Doc.concat - [ - lhs_doc; - Doc.text " ="; - (if should_indent then - Doc.group (Doc.indent (Doc.concat [Doc.line; rhs_doc])) - else Doc.concat [Doc.space; rhs_doc]); - ]) - in + let doc = print_object_set_expr ~state obj member rhs cmt_tbl in let doc = match expr.pexp_attributes with | [] -> doc @@ -4304,12 +4222,27 @@ and print_binary_expression ~state (expr : Parsetree.expression) cmt_tbl = if is_lhs then add_parens doc else doc | _ -> ( let doc = print_expression_with_comments ~state expr cmt_tbl in - match Parens.binary_expr_operand ~is_lhs expr with + let parens = + match expr.pexp_desc with + | Pexp_fun _ when parent_operator = ":=" && not is_lhs -> + Parens.expr expr + | _ -> Parens.binary_expr_operand ~is_lhs expr + in + match parens with | Parens.Parenthesized -> add_parens doc | Braced braces -> print_braces doc expr braces | Nothing -> doc) in - flatten ~is_lhs ~is_multiline expr parent_operator + if + parent_operator = ":=" && (not is_lhs) + && Parsetree_viewer.is_braced_expr expr + then + let doc = print_expression_with_comments ~state expr cmt_tbl in + match Parens.expr expr with + | Braced braces -> print_braces doc expr braces + | Parenthesized -> add_parens doc + | Nothing -> doc + else flatten ~is_lhs ~is_multiline expr parent_operator in match expr.pexp_desc with | Pexp_apply @@ -4351,23 +4284,25 @@ and print_binary_expression ~state (expr : Parsetree.expression) cmt_tbl = in let right = - let operator_with_rhs = - let rhs_doc = - print_operand - ~is_lhs:(Parsetree_viewer.is_rhs_binary_operator operator) - ~is_multiline rhs operator - in - Doc.concat - [ - print_binary_operator - ~inline_rhs:(Parsetree_viewer.should_inline_rhs_binary_expr rhs) - operator; - rhs_doc; - ] + let rhs_doc = + print_operand + ~is_lhs:(Parsetree_viewer.is_rhs_binary_operator operator) + ~is_multiline rhs operator in - if Parsetree_viewer.should_indent_binary_expr expr then - Doc.group (Doc.indent operator_with_rhs) - else operator_with_rhs + if operator = ":=" then print_assignment_rhs ~operator:" :=" rhs rhs_doc + else + let operator_with_rhs = + Doc.concat + [ + print_binary_operator + ~inline_rhs:(Parsetree_viewer.should_inline_rhs_binary_expr rhs) + operator; + rhs_doc; + ] + in + if Parsetree_viewer.should_indent_binary_expr expr then + Doc.group (Doc.indent operator_with_rhs) + else operator_with_rhs in let doc = Doc.group diff --git a/tests/syntax_benchmarks/data/Napkinscript.res b/tests/syntax_benchmarks/data/Napkinscript.res index 03395e1ad1..c8596e584b 100644 --- a/tests/syntax_benchmarks/data/Napkinscript.res +++ b/tests/syntax_benchmarks/data/Napkinscript.res @@ -2608,10 +2608,10 @@ module Reporting = { | Invalid_argument(_) => "" } reportDoc := { - open TerminalDoc - let ix = startLine + i - group(~break_=Always, append(reportDoc.contents, renderLine(line, ix))) - } + open TerminalDoc + let ix = startLine + i + group(~break_=Always, append(reportDoc.contents, renderLine(line, ix))) + } } TerminalDoc.toString(reportDoc.contents) diff --git a/tests/syntax_tests/data/printer/expr/assignment.res b/tests/syntax_tests/data/printer/expr/assignment.res new file mode 100644 index 0000000000..aff3c658aa --- /dev/null +++ b/tests/syntax_tests/data/printer/expr/assignment.res @@ -0,0 +1,99 @@ +f := { + first: one, + second: two, +} +f.contents = { + first: one, + second: two, +} +obj["value"] = { + first: one, + second: two, +} + +f := [ + firstVeryLongArrayElement, + secondVeryLongArrayElement, + thirdVeryLongArrayElement, + fourthVeryLongArrayElement, +] +f.contents = [ + firstVeryLongArrayElement, + secondVeryLongArrayElement, + thirdVeryLongArrayElement, + fourthVeryLongArrayElement, +] +obj["value"] = [ + firstVeryLongArrayElement, + secondVeryLongArrayElement, + thirdVeryLongArrayElement, + fourthVeryLongArrayElement, +] + +f := () => { + doA() + doB() +} +f.contents = () => { + doA() + doB() +} +obj["value"] = () => { + doA() + doB() +} + +f := @attr () => { + doA() + doB() +} +f.contents = @attr () => { + doA() + doB() +} +obj["value"] = @attr () => { + doA() + doB() +} + +f := switch value { +| Some(value) => value +| None => fallback +} +f.contents = switch value { +| Some(value) => value +| None => fallback +} +obj["value"] = switch value { +| Some(value) => value +| None => fallback +} + +f := firstVeryLongOperand + secondVeryLongOperand + thirdVeryLongOperand + fourthVeryLongOperand +f.contents = firstVeryLongOperand + secondVeryLongOperand + thirdVeryLongOperand + fourthVeryLongOperand +obj["value"] = firstVeryLongOperand + secondVeryLongOperand + thirdVeryLongOperand + fourthVeryLongOperand + +f := {firstVeryLongOperand + secondVeryLongOperand + thirdVeryLongOperand + fourthVeryLongOperand} +f.contents = {firstVeryLongOperand + secondVeryLongOperand + thirdVeryLongOperand + fourthVeryLongOperand} +obj["value"] = {firstVeryLongOperand + secondVeryLongOperand + thirdVeryLongOperand + fourthVeryLongOperand} + +// Assignments used as operands retain their grouping. +let nested = (f := {first: one, second: two})->ignore +let field = (f.contents = { + first: one, + second: two, +})->ignore +let object = (obj["value"] = { + first: one, + second: two, +})->ignore + +f := /* before value */ { + // first field + first: one, + second: two, // second field +} // after assignment + +let bracedObject = (obj["value"] = { + firstVeryLongOperand + secondVeryLongOperand + thirdVeryLongOperand + fourthVeryLongOperand +})->ignore diff --git a/tests/syntax_tests/data/printer/expr/expected/assignment.res.txt b/tests/syntax_tests/data/printer/expr/expected/assignment.res.txt new file mode 100644 index 0000000000..887afd4b4c --- /dev/null +++ b/tests/syntax_tests/data/printer/expr/expected/assignment.res.txt @@ -0,0 +1,114 @@ +f := { + first: one, + second: two, +} +f.contents = { + first: one, + second: two, +} +obj["value"] = { + first: one, + second: two, +} + +f := [ + firstVeryLongArrayElement, + secondVeryLongArrayElement, + thirdVeryLongArrayElement, + fourthVeryLongArrayElement, +] +f.contents = [ + firstVeryLongArrayElement, + secondVeryLongArrayElement, + thirdVeryLongArrayElement, + fourthVeryLongArrayElement, +] +obj["value"] = [ + firstVeryLongArrayElement, + secondVeryLongArrayElement, + thirdVeryLongArrayElement, + fourthVeryLongArrayElement, +] + +f := () => { + doA() + doB() +} +f.contents = () => { + doA() + doB() +} +obj["value"] = () => { + doA() + doB() +} + +f := @attr () => { + doA() + doB() +} +f.contents = @attr () => { + doA() + doB() +} +obj["value"] = @attr () => { + doA() + doB() +} + +f := + switch value { + | Some(value) => value + | None => fallback + } +f.contents = + switch value { + | Some(value) => value + | None => fallback + } +obj["value"] = + switch value { + | Some(value) => value + | None => fallback + } + +f := firstVeryLongOperand + secondVeryLongOperand + thirdVeryLongOperand + fourthVeryLongOperand +f.contents = + firstVeryLongOperand + secondVeryLongOperand + thirdVeryLongOperand + fourthVeryLongOperand +obj["value"] = + firstVeryLongOperand + secondVeryLongOperand + thirdVeryLongOperand + fourthVeryLongOperand + +f := {firstVeryLongOperand + secondVeryLongOperand + thirdVeryLongOperand + fourthVeryLongOperand} +f.contents = { + firstVeryLongOperand + secondVeryLongOperand + thirdVeryLongOperand + fourthVeryLongOperand +} +obj["value"] = { + firstVeryLongOperand + secondVeryLongOperand + thirdVeryLongOperand + fourthVeryLongOperand +} + +// Assignments used as operands retain their grouping. +let nested = (f := {first: one, second: two})->ignore +let field = ( + f.contents = { + first: one, + second: two, + } +)->ignore +let object = ( + obj["value"] = { + first: one, + second: two, + } +)->ignore + +f := /* before value */ { + // first field + first: one, + second: two, // second field +} // after assignment + +let bracedObject = ( + obj["value"] = { + firstVeryLongOperand + secondVeryLongOperand + thirdVeryLongOperand + fourthVeryLongOperand + } +)->ignore diff --git a/tests/syntax_tests/data/printer/expr/expected/functionLayouts.res.txt b/tests/syntax_tests/data/printer/expr/expected/functionLayouts.res.txt new file mode 100644 index 0000000000..96e6031721 --- /dev/null +++ b/tests/syntax_tests/data/printer/expr/expected/functionLayouts.res.txt @@ -0,0 +1,34 @@ +let standalone = @attr (value): (int => int) => other => value + other +consume(@attr (value): (int => int) => other => value + other) +consume(@attr (value): (int => int) => other => value + other, argument) +consume(argument, @attr (value): (int => int) => other => value + other) + +let fragment = value => <> + + + +consume(value => <> + + +) +consume(value => <> + + +, argument) +consume(argument, value => <> + + +) + +let block = + @attr async (~first, ~second=defaultValue): result => { + // Keep the body comment. + await compute(first, second) + } +consume(argument, @attr async (~first, ~second=defaultValue): result => { + // Keep the callback comment. + await compute(first, second) +}) +consume(firstVeryLongArgument, secondVeryLongArgument, (firstParameter, secondParameter) => + compute(firstParameter, secondParameter) +) diff --git a/tests/syntax_tests/data/printer/expr/expected/refFunction.res.txt b/tests/syntax_tests/data/printer/expr/expected/refFunction.res.txt new file mode 100644 index 0000000000..9f94c7ef7d --- /dev/null +++ b/tests/syntax_tests/data/printer/expr/expected/refFunction.res.txt @@ -0,0 +1,67 @@ +let f = ref(ignore) + +f.contents = () => { + doA() + doB() +} +f := () => { + doA() + doB() +} + +f.contents = @attr () => { + doA() + doB() +} +f := @attr () => { + doA() + doB() +} + +f := () => doA() +f := @first @second () => doA() +f := async () => { + await doA() + doB() +} +f := (value: int): int => value + 1 +f := (firstArgument, secondArgument, thirdArgument, fourthArgument, fifthArgument) => { + doA(firstArgument, secondArgument) + doB(thirdArgument, fourthArgument, fifthArgument) +} + +// Keep comments on the assigned function and its body. +f := /* function */ () => { + // body + doA() + doB() +} +f.contents = /* attribute */ @attr () => { + doA() + doB() +} +f := @attr () => { + doA() + doB() +} // assignment + +// Explicit blocks and type constraints still need their delimiters. +f := { + () => { + doA() + doB() + } +} +f := (() => doA(): unit => unit) + +// Other operators still need parentheses around functions. +let equal = + f.contents == + ( + () => { + doA() + doB() + } + ) +let left = (() => doA()) == f.contents +let nested = (f := () => doA())->ignore diff --git a/tests/syntax_tests/data/printer/expr/functionLayouts.res b/tests/syntax_tests/data/printer/expr/functionLayouts.res new file mode 100644 index 0000000000..80f8b3d9ae --- /dev/null +++ b/tests/syntax_tests/data/printer/expr/functionLayouts.res @@ -0,0 +1,33 @@ +let standalone = @attr (value): (int => int) => other => value + other +consume(@attr (value): (int => int) => other => value + other) +consume(@attr (value): (int => int) => other => value + other, argument) +consume(argument, @attr (value): (int => int) => other => value + other) + +let fragment = value => <> + + + +consume(value => <> + + +) +consume(value => <> + + +, argument) +consume(argument, value => <> + + +) + +let block = @attr async (~first, ~second=defaultValue): result => { + // Keep the body comment. + await compute(first, second) +} +consume(argument, @attr async (~first, ~second=defaultValue): result => { + // Keep the callback comment. + await compute(first, second) +}) +consume(firstVeryLongArgument, secondVeryLongArgument, (firstParameter, secondParameter) => + compute(firstParameter, secondParameter) +) diff --git a/tests/syntax_tests/data/printer/expr/refFunction.res b/tests/syntax_tests/data/printer/expr/refFunction.res new file mode 100644 index 0000000000..fc53b313ba --- /dev/null +++ b/tests/syntax_tests/data/printer/expr/refFunction.res @@ -0,0 +1,61 @@ +let f = ref(ignore) + +f.contents = () => { + doA() + doB() +} +f := () => { + doA() + doB() +} + +f.contents = @attr () => { + doA() + doB() +} +f := @attr () => { + doA() + doB() +} + +f := () => doA() +f := @first @second () => doA() +f := async () => { + await doA() + doB() +} +f := (value: int): int => value + 1 +f := (firstArgument, secondArgument, thirdArgument, fourthArgument, fifthArgument) => { + doA(firstArgument, secondArgument) + doB(thirdArgument, fourthArgument, fifthArgument) +} + +// Keep comments on the assigned function and its body. +f := /* function */ () => { + // body + doA() + doB() +} +f.contents = @attr /* attribute */ () => { + doA() + doB() +} +f := @attr () => { + doA() + doB() +} // assignment + +// Explicit blocks and type constraints still need their delimiters. +f := {() => { + doA() + doB() +}} +f := (() => doA(): unit => unit) + +// Other operators still need parentheses around functions. +let equal = f.contents == (() => { + doA() + doB() +}) +let left = (() => doA()) == f.contents +let nested = (f := () => doA())->ignore diff --git a/tests/tests/src/UncurriedExternals.res b/tests/tests/src/UncurriedExternals.res index f48940a301..af1e40ac67 100644 --- a/tests/tests/src/UncurriedExternals.res +++ b/tests/tests/src/UncurriedExternals.res @@ -44,8 +44,7 @@ module AsyncMethod = { type p = {watch: @this (pluginContext, string, changeEvent) => promise} let p1 = { - watch: @this - async (pc, name, ev) => { + watch: @this async (pc, name, ev) => { Console.log(pc) }, } diff --git a/tests/tests/src/alias_default_value_test.res b/tests/tests/src/alias_default_value_test.res index 032b1e75d4..3849a08a34 100644 --- a/tests/tests/src/alias_default_value_test.res +++ b/tests/tests/src/alias_default_value_test.res @@ -47,8 +47,7 @@ module C6 = { module C7 = { @react.component let make = - @directive("'use memo'") - (~count, ~username=?) => { + @directive("'use memo'") (~count, ~username=?) => { let times = switch count { | 1 => "once" | 2 => "twice" @@ -70,8 +69,7 @@ module C8 = { @react.componentWithProps let make = - @directive("'use memo'") - props => { + @directive("'use memo'") props => { React.int(props.count) } } diff --git a/tests/tests/src/function_directives_async.res b/tests/tests/src/function_directives_async.res index 77f5a1e317..c91854e861 100644 --- a/tests/tests/src/function_directives_async.res +++ b/tests/tests/src/function_directives_async.res @@ -1,6 +1,5 @@ let f = - @directive("'use cache'") - async (p1, ~p2, ~p3) => { + @directive("'use cache'") async (p1, ~p2, ~p3) => { await Promise.make((resolve, _reject) => resolve((p1, p2, p3))) } diff --git a/tests/tests/src/gpr_858_unit2_test.res b/tests/tests/src/gpr_858_unit2_test.res index 1126522150..ceea77a82f 100644 --- a/tests/tests/src/gpr_858_unit2_test.res +++ b/tests/tests/src/gpr_858_unit2_test.res @@ -6,12 +6,12 @@ let () = { | 0 => assert(i == n) | j => delayed := { - let prev = delayed.contents - () => { - prev() - f(n + 1 + i - i, j - 1) - } + let prev = delayed.contents + () => { + prev() + f(n + 1 + i - i, j - 1) } + } } f(0, i) } diff --git a/tests/tests/src/int_overflow_test.mjs b/tests/tests/src/int_overflow_test.mjs index ea4ed28dec..8255470788 100644 --- a/tests/tests/src/int_overflow_test.mjs +++ b/tests/tests/src/int_overflow_test.mjs @@ -39,18 +39,18 @@ function fib(x) { } Mocha.describe("Int_overflow_test", () => { - Mocha.test("plus_overflow", () => Test_utils.eq("File \"int_overflow_test.res\", line 55, characters 33-40", true, true)); - Mocha.test("minus_overflow", () => Test_utils.eq("File \"int_overflow_test.res\", line 56, characters 34-41", true, true)); - Mocha.test("flow_again1", () => Test_utils.eq("File \"int_overflow_test.res\", line 57, characters 31-38", 2147483646, 2147483646)); - Mocha.test("flow_again2", () => Test_utils.eq("File \"int_overflow_test.res\", line 58, characters 31-38", -2, -2)); - Mocha.test("hash_test", () => Test_utils.eq("File \"int_overflow_test.res\", line 59, characters 29-36", hash_variant("xxyyzzuuxxzzyy00112233"), 544087776)); - Mocha.test("hash_test2", () => Test_utils.eq("File \"int_overflow_test.res\", line 60, characters 30-37", hash_variant("xxyyzxzzyy"), -449896130)); - Mocha.test("hash_variant_test1", () => Test_utils.eq("File \"int_overflow_test.res\", line 61, characters 38-45", hash_variant2("xxyyzzuuxxzzyy00112233"), 544087776)); - Mocha.test("hash_variant_test2", () => Test_utils.eq("File \"int_overflow_test.res\", line 62, characters 38-45", hash_variant2("xxyyzxzzyy"), -449896130)); - Mocha.test("int_literal_flow", () => Test_utils.eq("File \"int_overflow_test.res\", line 63, characters 36-43", -1, -1)); - Mocha.test("int_literal_flow2", () => Test_utils.eq("File \"int_overflow_test.res\", line 64, characters 37-44", -1, -1)); - Mocha.test("float_conversion_test1", () => Test_utils.eq("File \"int_overflow_test.res\", line 66, characters 7-14", Stdlib_Option.map(Stdlib_Float.fromString("3"), prim => prim | 0), 3)); - Mocha.test("float_conversion_test2", () => Test_utils.eq("File \"int_overflow_test.res\", line 69, characters 7-14", Stdlib_Option.map(Stdlib_Float.fromString("3.2"), prim => prim | 0), 3)); + Mocha.test("plus_overflow", () => Test_utils.eq("File \"int_overflow_test.res\", line 54, characters 33-40", true, true)); + Mocha.test("minus_overflow", () => Test_utils.eq("File \"int_overflow_test.res\", line 55, characters 34-41", true, true)); + Mocha.test("flow_again1", () => Test_utils.eq("File \"int_overflow_test.res\", line 56, characters 31-38", 2147483646, 2147483646)); + Mocha.test("flow_again2", () => Test_utils.eq("File \"int_overflow_test.res\", line 57, characters 31-38", -2, -2)); + Mocha.test("hash_test", () => Test_utils.eq("File \"int_overflow_test.res\", line 58, characters 29-36", hash_variant("xxyyzzuuxxzzyy00112233"), 544087776)); + Mocha.test("hash_test2", () => Test_utils.eq("File \"int_overflow_test.res\", line 59, characters 30-37", hash_variant("xxyyzxzzyy"), -449896130)); + Mocha.test("hash_variant_test1", () => Test_utils.eq("File \"int_overflow_test.res\", line 60, characters 38-45", hash_variant2("xxyyzzuuxxzzyy00112233"), 544087776)); + Mocha.test("hash_variant_test2", () => Test_utils.eq("File \"int_overflow_test.res\", line 61, characters 38-45", hash_variant2("xxyyzxzzyy"), -449896130)); + Mocha.test("int_literal_flow", () => Test_utils.eq("File \"int_overflow_test.res\", line 62, characters 36-43", -1, -1)); + Mocha.test("int_literal_flow2", () => Test_utils.eq("File \"int_overflow_test.res\", line 63, characters 37-44", -1, -1)); + Mocha.test("float_conversion_test1", () => Test_utils.eq("File \"int_overflow_test.res\", line 65, characters 7-14", Stdlib_Option.map(Stdlib_Float.fromString("3"), prim => prim | 0), 3)); + Mocha.test("float_conversion_test2", () => Test_utils.eq("File \"int_overflow_test.res\", line 68, characters 7-14", Stdlib_Option.map(Stdlib_Float.fromString("3.2"), prim => prim | 0), 3)); }); let max_int = 2147483647; diff --git a/tests/tests/src/int_overflow_test.res b/tests/tests/src/int_overflow_test.res index 638ff77fa3..bd013a3ed1 100644 --- a/tests/tests/src/int_overflow_test.res +++ b/tests/tests/src/int_overflow_test.res @@ -9,11 +9,10 @@ let min_int = -2147483648 // 0x7FFFFFFF let hash_variant = s => { let accu = ref(0) for i in 0 to String.length(s) - 1 { - accu := - Int.bitwiseAnd( - 223 * accu.contents + String.codePointAt(s, i)->Option.getUnsafe, - Int.shiftLeft(1, 31) - 1, - ) + accu := Int.bitwiseAnd( + 223 * accu.contents + String.codePointAt(s, i)->Option.getUnsafe, + Int.shiftLeft(1, 31) - 1, + ) /* Here accu is 31 bits, times 223 will not be than 53 bits.. TODO: we can use `Sys.backend_type` for patching */ diff --git a/tests/tests/src/stdlib/Stdlib_IteratorTests.mjs b/tests/tests/src/stdlib/Stdlib_IteratorTests.mjs index eaa5431430..c800fa3be6 100644 --- a/tests/tests/src/stdlib/Stdlib_IteratorTests.mjs +++ b/tests/tests/src/stdlib/Stdlib_IteratorTests.mjs @@ -706,7 +706,7 @@ loop_0: for await (let value$1 of asyncIterableForAwaitLoopControl) { Test.run([ [ "Stdlib_IteratorTests.res", - 506, + 508, 13, 44 ], @@ -739,7 +739,7 @@ let startupLogs = ((async function* () { Test.run([ [ "Stdlib_IteratorTests.res", - 540, + 542, 13, 54 ], @@ -773,7 +773,7 @@ if (match$19.done !== false && match$19.value === "stopped") { Test.run([ [ "Stdlib_IteratorTests.res", - 569, + 571, 13, 41 ], @@ -804,7 +804,7 @@ if (match$21.done !== false) { Test.run([ [ "Stdlib_IteratorTests.res", - 598, + 600, 13, 40 ], @@ -831,7 +831,7 @@ await Stdlib_AsyncIterableIterator.forEach(createdAsyncIterableIterator, value = Test.run([ [ "Stdlib_IteratorTests.res", - 622, + 624, 13, 56 ], diff --git a/tests/tests/src/stdlib/Stdlib_IteratorTests.res b/tests/tests/src/stdlib/Stdlib_IteratorTests.res index 4a7aff085d..cedbf409e0 100644 --- a/tests/tests/src/stdlib/Stdlib_IteratorTests.res +++ b/tests/tests/src/stdlib/Stdlib_IteratorTests.res @@ -497,8 +497,10 @@ for await value of asyncIterableForAwaitLoopControl { | 1 => continue | 3 => break | _ => - asyncIterableForAwaitLoopControlValues := - [...asyncIterableForAwaitLoopControlValues.contents, value] + asyncIterableForAwaitLoopControlValues := [ + ...asyncIterableForAwaitLoopControlValues.contents, + value, + ] } } diff --git a/tests/tests/src/test_bs_this.res b/tests/tests/src/test_bs_this.res index 90e1eccc70..f189439d98 100644 --- a/tests/tests/src/test_bs_this.res +++ b/tests/tests/src/test_bs_this.res @@ -3,15 +3,13 @@ let uux_this: @this ({"length": int}, int, int) => int = @this (o, x, y) => o["l let even = @this (o, x) => x + o let bark = () => - @this - (o: 'self, x, y) => { + @this (o: 'self, x, y) => { Console.log((o["length"], o["x"], o["y"], x, y)) x + y } let js_obj: 'self = { - "bark": @this - (o: 'self, x, y) => { + "bark": @this (o: 'self, x, y) => { Console.log(o) x + y },