From 0d0aaa1a54d8136524d0cfd4bd6346f8b431afb7 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 20 Apr 2026 19:56:55 +1000 Subject: [PATCH 01/16] Some arena macro tweaks. - Improve a comment. - Remove unused `[decode]` annotations on some arenas. - Improve `impl_arena_allocatable_decoder`: - Make the first rule more like the second rule. - Remove unnecessary brackets. - Remove unused support for attributes other than `decode`. --- compiler/rustc_middle/src/arena.rs | 11 +++++------ compiler/rustc_middle/src/ty/codec.rs | 7 +++---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index de6a105ee2b7b..e12583f38fe72 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -1,7 +1,7 @@ /// This higher-order macro declares a list of types which can be allocated by `Arena`. /// /// Specifying the `decode` modifier will add decode impls for `&T` and `&[T]` where `T` is the type -/// listed. These impls will appear in the implement_ty_decoder! macro. +/// listed. See the `impl_arena_allocatable_decoder!` macro for more. #[macro_export] macro_rules! arena_types { ($macro:path) => ( @@ -9,8 +9,7 @@ macro_rules! arena_types { [] layout: rustc_abi::LayoutData, [] proxy_coroutine_layout: rustc_middle::mir::CoroutineLayout<'tcx>, [] fn_abi: rustc_target::callconv::FnAbi<'tcx, rustc_middle::ty::Ty<'tcx>>, - // AdtDef are interned and compared by address - [decode] adt_def: rustc_middle::ty::AdtDefData, + [] adt_def: rustc_middle::ty::AdtDefData, [] steal_thir: rustc_data_structures::steal::Steal>, [] steal_mir: rustc_data_structures::steal::Steal>, [decode] mir: rustc_middle::mir::Body<'tcx>, @@ -27,7 +26,7 @@ macro_rules! arena_types { rustc_middle::mir::Body<'tcx> >, [decode] typeck_results: rustc_middle::ty::TypeckResults<'tcx>, - [decode] borrowck_result: rustc_data_structures::fx::FxIndexMap< + [] borrowck_result: rustc_data_structures::fx::FxIndexMap< rustc_hir::def_id::LocalDefId, rustc_middle::ty::DefinitionSiteHiddenType<'tcx>, >, @@ -100,7 +99,7 @@ macro_rules! arena_types { // (during lowering) and the `rustc_middle` arena (for decoding MIR) [decode] asm_template: rustc_ast::InlineAsmTemplatePiece, [decode] used_trait_imports: rustc_data_structures::unord::UnordSet, - [decode] is_late_bound_map: rustc_data_structures::fx::FxIndexSet, + [] is_late_bound_map: rustc_data_structures::fx::FxIndexSet, [decode] impl_source: rustc_middle::traits::ImplSource<'tcx, ()>, [] dep_kind_vtable: rustc_middle::dep_graph::DepKindVTable<'tcx>, @@ -111,7 +110,7 @@ macro_rules! arena_types { rustc_middle::ty::EarlyBinder<'tcx, rustc_middle::ty::Ty<'tcx>> >, [] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData>, - [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap, + [] doc_link_resolutions: rustc_hir::def::DocLinkResMap, [] stripped_cfg_items: rustc_hir::attrs::StrippedCfgItem, [] mod_child: rustc_middle::metadata::ModChild, [] features: rustc_feature::Features, diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 28bdeabf34dc1..8b1e812582783 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -513,9 +513,8 @@ macro_rules! __impl_decoder_methods { } macro_rules! impl_arena_allocatable_decoder { - ([]$args:tt) => {}; - ([decode $(, $attrs:ident)*] - [$name:ident: $ty:ty]) => { + ([] $name:ident: $ty:ty) => {}; + ([decode] $name:ident: $ty:ty) => { impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for $ty { #[inline] fn decode(decoder: &mut D) -> &'tcx Self { @@ -535,7 +534,7 @@ macro_rules! impl_arena_allocatable_decoder { macro_rules! impl_arena_allocatable_decoders { ([$($a:tt $name:ident: $ty:ty,)*]) => { $( - impl_arena_allocatable_decoder!($a [$name: $ty]); + impl_arena_allocatable_decoder!($a $name: $ty); )* } } From cdefdd054cd04e43aec96d99042f14d01a8b8bb8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 23 Apr 2026 08:54:38 +1000 Subject: [PATCH 02/16] Fix a metadata table name. The table names usually match the name of the corresponding query. The `trait_impl_trait_tys` table is an exception; this commit renames it `collect_return_position_impl_trait_in_trait_tys` to match the query. --- compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs | 8 +++++--- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_metadata/src/rmeta/mod.rs | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index a00fb59963ac2..cbd6afd68473a 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -261,7 +261,8 @@ provide! { tcx, def_id, other, cdata, .coerce_unsized_info .get(cdata, def_id.index) .map(|lazy| lazy.decode((cdata, tcx))) - .process_decoded(tcx, || panic!("{def_id:?} does not have coerce_unsized_info"))) } + .process_decoded(tcx, || panic!("{def_id:?} does not have coerce_unsized_info"))) + } mir_const_qualif => { table } rendered_const => { table } rendered_precise_capturing_args => { table } @@ -300,10 +301,10 @@ provide! { tcx, def_id, other, cdata, Ok(cdata .root .tables - .trait_impl_trait_tys + .collect_return_position_impl_trait_in_trait_tys .get(cdata, def_id.index) .map(|lazy| lazy.decode((cdata, tcx))) - .process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys"))) + .process_decoded(tcx, || panic!("{def_id:?} does not have collect_return_position_impl_trait_in_trait_tys"))) } associated_types_for_impl_traits_in_trait_or_impl => { table } @@ -695,6 +696,7 @@ impl CrateStore for CStore { fn as_any(&self) -> &dyn Any { self } + fn untracked_as_any(&mut self) -> &mut dyn Any { self } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 729a0dda7cf3b..57fe9210c6276 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1628,7 +1628,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if tcx.impl_method_has_trait_impl_trait_tys(def_id) && let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id) { - record!(self.tables.trait_impl_trait_tys[def_id] <- table); + record!(self.tables.collect_return_position_impl_trait_in_trait_tys[def_id] <- table); } if let DefKind::Impl { .. } | DefKind::Trait = def_kind { let table = tcx.associated_types_for_impl_traits_in_trait_or_impl(def_id); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index c7b2eaa15ebfb..a3645a5556bf3 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -466,7 +466,7 @@ define_tables! { macro_definition: Table>, proc_macro: Table, deduced_param_attrs: Table>, - trait_impl_trait_tys: Table>>>>, + collect_return_position_impl_trait_in_trait_tys: Table>>>>, doc_link_resolutions: Table>, doc_link_traits_in_scope: Table>, assumed_wf_types_for_rpitit: Table, Span)>>, From 3b5dd18d9a6b9e738daf4b3918e2eb2414b7e304 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 24 Apr 2026 14:57:56 +1000 Subject: [PATCH 03/16] Fix some comments. - Remove comment about blanket implementation of `FallibleTypeFolder` -- it does not exist. (It probably used to.) - Fix some incorrect name references. - Fix minor grammatical errors. - Fix stale comment on `visit_region` -- it was a no-op once, but no longer. LLM disclosure: Claude Code identified these problems with comments when I asked it to review `fold.rs` and `visit.rs`. I verified the correctness of the problems and made the changes by hand. --- compiler/rustc_type_ir/src/flags.rs | 2 +- compiler/rustc_type_ir/src/fold.rs | 12 ++++-------- compiler/rustc_type_ir/src/visit.rs | 16 ++++++++-------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 50c30f4252703..3debef168cffd 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -139,7 +139,7 @@ bitflags::bitflags! { /// Does this type have any coroutines in it? const HAS_TY_CORO = 1 << 25; - /// Does this have have a `Bound(BoundVarIndexKind::Canonical, _)`? + /// Does this have a `Bound(BoundVarIndexKind::Canonical, _)`? const HAS_CANONICAL_BOUND = 1 << 26; } } diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index d1a50599e8b9c..94d9845594628 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -121,10 +121,6 @@ pub trait TypeSuperFoldable: TypeFoldable { /// default that does an "identity" fold. Implementations of these methods /// often fall back to a `super_fold_with` method if the primary argument /// doesn't satisfy a particular condition. -/// -/// A blanket implementation of [`FallibleTypeFolder`] will defer to -/// the infallible methods of this trait to ensure that the two APIs -/// are coherent. pub trait TypeFolder: Sized { fn cx(&self) -> I; @@ -477,10 +473,10 @@ where /// Folds over the substructure of a type, visiting its component /// types and all regions that occur *free* within it. /// -/// That is, function pointer types and trait object can introduce -/// new bound regions which are not visited by this visitors as +/// That is, function pointer types and trait objects can introduce +/// new bound regions which are not visited by this visitor as /// they are not free; only regions that occur free will be -/// visited by `fld_r`. +/// visited by `fold_region_fn`. pub struct RegionFolder { cx: I, @@ -489,7 +485,7 @@ pub struct RegionFolder { /// binder, it is incremented (via `shift_in`). current_index: ty::DebruijnIndex, - /// Callback invokes for each free region. The `DebruijnIndex` + /// Callback invoked for each free region. The `DebruijnIndex` /// points to the binder *just outside* the ones we have passed /// through. fold_region_fn: F, diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index a078b860be774..6d0b307f013eb 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -67,7 +67,7 @@ pub trait TypeVisitable: fmt::Debug { /// each field/element. /// /// For types of interest (such as `Ty`), the implementation of this method - /// that calls a visitor method specifically for that type (such as + /// calls a visitor method specifically for that type (such as /// `V::visit_ty`). This is where control transfers from `TypeVisitable` to /// `TypeVisitor`. fn visit_with>(&self, visitor: &mut V) -> V::Result; @@ -102,8 +102,8 @@ pub trait TypeVisitor: Sized { t.super_visit_with(self) } - // The default region visitor is a no-op because `Region` is non-recursive - // and has no `super_visit_with` method to call. + // `Region` is non-recursive so the default region visitor has no + // `super_visit_with` method to call. fn visit_region(&mut self, r: I::Region) -> Self::Result { if let ty::ReError(guar) = r.kind() { self.visit_error(guar) @@ -251,10 +251,10 @@ pub trait TypeVisitableExt: TypeVisitable { self.has_vars_bound_at_or_above(binder.shifted_in(1)) } - /// Return `true` if this type has regions that are not a part of the type. - /// For example, `for<'a> fn(&'a i32)` return `false`, while `fn(&'a i32)` - /// would return `true`. The latter can occur when traversing through the - /// former. + /// Returns `true` if this type has regions that are not a part of the + /// type. For example, given a `for<'a> fn(&'a i32)` this function returns + /// `false`, while given a `fn(&'a i32)` it returns `true`. The latter can + /// occur when traversing through the former. /// /// See [`HasEscapingVarsVisitor`] for more information. fn has_escaping_bound_vars(&self) -> bool { @@ -571,7 +571,7 @@ impl TypeVisitor for HasEscapingVarsVisitor { // `outer_index`, that means that `ct` contains some content // bound at `outer_index` or above (because // `outer_exclusive_binder` is always 1 higher than the - // content in `t`). Therefore, `t` has some escaping vars. + // content in `ct`). Therefore, `ct` has some escaping vars. if ct.outer_exclusive_binder() > self.outer_index { ControlFlow::Break(FoundEscapingVars) } else { From 3c83d3adc91b253d1e4c500a94244d7ff1f12579 Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Tue, 5 May 2026 19:11:16 +0000 Subject: [PATCH 04/16] llvm: Use correct type for splat mask After llvm/llvm-project#195486 , LLVM has explicit handling for null pointers in simd operations instead of using special handling based on zeroes. This causes LLVM with asserts enabled to detect an improperly typed mask passed to splat (if the output vector does not have i32 elements), and can cause SIGSEGV in more complex cases with asserts disabled. --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index dd9ebf298b229..84c1e8e6f3d47 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -2086,7 +2086,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } if name == sym::simd_splat { - let (_out_len, out_ty) = require_simd!(ret_ty, SimdReturn); + let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn); require!( args[0].layout.ty == out_ty, @@ -2105,7 +2105,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // `shufflevector v0, poison, zeroinitializer` // The masks is all zeros, so this splats lane 0 (which has our element in it). - let splat = bx.shuffle_vector(v0, poison_vec, bx.const_null(llret_ty)); + let mask_ty = bx.type_vector(bx.type_i32(), out_len); + let splat = bx.shuffle_vector(v0, poison_vec, bx.const_null(mask_ty)); return Ok(splat); } From 411a932b382c783b5ac8f110996c44682a030f85 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Wed, 10 Sep 2025 21:58:56 +0200 Subject: [PATCH 05/16] ImproperCTypes: merge outer_ty information into VisitorState Another user-transparent change, unifying outer-type information and the existing VisitorState flags. --- .../rustc_lint/src/types/improper_ctypes.rs | 223 +++++++++++------- 1 file changed, 132 insertions(+), 91 deletions(-) diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index eddc3f628ae63..2d58066a96fc2 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -257,12 +257,22 @@ fn check_struct_for_power_alignment<'tcx>( } } +/// Annotates whether we are in the context of an item *defined* in rust +/// and exposed to an FFI boundary, +/// or the context of an item from elsewhere, whose interface is re-*declared* in rust. #[derive(Clone, Copy)] enum CItemKind { Declaration, Definition, } +/// Annotates whether we are in the context of a function's argument types or return type. +#[derive(Clone, Copy)] +enum FnPos { + Arg, + Ret, +} + enum FfiResult<'tcx> { FfiSafe, FfiPhantom(Ty<'tcx>), @@ -286,8 +296,10 @@ enum IndirectionKind { } bitflags! { + /// VisitorState flags that are linked with the root type's use. + /// (These are the permanent part of the state, kept when visiting new Ty.) #[derive(Clone, Copy, Debug, PartialEq, Eq)] - struct VisitorState: u8 { + struct RootUseFlags: u8 { /// For use in (externally-linked) static variables. const STATIC = 0b000001; /// For use in functions in general. @@ -302,7 +314,45 @@ bitflags! { } } -impl VisitorState { +/// Description of the relationship between current Ty and +/// the type (or lack thereof) immediately containing it +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum OuterTyKind { + None, + /// A variant that should not exist, + /// but is needed because we don't change the lint's behavior yet + NoneThroughFnPtr, + /// Placeholder for properties that will be used eventually + Other, +} + +impl OuterTyKind { + /// Computes the relationship by providing the containing Ty itself + fn from_ty<'tcx>(ty: Ty<'tcx>) -> Self { + match ty.kind() { + ty::FnPtr(..) => Self::NoneThroughFnPtr, + ty::RawPtr(..) + | ty::Ref(..) + | ty::Adt(..) + | ty::Tuple(..) + | ty::Array(..) + | ty::Slice(_) => OuterTyKind::Other, + _ => bug!("Unexpected outer type {ty:?}"), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +struct VisitorState { + /// Flags describing both the overall context in which the current Ty is, + /// linked to how the Visitor's original Ty was used. + root_use_flags: RootUseFlags, + /// Flags describing both the immediate context in which the current Ty is, + /// linked to how it relates to its parent Ty (or lack thereof). + outer_ty_kind: OuterTyKind, +} + +impl RootUseFlags { // The values that can be set. const STATIC_TY: Self = Self::STATIC; const ARGUMENT_TY_IN_DEFINITION: Self = @@ -317,86 +367,85 @@ impl VisitorState { const RETURN_TY_IN_FNPTR: Self = Self::from_bits(Self::FUNC.bits() | Self::THEORETICAL.bits() | Self::FN_RETURN.bits()) .unwrap(); +} - /// Get the proper visitor state for a given function's arguments. - fn argument_from_fnmode(fn_mode: CItemKind) -> Self { - match fn_mode { - CItemKind::Definition => VisitorState::ARGUMENT_TY_IN_DEFINITION, - CItemKind::Declaration => VisitorState::ARGUMENT_TY_IN_DECLARATION, +impl VisitorState { + /// From an existing state, compute the state of any subtype of the current type. + /// (General case. For the case where the current type is a function pointer, see `next_in_fnptr`.) + fn next(&self, current_ty: Ty<'_>) -> Self { + assert!(!matches!(current_ty.kind(), ty::FnPtr(..))); + VisitorState { + root_use_flags: self.root_use_flags, + outer_ty_kind: OuterTyKind::from_ty(current_ty), } } - /// Get the proper visitor state for a given function's return type. - fn return_from_fnmode(fn_mode: CItemKind) -> Self { - match fn_mode { - CItemKind::Definition => VisitorState::RETURN_TY_IN_DEFINITION, - CItemKind::Declaration => VisitorState::RETURN_TY_IN_DECLARATION, + /// From an existing state, compute the state of any subtype of the current type. + /// (Case where the current type is a function pointer, + /// meaning we need to specify if the subtype is an argument or the return.) + fn next_in_fnptr(&self, current_ty: Ty<'_>, fn_pos: FnPos) -> Self { + assert!(matches!(current_ty.kind(), ty::FnPtr(..))); + VisitorState { + root_use_flags: match fn_pos { + FnPos::Ret => RootUseFlags::RETURN_TY_IN_FNPTR, + FnPos::Arg => RootUseFlags::ARGUMENT_TY_IN_FNPTR, + }, + outer_ty_kind: OuterTyKind::from_ty(current_ty), } } + /// Get the proper visitor state for a given function's arguments or return type. + fn fn_entry_point(fn_mode: CItemKind, fn_pos: FnPos) -> Self { + let p_flags = match (fn_mode, fn_pos) { + (CItemKind::Definition, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DEFINITION, + (CItemKind::Declaration, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DECLARATION, + (CItemKind::Definition, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DEFINITION, + (CItemKind::Declaration, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DECLARATION, + }; + VisitorState { root_use_flags: p_flags, outer_ty_kind: OuterTyKind::None } + } + + /// Get the proper visitor state for a static variable's type + fn static_entry_point() -> Self { + VisitorState { root_use_flags: RootUseFlags::STATIC_TY, outer_ty_kind: OuterTyKind::None } + } + /// Whether the type is used in a function. - fn is_in_function(self) -> bool { - let ret = self.contains(Self::FUNC); + fn is_in_function(&self) -> bool { + let ret = self.root_use_flags.contains(RootUseFlags::FUNC); if ret { - debug_assert!(!self.contains(Self::STATIC)); + debug_assert!(!self.root_use_flags.contains(RootUseFlags::STATIC)); } ret } + /// Whether the type is used (directly or not) in a function, in return position. - fn is_in_function_return(self) -> bool { - let ret = self.contains(Self::FN_RETURN); + fn is_in_function_return(&self) -> bool { + let ret = self.root_use_flags.contains(RootUseFlags::FN_RETURN); if ret { debug_assert!(self.is_in_function()); } ret } + /// Whether the type is used (directly or not) in a defined function. /// In other words, whether or not we allow non-FFI-safe types behind a C pointer, /// to be treated as an opaque type on the other side of the FFI boundary. - fn is_in_defined_function(self) -> bool { - self.contains(Self::DEFINED) && self.is_in_function() + fn is_in_defined_function(&self) -> bool { + self.root_use_flags.contains(RootUseFlags::DEFINED) && self.is_in_function() } /// Whether the type is used (directly or not) in a function pointer type. /// Here, we also allow non-FFI-safe types behind a C pointer, /// to be treated as an opaque type on the other side of the FFI boundary. - fn is_in_fnptr(self) -> bool { - self.contains(Self::THEORETICAL) && self.is_in_function() + fn is_in_fnptr(&self) -> bool { + self.root_use_flags.contains(RootUseFlags::THEORETICAL) && self.is_in_function() } /// Whether we can expect type parameters and co in a given type. - fn can_expect_ty_params(self) -> bool { + fn can_expect_ty_params(&self) -> bool { // rust-defined functions, as well as FnPtrs - self.contains(Self::THEORETICAL) || self.is_in_defined_function() - } -} - -bitflags! { - /// Data that summarises how an "outer type" surrounds its inner type(s) - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - struct OuterTyData: u8 { - /// To show that there is no outer type, the current type is directly used by a `static` - /// variable or a function/FnPtr - const NO_OUTER_TY = 0b01; - /// For NO_OUTER_TY cases, show that we are being directly used by a FnPtr specifically - /// FIXME(ctypes): this is only used for "bad behaviour" reproduced for compatibility's sake - const NO_OUTER_TY_FNPTR = 0b10; - } -} - -impl OuterTyData { - /// Get the proper data for a given outer type. - fn from_ty<'tcx>(ty: Ty<'tcx>) -> Self { - match ty.kind() { - ty::FnPtr(..) => Self::NO_OUTER_TY | Self::NO_OUTER_TY_FNPTR, - ty::RawPtr(..) - | ty::Ref(..) - | ty::Adt(..) - | ty::Tuple(..) - | ty::Array(..) - | ty::Slice(_) => Self::empty(), - k @ _ => bug!("unexpected outer type {:?} of kind {:?}", ty, k), - } + self.root_use_flags.contains(RootUseFlags::THEORETICAL) || self.is_in_defined_function() } } @@ -416,7 +465,7 @@ struct ImproperCTypesVisitor<'a, 'tcx> { impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>, base_ty: Ty<'tcx>, base_fn_mode: CItemKind) -> Self { - Self { cx, base_ty, base_fn_mode, cache: FxHashSet::default() } + ImproperCTypesVisitor { cx, base_ty, base_fn_mode, cache: FxHashSet::default() } } /// Checks if the given indirection (box,ref,pointer) is "ffi-safe". @@ -485,7 +534,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { { FfiSafe } else { - self.visit_type(state, OuterTyData::from_ty(ty), inner_ty) + self.visit_type(state.next(ty), inner_ty) } } } @@ -506,7 +555,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if let Some(field) = super::transparent_newtype_field(self.cx.tcx, variant) { // Transparent newtypes have at most one non-ZST field which needs to be checked.. let field_ty = get_type_from_field(self.cx, field, args); - match self.visit_type(state, OuterTyData::from_ty(ty), field_ty) { + match self.visit_type(state.next(ty), field_ty) { FfiUnsafe { ty, .. } if ty.is_unit() => (), r => return r, } @@ -525,7 +574,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let mut all_phantom = !variant.fields.is_empty(); for field in &variant.fields { let field_ty = get_type_from_field(self.cx, field, args); - all_phantom &= match self.visit_type(state, OuterTyData::from_ty(ty), field_ty) { + all_phantom &= match self.visit_type(state.next(ty), field_ty) { FfiSafe => false, // `()` fields are FFI-safe! FfiUnsafe { ty, .. } if ty.is_unit() => false, @@ -570,7 +619,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { "consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct" )) } else { - // FIXME(ctypes): confirm that this makes sense for unions once #60405 / RFC2645 stabilises + // FIXME(#60405): confirm that this makes sense for unions once #60405 / RFC2645 stabilises Some(msg!( "consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union" )) @@ -628,7 +677,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() { // Special-case types like `Option` and `Result` if let Some(inner_ty) = repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty) { - return self.visit_type(state, OuterTyData::from_ty(ty), inner_ty); + return self.visit_type(state.next(ty), inner_ty); } return FfiUnsafe { @@ -660,12 +709,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// Checks if the given type is "ffi-safe" (has a stable, well-defined /// representation which can be exported to C code). - fn visit_type( - &mut self, - state: VisitorState, - outer_ty: OuterTyData, - ty: Ty<'tcx>, - ) -> FfiResult<'tcx> { + fn visit_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { use FfiResult::*; let tcx = self.cx.tcx; @@ -709,7 +753,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // Pattern types are just extra invariants on the type that you need to uphold, // but only the base type is relevant for being representable in FFI. // (note: this lint was written when pattern types could only be integers constrained to ranges) - ty::Pat(pat_ty, _) => self.visit_type(state, outer_ty, pat_ty), + // (also note: the lack of ".next(ty)" on the state is on purpose) + ty::Pat(pat_ty, _) => self.visit_type(state, pat_ty), // types which likely have a stable representation, if the target architecture defines those // note: before rust 1.77, 128-bit ints were not FFI-safe on x86_64 @@ -740,12 +785,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }, ty::Tuple(tuple) => { - // C functions can return void - let empty_and_safe = tuple.is_empty() - && outer_ty.contains(OuterTyData::NO_OUTER_TY) - && state.is_in_function_return(); - - if empty_and_safe { + if tuple.is_empty() + && state.is_in_function_return() + && matches!( + state.outer_ty_kind, + OuterTyKind::None | OuterTyKind::NoneThroughFnPtr + ) + { + // C functions can return void FfiSafe } else { FfiUnsafe { @@ -774,9 +821,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Array(inner_ty, _) => { if state.is_in_function() - && outer_ty.contains(OuterTyData::NO_OUTER_TY) - // FIXME(ctypes): VVV-this-VVV shouldn't be the case - && !outer_ty.contains(OuterTyData::NO_OUTER_TY_FNPTR) + // FIXME(ctypes): VVV-this-VVV shouldn't make a difference between ::None and ::NoneThroughFnPtr + && matches!(state.outer_ty_kind, OuterTyKind::None) { // C doesn't really support passing arrays by value - the only way to pass an array by value // is through a struct. @@ -788,7 +834,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } else { // let's allow phantoms to go through, // since an array of 1-ZSTs is also a 1-ZST - self.visit_type(state, OuterTyData::from_ty(ty), inner_ty) + self.visit_type(state.next(ty), inner_ty) } } @@ -806,19 +852,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let sig = tcx.instantiate_bound_regions_with_erased(sig); for arg in sig.inputs() { - match self.visit_type( - VisitorState::ARGUMENT_TY_IN_FNPTR, - OuterTyData::from_ty(ty), - *arg, - ) { + match self.visit_type(state.next_in_fnptr(ty, FnPos::Arg), *arg) { FfiSafe => {} r => return r, } } let ret_ty = sig.output(); - - self.visit_type(VisitorState::RETURN_TY_IN_FNPTR, OuterTyData::from_ty(ty), ret_ty) + self.visit_type(state.next_in_fnptr(ty, FnPos::Ret), ret_ty) } ty::Foreign(..) => FfiSafe, @@ -896,7 +937,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return res; } - self.visit_type(state, OuterTyData::NO_OUTER_TY, ty) + self.visit_type(state, ty) } } @@ -925,7 +966,7 @@ impl<'tcx> ImproperCTypesLint { self.spans.push(ty.span); } - hir::intravisit::walk_ty(self, ty) + hir::intravisit::walk_ty(self, ty); } } @@ -970,12 +1011,12 @@ impl<'tcx> ImproperCTypesLint { let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - let state = VisitorState::argument_from_fnmode(fn_mode); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg); self.check_type_for_external_abi_fnptr(cx, state, input_hir, *input_ty, fn_mode); } if let hir::FnRetTy::Return(ret_hir) = decl.output { - let state = VisitorState::return_from_fnmode(fn_mode); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret); self.check_type_for_external_abi_fnptr(cx, state, ret_hir, sig.output(), fn_mode); } } @@ -1000,7 +1041,7 @@ impl<'tcx> ImproperCTypesLint { fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) { let ty = cx.tcx.type_of(id).instantiate_identity().skip_norm_wip(); let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration); - let ffi_res = visitor.check_type(VisitorState::STATIC_TY, ty); + let ffi_res = visitor.check_type(VisitorState::static_entry_point(), ty); self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration); } @@ -1016,14 +1057,14 @@ impl<'tcx> ImproperCTypesLint { let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - let state = VisitorState::argument_from_fnmode(fn_mode); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg); let mut visitor = ImproperCTypesVisitor::new(cx, *input_ty, fn_mode); let ffi_res = visitor.check_type(state, *input_ty); self.process_ffi_result(cx, input_hir.span, ffi_res, fn_mode); } if let hir::FnRetTy::Return(ret_hir) = decl.output { - let state = VisitorState::return_from_fnmode(fn_mode); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret); let mut visitor = ImproperCTypesVisitor::new(cx, sig.output(), fn_mode); let ffi_res = visitor.check_type(state, sig.output()); self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode); @@ -1124,7 +1165,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint { | hir::ItemKind::TyAlias(_, _, ty) => { self.check_type_for_external_abi_fnptr( cx, - VisitorState::STATIC_TY, + VisitorState::static_entry_point(), ty, cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip(), CItemKind::Definition, @@ -1158,7 +1199,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint { fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) { self.check_type_for_external_abi_fnptr( cx, - VisitorState::STATIC_TY, + VisitorState::static_entry_point(), field.ty, cx.tcx.type_of(field.def_id).instantiate_identity().skip_norm_wip(), CItemKind::Definition, From 6839797f18f4e8b014932c19df77e3e0e2874481 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Sat, 25 Apr 2026 00:00:01 +0200 Subject: [PATCH 06/16] ImproperCTypes: Use more `Unnormalized` type wrappers In order to follow along with the efforts to properly distinguish already-normalised and unnormalized types, we separate the internal interfaces of this lint that rely on normalized types from those that do not. We do that by adding the `Unnormalized` wrapper to some interfaces. --- .../rustc_lint/src/types/improper_ctypes.rs | 65 ++++++++++++------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 2d58066a96fc2..03928c78cfb61 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -138,17 +138,16 @@ declare_lint_pass!(ImproperCTypesLint => [ USES_POWER_ALIGNMENT ]); -/// Getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple). -#[inline] -fn get_type_from_field<'tcx>( +/// A common pattern in this lint is to attempt normalize_erasing_regions, +/// but keep the original type if it were to fail. +/// This may or may not be supported in the logic behind the `Unnormalized` wrapper, +/// (FIXME?) +/// but it should be enough for non-wrapped types to be as normalised as this lint needs them to be. +fn maybe_normalize_erasing_regions<'tcx>( cx: &LateContext<'tcx>, - field: &ty::FieldDef, - args: GenericArgsRef<'tcx>, + value: Unnormalized<'tcx, Ty<'tcx>>, ) -> Ty<'tcx> { - let field_ty = field.ty(cx.tcx, args); - cx.tcx - .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(field_ty)) - .unwrap_or(field_ty) + cx.tcx.try_normalize_erasing_regions(cx.typing_env(), value).unwrap_or(value.skip_norm_wip()) } /// Check a variant of a non-exhaustive enum for improper ctypes @@ -464,8 +463,17 @@ struct ImproperCTypesVisitor<'a, 'tcx> { } impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>, base_ty: Ty<'tcx>, base_fn_mode: CItemKind) -> Self { - ImproperCTypesVisitor { cx, base_ty, base_fn_mode, cache: FxHashSet::default() } + fn new( + cx: &'a LateContext<'tcx>, + base_ty: Unnormalized<'tcx, Ty<'tcx>>, + base_fn_mode: CItemKind, + ) -> Self { + ImproperCTypesVisitor { + cx, + base_ty: maybe_normalize_erasing_regions(cx, base_ty), + base_fn_mode, + cache: FxHashSet::default(), + } } /// Checks if the given indirection (box,ref,pointer) is "ffi-safe". @@ -554,7 +562,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let transparent_with_all_zst_fields = if def.repr().transparent() { if let Some(field) = super::transparent_newtype_field(self.cx.tcx, variant) { // Transparent newtypes have at most one non-ZST field which needs to be checked.. - let field_ty = get_type_from_field(self.cx, field, args); + let field_ty = maybe_normalize_erasing_regions( + self.cx, + Unnormalized::new_wip(field.ty(self.cx.tcx, args)), + ); match self.visit_type(state.next(ty), field_ty) { FfiUnsafe { ty, .. } if ty.is_unit() => (), r => return r, @@ -573,7 +584,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe. let mut all_phantom = !variant.fields.is_empty(); for field in &variant.fields { - let field_ty = get_type_from_field(self.cx, field, args); + let field_ty = maybe_normalize_erasing_regions( + self.cx, + Unnormalized::new_wip(field.ty(self.cx.tcx, args)), + ); all_phantom &= match self.visit_type(state.next(ty), field_ty) { FfiSafe => false, // `()` fields are FFI-safe! @@ -927,12 +941,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }) } - fn check_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { - let ty = self - .cx - .tcx - .try_normalize_erasing_regions(self.cx.typing_env(), Unnormalized::new_wip(ty)) - .unwrap_or(ty); + fn check_type( + &mut self, + state: VisitorState, + ty: Unnormalized<'tcx, Ty<'tcx>>, + ) -> FfiResult<'tcx> { + let ty = maybe_normalize_erasing_regions(self.cx, ty); if let Some(res) = self.visit_for_opaque_ty(ty) { return res; } @@ -990,6 +1004,7 @@ impl<'tcx> ImproperCTypesLint { let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)); for (fn_ptr_ty, span) in all_types { + let fn_ptr_ty = Unnormalized::new_wip(fn_ptr_ty); let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode); // FIXME(ctypes): make a check_for_fnptr let ffi_res = visitor.check_type(state, fn_ptr_ty); @@ -1039,7 +1054,7 @@ impl<'tcx> ImproperCTypesLint { } fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) { - let ty = cx.tcx.type_of(id).instantiate_identity().skip_norm_wip(); + let ty = cx.tcx.type_of(id).instantiate_identity(); let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration); let ffi_res = visitor.check_type(VisitorState::static_entry_point(), ty); self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration); @@ -1057,16 +1072,18 @@ impl<'tcx> ImproperCTypesLint { let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { + let input_ty = Unnormalized::new_wip(*input_ty); let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg); - let mut visitor = ImproperCTypesVisitor::new(cx, *input_ty, fn_mode); - let ffi_res = visitor.check_type(state, *input_ty); + let mut visitor = ImproperCTypesVisitor::new(cx, input_ty, fn_mode); + let ffi_res = visitor.check_type(state, input_ty); self.process_ffi_result(cx, input_hir.span, ffi_res, fn_mode); } if let hir::FnRetTy::Return(ret_hir) = decl.output { + let output_ty = Unnormalized::new_wip(sig.output()); let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret); - let mut visitor = ImproperCTypesVisitor::new(cx, sig.output(), fn_mode); - let ffi_res = visitor.check_type(state, sig.output()); + let mut visitor = ImproperCTypesVisitor::new(cx, output_ty, fn_mode); + let ffi_res = visitor.check_type(state, output_ty); self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode); } } From 803c7ba50bc1dfd27bd4241edc3619126c9b7350 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 24 Apr 2026 15:08:32 +1000 Subject: [PATCH 07/16] Some fold/visit tweaks. - Introduce `HAS_REGIONS`/`has_regions`, which is true if any regions are present. - Add `fold_clauses` to `Shifter` and `RegionFolder` for consistency. - Simplify `has_type_flags`. - Remove unnecessary local variables in `HasTypeFlagsVisitor` methods. - Use `|=` operator in one place. - Avoid `is_break` in one place for consistency with nearby code. LLM disclosure: Claude Code suggested these changes when I asked it to review `fold.rs` and `visit.rs`. I verified the correctness of the suggestions and made the changes by hand. --- compiler/rustc_type_ir/src/flags.rs | 7 +++++- compiler/rustc_type_ir/src/fold.rs | 34 ++++++++++------------------- compiler/rustc_type_ir/src/visit.rs | 17 ++++++++------- 3 files changed, 27 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 3debef168cffd..19c59df0604c3 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -118,6 +118,11 @@ bitflags::bitflags! { /// Does this have any `ReErased` regions? const HAS_RE_ERASED = 1 << 21; + /// Does this have any regions of any kind? + const HAS_REGIONS = TypeFlags::HAS_FREE_REGIONS.bits() + | TypeFlags::HAS_RE_BOUND.bits() + | TypeFlags::HAS_RE_ERASED.bits(); + /// Does this value have parameters/placeholders/inference variables which could be /// replaced later, in a way that would change the results of `impl` specialization? const STILL_FURTHER_SPECIALIZABLE = TypeFlags::HAS_TY_PARAM.bits() @@ -192,7 +197,7 @@ impl FlagComputation { } fn add_flags(&mut self, flags: TypeFlags) { - self.flags = self.flags | flags; + self.flags |= flags; } /// indicates that `self` refers to something at binding level `binder` diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 94d9845594628..0fe68b5256691 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -55,7 +55,7 @@ use tracing::{debug, instrument}; use crate::inherent::*; use crate::visit::{TypeVisitable, TypeVisitableExt as _}; -use crate::{self as ty, BoundVarIndexKind, Interner, TypeFlags}; +use crate::{self as ty, BoundVarIndexKind, Interner}; /// This trait is implemented for every type that can be folded, /// providing the skeleton of the traversal. @@ -433,6 +433,10 @@ impl TypeFolder for Shifter { fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } } + + fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { + if c.has_vars_bound_at_or_above(self.current_index) { c.super_fold_with(self) } else { c } + } } pub fn shift_region(cx: I, region: I::Region, amount: u32) -> I::Region { @@ -535,32 +539,18 @@ where } fn fold_ty(&mut self, t: I::Ty) -> I::Ty { - if t.has_type_flags( - TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED, - ) { - t.super_fold_with(self) - } else { - t - } + if t.has_regions() { t.super_fold_with(self) } else { t } } fn fold_const(&mut self, ct: I::Const) -> I::Const { - if ct.has_type_flags( - TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED, - ) { - ct.super_fold_with(self) - } else { - ct - } + if ct.has_regions() { ct.super_fold_with(self) } else { ct } } fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { - if p.has_type_flags( - TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED, - ) { - p.super_fold_with(self) - } else { - p - } + if p.has_regions() { p.super_fold_with(self) } else { p } + } + + fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { + if c.has_regions() { c.super_fold_with(self) } else { c } } } diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 6d0b307f013eb..492c37481298b 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -285,6 +285,10 @@ pub trait TypeVisitableExt: TypeVisitable { self.has_type_flags(TypeFlags::HAS_PARAM - TypeFlags::HAS_RE_PARAM) } + fn has_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_REGIONS) + } + fn has_infer_regions(&self) -> bool { self.has_type_flags(TypeFlags::HAS_RE_INFER) } @@ -363,13 +367,12 @@ pub trait TypeVisitableExt: TypeVisitable { impl> TypeVisitableExt for T { fn has_type_flags(&self, flags: TypeFlags) -> bool { - let res = - self.visit_with(&mut HasTypeFlagsVisitor { flags }) == ControlFlow::Break(FoundFlags); - res + self.visit_with(&mut HasTypeFlagsVisitor { flags }) == ControlFlow::Break(FoundFlags) } fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { - self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break() + self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }) + == ControlFlow::Break(FoundEscapingVars) } fn error_reported(&self) -> Result<(), I::ErrorGuaranteed> { @@ -438,8 +441,7 @@ impl TypeVisitor for HasTypeFlagsVisitor { #[inline] fn visit_ty(&mut self, t: I::Ty) -> Self::Result { // Note: no `super_visit_with` call. - let flags = t.flags(); - if flags.intersects(self.flags) { + if t.flags().intersects(self.flags) { ControlFlow::Break(FoundFlags) } else { ControlFlow::Continue(()) @@ -449,8 +451,7 @@ impl TypeVisitor for HasTypeFlagsVisitor { #[inline] fn visit_region(&mut self, r: I::Region) -> Self::Result { // Note: no `super_visit_with` call, as usual for `Region`. - let flags = r.flags(); - if flags.intersects(self.flags) { + if r.flags().intersects(self.flags) { ControlFlow::Break(FoundFlags) } else { ControlFlow::Continue(()) From 814fc17cb6c5860b04fde34abc12fa92151d9505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 29 Apr 2026 10:53:45 +0200 Subject: [PATCH 08/16] Deny warnings in rustc crates on stable --- tests/run-make-cargo/rustc-crates-on-stable/rmake.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs b/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs index cbc1f24b8c16e..de29abfd7c3a2 100644 --- a/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs +++ b/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs @@ -10,7 +10,7 @@ fn main() { .env("RUSTC_STAGE", "0") .env("RUSTC", rustc_path()) // We want to disallow all nightly features to simulate a stable build - .env("RUSTFLAGS", "-Zallow-features=") + .env("RUSTFLAGS", "-D warnings -Zallow-features=") .arg("build") .arg("--manifest-path") .arg(source_root().join("Cargo.toml")) From 0afe083fd1f0e9fa2c627b8c3155f221675f3d68 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 5 May 2026 06:31:19 +0200 Subject: [PATCH 09/16] Avoid some global `node_id_to_def_id` lookups in import resolution --- .../rustc_resolve/src/build_reduced_graph.rs | 11 +++++- compiler/rustc_resolve/src/imports.rs | 39 +++++++++++-------- compiler/rustc_resolve/src/lib.rs | 4 +- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 524c9ce8ad5d7..642a618e18676 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -720,13 +720,15 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { decls: Default::default(), nested, id, + def_id: feed.def_id(), }; self.add_import(module_path, kind, use_tree.span(), item, root_span, item.id, vis); } ast::UseTreeKind::Glob(_) => { if !ast::attr::contains_name(&item.attrs, sym::prelude_import) { - let kind = ImportKind::Glob { max_vis: CmCell::new(None), id }; + let kind = + ImportKind::Glob { max_vis: CmCell::new(None), id, def_id: feed.def_id() }; self.add_import(prefix, kind, use_tree.span(), item, root_span, item.id, vis); } else { // Resolve the prelude import early. @@ -1018,7 +1020,12 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { }) .unwrap_or((true, None, self.r.dummy_decl)); let import = self.r.arenas.alloc_import(ImportData { - kind: ImportKind::ExternCrate { source: orig_name, target: orig_ident, id: item.id }, + kind: ImportKind::ExternCrate { + source: orig_name, + target: orig_ident, + id: item.id, + def_id: local_def_id, + }, root_id: item.id, parent_scope, imported_module: CmCell::new(module), diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index e63ab9f21a6ad..4de7a89ba2907 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -16,7 +16,7 @@ use rustc_hir::Attribute; use rustc_hir::attrs::AttributeKind; use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs}; use rustc_hir::def::{self, DefKind, PartialRes}; -use rustc_hir::def_id::{DefId, LocalDefIdMap}; +use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap}; use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport}; use rustc_middle::span_bug; use rustc_middle::ty::{TyCtxt, Visibility}; @@ -90,17 +90,20 @@ pub(crate) enum ImportKind<'ra> { /// If this is the import for `foo::bar::a`, we would have the ID of the `UseTree` /// for `a` in this field. id: NodeId, + def_id: LocalDefId, }, Glob { // The visibility of the greatest re-export. // n.b. `max_vis` is only used in `finalize_import` to check for re-export errors. max_vis: CmCell>, id: NodeId, + def_id: LocalDefId, }, ExternCrate { source: Option, target: Ident, id: NodeId, + def_id: LocalDefId, }, MacroUse { /// A field has been added indicating whether it should be reported as a lint, @@ -116,7 +119,7 @@ impl<'ra> std::fmt::Debug for ImportKind<'ra> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use ImportKind::*; match self { - Single { source, target, decls, nested, id, .. } => f + Single { source, target, decls, nested, id, def_id } => f .debug_struct("Single") .field("source", source) .field("target", target) @@ -127,15 +130,20 @@ impl<'ra> std::fmt::Debug for ImportKind<'ra> { ) .field("nested", nested) .field("id", id) + .field("def_id", def_id) .finish(), - Glob { max_vis, id } => { - f.debug_struct("Glob").field("max_vis", max_vis).field("id", id).finish() - } - ExternCrate { source, target, id } => f + Glob { max_vis, id, def_id } => f + .debug_struct("Glob") + .field("max_vis", max_vis) + .field("id", id) + .field("def_id", def_id) + .finish(), + ExternCrate { source, target, id, def_id } => f .debug_struct("ExternCrate") .field("source", source) .field("target", target) .field("id", id) + .field("def_id", def_id) .finish(), MacroUse { warn_private } => { f.debug_struct("MacroUse").field("warn_private", warn_private).finish() @@ -260,12 +268,11 @@ impl<'ra> ImportData<'ra> { } } - pub(crate) fn simplify(&self, r: &Resolver<'_, '_>) -> Reexport { - let to_def_id = |id| r.local_def_id(id).to_def_id(); + pub(crate) fn simplify(&self) -> Reexport { match self.kind { - ImportKind::Single { id, .. } => Reexport::Single(to_def_id(id)), - ImportKind::Glob { id, .. } => Reexport::Glob(to_def_id(id)), - ImportKind::ExternCrate { id, .. } => Reexport::ExternCrate(to_def_id(id)), + ImportKind::Single { def_id, .. } => Reexport::Single(def_id.to_def_id()), + ImportKind::Glob { def_id, .. } => Reexport::Glob(def_id.to_def_id()), + ImportKind::ExternCrate { def_id, .. } => Reexport::ExternCrate(def_id.to_def_id()), ImportKind::MacroUse { .. } => Reexport::MacroUse, ImportKind::MacroExport => Reexport::MacroExport, } @@ -1267,7 +1274,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let (ident, target, bindings, import_id) = match import.kind { ImportKind::Single { source, target, ref decls, id, .. } => (source, target, decls, id), - ImportKind::Glob { ref max_vis, id } => { + ImportKind::Glob { ref max_vis, id, def_id: _ } => { if import.module_path.len() <= 1 { // HACK(eddyb) `lint_if_path_starts_with_module` needs at least // 2 segments, so the `resolve_path` above won't trigger it. @@ -1829,23 +1836,23 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut children = Vec::new(); let mut ambig_children = Vec::new(); - module.to_module().for_each_child(self, |this, ident, orig_ident_span, _, binding| { + module.to_module().for_each_child(self, |_this, ident, orig_ident_span, _, binding| { let res = binding.res().expect_non_local(); if res != def::Res::Err { let ident = ident.orig(orig_ident_span); let child = |reexport_chain| ModChild { ident, res, vis: binding.vis(), reexport_chain }; if let Some((ambig_binding1, ambig_binding2)) = binding.descent_to_ambiguity() { - let main = child(ambig_binding1.reexport_chain(this)); + let main = child(ambig_binding1.reexport_chain()); let second = ModChild { ident, res: ambig_binding2.res().expect_non_local(), vis: ambig_binding2.vis(), - reexport_chain: ambig_binding2.reexport_chain(this), + reexport_chain: ambig_binding2.reexport_chain(), }; ambig_children.push(AmbigModChild { main, second }) } else { - children.push(child(binding.reexport_chain(this))); + children.push(child(binding.reexport_chain())); } } }); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 1141ac75ce4c0..7f8ff91d3ad68 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1171,11 +1171,11 @@ impl<'ra> DeclData<'ra> { self.res().macro_kinds() } - fn reexport_chain(self: Decl<'ra>, r: &Resolver<'_, '_>) -> SmallVec<[Reexport; 2]> { + fn reexport_chain(self: Decl<'ra>) -> SmallVec<[Reexport; 2]> { let mut reexport_chain = SmallVec::new(); let mut next_binding = self; while let DeclKind::Import { source_decl, import, .. } = next_binding.kind { - reexport_chain.push(import.simplify(r)); + reexport_chain.push(import.simplify()); next_binding = source_decl; } reexport_chain From 191cda55e4e2612e47d1ce06980cb371aa96e776 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 5 May 2026 06:36:36 +0200 Subject: [PATCH 10/16] Avoid using `id` followed by `local_def_id` if we can just call `def_id` instead --- .../rustc_resolve/src/effective_visibilities.rs | 9 +++------ compiler/rustc_resolve/src/imports.rs | 14 +++++++++++--- compiler/rustc_resolve/src/lib.rs | 3 +-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index b5614106fe997..693e49995c1cc 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -45,10 +45,7 @@ impl Resolver<'_, '_> { fn private_vis_import(&self, decl: Decl<'_>) -> Visibility { let DeclKind::Import { import, .. } = decl.kind else { unreachable!() }; Visibility::Restricted( - import - .id() - .map(|id| self.nearest_normal_mod(self.local_def_id(id))) - .unwrap_or(CRATE_DEF_ID), + import.def_id().map(|id| self.nearest_normal_mod(id)).unwrap_or(CRATE_DEF_ID), ) } @@ -96,8 +93,8 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { // is the maximum value among visibilities of declarations corresponding to that def id. for (decl, eff_vis) in visitor.import_effective_visibilities.iter() { let DeclKind::Import { import, .. } = decl.kind else { unreachable!() }; - if let Some(node_id) = import.id() { - r.effective_visibilities.update_eff_vis(r.local_def_id(node_id), eff_vis, r.tcx) + if let Some(def_id) = import.def_id() { + r.effective_visibilities.update_eff_vis(def_id, eff_vis, r.tcx) } if decl.ambiguity.get().is_some() && eff_vis.is_public_at_level(Level::Reexported) { exported_ambiguities.insert(*decl); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 4de7a89ba2907..c778df19818fc 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -268,6 +268,15 @@ impl<'ra> ImportData<'ra> { } } + pub(crate) fn def_id(&self) -> Option { + match self.kind { + ImportKind::Single { def_id, .. } + | ImportKind::Glob { def_id, .. } + | ImportKind::ExternCrate { def_id, .. } => Some(def_id), + ImportKind::MacroUse { .. } | ImportKind::MacroExport => None, + } + } + pub(crate) fn simplify(&self) -> Reexport { match self.kind { ImportKind::Single { def_id, .. } => Reexport::Single(def_id.to_def_id()), @@ -852,8 +861,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if binding.res() != Res::Err && glob_decl.res() != Res::Err && let DeclKind::Import { import: glob_import, .. } = glob_decl.kind - && let Some(glob_import_id) = glob_import.id() - && let glob_import_def_id = self.local_def_id(glob_import_id) + && let Some(glob_import_def_id) = glob_import.def_id() && self.effective_visibilities.is_exported(glob_import_def_id) && glob_decl.vis().is_public() && !binding.vis().is_public() @@ -882,7 +890,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let DeclKind::Import { import, .. } = binding.kind && let Some(binding_id) = import.id() - && let import_def_id = self.local_def_id(binding_id) + && let import_def_id = import.def_id().unwrap() && self.effective_visibilities.is_exported(import_def_id) && let Res::Def(reexported_kind, reexported_def_id) = binding.res() && !matches!(reexported_kind, DefKind::Ctor(..)) diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 7f8ff91d3ad68..25cb4659758d0 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -2155,8 +2155,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) -> &'tcx [LocalDefId] { let mut import_ids: SmallVec<[LocalDefId; 1]> = smallvec![]; while let DeclKind::Import { import, source_decl, .. } = kind { - if let Some(node_id) = import.id() { - let def_id = self.local_def_id(node_id); + if let Some(def_id) = import.def_id() { self.maybe_unused_trait_imports.insert(def_id); import_ids.push(def_id); } From 400240adb4dc4f5fe0bad053d42215583a76e155 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 5 May 2026 06:15:58 +0200 Subject: [PATCH 11/16] Avoid hitting the global node_id_to_def_id table for unused macros --- compiler/rustc_resolve/src/build_reduced_graph.rs | 2 +- compiler/rustc_resolve/src/lib.rs | 4 ++-- compiler/rustc_resolve/src/macros.rs | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 642a618e18676..da75238bacd56 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1277,7 +1277,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { if !ident.as_str().starts_with('_') { self.r.unused_macros.insert(def_id, (node_id, ident)); let nrules = self.r.local_macro_map[&def_id].nrules; - self.r.unused_macro_rules.insert(node_id, DenseBitSet::new_filled(nrules)); + self.r.unused_macro_rules.insert(node_id, (def_id, DenseBitSet::new_filled(nrules))); } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 25cb4659758d0..6f777984e6c03 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1405,8 +1405,8 @@ pub struct Resolver<'ra, 'tcx> { local_macro_def_scopes: FxHashMap> = default::fx_hash_map(), ast_transform_scopes: FxHashMap> = default::fx_hash_map(), unused_macros: FxIndexMap, - /// A map from the macro to all its potentially unused arms. - unused_macro_rules: FxIndexMap>, + /// A map from the macro to all its potentially unused arms and the `LocalDefId` of the macro itself. + unused_macro_rules: FxIndexMap)>, proc_macro_stubs: FxHashSet = default::fx_hash_set(), /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index d4349b9d73a77..27390c0394d22 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -339,7 +339,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { } fn record_macro_rule_usage(&mut self, id: NodeId, rule_i: usize) { - if let Some(rules) = self.unused_macro_rules.get_mut(&id) { + if let Some((_, rules)) = self.unused_macro_rules.get_mut(&id) { rules.remove(rule_i); } } @@ -356,11 +356,10 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { self.unused_macro_rules.swap_remove(&node_id); } - for (&node_id, unused_arms) in self.unused_macro_rules.iter() { + for (&node_id, (def_id, unused_arms)) in self.unused_macro_rules.iter() { if unused_arms.is_empty() { continue; } - let def_id = self.local_def_id(node_id); let m = &self.local_macro_map[&def_id]; let SyntaxExtensionKind::MacroRules(ref m) = m.ext.kind else { continue; From a082567e7b77a4ce6416a4445c9a1e915319c4e9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 5 May 2026 07:23:24 +0200 Subject: [PATCH 12/16] Follow-up cleanups reusing the now-available `LocalDefId`s --- compiler/rustc_resolve/src/check_unused.rs | 3 +-- compiler/rustc_resolve/src/imports.rs | 18 ++++++++++-------- compiler/rustc_resolve/src/lib.rs | 3 +-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 7e1b1bce3ff74..8ab0087ae6158 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -431,8 +431,7 @@ impl Resolver<'_, '_> { } } } - ImportKind::ExternCrate { id, .. } => { - let def_id = self.local_def_id(id); + ImportKind::ExternCrate { id, def_id, .. } => { if self.extern_crate_map.get(&def_id).is_none_or(|&cnum| { !tcx.is_compiler_builtins(cnum) && !tcx.is_panic_runtime(cnum) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index c778df19818fc..1b9e5d2a2daa6 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -356,13 +356,16 @@ struct UnresolvedImportError { // Reexports of the form `pub use foo as bar;` where `foo` is `extern crate foo;` // are permitted for backward-compatibility under a deprecation lint. -fn pub_use_of_private_extern_crate_hack(import: ImportSummary, decl: Decl<'_>) -> Option { +fn pub_use_of_private_extern_crate_hack( + import: ImportSummary, + decl: Decl<'_>, +) -> Option { match (import.is_single, decl.kind) { (true, DeclKind::Import { import: decl_import, .. }) - if let ImportKind::ExternCrate { id, .. } = decl_import.kind + if let ImportKind::ExternCrate { def_id, .. } = decl_import.kind && import.vis.is_public() => { - Some(id) + Some(def_id) } _ => None, } @@ -1282,7 +1285,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let (ident, target, bindings, import_id) = match import.kind { ImportKind::Single { source, target, ref decls, id, .. } => (source, target, decls, id), - ImportKind::Glob { ref max_vis, id, def_id: _ } => { + ImportKind::Glob { ref max_vis, id, def_id } => { if import.module_path.len() <= 1 { // HACK(eddyb) `lint_if_path_starts_with_module` needs at least // 2 segments, so the `resolve_path` above won't trigger it. @@ -1309,7 +1312,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(max_vis) = max_vis.get() && import.vis.greater_than(max_vis, self.tcx) { - let def_id = self.local_def_id(id); self.lint_buffer.buffer_lint( UNUSED_IMPORTS, id, @@ -1629,7 +1631,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(extern_crate_id) = pub_use_of_private_extern_crate_hack(import.summary(), decl) { let ImportKind::Single { id, .. } = import.kind else { unreachable!() }; - let sugg = self.tcx.source_span(self.local_def_id(extern_crate_id)).shrink_to_lo(); + let sugg = self.tcx.source_span(extern_crate_id).shrink_to_lo(); let diagnostic = crate::errors::PrivateExternCrateReexport { ident, sugg }; return Some(BufferedEarlyLint { lint_id: LintId::of(PUB_USE_OF_PRIVATE_EXTERN_CRATE), @@ -1671,7 +1673,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { pub(crate) fn check_for_redundant_imports(&mut self, import: Import<'ra>) -> bool { // This function is only called for single imports. - let ImportKind::Single { source, target, ref decls, id, .. } = import.kind else { + let ImportKind::Single { source, target, ref decls, id, def_id, .. } = import.kind else { unreachable!() }; @@ -1690,7 +1692,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Skip if the import is public or was used through non scope-based resolution, // e.g. through a module-relative path. if self.import_use_map.get(&import) == Some(&Used::Other) - || self.effective_visibilities.is_exported(self.local_def_id(id)) + || self.effective_visibilities.is_exported(def_id) { return false; } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 6f777984e6c03..68e1dd9291dfe 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -2291,8 +2291,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { #[inline] fn add_to_glob_map(&mut self, import: Import<'_>, name: Symbol) { - if let ImportKind::Glob { id, .. } = import.kind { - let def_id = self.local_def_id(id); + if let ImportKind::Glob { def_id, .. } = import.kind { self.glob_map.entry(def_id).or_default().insert(name); } } From b8e547a8dc5caeee4d3ab8751a2ecb84ff7f244c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 29 Apr 2026 10:53:45 +0200 Subject: [PATCH 13/16] fix warnings in rustc_type_ir --- compiler/rustc_type_ir/src/binder.rs | 12 ++++++++---- compiler/rustc_type_ir/src/ir_print.rs | 9 +++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index b55cbc3164d96..de3e04626f823 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -375,13 +375,17 @@ pub struct EarlyBinder { impl Eq for EarlyBinder {} -/// For early binders, you should first call `instantiate` before using any visitors. +// FIXME(154045): Recommended as per https://github.com/rust-lang/rust/issues/154045, this is so sad :(( #[cfg(feature = "nightly")] -impl !TypeFoldable for ty::EarlyBinder {} +macro_rules! generate { ($( $tt:tt )*) => { $( $tt )* } } -/// For early binders, you should first call `instantiate` before using any visitors. #[cfg(feature = "nightly")] -impl !TypeVisitable for ty::EarlyBinder {} +generate!( + /// For early binders, you should first call `instantiate` before using any visitors. + impl !TypeFoldable for ty::EarlyBinder {} + /// For early binders, you should first call `instantiate` before using any visitors. + impl !TypeVisitable for ty::EarlyBinder {} +); impl EarlyBinder { pub fn bind(value: T) -> EarlyBinder { diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs index 5af2bd811bab4..c6ab804d81c9f 100644 --- a/compiler/rustc_type_ir/src/ir_print.rs +++ b/compiler/rustc_type_ir/src/ir_print.rs @@ -1,11 +1,12 @@ use std::fmt; use crate::{ - AliasTerm, AliasTy, Binder, ClosureKind, CoercePredicate, ExistentialProjection, - ExistentialTraitRef, FnSig, HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, - PatternKind, Placeholder, ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, - UnevaluatedConst, + AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig, + HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, PatternKind, Placeholder, + ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, }; +#[cfg(feature = "nightly")] +use crate::{ClosureKind, UnevaluatedConst}; pub trait IrPrint { fn print(t: &T, fmt: &mut fmt::Formatter<'_>) -> fmt::Result; From c44e9222a08375c6793b25e2cc6cf704660d1f5e Mon Sep 17 00:00:00 2001 From: mu001999 Date: Mon, 6 Apr 2026 19:55:33 +0800 Subject: [PATCH 14/16] Refactor dead code liveness plumbing --- compiler/rustc_middle/src/middle/dead_code.rs | 23 +++ compiler/rustc_middle/src/middle/mod.rs | 1 + compiler/rustc_middle/src/queries.rs | 14 +- compiler/rustc_passes/src/dead.rs | 147 ++++++++++++------ 4 files changed, 131 insertions(+), 54 deletions(-) create mode 100644 compiler/rustc_middle/src/middle/dead_code.rs diff --git a/compiler/rustc_middle/src/middle/dead_code.rs b/compiler/rustc_middle/src/middle/dead_code.rs new file mode 100644 index 0000000000000..0881b0a2c1a56 --- /dev/null +++ b/compiler/rustc_middle/src/middle/dead_code.rs @@ -0,0 +1,23 @@ +use rustc_data_structures::fx::FxIndexSet; +use rustc_hir::def_id::{DefId, LocalDefIdMap, LocalDefIdSet}; +use rustc_macros::StableHash; + +/// A single snapshot of dead-code liveness analysis state. +#[derive(Clone, Debug, StableHash)] +pub struct DeadCodeLivenessSnapshot { + pub live_symbols: LocalDefIdSet, + /// Maps each ADT to derived traits (for example `Debug` and `Clone`) that should be ignored + /// when checking for dead code diagnostics. + pub ignored_derived_traits: LocalDefIdMap>, +} + +/// Dead-code liveness data across primary and deferred seeding. +/// +/// `pre_deferred_seeding` is computed after the initial analysis pass but before deferred seeds +/// are added. +/// `final_result` is the liveness snapshot after all seeds have been processed. +#[derive(Clone, Debug, StableHash)] +pub struct DeadCodeLivenessSummary { + pub pre_deferred_seeding: DeadCodeLivenessSnapshot, + pub final_result: DeadCodeLivenessSnapshot, +} diff --git a/compiler/rustc_middle/src/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs index 203845143fa06..7967a6222c3be 100644 --- a/compiler/rustc_middle/src/middle/mod.rs +++ b/compiler/rustc_middle/src/middle/mod.rs @@ -1,4 +1,5 @@ pub mod codegen_fn_attrs; +pub mod dead_code; pub mod debugger_visualizer; pub mod deduced_param_attrs; pub mod dependency_format; diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index f0c367beefd65..f8200badb345b 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -64,9 +64,7 @@ use rustc_errors::{ErrorGuaranteed, catch_fatal_errors}; use rustc_hir as hir; use rustc_hir::attrs::{EiiDecl, EiiImpl, StrippedCfgItem}; use rustc_hir::def::{DefKind, DocLinkResMap}; -use rustc_hir::def_id::{ - CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId, -}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdSet, LocalModDefId}; use rustc_hir::lang_items::{LangItem, LanguageItems}; use rustc_hir::{ItemLocalId, ItemLocalMap, PreciseCapturingArgKind, TraitCandidate}; use rustc_index::IndexVec; @@ -87,6 +85,7 @@ use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; use crate::metadata::ModChild; use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, SanitizerFnAttrs}; +use crate::middle::dead_code::DeadCodeLivenessSummary; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::middle::deduced_param_attrs::DeducedParamAttrs; use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; @@ -1203,13 +1202,8 @@ rustc_queries! { desc { "checking liveness of variables in `{}`", tcx.def_path_str(key.to_def_id()) } } - /// Return the live symbols in the crate for dead code check. - /// - /// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone). - query live_symbols_and_ignored_derived_traits(_: ()) -> Result<&'tcx ( - LocalDefIdSet, - LocalDefIdMap>, - ), ErrorGuaranteed> { + /// Return dead-code liveness summary for the crate. + query live_symbols_and_ignored_derived_traits(_: ()) -> Result<&'tcx DeadCodeLivenessSummary, ErrorGuaranteed> { arena_cache desc { "finding live symbols in crate" } } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 7eb484b1d57a7..e0652d69bf75e 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -13,14 +13,15 @@ use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, Node, PatKind, QPath, find_attr}; +use rustc_hir::{self as hir, ForeignItemId, ItemId, Node, PatKind, QPath, find_attr}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::middle::dead_code::{DeadCodeLivenessSnapshot, DeadCodeLivenessSummary}; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; use rustc_middle::ty::{self, AssocTag, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::DEAD_CODE; -use rustc_session::lint::{self, LintExpectationId}; +use rustc_session::lint::{self, Lint, LintExpectationId}; use rustc_span::{Symbol, kw}; use crate::errors::{ @@ -819,12 +820,12 @@ fn has_allow_dead_code_or_lang_attr( fn maybe_record_as_seed<'tcx>( tcx: TyCtxt<'tcx>, owner_id: hir::OwnerId, - worklist: &mut Vec, + push_into_worklist: &mut impl FnMut(WorkItem), unsolved_items: &mut Vec, ) { let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, owner_id.def_id); if let Some(comes_from_allow) = allow_dead_code { - worklist.push(WorkItem { + push_into_worklist(WorkItem { id: owner_id.def_id, propagated: comes_from_allow, own: comes_from_allow, @@ -835,11 +836,13 @@ fn maybe_record_as_seed<'tcx>( DefKind::Enum => { if let Some(comes_from_allow) = allow_dead_code { let adt = tcx.adt_def(owner_id); - worklist.extend(adt.variants().iter().map(|variant| WorkItem { - id: variant.def_id.expect_local(), - propagated: comes_from_allow, - own: comes_from_allow, - })); + for variant in adt.variants().iter() { + push_into_worklist(WorkItem { + id: variant.def_id.expect_local(), + propagated: comes_from_allow, + own: comes_from_allow, + }); + } } } DefKind::AssocFn | DefKind::AssocConst { .. } | DefKind::AssocTy => { @@ -854,7 +857,7 @@ fn maybe_record_as_seed<'tcx>( && let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, trait_item_local_def_id) { - worklist.push(WorkItem { + push_into_worklist(WorkItem { id: owner_id.def_id, propagated: comes_from_allow, own: comes_from_allow, @@ -879,7 +882,7 @@ fn maybe_record_as_seed<'tcx>( && let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, trait_def_id) { - worklist.push(WorkItem { + push_into_worklist(WorkItem { id: owner_id.def_id, propagated: comes_from_allow, own: comes_from_allow, @@ -891,7 +894,7 @@ fn maybe_record_as_seed<'tcx>( } DefKind::GlobalAsm => { // global_asm! is always live. - worklist.push(WorkItem { + push_into_worklist(WorkItem { id: owner_id.def_id, propagated: ComesFromAllowExpect::No, own: ComesFromAllowExpect::No, @@ -902,7 +905,7 @@ fn maybe_record_as_seed<'tcx>( // `const _` is always live, as that syntax only exists for the side effects // of type checking and evaluating the constant expression, and marking them // as dead code would defeat that purpose. - worklist.push(WorkItem { + push_into_worklist(WorkItem { id: owner_id.def_id, propagated: ComesFromAllowExpect::No, own: ComesFromAllowExpect::No, @@ -913,41 +916,55 @@ fn maybe_record_as_seed<'tcx>( } } -fn create_and_seed_worklist(tcx: TyCtxt<'_>) -> (Vec, Vec) { - let effective_visibilities = &tcx.effective_visibilities(()); - let mut unsolved_impl_item = Vec::new(); - let mut worklist = effective_visibilities - .iter() - .filter_map(|(&id, effective_vis)| { - effective_vis.is_public_at_level(Level::Reachable).then_some(id).map(|id| WorkItem { - id, - propagated: ComesFromAllowExpect::No, - own: ComesFromAllowExpect::No, - }) - }) - // Seed entry point - .chain(tcx.entry_fn(()).and_then(|(def_id, _)| { - def_id.as_local().map(|id| WorkItem { - id, +struct SeedWorklists { + worklist: Vec, + deferred_seeds: Vec, + unsolved_items: Vec, +} + +fn create_and_seed_worklist(tcx: TyCtxt<'_>) -> SeedWorklists { + let mut unsolved_items = Vec::new(); + let mut deferred_seeds = Vec::new(); + let mut worklist = Vec::new(); + + if let Some((def_id, _)) = tcx.entry_fn(()) + && let Some(local_def_id) = def_id.as_local() + { + worklist.push(WorkItem { + id: local_def_id, + propagated: ComesFromAllowExpect::No, + own: ComesFromAllowExpect::No, + }); + } + + for (id, effective_vis) in tcx.effective_visibilities(()).iter() { + if effective_vis.is_public_at_level(Level::Reachable) { + deferred_seeds.push(WorkItem { + id: *id, propagated: ComesFromAllowExpect::No, own: ComesFromAllowExpect::No, - }) - })) - .collect::>(); + }); + } + } + let mut push_into_worklist = |work_item: WorkItem| match work_item.own { + ComesFromAllowExpect::Yes => deferred_seeds.push(work_item), + ComesFromAllowExpect::No => worklist.push(work_item), + }; let crate_items = tcx.hir_crate_items(()); for id in crate_items.owners() { - maybe_record_as_seed(tcx, id, &mut worklist, &mut unsolved_impl_item); + maybe_record_as_seed(tcx, id, &mut push_into_worklist, &mut unsolved_items); } - (worklist, unsolved_impl_item) + SeedWorklists { worklist, deferred_seeds, unsolved_items } } fn live_symbols_and_ignored_derived_traits( tcx: TyCtxt<'_>, (): (), -) -> Result<(LocalDefIdSet, LocalDefIdMap>), ErrorGuaranteed> { - let (worklist, mut unsolved_items) = create_and_seed_worklist(tcx); +) -> Result { + let SeedWorklists { worklist, deferred_seeds, mut unsolved_items } = + create_and_seed_worklist(tcx); let mut symbol_visitor = MarkSymbolVisitor { worklist, tcx, @@ -961,6 +978,28 @@ fn live_symbols_and_ignored_derived_traits( ignored_derived_traits: Default::default(), propagated_comes_from_allow_expect: ComesFromAllowExpect::No, }; + mark_live_symbols_and_ignored_derived_traits(&mut symbol_visitor, &mut unsolved_items)?; + let pre_deferred_seeding = DeadCodeLivenessSnapshot { + live_symbols: symbol_visitor.live_symbols.clone(), + ignored_derived_traits: symbol_visitor.ignored_derived_traits.clone(), + }; + + symbol_visitor.worklist.extend(deferred_seeds); + mark_live_symbols_and_ignored_derived_traits(&mut symbol_visitor, &mut unsolved_items)?; + + Ok(DeadCodeLivenessSummary { + pre_deferred_seeding, + final_result: DeadCodeLivenessSnapshot { + live_symbols: symbol_visitor.live_symbols, + ignored_derived_traits: symbol_visitor.ignored_derived_traits, + }, + }) +} + +fn mark_live_symbols_and_ignored_derived_traits( + symbol_visitor: &mut MarkSymbolVisitor<'_>, + unsolved_items: &mut Vec, +) -> Result<(), ErrorGuaranteed> { if let ControlFlow::Break(guar) = symbol_visitor.mark_live_symbols() { return Err(guar); } @@ -988,7 +1027,7 @@ fn live_symbols_and_ignored_derived_traits( })); } - Ok((symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)) + Ok(()) } struct DeadItem { @@ -999,6 +1038,7 @@ struct DeadItem { struct DeadVisitor<'tcx> { tcx: TyCtxt<'tcx>, + target_lint: &'static Lint, live_symbols: &'tcx LocalDefIdSet, ignored_derived_traits: &'tcx LocalDefIdMap>, } @@ -1042,7 +1082,7 @@ impl<'tcx> DeadVisitor<'tcx> { fn def_lint_level(&self, id: LocalDefId) -> (lint::Level, Option) { let hir_id = self.tcx.local_def_id_to_hir_id(id); - let level = self.tcx.lint_level_at_node(DEAD_CODE, hir_id); + let level = self.tcx.lint_level_at_node(self.target_lint, hir_id); (level.level, level.lint_id) } @@ -1201,7 +1241,7 @@ impl<'tcx> DeadVisitor<'tcx> { }; let hir_id = tcx.local_def_id_to_hir_id(first_item.def_id); - self.tcx.emit_node_span_lint(DEAD_CODE, hir_id, MultiSpan::from_spans(spans), diag); + self.tcx.emit_node_span_lint(self.target_lint, hir_id, MultiSpan::from_spans(spans), diag); } fn warn_multiple( @@ -1268,17 +1308,36 @@ impl<'tcx> DeadVisitor<'tcx> { } fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { - let Ok((live_symbols, ignored_derived_traits)) = + let Ok(DeadCodeLivenessSummary { final_result, .. }) = tcx.live_symbols_and_ignored_derived_traits(()).as_ref() else { return; }; - let mut visitor = DeadVisitor { tcx, live_symbols, ignored_derived_traits }; - let module_items = tcx.hir_module_items(module); - for item in module_items.free_items() { + lint_dead_code_or_unused_pub_items_in_binary( + tcx, + DEAD_CODE, + module, + &final_result.live_symbols, + &final_result.ignored_derived_traits, + module_items.free_items(), + module_items.foreign_items(), + ); +} + +fn lint_dead_code_or_unused_pub_items_in_binary<'tcx>( + tcx: TyCtxt<'tcx>, + target_lint: &'static Lint, + module: LocalModDefId, + live_symbols: &'tcx LocalDefIdSet, + ignored_derived_traits: &'tcx LocalDefIdMap>, + free_items: impl Iterator, + foreign_items: impl Iterator, +) { + let mut visitor = DeadVisitor { tcx, target_lint, live_symbols, ignored_derived_traits }; + for item in free_items { let def_kind = tcx.def_kind(item.owner_id); let mut dead_codes = Vec::new(); @@ -1357,7 +1416,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { } } - for foreign_item in module_items.foreign_items() { + for foreign_item in foreign_items { visitor.check_definition(foreign_item.owner_id.def_id); } } From 27a8bb677b6570c31a321a08b9a91088a0918107 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Mon, 6 Apr 2026 19:56:03 +0800 Subject: [PATCH 15/16] Implement unused_pub_items_in_binary lint --- compiler/rustc_lint_defs/src/builtin.rs | 32 +++++++++++++++++ compiler/rustc_middle/src/middle/dead_code.rs | 8 ++--- compiler/rustc_passes/src/check_attr.rs | 10 ++++++ compiler/rustc_passes/src/dead.rs | 36 +++++++++++++++++-- compiler/rustc_passes/src/errors.rs | 12 +++++++ compiler/rustc_span/src/symbol.rs | 1 + .../allow-dead-code-transitive.rs | 14 ++++++++ .../allow-dead-code-transitive.stderr | 15 ++++++++ .../unused-pub-items-in-binary/allow-lint.rs | 12 +++++++ .../allow-lint.stderr | 14 ++++++++ .../lint/unused-pub-items-in-binary/basic.rs | 12 +++++++ .../unused-pub-items-in-binary/basic.stderr | 27 ++++++++++++++ .../unused-pub-items-in-binary/deny-lint.rs | 6 ++++ .../deny-lint.stderr | 15 ++++++++ .../interaction-allow-dead-code.rs | 7 ++++ .../interaction-allow-dead-code.stderr | 15 ++++++++ .../interaction-allow-unused-pub.rs | 7 ++++ .../interaction-allow-unused-pub.stderr | 14 ++++++++ .../interaction-deny-dead-code-only.rs | 7 ++++ .../unused-pub-items-in-binary/library.rs | 9 +++++ .../unused-pub-items-in-binary/library.stderr | 23 ++++++++++++ .../no-mangle-items.rs | 14 ++++++++ .../no-mangle-items.stderr | 27 ++++++++++++++ 23 files changed, 330 insertions(+), 7 deletions(-) create mode 100644 tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/allow-lint.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/basic.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/basic.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/interaction-deny-dead-code-only.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/library.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/library.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 4858e65992cd3..efb7925a43356 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -148,6 +148,7 @@ declare_lint_pass! { UNUSED_MACROS, UNUSED_MACRO_RULES, UNUSED_MUT, + UNUSED_PUB_ITEMS_IN_BINARY, UNUSED_QUALIFICATIONS, UNUSED_UNSAFE, UNUSED_VARIABLES, @@ -789,6 +790,37 @@ declare_lint! { "detect unused, unexported items" } +declare_lint! { + /// The `unused_pub_items_in_binary` lint detects unused `pub` items in + /// executable crates. + /// + /// ### Example + /// + /// ```rust + /// #![deny(unused_pub_items_in_binary)] + /// + /// pub fn unused_pub_fn() {} + /// + /// fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In executable crates, `pub` items are often implementation details + /// rather than part of an external API. This lint helps find those items + /// when they are never used. + /// + /// This lint only applies to executable crates. In library crates, public + /// items are considered part of the crate's API and are not reported by + /// this lint. + pub UNUSED_PUB_ITEMS_IN_BINARY, + Allow, + "detect public items in executable crates that are never used", + crate_level_only +} + declare_lint! { /// The `unused_attributes` lint detects attributes that were not used by /// the compiler. diff --git a/compiler/rustc_middle/src/middle/dead_code.rs b/compiler/rustc_middle/src/middle/dead_code.rs index 0881b0a2c1a56..5b4edee1425f2 100644 --- a/compiler/rustc_middle/src/middle/dead_code.rs +++ b/compiler/rustc_middle/src/middle/dead_code.rs @@ -11,11 +11,11 @@ pub struct DeadCodeLivenessSnapshot { pub ignored_derived_traits: LocalDefIdMap>, } -/// Dead-code liveness data across primary and deferred seeding. +/// Dead-code liveness data for both analysis phases. /// -/// `pre_deferred_seeding` is computed after the initial analysis pass but before deferred seeds -/// are added. -/// `final_result` is the liveness snapshot after all seeds have been processed. +/// `pre_deferred_seeding` is computed before reachable-public and `#[allow(dead_code)]` seeding, +/// and is used for lint `unused_pub_items_in_binary`. +/// `final_result` is the final liveness snapshot used for lint `dead_code`. #[derive(Clone, Debug, StableHash)] pub struct DeadCodeLivenessSummary { pub pre_deferred_seeding: DeadCodeLivenessSnapshot, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index a978138e3e1ff..b23d65d905cae 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1526,6 +1526,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { return; } } + } else if hir_id == CRATE_HIR_ID + && attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect]) + && let Some(meta) = attr.meta_item_list() + && meta.iter().any(|meta| { + meta.meta_item() + .is_some_and(|item| item.path == sym::unused_pub_items_in_binary) + }) + && !self.tcx.crate_types().contains(&CrateType::Executable) + { + errors::UnusedNote::NoEffectUnusedPubItemsInBinary } else if attr.has_name(sym::default_method_body_is_const) { errors::UnusedNote::DefaultMethodBodyConst } else { diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index e0652d69bf75e..1f5b1f75ed5dc 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -20,12 +20,14 @@ use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; use rustc_middle::ty::{self, AssocTag, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_session::lint::builtin::DEAD_CODE; +use rustc_session::config::CrateType; +use rustc_session::lint::builtin::{DEAD_CODE, UNUSED_PUB_ITEMS_IN_BINARY}; use rustc_session::lint::{self, Lint, LintExpectationId}; use rustc_span::{Symbol, kw}; use crate::errors::{ - ChangeFields, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UselessAssignment, + ChangeFields, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UnusedPubItemsInBinaryNote, + UselessAssignment, }; /// Any local definition that may call something in its body block should be explored. For example, @@ -1086,6 +1088,13 @@ impl<'tcx> DeadVisitor<'tcx> { (level.level, level.lint_id) } + fn unused_pub_items_in_binary_note(&self) -> Option { + self.target_lint + .name + .eq(UNUSED_PUB_ITEMS_IN_BINARY.name) + .then_some(UnusedPubItemsInBinaryNote) + } + // # Panics // All `dead_codes` must have the same lint level, otherwise we will intentionally ICE. // This is because we emit a multi-spanned lint using the lint level of the `dead_codes`'s @@ -1197,6 +1206,7 @@ impl<'tcx> DeadVisitor<'tcx> { descr, participle, name_list, + unused_pub_items_in_binary_note: self.unused_pub_items_in_binary_note(), change_fields_suggestion: fields_suggestion, parent_info, ignored_derived_impls, @@ -1233,6 +1243,7 @@ impl<'tcx> DeadVisitor<'tcx> { descr, participle, name_list, + unused_pub_items_in_binary_note: self.unused_pub_items_in_binary_note(), parent_info, ignored_derived_impls, enum_variants_with_same_name, @@ -1308,7 +1319,7 @@ impl<'tcx> DeadVisitor<'tcx> { } fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { - let Ok(DeadCodeLivenessSummary { final_result, .. }) = + let Ok(DeadCodeLivenessSummary { pre_deferred_seeding, final_result }) = tcx.live_symbols_and_ignored_derived_traits(()).as_ref() else { return; @@ -1316,6 +1327,25 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { let module_items = tcx.hir_module_items(module); + if tcx.crate_types().contains(&CrateType::Executable) { + let is_unused_pub = |def_id: LocalDefId| { + tcx.effective_visibilities(()).is_public_at_level(def_id, Level::Reachable) + && !pre_deferred_seeding.live_symbols.contains(&def_id) + }; + + lint_dead_code_or_unused_pub_items_in_binary( + tcx, + UNUSED_PUB_ITEMS_IN_BINARY, + module, + &pre_deferred_seeding.live_symbols, + &pre_deferred_seeding.ignored_derived_traits, + module_items.free_items().filter(|free_item| is_unused_pub(free_item.owner_id.def_id)), + module_items + .foreign_items() + .filter(|foreign_item| is_unused_pub(foreign_item.owner_id.def_id)), + ); + } + lint_dead_code_or_unused_pub_items_in_binary( tcx, DEAD_CODE, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index a916b4670fded..b463199b6b3b2 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -298,6 +298,8 @@ pub(crate) enum UnusedNote { "the `linker_messages` and `linker_info` lints can only be controlled at the root of a crate that needs to be linked" )] LinkerMessagesBinaryCrateOnly, + #[note("the `unused_pub_items_in_binary` lint has no effect in library crates")] + NoEffectUnusedPubItemsInBinary, } #[derive(Diagnostic)] @@ -920,6 +922,8 @@ pub(crate) enum MultipleDeadCodes<'tcx> { participle: &'tcx str, name_list: DiagSymbolList, #[subdiagnostic] + unused_pub_items_in_binary_note: Option, + #[subdiagnostic] // only on DeadCodes since it's never a problem for tuple struct fields enum_variants_with_same_name: Vec>, #[subdiagnostic] @@ -943,6 +947,8 @@ pub(crate) enum MultipleDeadCodes<'tcx> { participle: &'tcx str, name_list: DiagSymbolList, #[subdiagnostic] + unused_pub_items_in_binary_note: Option, + #[subdiagnostic] change_fields_suggestion: ChangeFields, #[subdiagnostic] parent_info: Option>, @@ -951,6 +957,12 @@ pub(crate) enum MultipleDeadCodes<'tcx> { }, } +#[derive(Subdiagnostic)] +#[note( + "in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused" +)] +pub(crate) struct UnusedPubItemsInBinaryNote; + #[derive(Subdiagnostic)] #[note( "it is impossible to refer to the {$dead_descr} `{$dead_name}` because it is shadowed by this enum variant with the same name" diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 695103a249b49..67cdac8bc3e11 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2192,6 +2192,7 @@ symbols! { unstable_removed, untagged_unions, unused_imports, + unused_pub_items_in_binary, unwind, unwind_attributes, unwind_safe_trait, diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs b/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs new file mode 100644 index 0000000000000..0b939fd435b69 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs @@ -0,0 +1,14 @@ +#![deny(dead_code)] +#![deny(unused_pub_items_in_binary)] + +pub fn g() {} //~ ERROR function `g` is never used + +fn h() {} + +#[allow(dead_code)] +fn f() { + g(); + h(); +} + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr b/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr new file mode 100644 index 0000000000000..99710b18c6988 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr @@ -0,0 +1,15 @@ +error: function `g` is never used + --> $DIR/allow-dead-code-transitive.rs:4:8 + | +LL | pub fn g() {} + | ^ + | + = note: in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused +note: the lint level is defined here + --> $DIR/allow-dead-code-transitive.rs:2:9 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs b/tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs new file mode 100644 index 0000000000000..af737ae6243c5 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs @@ -0,0 +1,12 @@ +#![deny(dead_code)] +#![allow(unused_pub_items_in_binary)] + +pub fn unused_pub_fn() { + helper(); +} + +fn helper() {} + +fn unused_priv_fn() {} //~ ERROR function `unused_priv_fn` is never used + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-lint.stderr b/tests/ui/lint/unused-pub-items-in-binary/allow-lint.stderr new file mode 100644 index 0000000000000..ae864e50fcf22 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/allow-lint.stderr @@ -0,0 +1,14 @@ +error: function `unused_priv_fn` is never used + --> $DIR/allow-lint.rs:10:4 + | +LL | fn unused_priv_fn() {} + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/allow-lint.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused-pub-items-in-binary/basic.rs b/tests/ui/lint/unused-pub-items-in-binary/basic.rs new file mode 100644 index 0000000000000..de76c55fbc683 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/basic.rs @@ -0,0 +1,12 @@ +#![deny(dead_code)] +#![deny(unused_pub_items_in_binary)] + +pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used + +pub fn used_pub_fn() {} + +fn unused_priv_fn() {} //~ ERROR function `unused_priv_fn` is never used + +fn main() { + used_pub_fn(); +} diff --git a/tests/ui/lint/unused-pub-items-in-binary/basic.stderr b/tests/ui/lint/unused-pub-items-in-binary/basic.stderr new file mode 100644 index 0000000000000..240e3ae02b5e1 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/basic.stderr @@ -0,0 +1,27 @@ +error: function `unused_pub_fn` is never used + --> $DIR/basic.rs:4:8 + | +LL | pub fn unused_pub_fn() {} + | ^^^^^^^^^^^^^ + | + = note: in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused +note: the lint level is defined here + --> $DIR/basic.rs:2:9 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: function `unused_priv_fn` is never used + --> $DIR/basic.rs:8:4 + | +LL | fn unused_priv_fn() {} + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/basic.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs b/tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs new file mode 100644 index 0000000000000..78fd6d059d8b7 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs @@ -0,0 +1,6 @@ +#![deny(unused_pub_items_in_binary)] +#![allow(dead_code)] + +pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr b/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr new file mode 100644 index 0000000000000..75b082dc018d8 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr @@ -0,0 +1,15 @@ +error: function `unused_pub_fn` is never used + --> $DIR/deny-lint.rs:4:8 + | +LL | pub fn unused_pub_fn() {} + | ^^^^^^^^^^^^^ + | + = note: in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused +note: the lint level is defined here + --> $DIR/deny-lint.rs:1:9 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs new file mode 100644 index 0000000000000..aa1022b447bad --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs @@ -0,0 +1,7 @@ +#![allow(dead_code)] +#![deny(unused_pub_items_in_binary)] + +pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used +fn unused_priv_fn() {} + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr new file mode 100644 index 0000000000000..64d938db4e880 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr @@ -0,0 +1,15 @@ +error: function `unused_pub_fn` is never used + --> $DIR/interaction-allow-dead-code.rs:4:8 + | +LL | pub fn unused_pub_fn() {} + | ^^^^^^^^^^^^^ + | + = note: in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused +note: the lint level is defined here + --> $DIR/interaction-allow-dead-code.rs:2:9 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs new file mode 100644 index 0000000000000..5564e3f96ccd8 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs @@ -0,0 +1,7 @@ +#![allow(unused_pub_items_in_binary)] +#![deny(dead_code)] + +pub fn unused_pub_fn() {} +fn unused_priv_fn() {} //~ ERROR function `unused_priv_fn` is never used + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.stderr b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.stderr new file mode 100644 index 0000000000000..3c7924257967d --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.stderr @@ -0,0 +1,14 @@ +error: function `unused_priv_fn` is never used + --> $DIR/interaction-allow-unused-pub.rs:5:4 + | +LL | fn unused_priv_fn() {} + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/interaction-allow-unused-pub.rs:2:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-deny-dead-code-only.rs b/tests/ui/lint/unused-pub-items-in-binary/interaction-deny-dead-code-only.rs new file mode 100644 index 0000000000000..0680ba7d4965d --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-deny-dead-code-only.rs @@ -0,0 +1,7 @@ +//@ check-pass + +#![deny(dead_code)] + +pub fn unused_pub_fn() {} + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/library.rs b/tests/ui/lint/unused-pub-items-in-binary/library.rs new file mode 100644 index 0000000000000..c44409d837ab5 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/library.rs @@ -0,0 +1,9 @@ +#![deny(dead_code)] +#![deny(unused_pub_items_in_binary)] +//~^ WARN unused attribute + +#![crate_type = "lib"] + +pub fn unused_pub_fn() {} // Should NOT error because it's a library + +fn unused_priv_fn() {} //~ ERROR function `unused_priv_fn` is never used diff --git a/tests/ui/lint/unused-pub-items-in-binary/library.stderr b/tests/ui/lint/unused-pub-items-in-binary/library.stderr new file mode 100644 index 0000000000000..071da07e9b044 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/library.stderr @@ -0,0 +1,23 @@ +warning: unused attribute + --> $DIR/library.rs:2:1 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | + = note: the `unused_pub_items_in_binary` lint has no effect in library crates + = note: requested on the command line with `-W unused-attributes` + +error: function `unused_priv_fn` is never used + --> $DIR/library.rs:9:4 + | +LL | fn unused_priv_fn() {} + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/library.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error; 1 warning emitted + diff --git a/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs b/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs new file mode 100644 index 0000000000000..13db56a9e0ad8 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs @@ -0,0 +1,14 @@ +#![deny(dead_code)] +#![deny(unused_pub_items_in_binary)] + +#[no_mangle] +pub fn pub_fn_no_mangle() {} + +pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used + +#[no_mangle] +pub fn unused_priv_fn_no_mangle() {} + +fn unused_priv_fn() {} //~ ERROR function `unused_priv_fn` is never used + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr b/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr new file mode 100644 index 0000000000000..18cb33abdf020 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr @@ -0,0 +1,27 @@ +error: function `unused_pub_fn` is never used + --> $DIR/no-mangle-items.rs:7:8 + | +LL | pub fn unused_pub_fn() {} + | ^^^^^^^^^^^^^ + | + = note: in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused +note: the lint level is defined here + --> $DIR/no-mangle-items.rs:2:9 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: function `unused_priv_fn` is never used + --> $DIR/no-mangle-items.rs:12:4 + | +LL | fn unused_priv_fn() {} + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/no-mangle-items.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 2 previous errors + From 26bfcf3f35a9bcdd78a493f6bcbe201884fac39a Mon Sep 17 00:00:00 2001 From: mu001999 Date: Tue, 28 Apr 2026 22:42:47 +0800 Subject: [PATCH 16/16] Rename unused_pub_items_in_binary to dead_code_pub_in_binary --- compiler/rustc_lint_defs/src/builtin.rs | 8 +++---- compiler/rustc_middle/src/middle/dead_code.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 5 ++-- compiler/rustc_passes/src/dead.rs | 23 ++++++++----------- compiler/rustc_passes/src/errors.rs | 10 ++++---- compiler/rustc_span/src/symbol.rs | 2 +- .../allow-dead-code-transitive.rs | 2 +- .../allow-dead-code-transitive.stderr | 4 ++-- .../allow-lint.rs | 2 +- .../allow-lint.stderr | 0 .../basic.rs | 2 +- .../basic.stderr | 4 ++-- .../deny-lint.rs | 2 +- .../deny-lint.stderr | 4 ++-- .../interaction-allow-dead-code.rs | 2 +- .../interaction-allow-dead-code.stderr | 4 ++-- .../interaction-allow-unused-pub.rs | 2 +- .../interaction-allow-unused-pub.stderr | 0 .../interaction-deny-dead-code-only.rs | 0 .../library.rs | 2 +- .../library.stderr | 6 ++--- .../no-mangle-items.rs | 2 +- .../no-mangle-items.stderr | 4 ++-- 23 files changed, 44 insertions(+), 48 deletions(-) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/allow-dead-code-transitive.rs (79%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/allow-dead-code-transitive.stderr (82%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/allow-lint.rs (81%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/allow-lint.stderr (100%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/basic.rs (86%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/basic.stderr (88%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/deny-lint.rs (75%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/deny-lint.stderr (82%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/interaction-allow-dead-code.rs (78%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/interaction-allow-dead-code.stderr (83%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/interaction-allow-unused-pub.rs (77%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/interaction-allow-unused-pub.stderr (100%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/interaction-deny-dead-code-only.rs (100%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/library.rs (85%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/library.stderr (69%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/no-mangle-items.rs (88%) rename tests/ui/lint/{unused-pub-items-in-binary => dead-code-pub-in-binary}/no-mangle-items.stderr (88%) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index efb7925a43356..c6892419443f8 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -34,6 +34,7 @@ declare_lint_pass! { CONST_EVALUATABLE_UNCHECKED, CONST_ITEM_MUTATION, DEAD_CODE, + DEAD_CODE_PUB_IN_BINARY, DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK, DEPRECATED, DEPRECATED_IN_FUTURE, @@ -148,7 +149,6 @@ declare_lint_pass! { UNUSED_MACROS, UNUSED_MACRO_RULES, UNUSED_MUT, - UNUSED_PUB_ITEMS_IN_BINARY, UNUSED_QUALIFICATIONS, UNUSED_UNSAFE, UNUSED_VARIABLES, @@ -791,13 +791,13 @@ declare_lint! { } declare_lint! { - /// The `unused_pub_items_in_binary` lint detects unused `pub` items in + /// The `dead_code_pub_in_binary` lint detects unused `pub` items in /// executable crates. /// /// ### Example /// /// ```rust - /// #![deny(unused_pub_items_in_binary)] + /// #![deny(dead_code_pub_in_binary)] /// /// pub fn unused_pub_fn() {} /// @@ -815,7 +815,7 @@ declare_lint! { /// This lint only applies to executable crates. In library crates, public /// items are considered part of the crate's API and are not reported by /// this lint. - pub UNUSED_PUB_ITEMS_IN_BINARY, + pub DEAD_CODE_PUB_IN_BINARY, Allow, "detect public items in executable crates that are never used", crate_level_only diff --git a/compiler/rustc_middle/src/middle/dead_code.rs b/compiler/rustc_middle/src/middle/dead_code.rs index 5b4edee1425f2..06275e1e0f3a0 100644 --- a/compiler/rustc_middle/src/middle/dead_code.rs +++ b/compiler/rustc_middle/src/middle/dead_code.rs @@ -14,7 +14,7 @@ pub struct DeadCodeLivenessSnapshot { /// Dead-code liveness data for both analysis phases. /// /// `pre_deferred_seeding` is computed before reachable-public and `#[allow(dead_code)]` seeding, -/// and is used for lint `unused_pub_items_in_binary`. +/// and is used for lint `dead_code_pub_in_binary`. /// `final_result` is the final liveness snapshot used for lint `dead_code`. #[derive(Clone, Debug, StableHash)] pub struct DeadCodeLivenessSummary { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index b23d65d905cae..f1596a0685768 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1530,12 +1530,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { && attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect]) && let Some(meta) = attr.meta_item_list() && meta.iter().any(|meta| { - meta.meta_item() - .is_some_and(|item| item.path == sym::unused_pub_items_in_binary) + meta.meta_item().is_some_and(|item| item.path == sym::dead_code_pub_in_binary) }) && !self.tcx.crate_types().contains(&CrateType::Executable) { - errors::UnusedNote::NoEffectUnusedPubItemsInBinary + errors::UnusedNote::NoEffectDeadCodePubInBinary } else if attr.has_name(sym::default_method_body_is_const) { errors::UnusedNote::DefaultMethodBodyConst } else { diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 1f5b1f75ed5dc..2ee094c87616c 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -21,12 +21,12 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{self, AssocTag, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; -use rustc_session::lint::builtin::{DEAD_CODE, UNUSED_PUB_ITEMS_IN_BINARY}; +use rustc_session::lint::builtin::{DEAD_CODE, DEAD_CODE_PUB_IN_BINARY}; use rustc_session::lint::{self, Lint, LintExpectationId}; use rustc_span::{Symbol, kw}; use crate::errors::{ - ChangeFields, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UnusedPubItemsInBinaryNote, + ChangeFields, DeadCodePubInBinaryNote, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UselessAssignment, }; @@ -1088,11 +1088,8 @@ impl<'tcx> DeadVisitor<'tcx> { (level.level, level.lint_id) } - fn unused_pub_items_in_binary_note(&self) -> Option { - self.target_lint - .name - .eq(UNUSED_PUB_ITEMS_IN_BINARY.name) - .then_some(UnusedPubItemsInBinaryNote) + fn dead_code_pub_in_binary_note(&self) -> Option { + self.target_lint.name.eq(DEAD_CODE_PUB_IN_BINARY.name).then_some(DeadCodePubInBinaryNote) } // # Panics @@ -1206,7 +1203,7 @@ impl<'tcx> DeadVisitor<'tcx> { descr, participle, name_list, - unused_pub_items_in_binary_note: self.unused_pub_items_in_binary_note(), + dead_code_pub_in_binary_note: self.dead_code_pub_in_binary_note(), change_fields_suggestion: fields_suggestion, parent_info, ignored_derived_impls, @@ -1243,7 +1240,7 @@ impl<'tcx> DeadVisitor<'tcx> { descr, participle, name_list, - unused_pub_items_in_binary_note: self.unused_pub_items_in_binary_note(), + dead_code_pub_in_binary_note: self.dead_code_pub_in_binary_note(), parent_info, ignored_derived_impls, enum_variants_with_same_name, @@ -1333,9 +1330,9 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { && !pre_deferred_seeding.live_symbols.contains(&def_id) }; - lint_dead_code_or_unused_pub_items_in_binary( + lint_dead_codes( tcx, - UNUSED_PUB_ITEMS_IN_BINARY, + DEAD_CODE_PUB_IN_BINARY, module, &pre_deferred_seeding.live_symbols, &pre_deferred_seeding.ignored_derived_traits, @@ -1346,7 +1343,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { ); } - lint_dead_code_or_unused_pub_items_in_binary( + lint_dead_codes( tcx, DEAD_CODE, module, @@ -1357,7 +1354,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { ); } -fn lint_dead_code_or_unused_pub_items_in_binary<'tcx>( +fn lint_dead_codes<'tcx>( tcx: TyCtxt<'tcx>, target_lint: &'static Lint, module: LocalModDefId, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index b463199b6b3b2..b87a43e9fcde0 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -298,8 +298,8 @@ pub(crate) enum UnusedNote { "the `linker_messages` and `linker_info` lints can only be controlled at the root of a crate that needs to be linked" )] LinkerMessagesBinaryCrateOnly, - #[note("the `unused_pub_items_in_binary` lint has no effect in library crates")] - NoEffectUnusedPubItemsInBinary, + #[note("the `dead_code_pub_in_binary` lint has no effect in library crates")] + NoEffectDeadCodePubInBinary, } #[derive(Diagnostic)] @@ -922,7 +922,7 @@ pub(crate) enum MultipleDeadCodes<'tcx> { participle: &'tcx str, name_list: DiagSymbolList, #[subdiagnostic] - unused_pub_items_in_binary_note: Option, + dead_code_pub_in_binary_note: Option, #[subdiagnostic] // only on DeadCodes since it's never a problem for tuple struct fields enum_variants_with_same_name: Vec>, @@ -947,7 +947,7 @@ pub(crate) enum MultipleDeadCodes<'tcx> { participle: &'tcx str, name_list: DiagSymbolList, #[subdiagnostic] - unused_pub_items_in_binary_note: Option, + dead_code_pub_in_binary_note: Option, #[subdiagnostic] change_fields_suggestion: ChangeFields, #[subdiagnostic] @@ -961,7 +961,7 @@ pub(crate) enum MultipleDeadCodes<'tcx> { #[note( "in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused" )] -pub(crate) struct UnusedPubItemsInBinaryNote; +pub(crate) struct DeadCodePubInBinaryNote; #[derive(Subdiagnostic)] #[note( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 67cdac8bc3e11..fb4a9cbd8c53f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -753,6 +753,7 @@ symbols! { d32, dbg_macro, dead_code, + dead_code_pub_in_binary, dealloc, debug, debug_assert_eq_macro, @@ -2192,7 +2193,6 @@ symbols! { unstable_removed, untagged_unions, unused_imports, - unused_pub_items_in_binary, unwind, unwind_attributes, unwind_safe_trait, diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs b/tests/ui/lint/dead-code-pub-in-binary/allow-dead-code-transitive.rs similarity index 79% rename from tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs rename to tests/ui/lint/dead-code-pub-in-binary/allow-dead-code-transitive.rs index 0b939fd435b69..a991317bec7c5 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs +++ b/tests/ui/lint/dead-code-pub-in-binary/allow-dead-code-transitive.rs @@ -1,5 +1,5 @@ #![deny(dead_code)] -#![deny(unused_pub_items_in_binary)] +#![deny(dead_code_pub_in_binary)] pub fn g() {} //~ ERROR function `g` is never used diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr b/tests/ui/lint/dead-code-pub-in-binary/allow-dead-code-transitive.stderr similarity index 82% rename from tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr rename to tests/ui/lint/dead-code-pub-in-binary/allow-dead-code-transitive.stderr index 99710b18c6988..a45e88646bd70 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr +++ b/tests/ui/lint/dead-code-pub-in-binary/allow-dead-code-transitive.stderr @@ -8,8 +8,8 @@ LL | pub fn g() {} note: the lint level is defined here --> $DIR/allow-dead-code-transitive.rs:2:9 | -LL | #![deny(unused_pub_items_in_binary)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(dead_code_pub_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs b/tests/ui/lint/dead-code-pub-in-binary/allow-lint.rs similarity index 81% rename from tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs rename to tests/ui/lint/dead-code-pub-in-binary/allow-lint.rs index af737ae6243c5..5daf188542d8f 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs +++ b/tests/ui/lint/dead-code-pub-in-binary/allow-lint.rs @@ -1,5 +1,5 @@ #![deny(dead_code)] -#![allow(unused_pub_items_in_binary)] +#![allow(dead_code_pub_in_binary)] pub fn unused_pub_fn() { helper(); diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-lint.stderr b/tests/ui/lint/dead-code-pub-in-binary/allow-lint.stderr similarity index 100% rename from tests/ui/lint/unused-pub-items-in-binary/allow-lint.stderr rename to tests/ui/lint/dead-code-pub-in-binary/allow-lint.stderr diff --git a/tests/ui/lint/unused-pub-items-in-binary/basic.rs b/tests/ui/lint/dead-code-pub-in-binary/basic.rs similarity index 86% rename from tests/ui/lint/unused-pub-items-in-binary/basic.rs rename to tests/ui/lint/dead-code-pub-in-binary/basic.rs index de76c55fbc683..6af8bd4448cf5 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/basic.rs +++ b/tests/ui/lint/dead-code-pub-in-binary/basic.rs @@ -1,5 +1,5 @@ #![deny(dead_code)] -#![deny(unused_pub_items_in_binary)] +#![deny(dead_code_pub_in_binary)] pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used diff --git a/tests/ui/lint/unused-pub-items-in-binary/basic.stderr b/tests/ui/lint/dead-code-pub-in-binary/basic.stderr similarity index 88% rename from tests/ui/lint/unused-pub-items-in-binary/basic.stderr rename to tests/ui/lint/dead-code-pub-in-binary/basic.stderr index 240e3ae02b5e1..aa6ac974f2eb5 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/basic.stderr +++ b/tests/ui/lint/dead-code-pub-in-binary/basic.stderr @@ -8,8 +8,8 @@ LL | pub fn unused_pub_fn() {} note: the lint level is defined here --> $DIR/basic.rs:2:9 | -LL | #![deny(unused_pub_items_in_binary)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(dead_code_pub_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^ error: function `unused_priv_fn` is never used --> $DIR/basic.rs:8:4 diff --git a/tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs b/tests/ui/lint/dead-code-pub-in-binary/deny-lint.rs similarity index 75% rename from tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs rename to tests/ui/lint/dead-code-pub-in-binary/deny-lint.rs index 78fd6d059d8b7..336e59e9a99fc 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs +++ b/tests/ui/lint/dead-code-pub-in-binary/deny-lint.rs @@ -1,4 +1,4 @@ -#![deny(unused_pub_items_in_binary)] +#![deny(dead_code_pub_in_binary)] #![allow(dead_code)] pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used diff --git a/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr b/tests/ui/lint/dead-code-pub-in-binary/deny-lint.stderr similarity index 82% rename from tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr rename to tests/ui/lint/dead-code-pub-in-binary/deny-lint.stderr index 75b082dc018d8..679edfe7ba722 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr +++ b/tests/ui/lint/dead-code-pub-in-binary/deny-lint.stderr @@ -8,8 +8,8 @@ LL | pub fn unused_pub_fn() {} note: the lint level is defined here --> $DIR/deny-lint.rs:1:9 | -LL | #![deny(unused_pub_items_in_binary)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(dead_code_pub_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs b/tests/ui/lint/dead-code-pub-in-binary/interaction-allow-dead-code.rs similarity index 78% rename from tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs rename to tests/ui/lint/dead-code-pub-in-binary/interaction-allow-dead-code.rs index aa1022b447bad..212a84c4d71b9 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs +++ b/tests/ui/lint/dead-code-pub-in-binary/interaction-allow-dead-code.rs @@ -1,5 +1,5 @@ #![allow(dead_code)] -#![deny(unused_pub_items_in_binary)] +#![deny(dead_code_pub_in_binary)] pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used fn unused_priv_fn() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr b/tests/ui/lint/dead-code-pub-in-binary/interaction-allow-dead-code.stderr similarity index 83% rename from tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr rename to tests/ui/lint/dead-code-pub-in-binary/interaction-allow-dead-code.stderr index 64d938db4e880..e3ded09678df0 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr +++ b/tests/ui/lint/dead-code-pub-in-binary/interaction-allow-dead-code.stderr @@ -8,8 +8,8 @@ LL | pub fn unused_pub_fn() {} note: the lint level is defined here --> $DIR/interaction-allow-dead-code.rs:2:9 | -LL | #![deny(unused_pub_items_in_binary)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(dead_code_pub_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs b/tests/ui/lint/dead-code-pub-in-binary/interaction-allow-unused-pub.rs similarity index 77% rename from tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs rename to tests/ui/lint/dead-code-pub-in-binary/interaction-allow-unused-pub.rs index 5564e3f96ccd8..d30df29d966b1 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs +++ b/tests/ui/lint/dead-code-pub-in-binary/interaction-allow-unused-pub.rs @@ -1,4 +1,4 @@ -#![allow(unused_pub_items_in_binary)] +#![allow(dead_code_pub_in_binary)] #![deny(dead_code)] pub fn unused_pub_fn() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.stderr b/tests/ui/lint/dead-code-pub-in-binary/interaction-allow-unused-pub.stderr similarity index 100% rename from tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.stderr rename to tests/ui/lint/dead-code-pub-in-binary/interaction-allow-unused-pub.stderr diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-deny-dead-code-only.rs b/tests/ui/lint/dead-code-pub-in-binary/interaction-deny-dead-code-only.rs similarity index 100% rename from tests/ui/lint/unused-pub-items-in-binary/interaction-deny-dead-code-only.rs rename to tests/ui/lint/dead-code-pub-in-binary/interaction-deny-dead-code-only.rs diff --git a/tests/ui/lint/unused-pub-items-in-binary/library.rs b/tests/ui/lint/dead-code-pub-in-binary/library.rs similarity index 85% rename from tests/ui/lint/unused-pub-items-in-binary/library.rs rename to tests/ui/lint/dead-code-pub-in-binary/library.rs index c44409d837ab5..1641ace1f0d9f 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/library.rs +++ b/tests/ui/lint/dead-code-pub-in-binary/library.rs @@ -1,5 +1,5 @@ #![deny(dead_code)] -#![deny(unused_pub_items_in_binary)] +#![deny(dead_code_pub_in_binary)] //~^ WARN unused attribute #![crate_type = "lib"] diff --git a/tests/ui/lint/unused-pub-items-in-binary/library.stderr b/tests/ui/lint/dead-code-pub-in-binary/library.stderr similarity index 69% rename from tests/ui/lint/unused-pub-items-in-binary/library.stderr rename to tests/ui/lint/dead-code-pub-in-binary/library.stderr index 071da07e9b044..8ac71b01c6627 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/library.stderr +++ b/tests/ui/lint/dead-code-pub-in-binary/library.stderr @@ -1,10 +1,10 @@ warning: unused attribute --> $DIR/library.rs:2:1 | -LL | #![deny(unused_pub_items_in_binary)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute +LL | #![deny(dead_code_pub_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | - = note: the `unused_pub_items_in_binary` lint has no effect in library crates + = note: the `dead_code_pub_in_binary` lint has no effect in library crates = note: requested on the command line with `-W unused-attributes` error: function `unused_priv_fn` is never used diff --git a/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs b/tests/ui/lint/dead-code-pub-in-binary/no-mangle-items.rs similarity index 88% rename from tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs rename to tests/ui/lint/dead-code-pub-in-binary/no-mangle-items.rs index 13db56a9e0ad8..60afa99dc0dee 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs +++ b/tests/ui/lint/dead-code-pub-in-binary/no-mangle-items.rs @@ -1,5 +1,5 @@ #![deny(dead_code)] -#![deny(unused_pub_items_in_binary)] +#![deny(dead_code_pub_in_binary)] #[no_mangle] pub fn pub_fn_no_mangle() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr b/tests/ui/lint/dead-code-pub-in-binary/no-mangle-items.stderr similarity index 88% rename from tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr rename to tests/ui/lint/dead-code-pub-in-binary/no-mangle-items.stderr index 18cb33abdf020..80be42c547652 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr +++ b/tests/ui/lint/dead-code-pub-in-binary/no-mangle-items.stderr @@ -8,8 +8,8 @@ LL | pub fn unused_pub_fn() {} note: the lint level is defined here --> $DIR/no-mangle-items.rs:2:9 | -LL | #![deny(unused_pub_items_in_binary)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(dead_code_pub_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^ error: function `unused_priv_fn` is never used --> $DIR/no-mangle-items.rs:12:4