Skip to content

Commit e26b423

Browse files
authored
Rollup merge of #148712 - JonathanBrouwer:cfg_select, r=jdonszelmann
Port `cfg_select!` to the new attribute parsing system Best reviewed commit by commit, since it involves some moving around of code r? `````@jdonszelmann`````
2 parents 3150714 + 90f36af commit e26b423

File tree

8 files changed

+247
-106
lines changed

8 files changed

+247
-106
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use rustc_ast::token::Token;
2+
use rustc_ast::tokenstream::TokenStream;
3+
use rustc_ast::{AttrStyle, NodeId, token};
4+
use rustc_feature::{AttributeTemplate, Features};
5+
use rustc_hir::AttrPath;
6+
use rustc_hir::attrs::CfgEntry;
7+
use rustc_parse::exp;
8+
use rustc_parse::parser::Parser;
9+
use rustc_session::Session;
10+
use rustc_span::{ErrorGuaranteed, Ident, Span};
11+
12+
use crate::parser::MetaItemOrLitParser;
13+
use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry};
14+
15+
pub enum CfgSelectPredicate {
16+
Cfg(CfgEntry),
17+
Wildcard(Token),
18+
}
19+
20+
#[derive(Default)]
21+
pub struct CfgSelectBranches {
22+
/// All the conditional branches.
23+
pub reachable: Vec<(CfgEntry, TokenStream, Span)>,
24+
/// The first wildcard `_ => { ... }` branch.
25+
pub wildcard: Option<(Token, TokenStream, Span)>,
26+
/// All branches after the first wildcard, including further wildcards.
27+
/// These branches are kept for formatting.
28+
pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>,
29+
}
30+
31+
pub fn parse_cfg_select(
32+
p: &mut Parser<'_>,
33+
sess: &Session,
34+
features: Option<&Features>,
35+
lint_node_id: NodeId,
36+
) -> Result<CfgSelectBranches, ErrorGuaranteed> {
37+
let mut branches = CfgSelectBranches::default();
38+
39+
while p.token != token::Eof {
40+
if p.eat_keyword(exp!(Underscore)) {
41+
let underscore = p.prev_token;
42+
p.expect(exp!(FatArrow)).map_err(|e| e.emit())?;
43+
44+
let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?;
45+
let span = underscore.span.to(p.token.span);
46+
47+
match branches.wildcard {
48+
None => branches.wildcard = Some((underscore, tts, span)),
49+
Some(_) => {
50+
branches.unreachable.push((CfgSelectPredicate::Wildcard(underscore), tts, span))
51+
}
52+
}
53+
} else {
54+
let meta = MetaItemOrLitParser::parse_single(p, ShouldEmit::ErrorsAndLints)
55+
.map_err(|diag| diag.emit())?;
56+
let cfg_span = meta.span();
57+
let cfg = AttributeParser::parse_single_args(
58+
sess,
59+
cfg_span,
60+
cfg_span,
61+
AttrStyle::Inner,
62+
AttrPath {
63+
segments: vec![Ident::from_str("cfg_select")].into_boxed_slice(),
64+
span: cfg_span,
65+
},
66+
ParsedDescription::Macro,
67+
cfg_span,
68+
lint_node_id,
69+
features,
70+
ShouldEmit::ErrorsAndLints,
71+
&meta,
72+
parse_cfg_entry,
73+
&AttributeTemplate::default(),
74+
)?;
75+
76+
p.expect(exp!(FatArrow)).map_err(|e| e.emit())?;
77+
78+
let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?;
79+
let span = cfg_span.to(p.token.span);
80+
81+
match branches.wildcard {
82+
None => branches.reachable.push((cfg, tts, span)),
83+
Some(_) => branches.unreachable.push((CfgSelectPredicate::Cfg(cfg), tts, span)),
84+
}
85+
}
86+
}
87+
88+
Ok(branches)
89+
}

compiler/rustc_attr_parsing/src/attributes/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub(crate) mod allow_unstable;
3333
pub(crate) mod body;
3434
pub(crate) mod cfg;
3535
pub(crate) mod cfg_old;
36+
pub(crate) mod cfg_select;
3637
pub(crate) mod codegen_attrs;
3738
pub(crate) mod confusables;
3839
pub(crate) mod crate_level;

compiler/rustc_attr_parsing/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ pub use attributes::cfg::{
106106
CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg, parse_cfg_attr, parse_cfg_entry,
107107
};
108108
pub use attributes::cfg_old::*;
109+
pub use attributes::cfg_select::*;
109110
pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version};
110111
pub use context::{Early, Late, OmitDoc, ShouldEmit};
111112
pub use interface::AttributeParser;
Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
use rustc_ast::tokenstream::TokenStream;
22
use rustc_attr_parsing as attr;
3+
use rustc_attr_parsing::{
4+
CfgSelectBranches, CfgSelectPredicate, EvalConfigResult, ShouldEmit, parse_cfg_select,
5+
};
36
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
4-
use rustc_parse::parser::cfg_select::{CfgSelectBranches, CfgSelectPredicate, parse_cfg_select};
57
use rustc_span::{Ident, Span, sym};
68

79
use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable};
810

911
/// Selects the first arm whose predicate evaluates to true.
1012
fn select_arm(ecx: &ExtCtxt<'_>, branches: CfgSelectBranches) -> Option<(TokenStream, Span)> {
1113
for (cfg, tt, arm_span) in branches.reachable {
12-
if attr::cfg_matches(
13-
&cfg,
14+
if let EvalConfigResult::True = attr::eval_config_entry(
1415
&ecx.sess,
16+
&cfg,
1517
ecx.current_expansion.lint_node_id,
16-
Some(ecx.ecfg.features),
18+
ShouldEmit::ErrorsAndLints,
1719
) {
1820
return Some((tt, arm_span));
1921
}
@@ -27,37 +29,41 @@ pub(super) fn expand_cfg_select<'cx>(
2729
sp: Span,
2830
tts: TokenStream,
2931
) -> 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 predicate. We store the fully parsed branch for rustfmt.
34-
for (predicate, _, _) in &branches.unreachable {
35-
let span = match predicate {
36-
CfgSelectPredicate::Wildcard(underscore) => underscore.span,
37-
CfgSelectPredicate::Cfg(cfg) => cfg.span(),
38-
};
39-
let err = CfgSelectUnreachable { span, wildcard_span: underscore.span };
40-
ecx.dcx().emit_warn(err);
32+
ExpandResult::Ready(
33+
match parse_cfg_select(
34+
&mut ecx.new_parser_from_tts(tts),
35+
ecx.sess,
36+
Some(ecx.ecfg.features),
37+
ecx.current_expansion.lint_node_id,
38+
) {
39+
Ok(branches) => {
40+
if let Some((underscore, _, _)) = branches.wildcard {
41+
// Warn for every unreachable predicate. We store the fully parsed branch for rustfmt.
42+
for (predicate, _, _) in &branches.unreachable {
43+
let span = match predicate {
44+
CfgSelectPredicate::Wildcard(underscore) => underscore.span,
45+
CfgSelectPredicate::Cfg(cfg) => cfg.span(),
46+
};
47+
let err = CfgSelectUnreachable { span, wildcard_span: underscore.span };
48+
ecx.dcx().emit_warn(err);
49+
}
4150
}
42-
}
4351

44-
if let Some((tts, arm_span)) = select_arm(ecx, branches) {
45-
return ExpandResult::from_tts(
46-
ecx,
47-
tts,
48-
sp,
49-
arm_span,
50-
Ident::with_dummy_span(sym::cfg_select),
51-
);
52-
} else {
53-
// Emit a compiler error when none of the predicates matched.
54-
let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp });
55-
DummyResult::any(sp, guar)
52+
if let Some((tts, arm_span)) = select_arm(ecx, branches) {
53+
return ExpandResult::from_tts(
54+
ecx,
55+
tts,
56+
sp,
57+
arm_span,
58+
Ident::with_dummy_span(sym::cfg_select),
59+
);
60+
} else {
61+
// Emit a compiler error when none of the predicates matched.
62+
let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp });
63+
DummyResult::any(sp, guar)
64+
}
5665
}
57-
}
58-
Err(err) => {
59-
let guar = err.emit();
60-
DummyResult::any(sp, guar)
61-
}
62-
})
66+
Err(guar) => DummyResult::any(sp, guar),
67+
},
68+
)
6369
}

compiler/rustc_hir/src/attrs/data_structures.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,18 @@ pub enum CfgEntry {
194194
Version(Option<RustcVersion>, Span),
195195
}
196196

197+
impl CfgEntry {
198+
pub fn span(&self) -> Span {
199+
let (CfgEntry::All(_, span)
200+
| CfgEntry::Any(_, span)
201+
| CfgEntry::Not(_, span)
202+
| CfgEntry::Bool(_, span)
203+
| CfgEntry::NameValue { span, .. }
204+
| CfgEntry::Version(_, span)) = self;
205+
*span
206+
}
207+
}
208+
197209
/// Possible values for the `#[linkage]` attribute, allowing to specify the
198210
/// linkage type for a `MonoItem`.
199211
///
Lines changed: 22 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,34 @@
1-
use rustc_ast::token::Token;
1+
use rustc_ast::token;
22
use rustc_ast::tokenstream::{TokenStream, TokenTree};
33
use rustc_ast::util::classify;
4-
use rustc_ast::{MetaItemInner, token};
54
use rustc_errors::PResult;
6-
use rustc_span::Span;
75

86
use crate::exp;
97
use crate::parser::{AttrWrapper, ForceCollect, Parser, Restrictions, Trailing, UsePreAttrPos};
108

