Conversation
`cfg_select!` parsing needs to be implemented in rustfmt right now because there's no good way to call `rustc_attr_parsing::parse_cfg_select`.
The plan is to leverage `rewrite_match_body` to help with `cfg_select!` formatting.
Since macro calls can use `{}`, `()`, or `[]` delimiters I'm making this
change to handle cases where users write:
```rust
cfg_select!();
cfg_select![];
```
|
Some changes occurred in src/tools/rustfmt cc @rust-lang/rustfmt |
|
r? @jieyouxu rustbot has assigned @jieyouxu. Use Why was this reviewer chosen?The reviewer was selected based on:
|
| // Original `()` delimiters | ||
| cfg_select!( // opening brace comment | ||
|
|
||
| ); | ||
| std::cfg_select!( // opening brace comment | ||
|
|
||
| ); | ||
| core::cfg_select!( // opening brace comment | ||
|
|
||
| ); | ||
|
|
||
| // Original `[]` delimiters | ||
| cfg_select![ // opening brace comment | ||
|
|
||
| ]; | ||
| std::cfg_select![ // opening brace comment | ||
|
|
||
| ]; | ||
| core::cfg_select![ // opening brace comment | ||
|
|
||
| ]; |
There was a problem hiding this comment.
The current implementation leaves empty cfg_select! calls written with () or [] delimiters unchanged so that comments aren't accidentally removed. That's why these examples aren't properly indented.
There was a problem hiding this comment.
Suggestion: could you leave this comment in the test too? (um, slightly funny for rustfmt tests because well, comments can be formatting-meaningful themselves)
| // trailing comments on the last line are a little buggy and always wrap back up | ||
| cfg_select! { | ||
| windows => { | ||
| "windows" | ||
| } | ||
| unix => { | ||
| "unix" | ||
| } | ||
| _ => { | ||
| "none" | ||
| } // FIXME. Prevent wrapping back up to the next line | ||
| } | ||
|
|
||
| cfg_select! { | ||
| windows => "windows", | ||
| unix => "unix", | ||
| _ => "none", // FIXME. Prevent wrapping back up to the next line | ||
| } |
There was a problem hiding this comment.
A pre-existing issue with trailing comments in rustfmt. I wanted to explicitly capture the current behavior in a test case.
| // Can't format cfg_select! at all with style_edition <= 2021. | ||
| // Things can be formatted with style_edition >= 2024 | ||
| cfg_select! { | ||
| feature = "debug-with-rustfmt-long-long-long-long-loooooooonnnnnnnnnnnnnnnggggggffffffffffffffff" => | ||
| { | ||
| // abc | ||
| println!(); | ||
| } | ||
| feature = "debug-with-rustfmt-long-long-long-long-loooooooonnnnnnnnnnnnnnnggggggffffffffffffffff" => | ||
| { | ||
| // abc | ||
| } | ||
| all(anything( | ||
| "some other long long long long long thing long long long long long long long long long long long", | ||
| feature = "debug-with-rustfmt-long-long-long-long-loooooooonnnnnnnnnnnnnnnggggggffffffffffffffff" | ||
| )) => { | ||
| let x = 7; | ||
| } | ||
| } |
There was a problem hiding this comment.
Through some testing I found that in style_edition <= 2021 this example won't be formatted at all because the predicate can't be formatted within max_width, but with style_edition >= 2024 that's not an issue.
| // comments within the predicate are fine with style_edition=2024+ | ||
| cfg_select! { | ||
| any( | ||
| true, /* comment */ | ||
| true, true, // true, | ||
| true, | ||
| ) => {} | ||
|
|
||
| not( | ||
| false // comment | ||
| ) => {} | ||
|
|
||
| any( | ||
| false // comment | ||
| ) => "any", | ||
| } |
There was a problem hiding this comment.
Not the best place for a comment to begin with IMO, but I wanted to call out that comments within the predicate aren't correctly handled in style_edition <= 2021. This is how the current example is formatted:
cfg_select! {
any(
true, /* comment */
true, true, // true,
true,
) => {}
not(false // comment) => {}
any(false // comment) => "any",
}Clearly that's wrong and produces invalid code, but fixing this seems like it's outisde the scope of this PR. since it only impacts style_edition <= 2021.
Maybe this is something we can address later?
There was a problem hiding this comment.
I agree, I think we shouldn't try to bundle that in this PR. We probably should open an issue in rustfmt/r-l/r to track this as a known-bug though.
|
(I'll gradually review this; need to build up some background ctx first.) |
There was a problem hiding this comment.
Thanks! The impl broadly looks good to me, just some minor nits and a few test coverage discussions.
@rustbot author
There was a problem hiding this comment.
Question: just to check my understanding,
cfg_select!parsing needs to be implemented in rustfmt right now
because there's no good way to callrustc_attr_parsing::parse_cfg_select.
I assume this is because rustfmt can't / don't want to (transitively) depend on things beyond parsing/AST? I.e. rustc_hir?
| @@ -0,0 +1,108 @@ | |||
| use rustc_ast::tokenstream::TokenStream; | |||
There was a problem hiding this comment.
Suggestion: add a backlink to the reference re. cfg_select!:
//! See <https://doc.rust-lang.org/nightly/reference/conditional-compilation.html#the-cfg_select-macro> for grammar.
| pub(crate) enum CfgSelectFormatPredicate { | ||
| Cfg(ast::MetaItemInner), | ||
| Wildcard(Span), | ||
| } |
There was a problem hiding this comment.
Suggestion: maybe leave a quick TL;DR example for this:
/// LHS predicate of a `cfg_select!` arm.
pub(crate) enum CfgSelectFormatPredicate {
/// Example: the `unix` in `unix => {}`. Notably, outer or inner attributes are not permitted.
Cfg(ast::MetaItemInner),
/// `_` in `_ => {}`.
Wildcard(Span),
}Re. outer/inner attributes, counter-example:
cfg_select! {
#[cfg(true)]
_ => {
fn main() {}
}
}| pub(crate) arrow: Token, | ||
| pub(crate) expr: Box<ast::Expr>, | ||
| pub(crate) trailing_comma: Option<Span>, | ||
| } |
There was a problem hiding this comment.
Suggestion: maybe some comments for "visual" purposes.
/// Each `$predicate => $production` arm in `cfg_select!`.
pub(crate) struct CfgSelectArm {
/// The `$predicate` part.
pub(crate) predicate: CfgSelectFormatPredicate,
/// Span of `=>`.
pub(crate) arrow: Token,
/// The RHS `$production` expression.
pub(crate) expr: Box<ast::Expr>,
/// `cfg_select!` arms `$production`s can be optionally `,` terminated, like `match` arms. The `,` is not needed when `$production` is itself braced `{}`.
pub(crate) trailing_comma: Option<Span>,
}| impl Spanned for CfgSelectArm { | ||
| fn span(&self) -> Span { | ||
| self.predicate | ||
| .span() | ||
| .with_hi(if let Some(comma) = self.trailing_comma { | ||
| comma.hi() | ||
| } else { | ||
| self.expr.span.hi() | ||
| }) | ||
| } | ||
| } |
There was a problem hiding this comment.
Question: could you double-check if the following cross-crate macro expansion case
// another_crate.rs
macro_rules! garbage {
() => {
fn main() {}
}
}
// main.rs
cfg_select! {
_ => {
garbage!();
}
}produces a span that can be used for formatting?
| // comments within the predicate are fine with style_edition=2024+ | ||
| cfg_select! { | ||
| any( | ||
| true, /* comment */ | ||
| true, true, // true, | ||
| true, | ||
| ) => {} | ||
|
|
||
| not( | ||
| false // comment | ||
| ) => {} | ||
|
|
||
| any( | ||
| false // comment | ||
| ) => "any", | ||
| } |
There was a problem hiding this comment.
I agree, I think we shouldn't try to bundle that in this PR. We probably should open an issue in rustfmt/r-l/r to track this as a known-bug though.
| // comments before and after the `=>` get dropped right now | ||
| cfg_select! { | ||
| any(true, true, true, true,) => {} | ||
|
|
||
| not(false) => {} | ||
|
|
||
| any(false) => "any", | ||
| } |
There was a problem hiding this comment.
Question: this should also a "known bug" right? Should we also include a follow-up issue to track this?
| // Original `()` delimiters | ||
| cfg_select!( // opening brace comment | ||
|
|
||
| ); | ||
| std::cfg_select!( // opening brace comment | ||
|
|
||
| ); | ||
| core::cfg_select!( // opening brace comment | ||
|
|
||
| ); | ||
|
|
||
| // Original `[]` delimiters | ||
| cfg_select![ // opening brace comment | ||
|
|
||
| ]; | ||
| std::cfg_select![ // opening brace comment | ||
|
|
||
| ]; | ||
| core::cfg_select![ // opening brace comment | ||
|
|
||
| ]; |
There was a problem hiding this comment.
Suggestion: could you leave this comment in the test too? (um, slightly funny for rustfmt tests because well, comments can be formatting-meaningful themselves)
There was a problem hiding this comment.
Suggestion: there's two cases of additional test coverage that we may want to add:
- Max-width and when to prefer
,versus{}.
This was originally discussed in cfg_select: Support unbraced expressions #145233 where it was decided that we also permit unbraced RHS productions when the RHS is an expression, that<expr>,versus{...}is picked based on max width IIUC. Should we add a test cases for non-default max-width versus default max-width and its influence on whether,or{}is picked? - Nested
cfg_select!. For better or worse, you can also writecfg_select! { _ => cfg_select! [ _ => { // I don't know why you would write this, // but you can, so... 🤷 } ] }
| (cfg_select! { | ||
| unix => Some(n), | ||
| _ => None, | ||
| }) => true, |
There was a problem hiding this comment.
Suggestion: should we add some coverage for how certain inline comments are handled?
match x {
(/* 1 */ cfg_select! /* 2 */ {
unix => Some(n),
_ => None,
} /* 3 */) => true,
}I'm less fussed about them, since I know certain inline comments gets dropped (long-standing known issues). But might still be good to have coverage to detect changes in case a fix is attempted.
|
Reminder, once the PR becomes ready for a review, use |
There was a problem hiding this comment.
Backlinks / context for myself (and future travellers):
Metadata
- Library feature:
cfg_select - std docs: https://doc.rust-lang.org/nightly/std/macro.cfg_select.html
- Tracking issue: Tracking issue for
cfg_select(formerlycfg_match) #115585 - Style team discussion: Formatting for cfg_select style-team#201
- Reference update PR:
cfg_select!macro reference#2103
History
Initial style team discussions
- The arms must be wrapped in braces, so
rustfmtwill have to ensure to not remove those.
Follow-up: unbraced expressions are permitted
PR: #145233
View all comments
tracking issue: #115585
Implementing
cfg_select!formatting here inrust-lang/rustso that the feature can get out to nightly quicker.The previous PR (#144323) is a bit old at this point and I felt like I could simplify the implementation so I've opted to reimplement the formatting instead of building off the previous PR.
I've tried to break the PR up into logical commits and I think this would be best reviewed one commit at a time.
Previous PR:
cfg_select#144323See also: