Skip to content

Commit 01f41cc

Browse files
committed
make cfg_select a builtin macro
1 parent 1b0bc59 commit 01f41cc

File tree

14 files changed

+281
-50
lines changed

14 files changed

+281
-50
lines changed

compiler/rustc_builtin_macros/messages.ftl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ builtin_macros_cfg_accessible_literal_path = `cfg_accessible` path cannot be a l
8181
builtin_macros_cfg_accessible_multiple_paths = multiple `cfg_accessible` paths are specified
8282
builtin_macros_cfg_accessible_unspecified_path = `cfg_accessible` path is not specified
8383
84+
builtin_macros_cfg_select_no_matches = none of the rules in this `cfg_select` evaluated to true
85+
86+
builtin_macros_cfg_select_unreachable = unreachable rule
87+
.label = always matches
88+
.label2 = this rules is never reached
89+
8490
builtin_macros_coerce_pointee_requires_maybe_sized = `derive(CoercePointee)` requires `{$name}` to be marked `?Sized`
8591
8692
builtin_macros_coerce_pointee_requires_one_field = `CoercePointee` can only be derived on `struct`s with at least one field
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use rustc_ast::tokenstream::TokenStream;
2+
use rustc_attr_parsing as attr;
3+
use rustc_expand::base::{ExtCtxt, MacroExpanderResult, *};
4+
use rustc_parse::parser::cfg_select::{CfgSelectBranches, CfgSelectRule, parse_cfg_select};
5+
use rustc_span::{Ident, Span, sym};
6+
7+
use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable};
8+
9+
/// Selects the first arm whose rule evaluates to true.
10+
fn select_arm(ecx: &ExtCtxt<'_>, branches: CfgSelectBranches) -> Option<(TokenStream, Span)> {
11+
for (cfg, tt, arm_span) in branches.reachable {
12+
if attr::cfg_matches(
13+
&cfg,
14+
&ecx.sess,
15+
ecx.current_expansion.lint_node_id,
16+
Some(ecx.ecfg.features),
17+
) {
18+
return Some((tt, arm_span));
19+
}
20+
}
21+
22+
branches.wildcard.map(|(_, tt, span)| (tt, span))
23+
}
24+
25+
pub(super) fn expand_cfg_select<'cx>(
26+
ecx: &'cx mut ExtCtxt<'_>,
27+
sp: Span,
28+
tts: TokenStream,
29+
) -> MacroExpanderResult<'cx> {
30+
ExpandResult::Ready(match parse_cfg_select(&mut ecx.new_parser_from_tts(tts)) {
31+
Ok(branches) => {
32+
if let Some((underscore, _, _)) = branches.wildcard {
33+
// Warn for every unreachable rule.
34+
for (rule, _, _) in &branches.unreachable {
35+
let span = match rule {
36+
CfgSelectRule::Wildcard(underscore) => underscore.span,
37+
CfgSelectRule::Cfg(cfg) => cfg.span(),
38+
};
39+
let err = CfgSelectUnreachable { span, wildcard_span: underscore.span };
40+
ecx.dcx().emit_warn(err);
41+
}
42+
}
43+
44+
if let Some((tts, arm_span)) = select_arm(ecx, branches) {
45+
rustc_expand::expand_token_stream(
46+
ecx,
47+
sp,
48+
arm_span,
49+
ecx.current_expansion.lint_node_id,
50+
Ident::with_dummy_span(sym::cfg_select),
51+
tts,
52+
)
53+
} else {
54+
// Emit a compiler error when none of the rules matched.
55+
let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp });
56+
DummyResult::any(sp, guar)
57+
}
58+
}
59+
Err(err) => {
60+
let guar = err.emit();
61+
DummyResult::any(sp, guar)
62+
}
63+
})
64+
}

