diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs index e80084021a849..e3b22ef548a1e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs @@ -7,6 +7,8 @@ use rustc_hir::{AttrPath, Target}; use rustc_parse::exp; use rustc_parse::parser::Parser; 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 crate::parser::MetaItemOrLitParser; @@ -17,6 +19,15 @@ pub enum CfgSelectPredicate { Wildcard(Token), } +impl CfgSelectPredicate { + fn span(&self) -> Span { + match self { + CfgSelectPredicate::Cfg(cfg_entry) => cfg_entry.span(), + CfgSelectPredicate::Wildcard(token) => token.span, + } + } +} + #[derive(Default)] pub struct CfgSelectBranches { /// All the conditional branches. @@ -112,5 +123,19 @@ pub fn parse_cfg_select( } } + if let Some((underscore, _, _)) = branches.wildcard + && features.map_or(false, |f| f.enabled(rustc_span::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 }, + ); + } + } + Ok(branches) } diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index f9ffddf790847..2d7ee1ee19680 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -85,10 +85,6 @@ builtin_macros_cfg_accessible_unspecified_path = `cfg_accessible` path is not sp builtin_macros_cfg_select_no_matches = none of the predicates in this `cfg_select` evaluated to true -builtin_macros_cfg_select_unreachable = unreachable predicate - .label = always matches - .label2 = this predicate is never reached - builtin_macros_coerce_pointee_requires_maybe_sized = `derive(CoercePointee)` requires `{$name}` to be marked `?Sized` builtin_macros_coerce_pointee_requires_one_field = `CoercePointee` can only be derived on `struct`s with at least one field diff --git a/compiler/rustc_builtin_macros/src/cfg_select.rs b/compiler/rustc_builtin_macros/src/cfg_select.rs index f11190b281053..35098722a910e 100644 --- a/compiler/rustc_builtin_macros/src/cfg_select.rs +++ b/compiler/rustc_builtin_macros/src/cfg_select.rs @@ -1,14 +1,12 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::{Expr, ast}; use rustc_attr_parsing as attr; -use rustc_attr_parsing::{ - CfgSelectBranches, CfgSelectPredicate, EvalConfigResult, parse_cfg_select, -}; +use rustc_attr_parsing::{CfgSelectBranches, EvalConfigResult, parse_cfg_select}; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult}; use rustc_span::{Ident, Span, sym}; use smallvec::SmallVec; -use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable}; +use crate::errors::CfgSelectNoMatches; /// This intermediate structure is used to emit parse errors for the branches that are not chosen. /// 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>( ecx.current_expansion.lint_node_id, ) { Ok(mut branches) => { - if let Some((underscore, _, _)) = branches.wildcard { - // Warn for every unreachable predicate. We store the fully parsed branch for rustfmt. - for (predicate, _, _) in &branches.unreachable { - let span = match predicate { - CfgSelectPredicate::Wildcard(underscore) => underscore.span, - CfgSelectPredicate::Cfg(cfg) => cfg.span(), - }; - let err = CfgSelectUnreachable { span, wildcard_span: underscore.span }; - ecx.dcx().emit_warn(err); - } - } - if let Some((selected_tts, selected_span)) = branches.pop_first_match(|cfg| { matches!(attr::eval_config_entry(&ecx.sess, cfg), EvalConfigResult::True) }) { diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 0aaa8cf558c43..864a0e20b1257 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -994,17 +994,6 @@ pub(crate) struct CfgSelectNoMatches { pub span: Span, } -#[derive(Diagnostic)] -#[diag(builtin_macros_cfg_select_unreachable)] -pub(crate) struct CfgSelectUnreachable { - #[primary_span] - #[label(builtin_macros_label2)] - pub span: Span, - - #[label] - pub wildcard_span: Span, -} - #[derive(Diagnostic)] #[diag(builtin_macros_eii_declaration_expected_macro)] pub(crate) struct EiiExternTargetExpectedMacro { diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 105eb573967df..6709b26f0236e 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -392,6 +392,8 @@ declare_features! ( (unstable, cfg_sanitize, "1.41.0", Some(39699)), /// Allows `cfg(sanitizer_cfi_generalize_pointers)` and `cfg(sanitizer_cfi_normalize_integers)`. (unstable, cfg_sanitizer_cfi, "1.77.0", Some(89653)), + /// Provides a native way to easily manage multiple conditional flags without having to rewrite each clause multiple times. + (unstable, cfg_select, "CURRENT_RUSTC_VERSION", Some(115585)), /// Allows `cfg(target(abi = "..."))`. (unstable, cfg_target_compact, "1.63.0", Some(96901)), /// Allows `cfg(target_has_atomic_load_store = "...")`. diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 867b937d4090d..a9d6756821f0b 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -986,6 +986,10 @@ 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 = always matches + .label2 = this configuration predicate is never reached + lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe .label = usage of unsafe attribute lint_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 377b3c439453e..5091e4f7cfad5 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -293,6 +293,10 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } + BuiltinLintDiag::UnreachableCfg { span, wildcard_span } => { + lints::UnreachableCfgSelectPredicate { span, wildcard_span }.decorate_lint(diag); + } + BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => { lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag) } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 80b32645a8953..ea31a6373cbc7 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -297,6 +297,9 @@ fn register_builtins(store: &mut LintStore) { UNUSED_ASSIGNMENTS, DEAD_CODE, UNUSED_MUT, + // FIXME: add this lint when it becomes stable, + // see https://github.com/rust-lang/rust/issues/115585. + // UNREACHABLE_CFG_SELECT_PREDICATES, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, UNUSED_MUST_USE, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index a20d90e1227e9..18811b49045a9 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3325,3 +3325,13 @@ pub(crate) struct UnknownCrateTypesSuggestion { pub span: Span, pub snippet: Symbol, } + +#[derive(LintDiagnostic)] +#[diag(lint_unreachable_cfg_select_predicate)] +pub(crate) struct UnreachableCfgSelectPredicate { + #[label(lint_label2)] + pub span: Span, + + #[label] + pub wildcard_span: Span, +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index ff108031badc1..b05ea1a9bc5c5 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -122,6 +122,7 @@ declare_lint_pass! { UNKNOWN_LINTS, UNNAMEABLE_TEST_ITEMS, UNNAMEABLE_TYPES, + UNREACHABLE_CFG_SELECT_PREDICATES, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, UNSAFE_ATTR_OUTSIDE_UNSAFE, @@ -854,6 +855,34 @@ declare_lint! { "detects unreachable patterns" } +declare_lint! { + /// The `unreachable_cfg_select_predicates` lint detects unreachable configuration + /// predicates in the `cfg_select!` macro. + /// + /// ### Example + /// + /// ```rust + /// #![feature(cfg_select)] + /// cfg_select! { + /// _ => (), + /// windows => (), + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This usually indicates a mistake in how the predicates are specified or + /// ordered. In this example, the `_` predicate will always match, so the + /// `windows` is impossible to reach. Remember, arms match in order, you + /// probably wanted to put the `windows` case above the `_` case. + pub UNREACHABLE_CFG_SELECT_PREDICATES, + Warn, + "detects unreachable configuration predicates in the cfg_select macro", + @feature_gate = cfg_select; +} + declare_lint! { /// The `overlapping_range_endpoints` lint detects `match` arms that have [range patterns] that /// overlap on their endpoints. diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 4c78287b7784f..6f2ef342e5626 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -748,6 +748,10 @@ pub enum BuiltinLintDiag { }, UnusedVisibility(Span), AttributeLint(AttributeLintKind), + UnreachableCfg { + span: Span, + wildcard_span: Span, + }, } #[derive(Debug, HashStable_Generic)] diff --git a/tests/ui/feature-gates/feature-gate-cfg-select.rs b/tests/ui/feature-gates/feature-gate-cfg-select.rs new file mode 100644 index 0000000000000..0963ed0015082 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-select.rs @@ -0,0 +1,11 @@ +#![warn(unreachable_cfg_select_predicates)] +//~^ WARN unknown lint: `unreachable_cfg_select_predicates` + +cfg_select! { + //~^ ERROR use of unstable library feature `cfg_select` + _ => {} + // With the feature enabled, this branch would trip the unreachable_cfg_select_predicate lint. + true => {} +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-cfg-select.stderr b/tests/ui/feature-gates/feature-gate-cfg-select.stderr new file mode 100644 index 0000000000000..0fdce49751645 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-select.stderr @@ -0,0 +1,25 @@ +error[E0658]: use of unstable library feature `cfg_select` + --> $DIR/feature-gate-cfg-select.rs:4:1 + | +LL | cfg_select! { + | ^^^^^^^^^^ + | + = note: see issue #115585 for more information + = help: add `#![feature(cfg_select)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: unknown lint: `unreachable_cfg_select_predicates` + --> $DIR/feature-gate-cfg-select.rs:1:9 + | +LL | #![warn(unreachable_cfg_select_predicates)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `unreachable_cfg_select_predicates` lint is unstable + = note: see issue #115585 for more information + = help: add `#![feature(cfg_select)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: `#[warn(unknown_lints)]` on by default + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/macros/cfg_select.rs b/tests/ui/macros/cfg_select.rs index 2369158ba82a2..ecdeb006edf5f 100644 --- a/tests/ui/macros/cfg_select.rs +++ b/tests/ui/macros/cfg_select.rs @@ -1,5 +1,6 @@ #![feature(cfg_select)] #![crate_type = "lib"] +#![warn(unreachable_cfg_select_predicates)] // Unused warnings are disabled by default in UI tests. fn print() { println!(cfg_select! { @@ -133,7 +134,7 @@ extern "C" { cfg_select! { _ => {} true => {} - //~^ WARN unreachable predicate + //~^ WARN unreachable configuration predicate } cfg_select! { diff --git a/tests/ui/macros/cfg_select.stderr b/tests/ui/macros/cfg_select.stderr index ffd8540425ab0..7ef4ec06d93bf 100644 --- a/tests/ui/macros/cfg_select.stderr +++ b/tests/ui/macros/cfg_select.stderr @@ -1,13 +1,5 @@ -warning: unreachable predicate - --> $DIR/cfg_select.rs:135:5 - | -LL | _ => {} - | - always matches -LL | true => {} - | ^^^^ this predicate is never reached - error: none of the predicates in this `cfg_select` evaluated to true - --> $DIR/cfg_select.rs:139:1 + --> $DIR/cfg_select.rs:140:1 | LL | / cfg_select! { LL | | @@ -16,55 +8,69 @@ LL | | } | |_^ error: none of the predicates in this `cfg_select` evaluated to true - --> $DIR/cfg_select.rs:144:1 + --> $DIR/cfg_select.rs:145:1 | LL | cfg_select! {} | ^^^^^^^^^^^^^^ error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `=>` - --> $DIR/cfg_select.rs:148:5 + --> $DIR/cfg_select.rs:149:5 | LL | => {} | ^^ error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression - --> $DIR/cfg_select.rs:153:5 + --> $DIR/cfg_select.rs:154:5 | LL | () => {} | ^^ expressions are not allowed here error[E0539]: malformed `cfg_select` macro input - --> $DIR/cfg_select.rs:158:5 + --> $DIR/cfg_select.rs:159:5 | LL | "str" => {} | ^^^^^ expected a valid identifier here error[E0539]: malformed `cfg_select` macro input - --> $DIR/cfg_select.rs:163:5 + --> $DIR/cfg_select.rs:164:5 | LL | a::b => {} | ^^^^ expected a valid identifier here error[E0537]: invalid predicate `a` - --> $DIR/cfg_select.rs:168:5 + --> $DIR/cfg_select.rs:169:5 | LL | a() => {} | ^^^ error: expected one of `(`, `::`, `=>`, or `=`, found `+` - --> $DIR/cfg_select.rs:173:7 + --> $DIR/cfg_select.rs:174:7 | LL | a + 1 => {} | ^ expected one of `(`, `::`, `=>`, or `=` error: expected one of `(`, `::`, `=>`, or `=`, found `!` - --> $DIR/cfg_select.rs:179:8 + --> $DIR/cfg_select.rs:180:8 | LL | cfg!() => {} | ^ expected one of `(`, `::`, `=>`, or `=` +warning: unreachable configuration predicate + --> $DIR/cfg_select.rs:136:5 + | +LL | _ => {} + | - always matches +LL | true => {} + | ^^^^ this configuration predicate is never reached + | +note: the lint level is defined here + --> $DIR/cfg_select.rs:3:9 + | +LL | #![warn(unreachable_cfg_select_predicates)] // Unused warnings are disabled by default in UI tests. + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + warning: unexpected `cfg` condition name: `a` - --> $DIR/cfg_select.rs:173:5 + --> $DIR/cfg_select.rs:174:5 | LL | a + 1 => {} | ^ help: found config with similar value: `target_feature = "a"` @@ -75,7 +81,7 @@ LL | a + 1 => {} = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition name: `cfg` - --> $DIR/cfg_select.rs:179:5 + --> $DIR/cfg_select.rs:180:5 | LL | cfg!() => {} | ^^^