Port cfg_select! to the new attribute parsing system

Signed-off-by: Jonathan Brouwer <jonathantbrouwer@gmail.com>
This commit is contained in:
Jonathan Brouwer 2025-11-08 21:43:58 +01:00
parent c111ccc45d
commit 90f36afc1b
No known key found for this signature in database
GPG key ID: 13619B051B673C52
5 changed files with 114 additions and 82 deletions

View file

@ -1,20 +1,26 @@
use rustc_ast::token::Token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{MetaItemInner, token};
use rustc_errors::PResult;
use rustc_ast::{AttrStyle, NodeId, token};
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::AttrPath;
use rustc_hir::attrs::CfgEntry;
use rustc_parse::exp;
use rustc_parse::parser::Parser;
use rustc_span::Span;
use rustc_session::Session;
use rustc_span::{ErrorGuaranteed, Ident, Span};
use crate::parser::MetaItemOrLitParser;
use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry};
pub enum CfgSelectPredicate {
Cfg(MetaItemInner),
Cfg(CfgEntry),
Wildcard(Token),
}
#[derive(Default)]
pub struct CfgSelectBranches {
/// All the conditional branches.
pub reachable: Vec<(MetaItemInner, TokenStream, Span)>,
pub reachable: Vec<(CfgEntry, TokenStream, Span)>,
/// The first wildcard `_ => { ... }` branch.
pub wildcard: Option<(Token, TokenStream, Span)>,
/// All branches after the first wildcard, including further wildcards.
@ -22,15 +28,20 @@ pub struct CfgSelectBranches {
pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>,
}
pub fn parse_cfg_select<'a>(p: &mut Parser<'a>) -> PResult<'a, CfgSelectBranches> {
pub fn parse_cfg_select(
p: &mut Parser<'_>,
sess: &Session,
features: Option<&Features>,
lint_node_id: NodeId,
) -> Result<CfgSelectBranches, ErrorGuaranteed> {
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))?;
p.expect(exp!(FatArrow)).map_err(|e| e.emit())?;
let tts = p.parse_delimited_token_tree()?;
let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?;
let span = underscore.span.to(p.token.span);
match branches.wildcard {
@ -40,17 +51,36 @@ pub fn parse_cfg_select<'a>(p: &mut Parser<'a>) -> PResult<'a, CfgSelectBranches
}
}
} else {
let meta_item = p.parse_meta_item_inner()?;
p.expect(exp!(FatArrow))?;
let meta = MetaItemOrLitParser::parse_single(p, ShouldEmit::ErrorsAndLints)
.map_err(|diag| diag.emit())?;
let cfg_span = meta.span();
let cfg = AttributeParser::parse_single_args(
sess,
cfg_span,
cfg_span,
AttrStyle::Inner,
AttrPath {
segments: vec![Ident::from_str("cfg_select")].into_boxed_slice(),
span: cfg_span,
},
ParsedDescription::Macro,
cfg_span,
lint_node_id,
features,
ShouldEmit::ErrorsAndLints,
&meta,
parse_cfg_entry,
&AttributeTemplate::default(),
)?;
let tts = p.parse_delimited_token_tree()?;
let span = meta_item.span().to(p.token.span);
p.expect(exp!(FatArrow)).map_err(|e| e.emit())?;
let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?;
let span = cfg_span.to(p.token.span);
match branches.wildcard {
None => branches.reachable.push((meta_item, tts, span)),
Some(_) => {
branches.unreachable.push((CfgSelectPredicate::Cfg(meta_item), tts, span))
}
None => branches.reachable.push((cfg, tts, span)),
Some(_) => branches.unreachable.push((CfgSelectPredicate::Cfg(cfg), tts, span)),
}
}
}

View file

@ -1,6 +1,8 @@
use rustc_ast::tokenstream::TokenStream;
use rustc_attr_parsing as attr;
use rustc_attr_parsing::{CfgSelectBranches, CfgSelectPredicate, parse_cfg_select};
use rustc_attr_parsing::{
CfgSelectBranches, CfgSelectPredicate, EvalConfigResult, ShouldEmit, parse_cfg_select,
};
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
use rustc_span::{Ident, Span, sym};
@ -9,11 +11,11 @@ use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable};
/// Selects the first arm whose predicate 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,
if let EvalConfigResult::True = attr::eval_config_entry(
&ecx.sess,
&cfg,
ecx.current_expansion.lint_node_id,
Some(ecx.ecfg.features),
ShouldEmit::ErrorsAndLints,
) {
return Some((tt, arm_span));
}
@ -27,37 +29,41 @@ pub(super) fn expand_cfg_select<'cx>(
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 predicate. We store the fully parsed branch for rustfmt.
for (predicate, _, _) in &branches.unreachable {
let span = match predicate {
CfgSelectPredicate::Wildcard(underscore) => underscore.span,
CfgSelectPredicate::Cfg(cfg) => cfg.span(),
};
let err = CfgSelectUnreachable { span, wildcard_span: underscore.span };
ecx.dcx().emit_warn(err);
ExpandResult::Ready(
match parse_cfg_select(
&mut ecx.new_parser_from_tts(tts),
ecx.sess,
Some(ecx.ecfg.features),
ecx.current_expansion.lint_node_id,
) {
Ok(branches) => {
if let Some((underscore, _, _)) = branches.wildcard {
// Warn for every unreachable predicate. We store the fully parsed branch for rustfmt.
for (predicate, _, _) in &branches.unreachable {
let span = match predicate {
CfgSelectPredicate::Wildcard(underscore) => underscore.span,
CfgSelectPredicate::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 predicates matched.
let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp });
DummyResult::any(sp, guar)
}
}
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 predicates matched.
let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp });
DummyResult::any(sp, guar)
}
}
Err(err) => {
let guar = err.emit();
DummyResult::any(sp, guar)
}
})
Err(guar) => DummyResult::any(sp, guar),
},
)
}

