diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs index 7ee92d692672..8006fb963b19 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs @@ -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 { 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)), } } } diff --git a/compiler/rustc_builtin_macros/src/cfg_select.rs b/compiler/rustc_builtin_macros/src/cfg_select.rs index 9bb222b1b87a..f2e454c3d437 100644 --- a/compiler/rustc_builtin_macros/src/cfg_select.rs +++ b/compiler/rustc_builtin_macros/src/cfg_select.rs @@ -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), + }, + ) } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index b1b872808c8f..592825cf4220 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -194,6 +194,18 @@ pub enum CfgEntry { Version(Option, 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`. /// diff --git a/tests/ui/macros/cfg_select.rs b/tests/ui/macros/cfg_select.rs index 54011f13df8c..2a627cc05b93 100644 --- a/tests/ui/macros/cfg_select.rs +++ b/tests/ui/macros/cfg_select.rs @@ -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] } diff --git a/tests/ui/macros/cfg_select.stderr b/tests/ui/macros/cfg_select.stderr index 78145a14649c..3a5d2b0a1e1e 100644 --- a/tests/ui/macros/cfg_select.stderr +++ b/tests/ui/macros/cfg_select.stderr @@ -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`.