diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs index 0c0915558089e..281d7d64b9ed9 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::attrs::CfgEntry; use rustc_parse::exp; use rustc_parse::parser::Parser; use rustc_session::Session; +use rustc_session::lint::BuiltinLintDiag; +use rustc_session::lint::builtin::UNREACHABLE_CFGS; use rustc_span::{ErrorGuaranteed, Ident, Span}; 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. @@ -86,5 +97,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_CFGS, + 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 86671d0326da2..6ee4eaca44b3d 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -86,10 +86,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 dc8077b2a1ffb..0906859122e37 100644 --- a/compiler/rustc_builtin_macros/src/cfg_select.rs +++ b/compiler/rustc_builtin_macros/src/cfg_select.rs @@ -1,12 +1,10 @@ use rustc_ast::tokenstream::TokenStream; 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, MacroExpanderResult}; use rustc_span::{Ident, Span, sym}; -use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable}; +use crate::errors::CfgSelectNoMatches; /// Selects the first arm whose predicate evaluates to true. fn select_arm(ecx: &ExtCtxt<'_>, branches: CfgSelectBranches) -> Option<(TokenStream, Span)> { @@ -32,18 +30,6 @@ pub(super) fn expand_cfg_select<'cx>( ecx.current_expansion.lint_node_id, ) { Ok(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((tts, arm_span)) = select_arm(ecx, branches) { return ExpandResult::from_tts( ecx, diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index dd6a5a20ccebc..5d1c00bbe4f3b 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -999,14 +999,3 @@ pub(crate) struct CfgSelectNoMatches { #[primary_span] 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, -} diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 56a0a3ceebf5a..83211ca814b6a 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -969,6 +969,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 = 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 f622de7f84d93..35c39c4be6fd9 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::UnreachableCfg { 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 49929a0a9bc76..f7c5f23ced161 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -296,6 +296,7 @@ fn register_builtins(store: &mut LintStore) { UNUSED_ASSIGNMENTS, DEAD_CODE, UNUSED_MUT, + UNREACHABLE_CFGS, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, UNUSED_MUST_USE, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 5017ce7caa525..79f0e22fd0a7b 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3298,3 +3298,13 @@ pub(crate) struct DocTestUnknown { #[derive(LintDiagnostic)] #[diag(lint_doc_test_literal)] pub(crate) struct DocTestLiteral; + +#[derive(LintDiagnostic)] +#[diag(lint_unreachable_cfg)] +pub(crate) struct UnreachableCfg { + #[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 99cce0c44b86d..b21718fd29778 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -120,6 +120,7 @@ declare_lint_pass! { UNKNOWN_LINTS, UNNAMEABLE_TEST_ITEMS, UNNAMEABLE_TYPES, + UNREACHABLE_CFGS, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, UNSAFE_ATTR_OUTSIDE_UNSAFE, @@ -852,6 +853,33 @@ declare_lint! { "detects unreachable patterns" } +declare_lint! { + /// The `unreachable_cfgs` lint detects unreachable configuration + /// predicates. + /// + /// ### 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_CFGS, + Warn, + "detects unreachable configuration predicates" +} + 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 7b41cfbb43ef0..a4de8b9099030 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/cfg_select.rs b/tests/ui/feature-gates/cfg_select.rs new file mode 100644 index 0000000000000..a50054b97171b --- /dev/null +++ b/tests/ui/feature-gates/cfg_select.rs @@ -0,0 +1,12 @@ +#![warn(unreachable_cfgs)] // Unused warnings are disabled by default in UI tests. + +// `#[feature(cfg_select)]` is a libs feature (so, not a lang feature), but it lints on unreachable +// branches, and that lint should only be emitted when the feature is enabled. + +cfg_select! { + //~^ ERROR use of unstable library feature `cfg_select` + _ => {} + true => {} // With the feature enabled, this branch would trip the unreachable_cfgs lint. +} + +fn main() {} diff --git a/tests/ui/feature-gates/cfg_select.stderr b/tests/ui/feature-gates/cfg_select.stderr new file mode 100644 index 0000000000000..4e7c411e55fc1 --- /dev/null +++ b/tests/ui/feature-gates/cfg_select.stderr @@ -0,0 +1,13 @@ +error[E0658]: use of unstable library feature `cfg_select` + --> $DIR/cfg_select.rs:6: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 + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/lint/issue-70819-dont-override-forbid-in-same-scope.stderr b/tests/ui/lint/issue-70819-dont-override-forbid-in-same-scope.stderr index 506791fd17269..f40fb73acbb8b 100644 --- a/tests/ui/lint/issue-70819-dont-override-forbid-in-same-scope.stderr +++ b/tests/ui/lint/issue-70819-dont-override-forbid-in-same-scope.stderr @@ -431,3 +431,21 @@ note: the lint level is defined here LL | #![forbid(forbidden_lint_groups)] | ^^^^^^^^^^^^^^^^^^^^^ +Future breakage diagnostic: +error: warn(unused) incompatible with previous forbid + --> $DIR/issue-70819-dont-override-forbid-in-same-scope.rs:22:13 + | +LL | #![forbid(unused)] + | ------ `forbid` level set here +LL | #![deny(unused)] +LL | #![warn(unused)] + | ^^^^^^ overruled by previous forbid + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #81670 +note: the lint level is defined here + --> $DIR/issue-70819-dont-override-forbid-in-same-scope.rs:17:11 + | +LL | #![forbid(forbidden_lint_groups)] + | ^^^^^^^^^^^^^^^^^^^^^ + diff --git a/tests/ui/lint/outer-forbid.stderr b/tests/ui/lint/outer-forbid.stderr index 7810ca223f8ad..abe5959176d4a 100644 --- a/tests/ui/lint/outer-forbid.stderr +++ b/tests/ui/lint/outer-forbid.stderr @@ -471,3 +471,21 @@ note: the lint level is defined here LL | #![forbid(forbidden_lint_groups)] | ^^^^^^^^^^^^^^^^^^^^^ +Future breakage diagnostic: +error: allow(unused) incompatible with previous forbid + --> $DIR/outer-forbid.rs:25:9 + | +LL | #![forbid(unused, non_snake_case)] + | ------ `forbid` level set here +... +LL | #[allow(unused)] + | ^^^^^^ overruled by previous forbid + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #81670 +note: the lint level is defined here + --> $DIR/outer-forbid.rs:18:11 + | +LL | #![forbid(forbidden_lint_groups)] + | ^^^^^^^^^^^^^^^^^^^^^ + diff --git a/tests/ui/macros/cfg_select.rs b/tests/ui/macros/cfg_select.rs index 9fc6803070258..22dfce8e8191f 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_cfgs)] // Unused warnings are disabled by default in UI tests. fn print() { println!(cfg_select! { @@ -50,7 +51,7 @@ fn arm_rhs_expr_3() -> i32 { 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 5e2d705760cf6..46caf472feac1 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:52: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:56:1 + --> $DIR/cfg_select.rs:57:1 | LL | / cfg_select! { LL | | @@ -16,57 +8,71 @@ LL | | } | |_^ error: none of the predicates in this `cfg_select` evaluated to true - --> $DIR/cfg_select.rs:61:1 + --> $DIR/cfg_select.rs:62:1 | LL | cfg_select! {} | ^^^^^^^^^^^^^^ error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `=>` - --> $DIR/cfg_select.rs:65:5 + --> $DIR/cfg_select.rs:66:5 | LL | => {} | ^^ error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression - --> $DIR/cfg_select.rs:70:5 + --> $DIR/cfg_select.rs:71:5 | LL | () => {} | ^^ expressions are not allowed here error[E0539]: malformed `cfg_select` macro input - --> $DIR/cfg_select.rs:75:5 + --> $DIR/cfg_select.rs:76:5 | LL | "str" => {} | ^^^^^ expected a valid identifier here | error[E0539]: malformed `cfg_select` macro input - --> $DIR/cfg_select.rs:80:5 + --> $DIR/cfg_select.rs:81:5 | LL | a::b => {} | ^^^^ expected a valid identifier here | error[E0537]: invalid predicate `a` - --> $DIR/cfg_select.rs:85:5 + --> $DIR/cfg_select.rs:86:5 | LL | a() => {} | ^^^ error: expected one of `(`, `::`, `=>`, or `=`, found `+` - --> $DIR/cfg_select.rs:90:7 + --> $DIR/cfg_select.rs:91:7 | LL | a + 1 => {} | ^ expected one of `(`, `::`, `=>`, or `=` error: expected one of `(`, `::`, `=>`, or `=`, found `!` - --> $DIR/cfg_select.rs:96:8 + --> $DIR/cfg_select.rs:97:8 | LL | cfg!() => {} | ^ expected one of `(`, `::`, `=>`, or `=` +warning: unreachable configuration predicate + --> $DIR/cfg_select.rs:53: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_cfgs)] // Unused warnings are disabled by default in UI tests. + | ^^^^^^^^^^^^^^^^ + warning: unexpected `cfg` condition name: `a` - --> $DIR/cfg_select.rs:90:5 + --> $DIR/cfg_select.rs:91:5 | LL | a + 1 => {} | ^ help: found config with similar value: `target_feature = "a"` @@ -77,7 +83,7 @@ LL | a + 1 => {} = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition name: `cfg` - --> $DIR/cfg_select.rs:96:5 + --> $DIR/cfg_select.rs:97:5 | LL | cfg!() => {} | ^^^