compiler/rustc_builtin_macros/src/errors.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,3 +954,21 @@ pub(crate) struct AsmExpectedOther {
954954
pub(crate) span: Span,
955955
pub(crate) is_inline_asm: bool,
956956
}
957+
958+
#[derive(Diagnostic)]
959+
#[diag(builtin_macros_cfg_select_no_matches)]
960+
pub(crate) struct CfgSelectNoMatches {
961+
#[primary_span]
962+
pub span: Span,
963+
}
964+
965+
#[derive(Diagnostic)]
966+
#[diag(builtin_macros_cfg_select_unreachable)]
967+
pub(crate) struct CfgSelectUnreachable {
968+
#[primary_span]
969+
#[label(builtin_macros_label2)]
970+
pub span: Span,
971+
972+
#[label]
973+
pub wildcard_span: Span,
974+
}

compiler/rustc_builtin_macros/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ mod autodiff;
3333
mod cfg;
3434
mod cfg_accessible;
3535
mod cfg_eval;
36+
mod cfg_select;
3637
mod compile_error;
3738
mod concat;
3839
mod concat_bytes;
@@ -79,6 +80,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
7980
asm: asm::expand_asm,
8081
assert: assert::expand_assert,
8182
cfg: cfg::expand_cfg,
83+
cfg_select: cfg_select::expand_cfg_select,
8284
column: source_util::expand_column,
8385
compile_error: compile_error::expand_compile_error,
8486
concat: concat::expand_concat,

compiler/rustc_expand/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ mod placeholders;
2222
mod proc_macro_server;
2323
mod stats;
2424

25-
pub use mbe::macro_rules::compile_declarative_macro;
25+
pub use mbe::macro_rules::{compile_declarative_macro, expand_token_stream};
2626
pub mod base;
2727
pub mod config;
2828
pub mod expand;

compiler/rustc_expand/src/mbe/macro_rules.rs

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,30 @@ impl<'matcher> Tracker<'matcher> for NoopTracker {
194194
}
195195
}
196196

197+
#[instrument(skip(cx, tts))]
198+
pub fn expand_token_stream<'cx>(
199+
cx: &'cx mut ExtCtxt<'_>,
200+
sp: Span,
201+
arm_span: Span,
202+
node_id: NodeId,
203+
name: Ident,
204+
tts: TokenStream,
205+
) -> Box<dyn MacResult + 'cx> {
206+
Box::new(ParserAnyMacro {
207+
parser: Parser::new(&cx.sess.psess, tts, None),
208+
209+
// Pass along the original expansion site and the name of the macro
210+
// so we can print a useful error message if the parse of the expanded
211+
// macro leaves unparsed tokens.
212+
site_span: sp,
213+
macro_ident: name,
214+
lint_node_id: cx.current_expansion.lint_node_id,
215+
is_trailing_mac: cx.current_expansion.is_trailing_mac,
216+
arm_span,
217+
is_local: is_defined_in_current_crate(node_id),
218+
})
219+
}
220+
197221
/// Expands the rules based macro defined by `rules` for a given input `arg`.
198222
#[instrument(skip(cx, transparency, arg, rules))]
199223
fn expand_macro<'cx>(
@@ -207,9 +231,6 @@ fn expand_macro<'cx>(
207231
rules: &[MacroRule],
208232
) -> Box<dyn MacResult + 'cx> {
209233
let psess = &cx.sess.psess;
210-
// Macros defined in the current crate have a real node id,
211-
// whereas macros from an external crate have a dummy id.
212-
let is_local = node_id != DUMMY_NODE_ID;
213234

214235
if cx.trace_macros() {
215236
let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(&arg));
@@ -220,7 +241,7 @@ fn expand_macro<'cx>(
220241
let try_success_result = try_match_macro(psess, name, &arg, rules, &mut NoopTracker);
221242

