Auto merge of #143461 - folkertdev:cfg-select-builtin-macro, r=petrochenkov

make `cfg_select` a builtin macro

tracking issue: https://github.com/rust-lang/rust/issues/115585

This parses mostly the same as the `macro cfg_select` version, except:

1. wrapping in double brackets is no longer supported (or needed): `cfg_select {{ /* ... */ }}` is now rejected.
2. in an expression context, the rhs is no longer wrapped in a block, so that this now works:
  ```rust
  fn main() {
      println!(cfg_select! {
          unix => { "foo" }
          _ => { "bar" }
      });
  }
  ```
3. a single wildcard rule is now supported: `cfg_select { _ => 1 }` now works

I've also added an error if none of the rules evaluate to true, and warnings for any arms that follow the `_` wildcard rule.

cc `@traviscross` if I'm missing any feature that should/should not be included
r? `@petrochenkov` for the macro logic details
This commit is contained in:
bors
2025-07-13 18:34:13 +00:00
14 changed files with 299 additions and 52 deletions

View File

@@ -0,0 +1,73 @@
use rustc_ast::token::Token;
use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_ast::{MetaItemInner, token};
use rustc_errors::PResult;
use rustc_span::Span;
use crate::exp;
use crate::parser::Parser;
pub enum CfgSelectRule {
Cfg(MetaItemInner),
Wildcard(Token),
}
#[derive(Default)]
pub struct CfgSelectBranches {
/// All the conditional branches.
pub reachable: Vec<(MetaItemInner, TokenStream, Span)>,
/// The first wildcard `_ => { ... }` branch.
pub wildcard: Option<(Token, TokenStream, Span)>,
/// All branches after the first wildcard, including further wildcards.
/// These branches are kept for formatting.
pub unreachable: Vec<(CfgSelectRule, TokenStream, Span)>,
}
/// Parses a `TokenTree` that must be of the form `{ /* ... */ }`, and returns a `TokenStream` where
/// the surrounding braces are stripped.
fn parse_token_tree<'a>(p: &mut Parser<'a>) -> PResult<'a, TokenStream> {
// Generate an error if the `=>` is not followed by `{`.
if p.token != token::OpenBrace {
p.expect(exp!(OpenBrace))?;
}
// Strip the outer '{' and '}'.
match p.parse_token_tree() {
TokenTree::Token(..) => unreachable!("because of the expect above"),
TokenTree::Delimited(.., tts) => Ok(tts),
}
}
pub fn parse_cfg_select<'a>(p: &mut Parser<'a>) -> PResult<'a, CfgSelectBranches> {
let mut branches = CfgSelectBranches::default();
while p.token != token::Eof {
if p.eat_keyword(exp!(Underscore)) {
let underscore = p.prev_token;
p.expect(exp!(FatArrow))?;
let tts = parse_token_tree(p)?;
let span = underscore.span.to(p.token.span);
match branches.wildcard {
None => branches.wildcard = Some((underscore, tts, span)),
Some(_) => {
branches.unreachable.push((CfgSelectRule::Wildcard(underscore), tts, span))
}
}
} else {
let meta_item = p.parse_meta_item_inner()?;
p.expect(exp!(FatArrow))?;
let tts = parse_token_tree(p)?;
let span = meta_item.span().to(p.token.span);
match branches.wildcard {
None => branches.reachable.push((meta_item, tts, span)),
Some(_) => branches.unreachable.push((CfgSelectRule::Cfg(meta_item), tts, span)),
}
}
}
Ok(branches)
}

View File

@@ -1,4 +1,3 @@
pub mod asm;
pub mod attr;
mod attr_wrapper;
mod diagnostics;
@@ -12,6 +11,11 @@ mod stmt;
pub mod token_type;
mod ty;
// Parsers for non-functionlike builtin macros are defined in rustc_parse so they can be used by
// both rustc_builtin_macros and rustfmt.
pub mod asm;
pub mod cfg_select;
use std::assert_matches::debug_assert_matches;
use std::{fmt, mem, slice};