From 2d547270335ca1f5c1c63859ea612f1ddfa18e6d Mon Sep 17 00:00:00 2001 From: devangpratap <115096812+devangpratap@users.noreply.github.com> Date: Wed, 26 Aug 2026 13:10:21 -0400 Subject: [PATCH] Shrink the const block shape by the width of the `const ` keyword fixes rust-lang/rustfmt#7055 `format_expr`'s `ConstBlock` arm rewrote the block at the full shape and then prepended `const `, so the block was laid out as if it had six more columns than it really did. An `unsafe` block is unaffected because its prefix is built inside `rewrite_block_inner`, where `rewrite_single_line_block` subtracts the prefix width from the shape. The visible symptom is that a `const` block used as the receiver of a method call is silently left unformatted: the over-wide rewrite is rejected by the chain, so the whole expression falls back to the original snippet with no diff and exit code 0. Gated behind style_edition=2027 because outside a chain the over-wide rewrite was accepted and emitted, so it is stable formatting. --- src/expr.rs | 8 ++ tests/source/issue_7055_narrow_max_width.rs | 21 ++++++ tests/source/issue_7055_style_edition_2021.rs | 74 +++++++++++++++++++ tests/source/issue_7055_style_edition_2024.rs | 74 +++++++++++++++++++ tests/source/issue_7055_style_edition_2027.rs | 74 +++++++++++++++++++ tests/target/issue_7055_narrow_max_width.rs | 23 ++++++ tests/target/issue_7055_style_edition_2021.rs | 70 ++++++++++++++++++ tests/target/issue_7055_style_edition_2024.rs | 70 ++++++++++++++++++ tests/target/issue_7055_style_edition_2027.rs | 70 ++++++++++++++++++ 9 files changed, 484 insertions(+) create mode 100644 tests/source/issue_7055_narrow_max_width.rs create mode 100644 tests/source/issue_7055_style_edition_2021.rs create mode 100644 tests/source/issue_7055_style_edition_2024.rs create mode 100644 tests/source/issue_7055_style_edition_2027.rs create mode 100644 tests/target/issue_7055_narrow_max_width.rs create mode 100644 tests/target/issue_7055_style_edition_2021.rs create mode 100644 tests/target/issue_7055_style_edition_2024.rs create mode 100644 tests/target/issue_7055_style_edition_2027.rs diff --git a/src/expr.rs b/src/expr.rs index 0499e2fcac4..2041722688f 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -180,6 +180,14 @@ pub(crate) fn format_expr( .unknown_error() .and_then(|control_flow| control_flow.rewrite_result(context, shape)), ast::ExprKind::ConstBlock(ref anon_const) => { + // 6 = "const ". The keyword sits on the first line of the rewrite, so the + // block has that many fewer columns to work with. Gated because widening + // the block by six columns is stable formatting on earlier style editions. + let shape = if context.config.style_edition() >= StyleEdition::Edition2027 { + shape.offset_left_opt(6).unwrap_or(shape) + } else { + shape + }; let rewrite = match anon_const.value.kind { ast::ExprKind::Block(ref block, opt_label) => { // Inner attributes are associated with the `ast::ExprKind::ConstBlock` node, diff --git a/tests/source/issue_7055_narrow_max_width.rs b/tests/source/issue_7055_narrow_max_width.rs new file mode 100644 index 00000000000..8d168a43b20 --- /dev/null +++ b/tests/source/issue_7055_narrow_max_width.rs @@ -0,0 +1,21 @@ +// rustfmt-style_edition: 2027 +// rustfmt-max_width: 30 +// rustfmt-error_on_line_overflow: false + +// Fewer than six columns of +// budget left: still format +// it, do not bail out and +// emit it verbatim. +fn deep() { + if a { + if b { + if c { + if d { + if e { + let q = const { 1 + 2 }; + } + } + } + } + } +} diff --git a/tests/source/issue_7055_style_edition_2021.rs b/tests/source/issue_7055_style_edition_2021.rs new file mode 100644 index 00000000000..60cf284fc33 --- /dev/null +++ b/tests/source/issue_7055_style_edition_2021.rs @@ -0,0 +1,74 @@ +// rustfmt-style_edition: 2021 +// rustfmt-max_width: 100 +// rustfmt-error_on_line_overflow: false + +struct S; + +impl S { + const fn new(_: &str) -> Self { + S + } + + fn go(&self) {} +} + +// The reported symptom. As the receiver of a method call the block was rewritten at +// the full width, the `const ` prefix pushed the result past `max_width`, the chain +// rejected it, and the whole expression was silently left unformatted. +fn receiver_74() { + const { + S::new( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + ) + } + .go(); +} + +fn receiver_77() { + const { + S::new( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + ) + } + .go(); +} + +fn receiver_80() { + const { + S::new( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + ) + } + .go(); +} + +// Not a method receiver, so the over-wide rewrite was accepted and emitted. This is +// the stable formatting the gate protects: on style editions below 2027 these keep +// producing a line past `max_width`. +fn statement_74() { + const { S::new( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) } ; +} + +fn statement_77() { + const { S::new( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) } ; +} + +fn statement_80() { + const { S::new( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) } ; +} + +// The `const ` budget applies to a nested block too. +fn nested() { + let _ = const { + const { S::new( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) } + }; +} + +// Inner attributes belong to the `ConstBlock` node, not the `Block`, so they take the +// `rewrite_block` path directly. Guard that the shape change leaves them alone. +fn inner_attrs() { + let _ = const { + #![allow(unused)] + S::new( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) + }; +} diff --git a/tests/source/issue_7055_style_edition_2024.rs b/tests/source/issue_7055_style_edition_2024.rs new file mode 100644 index 00000000000..876df636155 --- /dev/null +++ b/tests/source/issue_7055_style_edition_2024.rs @@ -0,0 +1,74 @@ +// rustfmt-style_edition: 2024 +// rustfmt-max_width: 100 +// rustfmt-error_on_line_overflow: false + +struct S; + +impl S { + const fn new(_: &str) -> Self { + S + } + + fn go(&self) {} +} + +// The reported symptom. As the receiver of a method call the block was rewritten at +// the full width, the `const ` prefix pushed the result past `max_width`, the chain +// rejected it, and the whole expression was silently left unformatted. +fn receiver_74() { + const { + S::new( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + ) + } + .go(); +} + +fn receiver_77() { + const { + S::new( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + ) + } + .go(); +} + +fn receiver_80() { + const { + S::new( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + ) + } + .go(); +} + +// Not a method receiver, so the over-wide rewrite was accepted and emitted. This is +// the stable formatting the gate protects: on style editions below 2027 these keep +// producing a line past `max_width`. +fn statement_74() { + const { S::new( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) } ; +} + +fn statement_77() { + const { S::new( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) } ; +} + +fn statement_80() { + const { S::new( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) } ; +} + +// The `const ` budget applies to a nested block too. +fn nested() { + let _ = const { + const { S::new( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) } + }; +} + +// Inner attributes belong to the `ConstBlock` node, not the `Block`, so they take the +// `rewrite_block` path directly. Guard that the shape change leaves them alone. +fn inner_attrs() { + let _ = const { + #![allow(unused)] + S::new( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) + }; +} diff --git a/tests/source/issue_7055_style_edition_2027.rs b/tests/source/issue_7055_style_edition_2027.rs new file mode 100644 index 00000000000..969daa60846 --- /dev/null +++ b/tests/source/issue_7055_style_edition_2027.rs @@ -0,0 +1,74 @@ +// rustfmt-style_edition: 2027 +// rustfmt-max_width: 100 +// rustfmt-error_on_line_overflow: false + +struct S; + +impl S { + const fn new(_: &str) -> Self { + S + } + + fn go(&self) {} +} + +// The reported symptom. As the receiver of a method call the block was rewritten at +// the full width, the `const ` prefix pushed the result past `max_width`, the chain +// rejected it, and the whole expression was silently left unformatted. +fn receiver_74() { + const { + S::new( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + ) + } + .go(); +} + +fn receiver_77() { + const { + S::new( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + ) + } + .go(); +} + +fn receiver_80() { + const { + S::new( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + ) + } + .go(); +} + +// Not a method receiver, so the over-wide rewrite was accepted and emitted. This is +// the stable formatting the gate protects: on style editions below 2027 these keep +// producing a line past `max_width`. +fn statement_74() { + const { S::new( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) } ; +} + +fn statement_77() { + const { S::new( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) } ; +} + +fn statement_80() { + const { S::new( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) } ; +} + +// The `const ` budget applies to a nested block too. +fn nested() { + let _ = const { + const { S::new( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) } + }; +} + +// Inner attributes belong to the `ConstBlock` node, not the `Block`, so they take the +// `rewrite_block` path directly. Guard that the shape change leaves them alone. +fn inner_attrs() { + let _ = const { + #![allow(unused)] + S::new( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) + }; +} diff --git a/tests/target/issue_7055_narrow_max_width.rs b/tests/target/issue_7055_narrow_max_width.rs new file mode 100644 index 00000000000..2e0cf163a5a --- /dev/null +++ b/tests/target/issue_7055_narrow_max_width.rs @@ -0,0 +1,23 @@ +// rustfmt-style_edition: 2027 +// rustfmt-max_width: 30 +// rustfmt-error_on_line_overflow: false + +// Fewer than six columns of +// budget left: still format +// it, do not bail out and +// emit it verbatim. +fn deep() { + if a { + if b { + if c { + if d { + if e { + let q = const { + 1 + 2 + }; + } + } + } + } + } +} diff --git a/tests/target/issue_7055_style_edition_2021.rs b/tests/target/issue_7055_style_edition_2021.rs new file mode 100644 index 00000000000..c5bfe097ae8 --- /dev/null +++ b/tests/target/issue_7055_style_edition_2021.rs @@ -0,0 +1,70 @@ +// rustfmt-style_edition: 2021 +// rustfmt-max_width: 100 +// rustfmt-error_on_line_overflow: false + +struct S; + +impl S { + const fn new(_: &str) -> Self { + S + } + + fn go(&self) {} +} + +// The reported symptom. As the receiver of a method call the block was rewritten at +// the full width, the `const ` prefix pushed the result past `max_width`, the chain +// rejected it, and the whole expression was silently left unformatted. +fn receiver_74() { + const { S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") } + .go(); +} + +fn receiver_77() { + const { + S::new( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + ) + } + .go(); +} + +fn receiver_80() { + const { + S::new( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + ) + } + .go(); +} + +// Not a method receiver, so the over-wide rewrite was accepted and emitted. This is +// the stable formatting the gate protects: on style editions below 2027 these keep +// producing a line past `max_width`. +fn statement_74() { + const { S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") }; +} + +fn statement_77() { + const { S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") }; +} + +fn statement_80() { + const { S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") }; +} + +// The `const ` budget applies to a nested block too. +fn nested() { + let _ = const { + const { S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") } + }; +} + +// Inner attributes belong to the `ConstBlock` node, not the `Block`, so they take the +// `rewrite_block` path directly. Guard that the shape change leaves them alone. +fn inner_attrs() { + let _ = const { + #![allow(unused)] + S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + }; +} diff --git a/tests/target/issue_7055_style_edition_2024.rs b/tests/target/issue_7055_style_edition_2024.rs new file mode 100644 index 00000000000..92e52604371 --- /dev/null +++ b/tests/target/issue_7055_style_edition_2024.rs @@ -0,0 +1,70 @@ +// rustfmt-style_edition: 2024 +// rustfmt-max_width: 100 +// rustfmt-error_on_line_overflow: false + +struct S; + +impl S { + const fn new(_: &str) -> Self { + S + } + + fn go(&self) {} +} + +// The reported symptom. As the receiver of a method call the block was rewritten at +// the full width, the `const ` prefix pushed the result past `max_width`, the chain +// rejected it, and the whole expression was silently left unformatted. +fn receiver_74() { + const { S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") } + .go(); +} + +fn receiver_77() { + const { + S::new( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + ) + } + .go(); +} + +fn receiver_80() { + const { + S::new( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + ) + } + .go(); +} + +// Not a method receiver, so the over-wide rewrite was accepted and emitted. This is +// the stable formatting the gate protects: on style editions below 2027 these keep +// producing a line past `max_width`. +fn statement_74() { + const { S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") }; +} + +fn statement_77() { + const { S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") }; +} + +fn statement_80() { + const { S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") }; +} + +// The `const ` budget applies to a nested block too. +fn nested() { + let _ = const { + const { S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") } + }; +} + +// Inner attributes belong to the `ConstBlock` node, not the `Block`, so they take the +// `rewrite_block` path directly. Guard that the shape change leaves them alone. +fn inner_attrs() { + let _ = const { + #![allow(unused)] + S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + }; +} diff --git a/tests/target/issue_7055_style_edition_2027.rs b/tests/target/issue_7055_style_edition_2027.rs new file mode 100644 index 00000000000..0142dd50e56 --- /dev/null +++ b/tests/target/issue_7055_style_edition_2027.rs @@ -0,0 +1,70 @@ +// rustfmt-style_edition: 2027 +// rustfmt-max_width: 100 +// rustfmt-error_on_line_overflow: false + +struct S; + +impl S { + const fn new(_: &str) -> Self { + S + } + + fn go(&self) {} +} + +// The reported symptom. As the receiver of a method call the block was rewritten at +// the full width, the `const ` prefix pushed the result past `max_width`, the chain +// rejected it, and the whole expression was silently left unformatted. +fn receiver_74() { + const { S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") } + .go(); +} + +fn receiver_77() { + const { + S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + } + .go(); +} + +fn receiver_80() { + const { + S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + } + .go(); +} + +// Not a method receiver, so the over-wide rewrite was accepted and emitted. This is +// the stable formatting the gate protects: on style editions below 2027 these keep +// producing a line past `max_width`. +fn statement_74() { + const { S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") }; +} + +fn statement_77() { + const { + S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + }; +} + +fn statement_80() { + const { + S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + }; +} + +// The `const ` budget applies to a nested block too. +fn nested() { + let _ = const { + const { S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") } + }; +} + +// Inner attributes belong to the `ConstBlock` node, not the `Block`, so they take the +// `rewrite_block` path directly. Guard that the shape change leaves them alone. +fn inner_attrs() { + let _ = const { + #![allow(unused)] + S::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + }; +}