about summary refs log tree commit diff
path: root/compiler/rustc_parse/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-07-13 18:34:13 +0000
committerbors <bors@rust-lang.org>2025-07-13 18:34:13 +0000
commite9182f195b8505c87c4bd055b9f6e114ccda0981 (patch)
treeb570601233e295aa02c88e5392f60d17b59ec5db /compiler/rustc_parse/src
parent56835d7ac14da9f966e1ff39fd9ffd2e29b764d1 (diff)
parent3689b80b75bb400e740897ec83e64be332098c0d (diff)
downloadrust-e9182f195b8505c87c4bd055b9f6e114ccda0981.tar.gz
rust-e9182f195b8505c87c4bd055b9f6e114ccda0981.zip
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
Diffstat (limited to 'compiler/rustc_parse/src')
-rw-r--r--compiler/rustc_parse/src/parser/cfg_select.rs73
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs6
2 files changed, 78 insertions, 1 deletions
diff --git a/compiler/rustc_parse/src/parser/cfg_select.rs b/compiler/rustc_parse/src/parser/cfg_select.rs
new file mode 100644
index 00000000000..24a05afff4a
--- /dev/null
+++ b/compiler/rustc_parse/src/parser/cfg_select.rs
@@ -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)
+}
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 2787be46f33..90491e53249 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -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};