11-
pub enum CfgSelectPredicate {
12-
Cfg(MetaItemInner),
13-
Wildcard(Token),
14-
}
15-
16-
#[derive(Default)]
17-
pub struct CfgSelectBranches {
18-
/// All the conditional branches.
19-
pub reachable: Vec<(MetaItemInner, TokenStream, Span)>,
20-
/// The first wildcard `_ => { ... }` branch.
21-
pub wildcard: Option<(Token, TokenStream, Span)>,
22-
/// All branches after the first wildcard, including further wildcards.
23-
/// These branches are kept for formatting.
24-
pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>,
25-
}
26-
27-
/// Parses a `TokenTree` consisting either of `{ /* ... */ }` (and strip the braces) or an
28-
/// expression followed by a comma (and strip the comma).
29-
fn parse_token_tree<'a>(p: &mut Parser<'a>) -> PResult<'a, TokenStream> {
30-
if p.token == token::OpenBrace {
31-
// Strip the outer '{' and '}'.
32-
match p.parse_token_tree() {
33-
TokenTree::Token(..) => unreachable!("because of the expect above"),
34-
TokenTree::Delimited(.., tts) => return Ok(tts),
35-
}
36-
}
37-
let expr = p.collect_tokens(None, AttrWrapper::empty(), ForceCollect::Yes, |p, _| {
38-
p.parse_expr_res(Restrictions::STMT_EXPR, AttrWrapper::empty())
39-
.map(|(expr, _)| (expr, Trailing::No, UsePreAttrPos::No))
40-
})?;
41-
if !classify::expr_is_complete(&expr) && p.token != token::CloseBrace && p.token != token::Eof {
42-
p.expect(exp!(Comma))?;
43-
} else {
44-
let _ = p.eat(exp!(Comma));
45-
}
46-
Ok(TokenStream::from_ast(&expr))
47-
}
48-
49-
pub fn parse_cfg_select<'a>(p: &mut Parser<'a>) -> PResult<'a, CfgSelectBranches> {
50-
let mut branches = CfgSelectBranches::default();
51-
52-
while p.token != token::Eof {
53-
if p.eat_keyword(exp!(Underscore)) {
54-
let underscore = p.prev_token;
55-
p.expect(exp!(FatArrow))?;
56-
57-
let tts = parse_token_tree(p)?;
58-
let span = underscore.span.to(p.token.span);
59-
60-
match branches.wildcard {
61-
None => branches.wildcard = Some((underscore, tts, span)),
62-
Some(_) => {
63-
branches.unreachable.push((CfgSelectPredicate::Wildcard(underscore), tts, span))
64-
}
9+
impl<'a> Parser<'a> {
10+
/// Parses a `TokenTree` consisting either of `{ /* ... */ }` (and strip the braces) or an
11+
/// expression followed by a comma (and strip the comma).
12+
pub fn parse_delimited_token_tree(&mut self) -> PResult<'a, TokenStream> {
13+
if self.token == token::OpenBrace {
14+
// Strip the outer '{' and '}'.
15+
match self.parse_token_tree() {
16+
TokenTree::Token(..) => unreachable!("because of the expect above"),
17+
TokenTree::Delimited(.., tts) => return Ok(tts),
6518
}
19+
}
20+
let expr = self.collect_tokens(None, AttrWrapper::empty(), ForceCollect::Yes, |p, _| {
21+
p.parse_expr_res(Restrictions::STMT_EXPR, AttrWrapper::empty())
22+
.map(|(expr, _)| (expr, Trailing::No, UsePreAttrPos::No))
23+
})?;
24+
if !classify::expr_is_complete(&expr)
25+
&& self.token != token::CloseBrace
26+
&& self.token != token::Eof
27+
{
28+
self.expect(exp!(Comma))?;
6629
} else {
67-
let meta_item = p.parse_meta_item_inner()?;
68-
p.expect(exp!(FatArrow))?;
69-
70-
let tts = parse_token_tree(p)?;
71-
let span = meta_item.span().to(p.token.span);
72-
73-
match branches.wildcard {
74-
None => branches.reachable.push((meta_item, tts, span)),
75-
Some(_) => {
76-
branches.unreachable.push((CfgSelectPredicate::Cfg(meta_item), tts, span))
77-
}
78-
}
30+
let _ = self.eat(exp!(Comma));
7931
}
32+
Ok(TokenStream::from_ast(&expr))
8033
}
81-
82-
Ok(branches)
8334
}

tests/ui/macros/cfg_select.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,38 @@ cfg_select! {
6060

6161
cfg_select! {}
6262
//~^ ERROR none of the predicates in this `cfg_select` evaluated to true
63+
64+
cfg_select! {
65+
=> {}
66+
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `=>`
67+
}
68+
69+
cfg_select! {
70+
() => {}
71+
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `(`
72+
}
73+
74+
cfg_select! {
75+
"str" => {}
76+
//~^ ERROR malformed `cfg_select` macro input [E0539]
77+
}
78+
79+
cfg_select! {
80+
a::b => {}
81+
//~^ ERROR malformed `cfg_select` macro input [E0539]
82+
}
83+
84+
cfg_select! {
85+
a() => {}
86+
//~^ ERROR invalid predicate `a` [E0537]
87+
}
88+
89+
cfg_select! {
90+
a + 1 => {}
91+
//~^ ERROR expected one of `(`, `::`, `=>`, or `=`, found `+`
92+
}
93+
94+
cfg_select! {
95+
cfg!() => {}
96+
//~^ ERROR expected one of `(`, `::`, `=>`, or `=`, found `!`
97+
}

0 commit comments

Comments
 (0)