Skip to content

Commit 97b1ed3

Browse files
committed
Auto merge of #149960 - folkertdev:cfg-select-unreachable-lint, r=<try>
add `unreachable_cfg_select_predicates` lint try-job: test-various
2 parents 06cafcb + bad1a45 commit 97b1ed3

17 files changed

Lines changed: 323 additions & 76 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3539,6 +3539,7 @@ dependencies = [
35393539
"rustc_abi",
35403540
"rustc_ast",
35413541
"rustc_ast_pretty",
3542+
"rustc_data_structures",
35423543
"rustc_errors",
35433544
"rustc_feature",
35443545
"rustc_hir",

compiler/rustc_attr_parsing/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ edition = "2024"
88
rustc_abi = { path = "../rustc_abi" }
99
rustc_ast = { path = "../rustc_ast" }
1010
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
11+
rustc_data_structures = { path = "../rustc_data_structures" }
1112
rustc_errors = { path = "../rustc_errors" }
1213
rustc_feature = { path = "../rustc_feature" }
1314
rustc_hir = { path = "../rustc_hir" }

compiler/rustc_attr_parsing/src/attributes/cfg_select.rs

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,35 @@
11
use rustc_ast::token::Token;
22
use rustc_ast::tokenstream::TokenStream;
33
use rustc_ast::{AttrStyle, NodeId, token};
4+
use rustc_data_structures::fx::FxHashMap;
45
use rustc_feature::{AttributeTemplate, Features};
56
use rustc_hir::attrs::CfgEntry;
67
use rustc_hir::{AttrPath, Target};
78
use rustc_parse::exp;
89
use rustc_parse::parser::{Parser, Recovery};
910
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};
1114

1215
use crate::parser::MetaItemOrLitParser;
1316
use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry};
1417

18+
#[derive(Clone)]
1519
pub enum CfgSelectPredicate {
1620
Cfg(CfgEntry),
1721
Wildcard(Token),
1822
}
1923

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+
2033
#[derive(Default)]
2134
pub struct CfgSelectBranches {
2235
/// All the conditional branches.
@@ -115,5 +128,102 @@ pub fn parse_cfg_select(
115128
}
116129
}
117130

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+
118146
Ok(branches)
119147
}
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+
}

compiler/rustc_builtin_macros/src/cfg_select.rs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
use rustc_ast::tokenstream::TokenStream;
22
use rustc_ast::{Expr, ast};
33
use rustc_attr_parsing as attr;
4-
use rustc_attr_parsing::{
5-
CfgSelectBranches, CfgSelectPredicate, EvalConfigResult, parse_cfg_select,
6-
};
4+
use rustc_attr_parsing::{CfgSelectBranches, EvalConfigResult, parse_cfg_select};
75
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult};
86
use rustc_span::{Ident, Span, sym};
97
use smallvec::SmallVec;
108

11-
use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable};
9+
use crate::errors::CfgSelectNoMatches;
1210

