Skip to content

fix: prevent rustfmt from removing necessary blocks in cfg_select! arms - #7183

Open
ytmimi wants to merge 2 commits into
rust-lang:mainfrom
ytmimi:issue_7045
Open

ytmimi wants to merge 2 commits into
rust-lang:mainfrom
ytmimi:issue_7045

Conversation

@ytmimi

@ytmimi ytmimi commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #7045

  • I did not use an LLM to create a change in this PR.
  • I used an LLM to create a change in this PR, and I have explained below how it was used.

@rustbot rustbot added the S-waiting-on-review Status: awaiting review from the assignee but also interested parties. label Oct 14, 2026

@jieyouxu jieyouxu left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Unfortunately as we know from

the "how many braces" problem is quite quirky. The heuristics here is not sufficiently precise (i.e. currently we would try to format more than we actually can).

Disclaimer: I reviewed this in a first pass, and then co-reviewed it again with an LLM, which came up with some adversarial cases I didn't initially consider (that caught me off guard as well). I double-checked the cases/reasoning. All communications are hand-written.

View changes since this review

Comment thread tests/source/issue_7045.rs Outdated
Comment thread src/parse/macros/cfg_select.rs
Comment thread src/parse/macros/cfg_select.rs
Comment thread src/macros.rs
Comment thread tests/source/issue_7045.rs
Comment thread tests/source/issue_7045.rs
Comment thread src/macros.rs Outdated
Comment thread tests/source/issue_7045.rs
@ytmimi

ytmimi commented Oct 15, 2026

Copy link
Copy Markdown
Contributor Author

the "how many braces" problem is quite quirky. The heuristics here is not sufficiently precise (i.e. currently we would try to format more than we actually can).

Could you elaborate with a concrete example? I'm not sure that I follow.

@jieyouxu

jieyouxu commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

Could you elaborate with a concrete example? I'm not sure that I follow.

Sorry, to clarify, I specifically mean