222243
match try_success_result {
223-
Ok((i, rule, named_matches)) => {
244+
Ok((rule_index, rule, named_matches)) => {
224245
let mbe::TokenTree::Delimited(rhs_span, _, ref rhs) = rule.rhs else {
225246
cx.dcx().span_bug(sp, "malformed macro rhs");
226247
};
@@ -241,27 +262,13 @@ fn expand_macro<'cx>(
241262
trace_macros_note(&mut cx.expansions, sp, msg);
242263
}
243264

244-
let p = Parser::new(psess, tts, None);
245-
246-
if is_local {
247-
cx.resolver.record_macro_rule_usage(node_id, i);
265+
if is_defined_in_current_crate(node_id) {
266+
cx.resolver.record_macro_rule_usage(node_id, rule_index);
248267
}
249268

250269
// Let the context choose how to interpret the result.
251270
// Weird, but useful for X-macros.
252-
Box::new(ParserAnyMacro {
253-
parser: p,
254-
255-
// Pass along the original expansion site and the name of the macro
256-
// so we can print a useful error message if the parse of the expanded
257-
// macro leaves unparsed tokens.
258-
site_span: sp,
259-
macro_ident: name,
260-
lint_node_id: cx.current_expansion.lint_node_id,
261-
is_trailing_mac: cx.current_expansion.is_trailing_mac,
262-
arm_span,
263-
is_local,
264-
})
271+
expand_token_stream(cx, sp, arm_span, node_id, name, tts)
265272
}
266273
Err(CanRetry::No(guar)) => {
267274
debug!("Will not retry matching as an error was emitted already");
@@ -373,10 +380,18 @@ pub fn compile_declarative_macro(
373380
node_id: NodeId,
374381
edition: Edition,
375382
) -> (SyntaxExtension, usize) {
376-
let is_local = node_id != DUMMY_NODE_ID;
377383
let mk_syn_ext = |expander| {
378384
let kind = SyntaxExtensionKind::LegacyBang(expander);
379-
SyntaxExtension::new(sess, kind, span, Vec::new(), edition, ident.name, attrs, is_local)
385+
SyntaxExtension::new(
386+
sess,
387+
kind,
388+
span,
389+
Vec::new(),
390+
edition,
391+
ident.name,
392+
attrs,
393+
is_defined_in_current_crate(node_id),
394+
)
380395
};
381396
let dummy_syn_ext = |guar| (mk_syn_ext(Arc::new(DummyExpander(guar))), 0);
382397

@@ -439,7 +454,7 @@ pub fn compile_declarative_macro(
439454
}
440455

441456
// Return the number of rules for unused rule linting, if this is a local macro.
442-
let nrules = if is_local { rules.len() } else { 0 };
457+
let nrules = if is_defined_in_current_crate(node_id) { rules.len() } else { 0 };
443458

444459
let expander =
445460
Arc::new(MacroRulesMacroExpander { name: ident, span, node_id, transparency, rules });
@@ -1034,9 +1049,7 @@ fn check_matcher_core<'tt>(
10341049
// definition of this macro_rules, not while (re)parsing
10351050
// the macro when compiling another crate that is using the
10361051
// macro. (See #86567.)
1037-
// Macros defined in the current crate have a real node id,
1038-
// whereas macros from an external crate have a dummy id.
1039-
if node_id != DUMMY_NODE_ID
1052+
if is_defined_in_current_crate(node_id)
10401053
&& matches!(kind, NonterminalKind::Pat(PatParam { inferred: true }))
10411054
&& matches!(
10421055
next_token,
@@ -1296,6 +1309,12 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
12961309
}
12971310
}
12981311

1312+
fn is_defined_in_current_crate(node_id: NodeId) -> bool {
1313+
// Macros defined in the current crate have a real node id,
1314+
// whereas macros from an external crate have a dummy id.
1315+
node_id != DUMMY_NODE_ID
1316+
}
1317+
12991318
pub(super) fn parser_from_cx(
13001319
psess: &ParseSess,
13011320
mut tts: TokenStream,
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use rustc_ast::token::Token;
2+
use rustc_ast::tokenstream::{TokenStream, TokenTree};
3+
use rustc_ast::{MetaItemInner, token};
4+
use rustc_errors::PResult;
5+
use rustc_span::{Span, kw};
6+
7+
use crate::exp;
8+
use crate::parser::Parser;
9+
10+
pub enum CfgSelectRule {
11+
Cfg(MetaItemInner),
12+
Wildcard(Token),
13+
}
14+
15+
#[derive(Default)]
16+
pub struct CfgSelectBranches {
17+
/// All the conditional branches.
18+
pub reachable: Vec<(MetaItemInner, TokenStream, Span)>,
19+
/// The first wildcard `_ => { ... }` branch.
20+
pub wildcard: Option<(Token, TokenStream, Span)>,
21+
/// All branches after the first wildcard, including further wildcards.
22+
pub unreachable: Vec<(CfgSelectRule, TokenStream, Span)>,
23+
}
24+
25+
/// Parses a `TokenTree` that must be of the form `{ /* ... */ }`, and returns a `TokenStream` where
26+
/// the surrounding braces are stripped.
27+
fn parse_token_tree<'a>(p: &mut Parser<'a>) -> PResult<'a, TokenStream> {
28+
// Generate an error if the `=>` is not followed by `{`.
29+
if p.token != token::OpenBrace {
30+
p.expect(exp!(OpenBrace))?;
31+
}
32+
33+
// Strip the outer '{' and '}'.
34+
match p.parse_token_tree() {
35+
TokenTree::Token(..) => unreachable!("because of the expect above"),
36+
TokenTree::Delimited(.., tts) => Ok(tts),
37+
}
38+
}
39+
40+
pub fn parse_cfg_select<'a>(p: &mut Parser<'a>) -> PResult<'a, CfgSelectBranches> {
41+
let mut branches = CfgSelectBranches::default();
42+
43+
while p.token != token::Eof {
44+
if p.token.is_keyword(kw::Underscore) {
45+
let underscore = p.token;
46+
p.bump();
47+
p.expect(exp!(FatArrow))?;
48+
49+
let tts = parse_token_tree(p)?;
50+
let span = underscore.span.to(p.token.span);
51+
52+
match branches.wildcard {
53+
None => branches.wildcard = Some((underscore, tts, span)),
54+
Some(_) => {
55+
branches.unreachable.push((CfgSelectRule::Wildcard(underscore), tts, span))
56+
}
57+
}
58+
} else {
59+
let meta_item = p.parse_meta_item_inner()?;
60+
p.expect(exp!(FatArrow))?;
61+
62+
let tts = parse_token_tree(p)?;
63+
let span = meta_item.span().to(p.token.span);
64+
65+
match branches.wildcard {
66+
None => branches.reachable.push((meta_item, tts, span)),
67+
Some(_) => branches.unreachable.push((CfgSelectRule::Cfg(meta_item), tts, span)),
68+
}
69+
}
70+
}
71+
72+
Ok(branches)
73+
}

