about summary refs log tree commit diff
path: root/compiler/rustc_builtin_macros
diff options
context:
space:
mode:
authorFolkert de Vries <folkert@folkertdev.nl>2025-07-04 23:56:16 +0200
committerFolkert de Vries <folkert@folkertdev.nl>2025-07-13 14:34:40 +0200
commit3689b80b75bb400e740897ec83e64be332098c0d (patch)
treedb23b5f9d95c0f4613ae1599111c0c7c595be85a /compiler/rustc_builtin_macros
parent1b0bc594a75dfc1cdedc6c17052cf44de101e632 (diff)
downloadrust-3689b80b75bb400e740897ec83e64be332098c0d.tar.gz
rust-3689b80b75bb400e740897ec83e64be332098c0d.zip
make `cfg_select` a builtin macro
Diffstat (limited to 'compiler/rustc_builtin_macros')
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl6
-rw-r--r--compiler/rustc_builtin_macros/src/cfg_select.rs63
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs18
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs2
4 files changed, 89 insertions, 0 deletions
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index 3594c7ec210..183927edb02 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -81,6 +81,12 @@ builtin_macros_cfg_accessible_literal_path = `cfg_accessible` path cannot be a l
 builtin_macros_cfg_accessible_multiple_paths = multiple `cfg_accessible` paths are specified
 builtin_macros_cfg_accessible_unspecified_path = `cfg_accessible` path is not specified
 
+builtin_macros_cfg_select_no_matches = none of the rules in this `cfg_select` evaluated to true
+
+builtin_macros_cfg_select_unreachable = unreachable rule
+    .label = always matches
+    .label2 = this rules is never reached
+
 builtin_macros_coerce_pointee_requires_maybe_sized = `derive(CoercePointee)` requires `{$name}` to be marked `?Sized`
 
 builtin_macros_coerce_pointee_requires_one_field = `CoercePointee` can only be derived on `struct`s with at least one field
diff --git a/compiler/rustc_builtin_macros/src/cfg_select.rs b/compiler/rustc_builtin_macros/src/cfg_select.rs
new file mode 100644
index 00000000000..2dc387d5866
--- /dev/null
+++ b/compiler/rustc_builtin_macros/src/cfg_select.rs
@@ -0,0 +1,63 @@
+use rustc_ast::tokenstream::TokenStream;
+use rustc_attr_parsing as attr;
+use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
+use rustc_parse::parser::cfg_select::{CfgSelectBranches, CfgSelectRule, parse_cfg_select};
+use rustc_span::{Ident, Span, sym};
+
+use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable};
+
+/// Selects the first arm whose rule evaluates to true.
+fn select_arm(ecx: &ExtCtxt<'_>, branches: CfgSelectBranches) -> Option<(TokenStream, Span)> {
+    for (cfg, tt, arm_span) in branches.reachable {
+        if attr::cfg_matches(
+            &cfg,
+            &ecx.sess,
+            ecx.current_expansion.lint_node_id,
+            Some(ecx.ecfg.features),
+        ) {
+            return Some((tt, arm_span));
+        }
+    }
+
+    branches.wildcard.map(|(_, tt, span)| (tt, span))
+}
+
+pub(super) fn expand_cfg_select<'cx>(
+    ecx: &'cx mut ExtCtxt<'_>,
+    sp: Span,
+    tts: TokenStream,
+) -> MacroExpanderResult<'cx> {
+    ExpandResult::Ready(match parse_cfg_select(&mut ecx.new_parser_from_tts(tts)) {
+        Ok(branches) => {
+            if let Some((underscore, _, _)) = branches.wildcard {
+                // Warn for every unreachable rule. We store the fully parsed branch for rustfmt.
+                for (rule, _, _) in &branches.unreachable {
+                    let span = match rule {
+                        CfgSelectRule::Wildcard(underscore) => underscore.span,
+                        CfgSelectRule::Cfg(cfg) => cfg.span(),
+                    };
+                    let err = CfgSelectUnreachable { span, wildcard_span: underscore.span };
+                    ecx.dcx().emit_warn(err);
+                }
+            }
+
+            if let Some((tts, arm_span)) = select_arm(ecx, branches) {
+                return ExpandResult::from_tts(
+                    ecx,
+                    tts,
+                    sp,
+                    arm_span,
+                    Ident::with_dummy_span(sym::cfg_select),
+                );
+            } else {
+                // Emit a compiler error when none of the rules matched.
+                let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp });
+                DummyResult::any(sp, guar)
+            }
+        }
+        Err(err) => {
+            let guar = err.emit();
+            DummyResult::any(sp, guar)
+        }
+    })
+}
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index a5ee7349fc6..6bcf4d3e0a2 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -954,3 +954,21 @@ pub(crate) struct AsmExpectedOther {
     pub(crate) span: Span,
     pub(crate) is_inline_asm: bool,
 }
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_cfg_select_no_matches)]
+pub(crate) struct CfgSelectNoMatches {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_cfg_select_unreachable)]
+pub(crate) struct CfgSelectUnreachable {
+    #[primary_span]
+    #[label(builtin_macros_label2)]
+    pub span: Span,
+
+    #[label]
+    pub wildcard_span: Span,
+}
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 6bf590df5c9..7bc448a9acb 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -33,6 +33,7 @@ mod autodiff;
 mod cfg;
 mod cfg_accessible;
 mod cfg_eval;
+mod cfg_select;
 mod compile_error;
 mod concat;
 mod concat_bytes;
@@ -79,6 +80,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
         asm: asm::expand_asm,
         assert: assert::expand_assert,
         cfg: cfg::expand_cfg,
+        cfg_select: cfg_select::expand_cfg_select,
         column: source_util::expand_column,
         compile_error: compile_error::expand_compile_error,
         concat: concat::expand_concat,