make the lint more sophisticated

This commit is contained in:
Folkert de Vries 2026-02-06 19:48:33 +01:00
parent 8aea4b1775
commit a6bd7cc54e
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
10 changed files with 194 additions and 54 deletions

View file

@ -3539,6 +3539,7 @@ dependencies = [
"rustc_abi",
"rustc_ast",
"rustc_ast_pretty",
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
"rustc_hir",

View file

@ -8,6 +8,7 @@ edition = "2024"
rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" }
rustc_hir = { path = "../rustc_hir" }

View file

@ -1,6 +1,7 @@
use rustc_ast::token::Token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{AttrStyle, NodeId, token};
use rustc_data_structures::fx::FxHashMap;
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::attrs::CfgEntry;
use rustc_hir::{AttrPath, Target};
@ -9,11 +10,12 @@ use rustc_parse::parser::{Parser, Recovery};
use rustc_session::Session;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::UNREACHABLE_CFG_SELECT_PREDICATES;
use rustc_span::{ErrorGuaranteed, Span, sym};
use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};
use crate::parser::MetaItemOrLitParser;
use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry};
#[derive(Clone)]
pub enum CfgSelectPredicate {
Cfg(CfgEntry),
Wildcard(Token),
@ -126,19 +128,102 @@ pub fn parse_cfg_select(
}
}
if let Some((underscore, _, _)) = branches.wildcard
&& features.map_or(false, |f| f.enabled(rustc_span::sym::cfg_select))
if let Some(features) = features
&& features.enabled(sym::cfg_select)
{
for (predicate, _, _) in &branches.unreachable {
let span = predicate.span();
p.psess.buffer_lint(
UNREACHABLE_CFG_SELECT_PREDICATES,
span,
lint_node_id,
BuiltinLintDiag::UnreachableCfg { span, wildcard_span: underscore.span },
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 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.buffer_lint(
UNREACHABLE_CFG_SELECT_PREDICATES,
span,
lint_node_id,
BuiltinLintDiag::UnreachableCfg { span, wildcard_span },
);
};
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(_) => { /* for now we don't bother solving these */ }
},
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;
}
}
}
_ => { /* for now we don't bother solving these */ }
},
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)
}
}

View file

@ -999,6 +999,9 @@ lint_unpredictable_fn_pointer_comparisons = function pointer comparisons do not
lint_unqualified_local_imports = `use` of a local item without leading `self::`, `super::`, or `crate::`
lint_unreachable_cfg_select_predicate = unreachable configuration predicate
.label = this configuration predicate is never reached
lint_unreachable_cfg_select_predicate_wildcard = unreachable configuration predicate
.label = always matches
.label2 = this configuration predicate is never reached

View file

@ -293,9 +293,13 @@ pub fn decorate_builtin_lint(
}
.decorate_lint(diag);
}
BuiltinLintDiag::UnreachableCfg { span, wildcard_span } => {
lints::UnreachableCfgSelectPredicate { span, wildcard_span }.decorate_lint(diag);
}
BuiltinLintDiag::UnreachableCfg { span, wildcard_span } => match wildcard_span {
Some(wildcard_span) => {
lints::UnreachableCfgSelectPredicateWildcard { span, wildcard_span }
.decorate_lint(diag)
}
None => lints::UnreachableCfgSelectPredicate { span }.decorate_lint(diag),
},
BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => {
lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)

View file

