| use rustc_ast::token::Token; | |
| use rustc_ast::tokenstream::TokenStream; | |
| use rustc_ast::{AttrStyle, NodeId, token}; | |
| use rustc_attr_ir::target::Target; | |
| use rustc_attr_ir::{AttrPath, CfgEntry}; | |
| use rustc_data_structures::fx::FxHashMap; | |
| use rustc_errors::{Diagnostic, MultiSpan}; | |
| use rustc_feature::Features; | |
| use rustc_parse::exp; | |
| use rustc_parse::parser::{Parser, Recovery}; | |
| use rustc_session::Session; | |
| use rustc_session::lint::builtin::UNREACHABLE_CFG_SELECT_PREDICATES; | |
| use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; | |
| use crate::attributes::AttributeSafety; | |
| use crate::parser::{AllowExprMetavar, MetaItemOrLitParser}; | |
| use crate::{ | |
| AttributeParser, AttributeTemplate, ParsedDescription, ShouldEmit, diagnostics, parse_cfg_entry, | |
| }; | |
| #[derive(Clone)] | |
| pub enum CfgSelectPredicate { | |
| Cfg(CfgEntry), | |
| Wildcard(Token), | |
| } | |
| impl CfgSelectPredicate { | |
| fn span(&self) -> Span { | |
| match self { | |
| CfgSelectPredicate::Cfg(cfg_entry) => cfg_entry.span(), | |
| CfgSelectPredicate::Wildcard(token) => token.span, | |
| } | |
| } | |
| } | |
| #[derive(Default)] | |
| pub struct CfgSelectBranches { | |
| /// All the conditional branches. | |
| 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. | |
| /// These branches are kept for formatting. | |
| pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>, | |
| } | |
| impl CfgSelectBranches { | |
| /// Removes the top-most branch for which `predicate` returns `true`, | |
| /// or the wildcard if none of the reachable branches satisfied the predicate. | |
| pub fn pop_first_match<F>(&mut self, predicate: F) -> Option<(TokenStream, Span)> | |
| where | |
| F: Fn(&CfgEntry) -> bool, | |
| { | |
| for (index, (cfg, _, _)) in self.reachable.iter().enumerate() { | |
| if predicate(cfg) { | |
| let matched = self.reachable.remove(index); | |
| return Some((matched.1, matched.2)); | |
| } | |
| } | |
| self.wildcard.take().map(|(_, tts, span)| (tts, span)) | |
| } | |
| /// Consume this value and iterate over all the `TokenStream`s that it stores. | |
| pub fn into_iter_tts(self) -> impl Iterator<Item = (TokenStream, Span)> { | |
| let it1 = self.reachable.into_iter().map(|(_, tts, span)| (tts, span)); | |
| let it2 = self.wildcard.into_iter().map(|(_, tts, span)| (tts, span)); | |
| let it3 = self.unreachable.into_iter().map(|(_, tts, span)| (tts, span)); | |
| it1.chain(it2).chain(it3) | |
| } | |
| } | |
| 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(); | |
| let mut branch_attr_error: Option<ErrorGuaranteed> = None; | |
| while p.token != token::Eof { | |
| reject_branch_outer_attrs(p, &mut branch_attr_error)?; | |
| if p.eat_keyword(exp!(Underscore)) { | |
| let underscore = p.prev_token; | |
| p.expect(exp!(FatArrow)).map_err(|e| e.emit())?; | |
| let tts = p.parse_cfg_select_branch_rhs().map_err(|e| e.emit())?; | |
| let span = underscore.span.to(p.token.span); | |
| match branches.wildcard { | |
| None => branches.wildcard = Some((underscore, tts, span)), | |
| Some(_) => { | |
| branches.unreachable.push((CfgSelectPredicate::Wildcard(underscore), tts, span)) | |
| } | |
| } | |
| } else { | |
| let meta = MetaItemOrLitParser::parse_single( | |
| p, | |
| ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, | |
| AllowExprMetavar::Yes, | |
| ) | |
| .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![sym::cfg_select].into_boxed_slice(), span: cfg_span }, | |
| None, | |
| AttributeSafety::Normal, | |
| ParsedDescription::Macro, | |
| cfg_span, | |
| lint_node_id, | |
| // Doesn't matter what the target actually is here. | |
| Target::Crate, | |
| features, | |
| ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, | |
| &meta, | |
| parse_cfg_entry, | |
| &AttributeTemplate::default(), | |
| )?; | |
| p.expect(exp!(FatArrow)).map_err(|e| e.emit())?; | |
| let tts = p.parse_cfg_select_branch_rhs().map_err(|e| e.emit())?; | |
| let span = cfg_span.to(p.token.span); | |
| match branches.wildcard { | |
| None => branches.reachable.push((cfg, tts, span)), | |
| Some(_) => branches.unreachable.push((CfgSelectPredicate::Cfg(cfg), tts, span)), | |
| } | |
| } | |
| } | |
| if let Some(guar) = branch_attr_error { | |
| return Err(guar); | |
| } | |
| let it = branches | |
| .reachable | |
| .iter() | |
| .map(|(entry, _, _)| CfgSelectPredicate::Cfg(entry.clone())) | |
| .chain(branches.wildcard.as_ref().map(|(t, _, _)| CfgSelectPredicate::Wildcard(*t))) | |
| .chain(branches.unreachable.iter().map(|(entry, _, _)| CfgSelectPredicate::clone(entry))); | |
| lint_unreachable(p, it, lint_node_id); | |
| Ok(branches) | |
| } | |
| fn reject_branch_outer_attrs( | |
| p: &mut Parser<'_>, | |
| branch_attr_error: &mut Option<ErrorGuaranteed>, | |
| ) -> Result<(), ErrorGuaranteed> { | |
| let Some(spans) = p.parse_cfg_select_branch_outer_attrs().map_err(|e| e.emit())? else { | |
| return Ok(()); | |
| }; | |
| for (spans, msg) in [ | |
| (spans.doc_comments, "doc comments are not allowed on `cfg_select` branches"), | |
| (spans.attrs, "attributes are not allowed on `cfg_select` branches"), | |
| ] { | |
| if !spans.is_empty() { | |
| branch_attr_error | |
| .get_or_insert(p.dcx().struct_span_err(MultiSpan::from_spans(spans), msg).emit()); | |
| } | |
| } | |
| Ok(()) | |
| } | |
| fn lint_unreachable( | |
| p: &mut Parser<'_>, | |
| predicates: impl Iterator<Item = CfgSelectPredicate>, | |
| lint_node_id: NodeId, | |
| ) { | |
| // Symbols that have a known value. | |
| let mut known = FxHashMap::<Symbol, bool>::default(); | |
| let mut wildcard_span = None; | |
| let mut it = predicates; | |
| let branch_is_unreachable = |predicate: CfgSelectPredicate, wildcard_span| { | |
| let span = predicate.span(); | |
| p.psess.dyn_buffer_lint( | |
| UNREACHABLE_CFG_SELECT_PREDICATES, | |
| span, | |
| lint_node_id, | |
| move |dcx, level| match wildcard_span { | |
| Some(wildcard_span) => { | |
| diagnostics::UnreachableCfgSelectPredicateWildcard { span, wildcard_span } | |
| .into_diag(dcx, level) | |
| } | |
| None => diagnostics::UnreachableCfgSelectPredicate { span }.into_diag(dcx, level), | |
| }, | |
| ); | |
| }; | |
| for predicate in &mut it { | |
| let CfgSelectPredicate::Cfg(ref cfg_entry) = predicate else { | |
| wildcard_span = Some(predicate.span()); | |
| break; | |
| }; | |
| match cfg_entry { | |
| CfgEntry::Bool(true, _) => { | |
| wildcard_span = Some(predicate.span()); | |
| break; | |
| } | |
| CfgEntry::Bool(false, _) => continue, | |
| CfgEntry::NameValue { name, value, .. } => match value { | |
| None => { | |
| // `name` will be false in all subsequent branches. | |
| let current = known.insert(*name, false); | |
| match current { | |
| None => continue, | |
| Some(false) => { | |
| branch_is_unreachable(predicate, None); | |
| break; | |
| } | |
| Some(true) => { | |
| // this branch will be taken, so all subsequent branches are unreachable. | |
| break; | |
| } | |
| } | |
| } | |
| Some(_) => { | |
| // FIXME(https://github.com/rust-lang/rust/pull/149960#issuecomment-3780301699): expand capabilities. | |
| } | |
| }, | |
| CfgEntry::Not(inner, _) => match &**inner { | |
| CfgEntry::NameValue { name, value: None, .. } => { | |
| // `name` will be true in all subsequent branches. | |
| let current = known.insert(*name, true); | |
| match current { | |
| None => continue, | |
| Some(true) => { | |
| branch_is_unreachable(predicate, None); | |
| break; | |
| } | |
| Some(false) => { | |
| // this branch will be taken, so all subsequent branches are unreachable. | |
| break; | |
| } | |
| } | |
| } | |
| _ => { | |
| // FIXME(https://github.com/rust-lang/rust/pull/149960#issuecomment-3780301699): expand capabilities. | |
| } | |
| }, | |
| CfgEntry::All(_, _) | CfgEntry::Any(_, _) => { | |
| /* for now we don't bother solving these */ | |
| } | |
| CfgEntry::Version(..) => { /* don't bother solving these */ } | |
| } | |
| } | |
| for predicate in it { | |
| branch_is_unreachable(predicate, wildcard_span) | |
| } | |
| } |