|
1 | 1 | use rustc_ast::token::Token; |
2 | 2 | use rustc_ast::tokenstream::TokenStream; |
3 | 3 | use rustc_ast::{AttrStyle, NodeId, token}; |
| 4 | +use rustc_data_structures::fx::FxHashMap; |
4 | 5 | use rustc_feature::{AttributeTemplate, Features}; |
5 | 6 | use rustc_hir::attrs::CfgEntry; |
6 | 7 | use rustc_hir::{AttrPath, Target}; |
7 | 8 | use rustc_parse::exp; |
8 | 9 | use rustc_parse::parser::{Parser, Recovery}; |
9 | 10 | use rustc_session::Session; |
10 | | -use rustc_span::{ErrorGuaranteed, Span, sym}; |
| 11 | +use rustc_session::lint::BuiltinLintDiag; |
| 12 | +use rustc_session::lint::builtin::UNREACHABLE_CFG_SELECT_PREDICATES; |
| 13 | +use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; |
11 | 14 |
|
12 | 15 | use crate::parser::MetaItemOrLitParser; |
13 | 16 | use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry}; |
14 | 17 |
|
| 18 | +#[derive(Clone)] |
15 | 19 | pub enum CfgSelectPredicate { |
16 | 20 | Cfg(CfgEntry), |
17 | 21 | Wildcard(Token), |
18 | 22 | } |
19 | 23 |
|
| 24 | +impl 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 | + |
20 | 33 | #[derive(Default)] |
21 | 34 | pub struct CfgSelectBranches { |
22 | 35 | /// All the conditional branches. |
@@ -115,5 +128,102 @@ pub fn parse_cfg_select( |
115 | 128 | } |
116 | 129 | } |
117 | 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 | + |
118 | 146 | Ok(branches) |
119 | 147 | } |
| 148 | + |
| 149 | +fn 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 | +} |
0 commit comments