@ -3344,6 +3344,13 @@ pub(crate) struct UnknownCrateTypesSuggestion {
#[derive(LintDiagnostic)]
#[diag(lint_unreachable_cfg_select_predicate)]
pub(crate) struct UnreachableCfgSelectPredicate {
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(lint_unreachable_cfg_select_predicate_wildcard)]
pub(crate) struct UnreachableCfgSelectPredicateWildcard {
#[label(lint_label2)]
pub span: Span,

View file

@ -750,7 +750,7 @@ pub enum BuiltinLintDiag {
AttributeLint(AttributeLintKind),
UnreachableCfg {
span: Span,
wildcard_span: Span,
wildcard_span: Option<Span>,
},
}

View file

@ -4,7 +4,7 @@
#![crate_type = "lib"]
cfg_select! {
true => {}
false => {}
invalid_cfg1 => {}
//~^ WARN unexpected `cfg` condition name
_ => {}
@ -13,6 +13,6 @@ cfg_select! {
cfg_select! {
invalid_cfg2 => {}
//~^ WARN unexpected `cfg` condition name
true => {}
false => {}
_ => {}
}

View file

@ -24,40 +24,40 @@ fn arm_rhs_expr_1() -> i32 {
fn arm_rhs_expr_2() -> i32 {
cfg_select! {
true => 1,
false => 2
false => 2,
true => 1
}
}
fn arm_rhs_expr_3() -> i32 {
cfg_select! {
true => 1,
false => 2,
true => { 42 }
false => -1 as i32,
true => 2 + 2,
false => "",
true => if true { 42 } else { 84 }
false => if true { 42 } else { 84 },
true => return 42,
false => loop {}
true => (1, 2),
false => (1, 2,),
true => todo!(),
false => println!("hello"),
any(true) => 1,
any(false) => 2,
any(true) => { 42 }
any(false) => -1 as i32,
any(true) => 2 + 2,
any(false) => "",
any(true) => if true { 42 } else { 84 }
any(false) => if true { 42 } else { 84 },
any(true) => return 42,
any(false) => loop {}
any(true) => (1, 2),
any(false) => (1, 2,),
any(true) => todo!(),
any(false) => println!("hello"),
}
}
fn expand_to_statements() -> i32 {
cfg_select! {
true => {
let a = 1;
a + 1
}
false => {
let b = 2;
b + 1
}
true => {
let a = 1;
a + 1
}
}
}
@ -77,7 +77,7 @@ fn expand_to_pattern(x: Option<i32>) -> bool {
}
cfg_select! {
true => {
false => {
fn foo() {}
}
_ => {
@ -89,7 +89,7 @@ struct S;
impl S {
cfg_select! {
true => {
false => {
fn foo() {}
}
_ => {
@ -100,7 +100,7 @@ impl S {
trait T {
cfg_select! {
true => {
false => {
fn a();
}
_ => {
@ -111,7 +111,7 @@ trait T {
impl T for S {
cfg_select! {
true => {
false => {
fn a() {}
}
_ => {
@ -122,7 +122,7 @@ impl T for S {
extern "C" {
cfg_select! {
true => {
false => {
fn puts(s: *const i8) -> i32;
}
_ => {
@ -137,6 +137,25 @@ cfg_select! {
//~^ WARN unreachable configuration predicate
}
cfg_select! {
true => {}
_ => {}
//~^ WARN unreachable configuration predicate
}
cfg_select! {
unix => {}
not(unix) => {}
_ => {}
//~^ WARN unreachable configuration predicate
}
cfg_select! {
unix => {}
unix => {}
//~^ WARN unreachable configuration predicate
}
cfg_select! {
//~^ ERROR none of the predicates in this `cfg_select` evaluated to true
false => {}

View file

@ -1,5 +1,5 @@
error: none of the predicates in this `cfg_select` evaluated to true
--> $DIR/cfg_select.rs:140:1
--> $DIR/cfg_select.rs:159:1
|
LL | / cfg_select! {
LL | |
@ -8,49 +8,49 @@ LL | | }
| |_^
error: none of the predicates in this `cfg_select` evaluated to true
--> $DIR/cfg_select.rs:145:1
--> $DIR/cfg_select.rs:164:1
|
LL | cfg_select! {}
| ^^^^^^^^^^^^^^
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `=>`
--> $DIR/cfg_select.rs:149:5
--> $DIR/cfg_select.rs:168:5
|
LL | => {}
| ^^
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression
--> $DIR/cfg_select.rs:154:5
--> $DIR/cfg_select.rs:173:5
|
LL | () => {}
| ^^ expressions are not allowed here
error[E0539]: malformed `cfg_select` macro input
--> $DIR/cfg_select.rs:159:5
--> $DIR/cfg_select.rs:178:5
|
LL | "str" => {}
| ^^^^^ expected a valid identifier here
error[E0539]: malformed `cfg_select` macro input
--> $DIR/cfg_select.rs:164:5
--> $DIR/cfg_select.rs:183:5
|
LL | a::b => {}
| ^^^^ expected a valid identifier here
error[E0537]: invalid predicate `a`
--> $DIR/cfg_select.rs:169:5
--> $DIR/cfg_select.rs:188:5
|
LL | a() => {}
| ^^^
error: expected one of `(`, `::`, `=>`, or `=`, found `+`
--> $DIR/cfg_select.rs:174:7
--> $DIR/cfg_select.rs:193:7
|
LL | a + 1 => {}
| ^ expected one of `(`, `::`, `=>`, or `=`
error: expected one of `(`, `::`, `=>`, or `=`, found `!`
--> $DIR/cfg_select.rs:180:8
--> $DIR/cfg_select.rs:199:8
|
LL | cfg!() => {}
| ^ expected one of `(`, `::`, `=>`, or `=`
@ -69,8 +69,28 @@ note: the lint level is defined here
LL | #![warn(unreachable_cfg_select_predicates)] // Unused warnings are disabled by default in UI tests.
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: unreachable configuration predicate
--> $DIR/cfg_select.rs:142:5
|
LL | true => {}
| ---- always matches
LL | _ => {}
| ^ this configuration predicate is never reached
warning: unreachable configuration predicate
--> $DIR/cfg_select.rs:149:5
|
LL | _ => {}
| ^ this configuration predicate is never reached
warning: unreachable configuration predicate
--> $DIR/cfg_select.rs:155:5
|
LL | unix => {}
| ^^^^ this configuration predicate is never reached
warning: unexpected `cfg` condition name: `a`
--> $DIR/cfg_select.rs:174:5
--> $DIR/cfg_select.rs:193:5
|
LL | a + 1 => {}
| ^ help: found config with similar value: `target_feature = "a"`
@ -81,7 +101,7 @@ LL | a + 1 => {}
= note: `#[warn(unexpected_cfgs)]` on by default
warning: unexpected `cfg` condition name: `cfg`
--> $DIR/cfg_select.rs:180:5
--> $DIR/cfg_select.rs:199:5
|
LL | cfg!() => {}
| ^^^
@ -89,7 +109,7 @@ LL | cfg!() => {}
= help: to expect this configuration use `--check-cfg=cfg(cfg)`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration
error: aborting due to 9 previous errors; 3 warnings emitted
error: aborting due to 9 previous errors; 6 warnings emitted
Some errors have detailed explanations: E0537, E0539.
For more information about an error, try `rustc --explain E0537`.