From 95a6db66a3be38086b48e9bac9cac9a77dd2b1df Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Thu, 8 Oct 2026 06:46:44 +0000 Subject: [PATCH 1/3] Remove obsolete attributes from the printer's filters and merge them `res.ternary` and `JSX` are now only part of the v0 PPX wire format: the parser produces `Pexp_ternary` and `Pexp_jsx_element`, and the v0 bridge converts both markers back into those nodes. Drop them from `is_parsing_attr`, `has_attributes` and `is_printable_attribute`. `is_printable_attribute` then only differed from `is_parsing_attr` in `res.patVariantSpread` and `res.dictPattern`, which the parser only puts on patterns, while the printable helpers are only used on expressions and module expressions. Define the printable helpers in terms of `is_parsing_attr`, so the printer has one list of internal attributes instead of two that disagreed (the cause of the `@JSX` review finding in #8735). `filter_printable_attributes` was the same as `filter_parsing_attrs` and is removed. A `@JSX` attribute written in source now gets the same parens as any other attribute, e.g. `(@JSX x) + 1`. Formatting of the repo's sources is unchanged. Signed-off-by: Christoph Knittel Co-Authored-By: Claude Opus 5.5 --- compiler/syntax/src/res_parsetree_viewer.ml | 27 +++++++------------- compiler/syntax/src/res_parsetree_viewer.mli | 1 - compiler/syntax/src/res_printer.ml | 3 +-- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/compiler/syntax/src/res_parsetree_viewer.ml b/compiler/syntax/src/res_parsetree_viewer.ml index 41c55e134d5..b3515e3690b 100644 --- a/compiler/syntax/src/res_parsetree_viewer.ml +++ b/compiler/syntax/src/res_parsetree_viewer.ml @@ -223,8 +223,8 @@ let is_parsing_attr (attr : Parsetree.attribute) = match attr with | ( { Location.txt = - ( "res.iflet" | "res.ternary" | "res.patVariantSpread" - | "res.dictPattern" | "res.dictSpread" ); + ( "res.iflet" | "res.patVariantSpread" | "res.dictPattern" + | "res.dictSpread" ); }, _ ) -> true @@ -382,7 +382,7 @@ let has_attributes attrs = List.exists (fun attr -> match attr with - | {Location.txt = "res.iflet" | "res.ternary"}, _ -> false + | {Location.txt = "res.iflet"}, _ -> false (* Remove the fragile pattern warning for iflet expressions *) | ( {Location.txt = "warning"}, PStr @@ -530,25 +530,16 @@ let should_inline_rhs_binary_expr rhs = true | _ -> false -let is_printable_attribute attr = - match attr with - | {Location.txt = "res.iflet" | "JSX" | "res.ternary" | "res.dictSpread"}, _ - -> - false - | _ -> true - -let has_printable_attributes attrs = List.exists is_printable_attribute attrs +let has_printable_attributes attrs = + List.exists (fun attr -> not (is_parsing_attr attr)) attrs -(* Attributes on a module expression, the ones [Res_printer.print_attributes] - prints: they print before it and bind less tightly than an application or - a constraint *) +(* Attributes on a module expression print before it and bind less tightly + than an application or a constraint *) let mod_expr_has_attributes (mod_expr : Parsetree.module_expr) = - List.exists (fun attr -> not (is_parsing_attr attr)) mod_expr.pmod_attributes - -let filter_printable_attributes attrs = List.filter is_printable_attribute attrs + has_printable_attributes mod_expr.pmod_attributes let partition_printable_attributes attrs = - List.partition is_printable_attribute attrs + List.partition (fun attr -> not (is_parsing_attr attr)) attrs let is_doc_comment_attribute ((id, payload) : Parsetree.attribute) = match (id, payload) with diff --git a/compiler/syntax/src/res_parsetree_viewer.mli b/compiler/syntax/src/res_parsetree_viewer.mli index 7140a84f655..6261a0d0e8c 100644 --- a/compiler/syntax/src/res_parsetree_viewer.mli +++ b/compiler/syntax/src/res_parsetree_viewer.mli @@ -96,7 +96,6 @@ 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 diff --git a/compiler/syntax/src/res_printer.ml b/compiler/syntax/src/res_printer.ml index aaf938e2ea1..e4ce9f5b9b7 100644 --- a/compiler/syntax/src/res_printer.ml +++ b/compiler/syntax/src/res_printer.ml @@ -4290,8 +4290,7 @@ and print_binary_expression ~state ~force_pipe_breaks { expr with pexp_attributes = - Parsetree_viewer.filter_printable_attributes - expr.pexp_attributes; + Parsetree_viewer.filter_parsing_attrs expr.pexp_attributes; } with | Braced braces_loc -> print_braces doc expr braces_loc From 3394532bee4160151f260ee55963f346be7940f4 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Thu, 8 Oct 2026 06:47:10 +0000 Subject: [PATCH 2/3] Add CHANGELOG entry for #8740 Signed-off-by: Christoph Knittel Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa82b4b535d..088e8338d6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ #### :house: Internal +- Remove the obsolete `res.ternary` and `JSX` attributes from the formatter's internal attribute filters and merge the filters into one. A `@JSX` attribute written in source is now formatted like any other attribute. https://github.com/rescript-lang/rescript/pull/8740 - Represent `await` on module expressions with a dedicated `Pmod_await` parsetree node instead of a `res.await` attribute, which remains only in the PPX wire representation. https://github.com/rescript-lang/rescript/pull/8738 - Remove dead compiler code: warning 24, the `J.String_index` JS IR node, the `Pisint` and `Parraysets` Lambda primitives with their unused `%` primitive names (`%obj_is_int`, `%obj_dup`, `%obj_field`, `%array_safe_set`, `%null_to_opt`, `%nullable_to_opt`), the no-op `-pp` bsc flag and unread `Clflags` fields (`-unsafe` stays accepted as an explicit no-op), and the unused `unit` slot of `Pexp_coerce`. https://github.com/rescript-lang/rescript/pull/8731 - Represent inline record definitions with an explicit parsetree origin while retaining the existing PPX wire representation. https://github.com/rescript-lang/rescript/pull/8686 From 1f3b6d6c2f350f63608b270b03276ce8f8f0ab4a Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Thu, 8 Oct 2026 16:29:00 +0000 Subject: [PATCH 3/3] Test the formatting of @JSX and @res.ternary written in source Printer cases for both marker names, and an ordinary attribute, on operands of binary operators, pipes, unary operators, field and array access, calls, ternaries and containers. Before the previous commit, @JSX lost its parens, e.g. @JSX x + 1, and @res.ternary was dropped. Signed-off-by: Christoph Knittel Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 3 +- .../expr/expected/markerAttributes.res.txt | 40 +++++++++++++++++++ .../data/printer/expr/markerAttributes.res | 40 +++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 tests/syntax_tests/data/printer/expr/expected/markerAttributes.res.txt create mode 100644 tests/syntax_tests/data/printer/expr/markerAttributes.res diff --git a/CHANGELOG.md b/CHANGELOG.md index 088e8338d6e..b0524761c0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ #### :bug: Bug fix +- Fix the formatter dropping a `@res.ternary` attribute written in source, e.g. `let b = @res.ternary x`, and print `@JSX` and `@res.ternary` with the same parens as other attributes, e.g. `(@JSX x) + 1`. https://github.com/rescript-lang/rescript/pull/8740 - Respect `@warning` attributes on the module in `module type of`, e.g. `module type of @warning("-3") DeprecatedModule`, which still reported the warning. https://github.com/rescript-lang/rescript/pull/8738 - 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 @@ -45,7 +46,7 @@ #### :house: Internal -- Remove the obsolete `res.ternary` and `JSX` attributes from the formatter's internal attribute filters and merge the filters into one. A `@JSX` attribute written in source is now formatted like any other attribute. https://github.com/rescript-lang/rescript/pull/8740 +- Remove the obsolete `res.ternary` and `JSX` attributes from the formatter's internal attribute filters and merge the filters into one. https://github.com/rescript-lang/rescript/pull/8740 - Represent `await` on module expressions with a dedicated `Pmod_await` parsetree node instead of a `res.await` attribute, which remains only in the PPX wire representation. https://github.com/rescript-lang/rescript/pull/8738 - Remove dead compiler code: warning 24, the `J.String_index` JS IR node, the `Pisint` and `Parraysets` Lambda primitives with their unused `%` primitive names (`%obj_is_int`, `%obj_dup`, `%obj_field`, `%array_safe_set`, `%null_to_opt`, `%nullable_to_opt`), the no-op `-pp` bsc flag and unread `Clflags` fields (`-unsafe` stays accepted as an explicit no-op), and the unused `unit` slot of `Pexp_coerce`. https://github.com/rescript-lang/rescript/pull/8731 - Represent inline record definitions with an explicit parsetree origin while retaining the existing PPX wire representation. https://github.com/rescript-lang/rescript/pull/8686 diff --git a/tests/syntax_tests/data/printer/expr/expected/markerAttributes.res.txt b/tests/syntax_tests/data/printer/expr/expected/markerAttributes.res.txt new file mode 100644 index 00000000000..999ae68d6e3 --- /dev/null +++ b/tests/syntax_tests/data/printer/expr/expected/markerAttributes.res.txt @@ -0,0 +1,40 @@ +// `@JSX` and `@res.ternary` written in source are ordinary attributes: the +// parser represents JSX and ternaries with their own nodes. They print, with +// the same parens as any other attribute, here `@foo`. + +let a = (@JSX x) + 1 +let a = 1 + (@JSX x) +let a = (@JSX x)->g +let a = x->(@JSX g) +let a = (@JSX x)->g(1) +let a = (@JSX f(x)) + 1 +let a = @JSX (a + b) * c +let a = (@JSX x) == y +let a = (@JSX x) && y +let a = !(@JSX x) +let a = (@JSX x).field +let a = @JSX x ? a : b + +let b = @res.ternary x +let b = (@res.ternary x) + 1 +let b = 1 + (@res.ternary x) +let b = (@res.ternary x)->g +let b = x->(@res.ternary g) +let b = (@res.ternary f(x)) + 1 +let b = @res.ternary (a + b) * c +let b = (@res.ternary (c ? a : b)) + 1 +let b = !(@res.ternary x) +let b = -(@res.ternary x) +let b = (@res.ternary x).field +let b = (@res.ternary x)[0] +let b = @res.ternary x ? a : b +let b = c ? @res.ternary x : b +let b = f(@res.ternary x) +let b = [@res.ternary x, y] +let b = {a: @res.ternary x} + +let c = (@foo x) + 1 +let c = (@foo x)->g +let c = x->(@foo g) +let c = @foo (a + b) * c +let c = !(@foo x) diff --git a/tests/syntax_tests/data/printer/expr/markerAttributes.res b/tests/syntax_tests/data/printer/expr/markerAttributes.res new file mode 100644 index 00000000000..daacfab7fbe --- /dev/null +++ b/tests/syntax_tests/data/printer/expr/markerAttributes.res @@ -0,0 +1,40 @@ +// `@JSX` and `@res.ternary` written in source are ordinary attributes: the +// parser represents JSX and ternaries with their own nodes. They print, with +// the same parens as any other attribute, here `@foo`. + +let a = (@JSX x) + 1 +let a = 1 + (@JSX x) +let a = (@JSX x)->g +let a = x->(@JSX g) +let a = (@JSX x)->g(1) +let a = (@JSX f(x)) + 1 +let a = (@JSX (a + b)) * c +let a = (@JSX x) == y +let a = (@JSX x) && y +let a = !(@JSX x) +let a = (@JSX x).field +let a = (@JSX x) ? a : b + +let b = @res.ternary x +let b = (@res.ternary x) + 1 +let b = 1 + (@res.ternary x) +let b = (@res.ternary x)->g +let b = x->(@res.ternary g) +let b = (@res.ternary f(x)) + 1 +let b = (@res.ternary (a + b)) * c +let b = (@res.ternary (c ? a : b)) + 1 +let b = !(@res.ternary x) +let b = -(@res.ternary x) +let b = (@res.ternary x).field +let b = (@res.ternary x)[0] +let b = (@res.ternary x) ? a : b +let b = c ? (@res.ternary x) : b +let b = f(@res.ternary x) +let b = [@res.ternary x, y] +let b = {a: @res.ternary x} + +let c = (@foo x) + 1 +let c = (@foo x)->g +let c = x->(@foo g) +let c = (@foo (a + b)) * c +let c = !(@foo x)