compiler/rustc_parse/src/parser/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod asm;
22
pub mod attr;
33
mod attr_wrapper;
4+
pub mod cfg_select;
45
mod diagnostics;
56
mod expr;
67
mod generics;

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,7 @@ symbols! {
624624
cfg_relocation_model,
625625
cfg_sanitize,
626626
cfg_sanitizer_cfi,
627+
cfg_select,
627628
cfg_target_abi,
628629
cfg_target_compact,
629630
cfg_target_feature,

library/core/src/macros/mod.rs

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -230,32 +230,17 @@ pub macro assert_matches {
230230
/// ```
231231
/// #![feature(cfg_select)]
232232
///
233-
/// let _some_string = cfg_select! {{
233+
/// let _some_string = cfg_select! {
234234
/// unix => { "With great power comes great electricity bills" }
235235
/// _ => { "Behind every successful diet is an unwatched pizza" }
236-
/// }};
236+
/// };
237237
/// ```
238238
#[unstable(feature = "cfg_select", issue = "115585")]
239239
#[rustc_diagnostic_item = "cfg_select"]
240240
#[rustc_macro_transparency = "semitransparent"]
241-
pub macro cfg_select {
242-
({ $($tt:tt)* }) => {{
243-
$crate::cfg_select! { $($tt)* }
244-
}},
245-
(_ => { $($output:tt)* }) => {
246-
$($output)*
247-
},
248-
(
249-
$cfg:meta => $output:tt
250-
$($( $rest:tt )+)?
251-
) => {
252-
#[cfg($cfg)]
253-
$crate::cfg_select! { _ => $output }
254-
$(
255-
#[cfg(not($cfg))]
256-
$crate::cfg_select! { $($rest)+ }
257-
)?
258-
},
241+
#[rustc_builtin_macro]
242+
pub macro cfg_select($($tt:tt)*) {
243+
/* compiler built-in */
259244
}
260245

261246
/// Asserts that a boolean expression is `true` at runtime.

0 commit comments

Comments
 (0)