View file

@ -194,6 +194,18 @@ pub enum CfgEntry {
Version(Option<RustcVersion>, Span),
}
impl CfgEntry {
pub fn span(&self) -> Span {
let (CfgEntry::All(_, span)
| CfgEntry::Any(_, span)
| CfgEntry::Not(_, span)
| CfgEntry::Bool(_, span)
| CfgEntry::NameValue { span, .. }
| CfgEntry::Version(_, span)) = self;
*span
}
}
/// Possible values for the `#[linkage]` attribute, allowing to specify the
/// linkage type for a `MonoItem`.
///

View file

@ -63,25 +63,25 @@ cfg_select! {}
cfg_select! {
=> {}
//~^ ERROR expected unsuffixed literal, found `=>`
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `=>`
}
cfg_select! {
() => {}
//~^ ERROR expected unsuffixed literal, found `(`
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `(`
}
cfg_select! { //~ ERROR none of the predicates in this `cfg_select` evaluated to true
cfg_select! {
"str" => {}
//~^ ERROR literal in `cfg` predicate value must be a boolean
//~^ ERROR malformed `cfg_select` macro input [E0539]
}
cfg_select! {
a::b => {}
//~^ ERROR `cfg` predicate key must be an identifier
//~^ ERROR malformed `cfg_select` macro input [E0539]
}
cfg_select! { //~ ERROR none of the predicates in this `cfg_select` evaluated to true
cfg_select! {
a() => {}
//~^ ERROR invalid predicate `a` [E0537]
}

View file

@ -21,38 +21,31 @@ error: none of the predicates in this `cfg_select` evaluated to true
LL | cfg_select! {}
| ^^^^^^^^^^^^^^
error: expected unsuffixed literal, found `=>`
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `=>`
--> $DIR/cfg_select.rs:65:5
|
LL | => {}
| ^^
error: expected unsuffixed literal, found `(`
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `(`
--> $DIR/cfg_select.rs:70:5
|
LL | () => {}
| ^
error[E0565]: literal in `cfg` predicate value must be a boolean
error[E0539]: malformed `cfg_select` macro input
--> $DIR/cfg_select.rs:75:5
|
LL | "str" => {}
| ^^^^^
error: none of the predicates in this `cfg_select` evaluated to true
--> $DIR/cfg_select.rs:74:1
| ^^^^^ expected a valid identifier here
|
LL | / cfg_select! {
LL | | "str" => {}
LL | |
LL | | }
| |_^
error: `cfg` predicate key must be an identifier
error[E0539]: malformed `cfg_select` macro input
--> $DIR/cfg_select.rs:80:5
|
LL | a::b => {}
| ^^^^
| ^^^^ expected a valid identifier here
|
error[E0537]: invalid predicate `a`
--> $DIR/cfg_select.rs:85:5
@ -60,15 +53,6 @@ error[E0537]: invalid predicate `a`
LL | a() => {}
| ^^^
error: none of the predicates in this `cfg_select` evaluated to true
--> $DIR/cfg_select.rs:84:1
|
LL | / cfg_select! {
LL | | a() => {}
LL | |
LL | | }
| |_^
error: expected one of `(`, `::`, `=>`, or `=`, found `+`
--> $DIR/cfg_select.rs:90:7
|
@ -81,7 +65,7 @@ error: expected one of `(`, `::`, `=>`, or `=`, found `!`
LL | cfg!() => {}
| ^ expected one of `(`, `::`, `=>`, or `=`
error: aborting due to 11 previous errors; 1 warning emitted
error: aborting due to 9 previous errors; 1 warning emitted
Some errors have detailed explanations: E0537, E0565.
Some errors have detailed explanations: E0537, E0539.
For more information about an error, try `rustc --explain E0537`.