Skip to main content

rustc_attr_parsing/attributes/
cfg_select.rs

1use rustc_ast::token::Token;
2use rustc_ast::tokenstream::TokenStream;
3use rustc_ast::{AttrStyle, NodeId, token};
4use rustc_data_structures::fx::FxHashMap;
5use rustc_errors::Diagnostic;
6use rustc_feature::{AttributeTemplate, Features};
7use rustc_hir::attrs::CfgEntry;
8use rustc_hir::{AttrPath, Target};
9use rustc_parse::exp;
10use rustc_parse::parser::{Parser, Recovery};
11use rustc_session::Session;
12use rustc_session::lint::builtin::UNREACHABLE_CFG_SELECT_PREDICATES;
13use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};
14
15use crate::attributes::AttributeSafety;
16use crate::parser::{AllowExprMetavar, MetaItemOrLitParser};
17use crate::{AttributeParser, ParsedDescription, ShouldEmit, errors, parse_cfg_entry};
18
19#[derive(#[automatically_derived]
impl ::core::clone::Clone for CfgSelectPredicate {
    #[inline]
    fn clone(&self) -> CfgSelectPredicate {
        match self {
            CfgSelectPredicate::Cfg(__self_0) =>
                CfgSelectPredicate::Cfg(::core::clone::Clone::clone(__self_0)),
            CfgSelectPredicate::Wildcard(__self_0) =>
                CfgSelectPredicate::Wildcard(::core::clone::Clone::clone(__self_0)),
        }
    }
}Clone)]
20pub enum CfgSelectPredicate {
21    Cfg(CfgEntry),
22    Wildcard(Token),
23}
24
25impl CfgSelectPredicate {
26    fn span(&self) -> Span {
27        match self {
28            CfgSelectPredicate::Cfg(cfg_entry) => cfg_entry.span(),
29            CfgSelectPredicate::Wildcard(token) => token.span,
30        }
31    }
32}
33
34#[derive(#[automatically_derived]
impl ::core::default::Default for CfgSelectBranches {
    #[inline]
    fn default() -> CfgSelectBranches {
        CfgSelectBranches {
            reachable: ::core::default::Default::default(),
            wildcard: ::core::default::Default::default(),
            unreachable: ::core::default::Default::default(),
        }
    }
}Default)]
35pub struct CfgSelectBranches {
36    /// All the conditional branches.
37    pub reachable: Vec<(CfgEntry, TokenStream, Span)>,
38    /// The first wildcard `_ => { ... }` branch.
39    pub wildcard: Option<(Token, TokenStream, Span)>,
40    /// All branches after the first wildcard, including further wildcards.
41    /// These branches are kept for formatting.
42    pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>,
43}
44
45impl CfgSelectBranches {
46    /// Removes the top-most branch for which `predicate` returns `true`,
47    /// or the wildcard if none of the reachable branches satisfied the predicate.
48    pub fn pop_first_match<F>(&mut self, predicate: F) -> Option<(TokenStream, Span)>
49    where
50        F: Fn(&CfgEntry) -> bool,
51    {
52        for (index, (cfg, _, _)) in self.reachable.iter().enumerate() {
53            if predicate(cfg) {
54                let matched = self.reachable.remove(index);
55                return Some((matched.1, matched.2));
56            }
57        }
58
59        self.wildcard.take().map(|(_, tts, span)| (tts, span))
60    }
61
62    /// Consume this value and iterate over all the `TokenStream`s that it stores.
63    pub fn into_iter_tts(self) -> impl Iterator<Item = (TokenStream, Span)> {
64        let it1 = self.reachable.into_iter().map(|(_, tts, span)| (tts, span));
65        let it2 = self.wildcard.into_iter().map(|(_, tts, span)| (tts, span));
66        let it3 = self.unreachable.into_iter().map(|(_, tts, span)| (tts, span));
67
68        it1.chain(it2).chain(it3)
69    }
70}
71
72pub fn parse_cfg_select(
73    p: &mut Parser<'_>,
74    sess: &Session,
75    features: Option<&Features>,
76    lint_node_id: NodeId,
77) -> Result<CfgSelectBranches, ErrorGuaranteed> {
78    let mut branches = CfgSelectBranches::default();
79
80    while p.token != token::Eof {
81        if p.eat_keyword(::rustc_parse::parser::token_type::ExpKeywordPair {
    kw: rustc_span::symbol::kw::Underscore,
    token_type: ::rustc_parse::parser::token_type::TokenType::KwUnderscore,
}exp!(Underscore)) {
82            let underscore = p.prev_token;
83            p.expect(::rustc_parse::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::FatArrow,
    token_type: ::rustc_parse::parser::token_type::TokenType::FatArrow,
}exp!(FatArrow)).map_err(|e| e.emit())?;
84
85            let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?;
86            let span = underscore.span.to(p.token.span);
87
88            match branches.wildcard {
89                None => branches.wildcard = Some((underscore, tts, span)),
90                Some(_) => {
91                    branches.unreachable.push((CfgSelectPredicate::Wildcard(underscore), tts, span))
92                }
93            }
94        } else {
95            let meta = MetaItemOrLitParser::parse_single(
96                p,
97                ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed },
98                AllowExprMetavar::Yes,
99            )
100            .map_err(|diag| diag.emit())?;
101            let cfg_span = meta.span();
102            let cfg = AttributeParser::parse_single_args(
103                sess,
104                cfg_span,
105                cfg_span,
106                AttrStyle::Inner,
107                AttrPath { segments: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [sym::cfg_select]))vec![sym::cfg_select].into_boxed_slice(), span: cfg_span },
108                None,
109                AttributeSafety::Normal,
110                ParsedDescription::Macro,
111                cfg_span,
112                lint_node_id,
113                // Doesn't matter what the target actually is here.
114                Target::Crate,
115                features,
116                ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed },
117                &meta,
118                parse_cfg_entry,
119                &AttributeTemplate::default(),
120            )?;
121
122            p.expect(::rustc_parse::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::FatArrow,
    token_type: ::rustc_parse::parser::token_type::TokenType::FatArrow,
}exp!(FatArrow)).map_err(|e| e.emit())?;
123
124            let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?;
125            let span = cfg_span.to(p.token.span);
126
127            match branches.wildcard {
128                None => branches.reachable.push((cfg, tts, span)),
129                Some(_) => branches.unreachable.push((CfgSelectPredicate::Cfg(cfg), tts, span)),
130            }
131        }
132    }
133
134    let it = branches
135        .reachable
136        .iter()
137        .map(|(entry, _, _)| CfgSelectPredicate::Cfg(entry.clone()))
138        .chain(branches.wildcard.as_ref().map(|(t, _, _)| CfgSelectPredicate::Wildcard(*t)))
139        .chain(branches.unreachable.iter().map(|(entry, _, _)| CfgSelectPredicate::clone(entry)));
140
141    lint_unreachable(p, it, lint_node_id);
142
143    Ok(branches)
144}
145
146fn lint_unreachable(
147    p: &mut Parser<'_>,
148    predicates: impl Iterator<Item = CfgSelectPredicate>,
149    lint_node_id: NodeId,
150) {
151    // Symbols that have a known value.
152    let mut known = FxHashMap::<Symbol, bool>::default();
153    let mut wildcard_span = None;
154    let mut it = predicates;
155
156    let branch_is_unreachable = |predicate: CfgSelectPredicate, wildcard_span| {
157        let span = predicate.span();
158        p.psess.dyn_buffer_lint(
159            UNREACHABLE_CFG_SELECT_PREDICATES,
160            span,
161            lint_node_id,
162            move |dcx, level| match wildcard_span {
163                Some(wildcard_span) => {
164                    errors::UnreachableCfgSelectPredicateWildcard { span, wildcard_span }
165                        .into_diag(dcx, level)
166                }
167                None => errors::UnreachableCfgSelectPredicate { span }.into_diag(dcx, level),
168            },
169        );
170    };
171
172    for predicate in &mut it {
173        let CfgSelectPredicate::Cfg(ref cfg_entry) = predicate else {
174            wildcard_span = Some(predicate.span());
175            break;
176        };
177
178        match cfg_entry {
179            CfgEntry::Bool(true, _) => {
180                wildcard_span = Some(predicate.span());
181                break;
182            }
183            CfgEntry::Bool(false, _) => continue,
184            CfgEntry::NameValue { name, value, .. } => match value {
185                None => {
186                    // `name` will be false in all subsequent branches.
187                    let current = known.insert(*name, false);
188
189                    match current {
190                        None => continue,
191                        Some(false) => {
192                            branch_is_unreachable(predicate, None);
193                            break;
194                        }
195                        Some(true) => {
196                            // this branch will be taken, so all subsequent branches are unreachable.
197                            break;
198                        }
199                    }
200                }
201                Some(_) => { /* for now we don't bother solving these */ }
202            },
203            CfgEntry::Not(inner, _) => match &**inner {
204                CfgEntry::NameValue { name, value: None, .. } => {
205                    // `name` will be true in all subsequent branches.
206                    let current = known.insert(*name, true);
207
208                    match current {
209                        None => continue,
210                        Some(true) => {
211                            branch_is_unreachable(predicate, None);
212                            break;
213                        }
214                        Some(false) => {
215                            // this branch will be taken, so all subsequent branches are unreachable.
216                            break;
217                        }
218                    }
219                }
220                _ => { /* for now we don't bother solving these */ }
221            },
222            CfgEntry::All(_, _) | CfgEntry::Any(_, _) => {
223                /* for now we don't bother solving these */
224            }
225            CfgEntry::Version(..) => { /* don't bother solving these */ }
226        }
227    }
228
229    for predicate in it {
230        branch_is_unreachable(predicate, wildcard_span)
231    }
232}