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
},