(I know that this PR is only about not removing when we can't, not adding.)

@jieyouxu jieyouxu added X-no-formatting-impact Expected formatting impact: none or not applicable A-cfg_select `cfg_select!` X-impacts-nightly-gated Expected formatting impact: only nightly gated formatting and removed X-no-formatting-impact Expected formatting impact: none or not applicable labels Oct 15, 2026
Comment thread src/macros.rs
@ytmimi ytmimi added S-waiting-on-author Status: awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: awaiting review from the assignee but also interested parties. labels Oct 16, 2026
@rustbot

This comment has been minimized.

Comment thread src/parse/macros/cfg_select.rs Outdated
Comment thread src/parse/macros/cfg_select.rs
Comment thread src/parse/macros/cfg_select.rs
Comment thread src/macros.rs

@jieyouxu jieyouxu Oct 16, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add snapshot test cases for

cfg_select! {
    _ => { // l1
        { x } // l2
    }
}

and

cfg_select! {
    _ => /* comment */ {{ x }}
}

I don't think this is blocking though (snapshot moreso to capture if that gets fixed in a follow-up).

I think we might be able to check the spans between

=> {   {  inner_stmts }   }
arrow_span
   outer_lbrace_span
       inner_lbrace_span
          inner_stmts[0].span
          ...
                      inner_rbrace_span
                           outer_rbrace_span

i.e. if there are comments in any of the following intervals

  • arrow_span.between(outer_lbrace_span)
  • outer_lbrace_span.between(inner_lbrace_span)
  • inner_rbrace_span.between(outer_rbrace_span)
  • outer_rbrace_span to end of body (?)

we don't take the "happy" {{}} path. EDIT: maybe only between outer bracee and inner braces?

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked unexpectedly removed comments as follow-up in #7191.

Comment thread src/macros.rs
Comment on lines +1641 to +1645
let trailing_comma = if arm.trailing_comma.is_some() {
","
} else {
""
};

@jieyouxu jieyouxu Oct 16, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should/does this need to account for match_block_trailing_comma? Since we add this unconditionally for this "happy" case but conditionally wrt. match_block_trailing_comma for the None branch via rewrite_match_body?

rustfmt/src/matches.rs

Lines 476 to 480 in 1738c93

let comma = if context.config.match_block_trailing_comma() {
","
} else {
""
};

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ Only if you want to address this in this PR. I don't want to block on this, fine as a follow-up as well.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked as follow-up #7191.

Comment thread src/parse/macros/cfg_select.rs
Comment thread tests/target/cfg_select_edge_cases.rs Outdated
Comment thread tests/source/cfg_select_edge_cases.rs Outdated
@jieyouxu

Copy link
Copy Markdown
Member

(Sorry, I know this is a lot of back-and-forth, didn't notice some things in earlier review passes)

@ytmimi

ytmimi commented Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

(Sorry, I know this is a lot of back-and-forth, didn't notice some things in earlier review passes)

@jieyouxu I genuinely appreciate it. You're helping me catch a lot of things I missed too. I felt a real sense of urgency to get this fixed after learning that this was impacting miri, and I definitely jumped in without considering a lot of these edge cases.

@jieyouxu

jieyouxu commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

Yeah no worries, it's very subtle and I would've made exactly the same mistakes... There's just, edge cases for edge cases.

@rustbot

rustbot commented Oct 16, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@ytmimi

ytmimi commented Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

Okay, added more edge case tests based on the discussion here and a few other things I wanted to check.

…nner

Also adds some extra test cases to make sure that rustfmt isn't adding /
removing braces that would change the code semantics.
@jieyouxu jieyouxu added S-waiting-on-review Status: awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: awaiting some action (such as code changes or more information) from the author. labels Oct 17, 2026

@jieyouxu jieyouxu left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final round of reviews I promise

View changes since this review

Comment on lines +72 to +83
fn cfg_select_comments_that_might_get_lost() {
cfg_select! {
_ => {{ x }}
}
cfg_select! {
_ => {{ x }}
}
cfg_select! {
_ => {{ x }}
}

cfg_select! {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is unfortunate but reasonable to address this in a follow-up.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked as #7191.

Comment thread src/macros.rs
Comment thread src/macros.rs
Comment on lines +1641 to +1645
let trailing_comma = if arm.trailing_comma.is_some() {
","
} else {
""
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ Only if you want to address this in this PR. I don't want to block on this, fine as a follow-up as well.

Comment on lines +291 to +294
_ => {
(///
{})
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, we actually add a brace here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked as #7191.

Comment on lines +50 to +59
fn fails() {
let x = cfg_select! {
// rustfmt will add braces here
_ =>
{
#[cfg(true)]
1
}
};
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one seems quite surprising, but still I'm fine as a follow-up since it only affects unstable language w/ stmt_expr_attributes. The brace adding here is a bit funny, because

#![feature(stmt_expr_attributes)]
fn main() {
    let x = cfg_select! {
        // rustfmt will add braces here
        _ => #[cfg(true)] 1
    };
}

is invalid syntax, but

#![feature(stmt_expr_attributes)]
fn main() {
    let x = cfg_select! {
        // rustfmt will add braces here
        _ => { #[cfg(true)] 1 }
    };
}

now compiles 😁 So this is a invalid syntax => compile pass. Program repair gaming.

Considering this is fairly niche it's probably best as a follow-up.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked as #7191.

Comment thread src/macros.rs
Comment on lines +1646 to +1648
let body = expr
.rewrite_result(context, nested_shape)
.map(|rw| format!("{{{rw}}}"))?;

@jieyouxu jieyouxu Oct 17, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we forgot to reserve the RHS } and trailing , here. For https://github.com/rust-lang/rustfmt/pull/7183/changes#r4291168609 and this, I think the nested_shape can be something to the effect of

let nested_shape = nested_shape.
    .offset_left(last_line_width(&predicate_str) + " => {".len(), arm.span())
    .sub_width("}".len() + trailing_comma.len(), arm.span())?;

Re. the predicate_str.len(), that doesn't yet use last_line_width so something like

let a = cfg_select! {
    all(target_os = "linux", target_arch = "x86_64", target_env = "gnu", feature = "hello", feature = "world") => {{ x }},
};

will give up.

Some cases we may want to add:

  • Source
fn with_trailing_comma_exceeds_max_width() {
    cfg_select! {
        // max_width = 100 ------------------------------------------------------------------------|
        _ => {{ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }},
    }
}

fn with_trailing_comma_exceeds_max_width() {
    cfg_select! {
        // max_width = 100 ------------------------------------------------------------------------|
        _ => {{ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }},
    }
}

fn without_trailing_comma_fits_max_width() {
    cfg_select! {
        // max_width = 100 ------------------------------------------------------------------------|
        _ => {{ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }}
    }
}

fn without_trailing_comma_exceeds_max_width() {
    cfg_select! {
        // max_width = 100 ------------------------------------------------------------------------|
        _ => {{ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }}
    }
}
  • Which should probably format into, target
fn with_trailing_comma_fits_max_width() {
    cfg_select! {
        // max_width = 100 ------------------------------------------------------------------------|
        _ => {{ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }},
    }
}

fn with_trailing_comma_exceeds_max_width() {
    cfg_select! {
        // max_width = 100 ------------------------------------------------------------------------|
        _ => {{
            xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        }},
    }
}

fn without_trailing_comma_fits_max_width() {
    cfg_select! {
        // max_width = 100 ------------------------------------------------------------------------|
        _ => {{ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }}
    }
}

fn without_trailing_comma_exceeds_max_width() {
    cfg_select! {
        // max_width = 100 ------------------------------------------------------------------------|
        _ => {{
            xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        }}
    }
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit pedantic so I'm also okay to defer this as follow-up.

@ytmimi

ytmimi commented Oct 17, 2026

Copy link
Copy Markdown
Contributor Author

@jieyouxu I'll address the wrapping behavior / last_line_width change tomorrow and add some more test cases around that. I appreciate the additional suggestions for those test cases 🙏🏼

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-cfg_select `cfg_select!` S-waiting-on-review Status: awaiting review from the assignee but also interested parties. X-impacts-nightly-gated Expected formatting impact: only nightly gated formatting

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cfg_select! in expr position broken after formatting

3 participants