diff --git a/Cargo.lock b/Cargo.lock index 1d22893573001..2d3a50ba48148 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3539,6 +3539,7 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_pretty", + "rustc_data_structures", "rustc_errors", "rustc_feature", "rustc_hir", diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index cde8b8f9fe2e9..12eaa99143e90 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1397,7 +1397,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // create a fake body so that the entire rest of the compiler doesn't have to deal with // this as a special case. return self.lower_fn_body(decl, contract, |this| { - if attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic)) + if find_attr!(attrs, AttributeKind::RustcIntrinsic) || this.tcx.is_sdylib_interface_build() { let span = this.lower_span(span); diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index 411f3f5ccbd14..0a11a2da0dcf8 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -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" } diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs index 4005ad2cba111..b6cb5b4504ee1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs @@ -1,22 +1,35 @@ 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}; use rustc_parse::exp; use rustc_parse::parser::{Parser, Recovery}; use rustc_session::Session; -use rustc_span::{ErrorGuaranteed, Span, sym}; +use rustc_session::lint::BuiltinLintDiag; +use rustc_session::lint::builtin::UNREACHABLE_CFG_SELECT_PREDICATES; +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), } +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. @@ -115,5 +128,102 @@ pub fn parse_cfg_select( } } + if let Some(features) = features + && features.enabled(sym::cfg_select) + { + 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, + lint_node_id: NodeId, +) { + // Symbols that have a known value. + let mut known = FxHashMap::::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) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 3b7d25b43ba2f..7e4feedd25482 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -490,6 +490,7 @@ impl CombineAttributeParser for RustcMirParser { .collect() } } + pub(crate) struct RustcNonConstTraitMethodParser; impl NoArgsAttributeParser for RustcNonConstTraitMethodParser { @@ -810,3 +811,21 @@ impl SingleAttributeParser for RustcDefPath { Some(AttributeKind::RustcDefPath(cx.attr_span)) } } + +pub(crate) struct RustcIntrinsicParser; + +impl NoArgsAttributeParser for RustcIntrinsicParser { + const PATH: &[Symbol] = &[sym::rustc_intrinsic]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsic; +} + +pub(crate) struct RustcIntrinsicConstStableIndirectParser; + +impl NoArgsAttributeParser for RustcIntrinsicConstStableIndirectParser { + const PATH: &'static [Symbol] = &[sym::rustc_intrinsic_const_stable_indirect]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsicConstStableIndirect; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 5b489d3064990..0d594270ba687 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -264,6 +264,8 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, + Single>, Single>, Single>, Single>, 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/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index bc86cdbc9f7ef..3fe5e89ef06db 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -540,6 +540,7 @@ impl<'a> TraitDef<'a> { .filter(|a| { a.has_any_name(&[ sym::allow, + sym::expect, sym::warn, sym::deny, sym::forbid, diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index d3f7e1c5d8e36..43bade3c43ac1 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -1086,17 +1086,6 @@ pub(crate) struct CfgSelectNoMatches { pub span: Span, } -#[derive(Diagnostic)] -#[diag("unreachable predicate")] -pub(crate) struct CfgSelectUnreachable { - #[primary_span] - #[label("this predicate is never reached")] - pub span: Span, - - #[label("always matches")] - pub wildcard_span: Span, -} - #[derive(Diagnostic)] #[diag("`#[eii_declaration(...)]` is only valid on macros")] pub(crate) struct EiiExternTargetExpectedMacro { diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index e2abf50cbd397..2ab7714f22ca9 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -394,6 +394,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_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index fa781a74c9e27..0e62088311908 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1120,6 +1120,12 @@ pub enum AttributeKind { /// Represents `#[rustc_if_this_changed]` RustcIfThisChanged(Span, Option), + /// Represents `#[rustc_intrinsic]` + RustcIntrinsic, + + /// Represents `#[rustc_intrinsic_const_stable_indirect]` + RustcIntrinsicConstStableIndirect, + /// Represents `#[rustc_layout]` RustcLayout(ThinVec), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 14b54c129d3ea..1f322d84fad4b 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -118,6 +118,8 @@ impl AttributeKind { RustcHasIncoherentInherentImpls => Yes, RustcHiddenTypeOfOpaques => No, RustcIfThisChanged(..) => No, + RustcIntrinsic => Yes, + RustcIntrinsicConstStableIndirect => No, RustcLayout(..) => No, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 2f5b7ed26952a..ff771532ae496 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -998,6 +998,13 @@ 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 + 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 c0ab0d1f00409..7681eedc75ed0 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -293,6 +293,14 @@ pub fn decorate_builtin_lint( } .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) } 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 1a87cc013e799..bdc171f11e3b3 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3340,3 +3340,20 @@ pub(crate) struct UnknownCrateTypesSuggestion { pub span: Span, pub snippet: Symbol, } + +#[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, + + #[label] + pub wildcard_span: Span, +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index f4e6e93356c75..488e3a70b6695 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -123,6 +123,7 @@ declare_lint_pass! { UNKNOWN_LINTS, UNNAMEABLE_TEST_ITEMS, UNNAMEABLE_TYPES, + UNREACHABLE_CFG_SELECT_PREDICATES, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, UNSAFE_ATTR_OUTSIDE_UNSAFE, @@ -855,6 +856,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 b3e5b93cf2fc7..0c454ec60f4a6 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: Option, + }, } #[derive(Debug, HashStable_Generic)] diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index d6e6ffae7300d..e01a5721a5e03 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -10,11 +10,11 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; use rustc_hashes::Hash128; -use rustc_hir as hir; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; use rustc_hir::limit::Limit; +use rustc_hir::{self as hir, find_attr}; use rustc_index::bit_set::GrowableBitSet; use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension}; use rustc_span::sym; @@ -1679,7 +1679,9 @@ pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { /// the compiler to make some assumptions about its shape; if the user doesn't use a feature gate, they may /// cause an ICE that we otherwise may want to prevent. pub fn intrinsic_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - if tcx.features().intrinsics() && tcx.has_attr(def_id, sym::rustc_intrinsic) { + if tcx.features().intrinsics() + && find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcIntrinsic) + { let must_be_overridden = match tcx.hir_node_by_def_id(def_id) { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { has_body, .. }, .. }) => { !has_body @@ -1689,7 +1691,10 @@ pub fn intrinsic_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option( // but at this stage we don't know whether codegen knows the intrinsic, // so just conservatively don't inline it. This also ensures that we do not // accidentally inline the body of an intrinsic that *must* be overridden. - if tcx.has_attr(def_id, sym::rustc_intrinsic) { + if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcIntrinsic) { return Err("callee is an intrinsic"); } diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index 71bdafa958ca1..da133861617f1 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -1,6 +1,7 @@ -use rustc_hir::attrs::InlineAttr; +use rustc_hir::attrs::{AttributeKind, InlineAttr}; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; +use rustc_hir::find_attr; use rustc_middle::bug; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; @@ -43,7 +44,7 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { return true; } - if tcx.has_attr(def_id, sym::rustc_intrinsic) { + if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcIntrinsic) { // Intrinsic fallback bodies are always cross-crate inlineable. // To ensure that the MIR inliner doesn't cluelessly try to inline fallback // bodies even when the backend would implement something better, we stop @@ -157,7 +158,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { // But intrinsics don't have a body that gets assigned to a CGU, so they are // ignored. if let Some((fn_def_id, _)) = func.const_fn_def() - && self.tcx.has_attr(fn_def_id, sym::rustc_intrinsic) + && find_attr!(tcx.get_all_attrs(fn_def_id), AttributeKind::RustcIntrinsic) { return; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 680a93908354c..284faa82412e0 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -313,6 +313,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcHasIncoherentInherentImpls | AttributeKind::RustcHiddenTypeOfOpaques | AttributeKind::RustcIfThisChanged(..) + | AttributeKind::RustcIntrinsic + | AttributeKind::RustcIntrinsicConstStableIndirect | AttributeKind::RustcLayout(..) | AttributeKind::RustcLayoutScalarValidRangeEnd(..) | AttributeKind::RustcLayoutScalarValidRangeStart(..) @@ -385,9 +387,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::rustc_no_mir_inline | sym::rustc_insignificant_dtor | sym::rustc_nonnull_optimization_guaranteed - | sym::rustc_intrinsic | sym::rustc_inherit_overflow_checks - | sym::rustc_intrinsic_const_stable_indirect | sym::rustc_trivial_field_reads | sym::rustc_on_unimplemented | sym::rustc_do_not_const_check diff --git a/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs b/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs index e809987d75a00..0a5b26bfa6898 100644 --- a/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs @@ -1,8 +1,9 @@ use super::EMPTY_LOOP; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{is_in_panic_handler, is_no_std_crate, sym}; +use clippy_utils::{is_in_panic_handler, is_no_std_crate}; -use rustc_hir::{Block, Expr, ItemKind, Node}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{Block, Expr, ItemKind, Node, find_attr}; use rustc_lint::LateContext; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, loop_block: &Block<'_>) { @@ -10,7 +11,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, loop_block: &Block<'_ if let Node::Item(parent_node) = cx.tcx.hir_node(parent_hir_id) && matches!(parent_node.kind, ItemKind::Fn { .. }) && let attrs = cx.tcx.hir_attrs(parent_hir_id) - && attrs.iter().any(|attr| attr.has_name(sym::rustc_intrinsic)) + && find_attr!(attrs, AttributeKind::RustcIntrinsic) { // Intrinsic functions are expanded into an empty loop when lowering the AST // to simplify the job of later passes which might expect any function to have a body. diff --git a/tests/ui/check-cfg/cfg-select.rs b/tests/ui/check-cfg/cfg-select.rs index 39703489818d8..ffa5e40bff085 100644 --- a/tests/ui/check-cfg/cfg-select.rs +++ b/tests/ui/check-cfg/cfg-select.rs @@ -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 => {} _ => {} } 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/lint/rfc-2383-lint-reason/derive-expect-issue-150553-2.rs b/tests/ui/lint/rfc-2383-lint-reason/derive-expect-issue-150553-2.rs new file mode 100644 index 0000000000000..43ee6bf26a69d --- /dev/null +++ b/tests/ui/lint/rfc-2383-lint-reason/derive-expect-issue-150553-2.rs @@ -0,0 +1,14 @@ +// Make sure that the copied `#[expect]` attr in the derived code does not trigger an unfulfilled +// expectation as it's linked to the original one which is fulfilled. +// +// See for rational. + +//@ check-pass + +#[expect(non_camel_case_types)] +#[derive(Debug)] +pub struct SCREAMING_CASE { + pub t_ref: i64, +} + +fn main() {} diff --git a/tests/ui/lint/rfc-2383-lint-reason/derive-expect-issue-150553-3.rs b/tests/ui/lint/rfc-2383-lint-reason/derive-expect-issue-150553-3.rs new file mode 100644 index 0000000000000..904366e6532e5 --- /dev/null +++ b/tests/ui/lint/rfc-2383-lint-reason/derive-expect-issue-150553-3.rs @@ -0,0 +1,14 @@ +// Make sure we produce the unfulfilled expectation lint if neither the struct or the +// derived code fulfilled it. + +//@ check-pass + +#[expect(unexpected_cfgs)] +//~^ WARN this lint expectation is unfulfilled +//~^^ WARN this lint expectation is unfulfilled +#[derive(Debug)] +pub struct MyStruct { + pub t_ref: i64, +} + +fn main() {} diff --git a/tests/ui/lint/rfc-2383-lint-reason/derive-expect-issue-150553-3.stderr b/tests/ui/lint/rfc-2383-lint-reason/derive-expect-issue-150553-3.stderr new file mode 100644 index 0000000000000..6478ec435db07 --- /dev/null +++ b/tests/ui/lint/rfc-2383-lint-reason/derive-expect-issue-150553-3.stderr @@ -0,0 +1,18 @@ +warning: this lint expectation is unfulfilled + --> $DIR/derive-expect-issue-150553-3.rs:6:10 + | +LL | #[expect(unexpected_cfgs)] + | ^^^^^^^^^^^^^^^ + | + = note: `#[warn(unfulfilled_lint_expectations)]` on by default + +warning: this lint expectation is unfulfilled + --> $DIR/derive-expect-issue-150553-3.rs:6:10 + | +LL | #[expect(unexpected_cfgs)] + | ^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: 2 warnings emitted + diff --git a/tests/ui/lint/rfc-2383-lint-reason/derive-expect-issue-150553.rs b/tests/ui/lint/rfc-2383-lint-reason/derive-expect-issue-150553.rs new file mode 100644 index 0000000000000..1752835c8bb81 --- /dev/null +++ b/tests/ui/lint/rfc-2383-lint-reason/derive-expect-issue-150553.rs @@ -0,0 +1,22 @@ +// Make sure we properly copy the `#[expect]` attr to the derived code and that no +// unfulfilled expectations are trigerred. +// +// See for rational. + +//@ check-pass + +#![deny(redundant_lifetimes)] + +use std::fmt::Debug; + +#[derive(Debug)] +#[expect(redundant_lifetimes)] +pub struct RefWrapper<'a, T> +where + 'a: 'static, + T: Debug, +{ + pub t_ref: &'a T, +} + +fn main() {} diff --git a/tests/ui/macros/cfg_select.rs b/tests/ui/macros/cfg_select.rs index 2369158ba82a2..0f009756e211a 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! { @@ -23,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 + } } } @@ -76,7 +77,7 @@ fn expand_to_pattern(x: Option) -> bool { } cfg_select! { - true => { + false => { fn foo() {} } _ => { @@ -88,7 +89,7 @@ struct S; impl S { cfg_select! { - true => { + false => { fn foo() {} } _ => { @@ -99,7 +100,7 @@ impl S { trait T { cfg_select! { - true => { + false => { fn a(); } _ => { @@ -110,7 +111,7 @@ trait T { impl T for S { cfg_select! { - true => { + false => { fn a() {} } _ => { @@ -121,7 +122,7 @@ impl T for S { extern "C" { cfg_select! { - true => { + false => { fn puts(s: *const i8) -> i32; } _ => { @@ -133,7 +134,28 @@ extern "C" { cfg_select! { _ => {} true => {} - //~^ WARN unreachable predicate + //~^ WARN unreachable configuration predicate +} + +cfg_select! { + true => {} + _ => {} + //~^ WARN unreachable configuration predicate +} + +cfg_select! { + unix => {} + not(unix) => {} + _ => {} + //~^ WARN unreachable configuration predicate +} + +cfg_select! { + test => {} + test => {} + //~^ WARN unreachable configuration 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..5878708668d34 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:161:1 | LL | / cfg_select! { LL | | @@ -16,55 +8,95 @@ LL | | } | |_^ error: none of the predicates in this `cfg_select` evaluated to true - --> $DIR/cfg_select.rs:144:1 + --> $DIR/cfg_select.rs:166: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:170:5 | LL | => {} | ^^ error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression - --> $DIR/cfg_select.rs:153:5 + --> $DIR/cfg_select.rs:175:5 | LL | () => {} | ^^ expressions are not allowed here error[E0539]: malformed `cfg_select` macro input - --> $DIR/cfg_select.rs:158:5 + --> $DIR/cfg_select.rs:180: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:185:5 | LL | a::b => {} | ^^^^ expected a valid identifier here error[E0537]: invalid predicate `a` - --> $DIR/cfg_select.rs:168:5 + --> $DIR/cfg_select.rs:190:5 | LL | a() => {} | ^^^ error: expected one of `(`, `::`, `=>`, or `=`, found `+` - --> $DIR/cfg_select.rs:173:7 + --> $DIR/cfg_select.rs:195:7 | LL | a + 1 => {} | ^ expected one of `(`, `::`, `=>`, or `=` error: expected one of `(`, `::`, `=>`, or `=`, found `!` - --> $DIR/cfg_select.rs:179:8 + --> $DIR/cfg_select.rs:201: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: 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 | test => {} + | ^^^^ this configuration predicate is never reached + +warning: unreachable configuration predicate + --> $DIR/cfg_select.rs:157:5 + | +LL | _ => {} + | ^ this configuration predicate is never reached + warning: unexpected `cfg` condition name: `a` - --> $DIR/cfg_select.rs:173:5 + --> $DIR/cfg_select.rs:195:5 | LL | a + 1 => {} | ^ help: found config with similar value: `target_feature = "a"` @@ -75,7 +107,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:201:5 | LL | cfg!() => {} | ^^^ @@ -83,7 +115,7 @@ LL | cfg!() => {} = help: to expect this configuration use `--check-cfg=cfg(cfg)` = note: see for more information about checking conditional configuration -error: aborting due to 9 previous errors; 3 warnings emitted +error: aborting due to 9 previous errors; 7 warnings emitted Some errors have detailed explanations: E0537, E0539. For more information about an error, try `rustc --explain E0537`.