1311
/// This intermediate structure is used to emit parse errors for the branches that are not chosen.
1412
/// The `MacResult` instance below parses all branches, emitting any errors it encounters, but only
@@ -75,18 +73,6 @@ pub(super) fn expand_cfg_select<'cx>(
7573
ecx.current_expansion.lint_node_id,
7674
) {
7775
Ok(mut branches) => {
78-
if let Some((underscore, _, _)) = branches.wildcard {
79-
// Warn for every unreachable predicate. We store the fully parsed branch for rustfmt.
80-
for (predicate, _, _) in &branches.unreachable {
81-
let span = match predicate {
82-
CfgSelectPredicate::Wildcard(underscore) => underscore.span,
83-
CfgSelectPredicate::Cfg(cfg) => cfg.span(),
84-
};
85-
let err = CfgSelectUnreachable { span, wildcard_span: underscore.span };
86-
ecx.dcx().emit_warn(err);
87-
}
88-
}
89-
9076
if let Some((selected_tts, selected_span)) = branches.pop_first_match(|cfg| {
9177
matches!(attr::eval_config_entry(&ecx.sess, cfg), EvalConfigResult::True)
9278
}) {

compiler/rustc_builtin_macros/src/errors.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,17 +1086,6 @@ pub(crate) struct CfgSelectNoMatches {
10861086
pub span: Span,
10871087
}
10881088

1089-
#[derive(Diagnostic)]
1090-
#[diag("unreachable predicate")]
1091-
pub(crate) struct CfgSelectUnreachable {
1092-
#[primary_span]
1093-
#[label("this predicate is never reached")]
1094-
pub span: Span,
1095-
1096-
#[label("always matches")]
1097-
pub wildcard_span: Span,
1098-
}
1099-
11001089
#[derive(Diagnostic)]
11011090
#[diag("`#[eii_declaration(...)]` is only valid on macros")]
11021091
pub(crate) struct EiiExternTargetExpectedMacro {

compiler/rustc_feature/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,8 @@ declare_features! (
394394
(unstable, cfg_sanitize, "1.41.0", Some(39699)),
395395
/// Allows `cfg(sanitizer_cfi_generalize_pointers)` and `cfg(sanitizer_cfi_normalize_integers)`.
396396
(unstable, cfg_sanitizer_cfi, "1.77.0", Some(89653)),
397+
/// Provides a native way to easily manage multiple conditional flags without having to rewrite each clause multiple times.
398+
(unstable, cfg_select, "CURRENT_RUSTC_VERSION", Some(115585)),
397399
/// Allows `cfg(target(abi = "..."))`.
398400
(unstable, cfg_target_compact, "1.63.0", Some(96901)),
399401
/// Allows `cfg(target_has_atomic_load_store = "...")`.

compiler/rustc_lint/messages.ftl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,13 @@ lint_unpredictable_fn_pointer_comparisons = function pointer comparisons do not
998998
999999
lint_unqualified_local_imports = `use` of a local item without leading `self::`, `super::`, or `crate::`
10001000
1001+
lint_unreachable_cfg_select_predicate = unreachable configuration predicate
1002+
.label = this configuration predicate is never reached
1003+
1004+
lint_unreachable_cfg_select_predicate_wildcard = unreachable configuration predicate
1005+
.label = always matches
1006+
.label2 = this configuration predicate is never reached
1007+
10011008
lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe
10021009
.label = usage of unsafe attribute
10031010
lint_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)`

compiler/rustc_lint/src/early/diagnostics.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,14 @@ pub fn decorate_builtin_lint(
293293
}
294294
.decorate_lint(diag);
295295
}
296+
BuiltinLintDiag::UnreachableCfg { span, wildcard_span } => match wildcard_span {
297+
Some(wildcard_span) => {
298+
lints::UnreachableCfgSelectPredicateWildcard { span, wildcard_span }
299+
.decorate_lint(diag)
300+
}
301+
None => lints::UnreachableCfgSelectPredicate { span }.decorate_lint(diag),
302+
},
303+
296304
BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => {
297305
lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)
298306
}

compiler/rustc_lint/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,9 @@ fn register_builtins(store: &mut LintStore) {
297297
UNUSED_ASSIGNMENTS,
298298
DEAD_CODE,
299299
UNUSED_MUT,
300+
// FIXME: add this lint when it becomes stable,
301+
// see https://github.com/rust-lang/rust/issues/115585.
302+
// UNREACHABLE_CFG_SELECT_PREDICATES,
300303
UNREACHABLE_CODE,
301304
UNREACHABLE_PATTERNS,
302305
UNUSED_MUST_USE,

compiler/rustc_lint/src/lints.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3340,3 +3340,20 @@ pub(crate) struct UnknownCrateTypesSuggestion {
33403340
pub span: Span,
33413341
pub snippet: Symbol,
33423342
}
3343+
3344+
#[derive(LintDiagnostic)]
3345+
#[diag(lint_unreachable_cfg_select_predicate)]
3346+
pub(crate) struct UnreachableCfgSelectPredicate {
3347+
#[label]
3348+
pub span: Span,
3349+
}
3350+
3351+
#[derive(LintDiagnostic)]
3352+
#[diag(lint_unreachable_cfg_select_predicate_wildcard)]
3353+
pub(crate) struct UnreachableCfgSelectPredicateWildcard {
3354+
#[label(lint_label2)]
3355+
pub span: Span,
3356+
3357+
#[label]
3358+
pub wildcard_span: Span,
3359+
}

0 commit comments

Comments
 (0)