about summary refs log tree commit diff
path: root/compiler/rustc_builtin_macros/src/cfg_select.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_builtin_macros/src/cfg_select.rs')
-rw-r--r--compiler/rustc_builtin_macros/src/cfg_select.rs63
1 files changed, 63 insertions, 0 deletions
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)
+        }
+    })
+}