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