diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 196b8fd52d53f..8c2c6045ffaed 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -281,6 +281,7 @@ fn register_builtins(store: &mut LintStore) { UNUSED_VARIABLES, UNUSED_ASSIGNMENTS, DEAD_CODE, + UNCONSTRUCTIBLE_PUB_STRUCT, UNUSED_MUT, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 246b516076461..c8808186a929f 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -106,6 +106,7 @@ declare_lint_pass! { TYVAR_BEHIND_RAW_POINTER, UNCONDITIONAL_PANIC, UNCONDITIONAL_RECURSION, + UNCONSTRUCTIBLE_PUB_STRUCT, UNCOVERED_PARAM_IN_PROJECTION, UNDEFINED_NAKED_FUNCTION_ABI, UNEXPECTED_CFGS, @@ -725,6 +726,38 @@ declare_lint! { "detect unused, unexported items" } +declare_lint! { + /// The `unconstructible_pub_struct` lint detects public structs that + /// are unused locally and cannot be constructed externally. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unconstructible_pub_struct)] + /// + /// pub struct Foo(i32); + /// # fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unconstructible pub structs may signal a mistake or unfinished code. + /// To silence the warning for individual items, prefix the name with an + /// underscore such as `_Foo`. + /// + /// To preserve this lint, add a field with units or never types which + /// indicates that the behaivor is intentional, or use `PhantomData` as + /// fields' type if the struct is only used at the type level to check + /// things like well-formedness. + /// + /// Otherwise, consider removing it if the struct is no longer in use. + pub UNCONSTRUCTIBLE_PUB_STRUCT, + Allow, + "detects pub structs that are unused locally and cannot be constructed externally" +} + declare_lint! { /// The `unused_attributes` lint detects attributes that were not used by /// the compiler. diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 8a931fc4158e3..7184edb865346 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -20,7 +20,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{self, AssocItemContainer, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint; -use rustc_session::lint::builtin::DEAD_CODE; +use rustc_session::lint::builtin::{DEAD_CODE, UNCONSTRUCTIBLE_PUB_STRUCT}; use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::FieldIdx; @@ -739,6 +739,12 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { } } +fn has_allow_unconstructible_pub_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + let hir_id = tcx.local_def_id_to_hir_id(def_id); + let lint_level = tcx.lint_level_at_node(lint::builtin::UNCONSTRUCTIBLE_PUB_STRUCT, hir_id).0; + matches!(lint_level, lint::Allow | lint::Expect(_)) +} + fn has_allow_dead_code_or_lang_attr( tcx: TyCtxt<'_>, def_id: LocalDefId, @@ -930,7 +936,7 @@ fn create_and_seed_worklist( match tcx.def_kind(id) { DefKind::Impl { .. } => false, DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer), - DefKind::Struct => struct_all_fields_are_public(tcx, id) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), + DefKind::Struct => has_allow_unconstructible_pub_struct(tcx, id) || struct_all_fields_are_public(tcx, id), _ => true }) .map(|id| (id, ComesFromAllowExpect::No)) @@ -1177,8 +1183,16 @@ impl<'tcx> DeadVisitor<'tcx> { }, }; + let lint = if tcx.effective_visibilities(()).is_reachable(first_item.def_id) + && matches!(tcx.def_kind(first_item.def_id), DefKind::Struct) + { + UNCONSTRUCTIBLE_PUB_STRUCT + } else { + DEAD_CODE + }; + 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(lint, hir_id, MultiSpan::from_spans(spans), diag); } fn warn_multiple( diff --git a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs index 4bf2fa761eae8..c23187598bceb 100644 --- a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs +++ b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs @@ -2,7 +2,6 @@ //@ run-pass -#[allow(dead_code)] #[repr(C)] pub struct Loaf { head: [T; N], diff --git a/tests/ui/const-generics/generic_const_exprs/associated-consts.rs b/tests/ui/const-generics/generic_const_exprs/associated-consts.rs index 50a6102c605a9..7b44d755e6310 100644 --- a/tests/ui/const-generics/generic_const_exprs/associated-consts.rs +++ b/tests/ui/const-generics/generic_const_exprs/associated-consts.rs @@ -16,7 +16,6 @@ impl BlockCipher for BarCipher { const BLOCK_SIZE: usize = 32; } -#[allow(dead_code)] pub struct Block(C); pub fn test() diff --git a/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs b/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs index 35c41ae461520..419d605d0c875 100644 --- a/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs +++ b/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs @@ -6,7 +6,6 @@ use std::mem::MaybeUninit; -#[allow(dead_code)] #[repr(transparent)] pub struct MaybeUninitWrapper(MaybeUninit<[u64; N]>); diff --git a/tests/ui/issues/issue-5708.rs b/tests/ui/issues/issue-5708.rs index 89ea9fbdcd8fe..ce9ef78ffcd9c 100644 --- a/tests/ui/issues/issue-5708.rs +++ b/tests/ui/issues/issue-5708.rs @@ -44,7 +44,6 @@ pub trait MyTrait { fn dummy(&self, t: T) -> T { panic!() } } -#[allow(dead_code)] pub struct MyContainer<'a, T:'a> { foos: Vec<&'a (dyn MyTrait+'a)> , } diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.rs b/tests/ui/lint/dead-code/lint-dead-code-1.rs index 3386dfa47470f..7ecd378c49527 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-1.rs +++ b/tests/ui/lint/dead-code/lint-dead-code-1.rs @@ -3,6 +3,7 @@ #![allow(non_camel_case_types)] #![allow(non_upper_case_globals)] #![deny(dead_code)] +#![deny(unconstructible_pub_struct)] #![crate_type="lib"] diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.stderr b/tests/ui/lint/dead-code/lint-dead-code-1.stderr index b0163df8855cd..5b2584b5c5577 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-1.stderr +++ b/tests/ui/lint/dead-code/lint-dead-code-1.stderr @@ -1,5 +1,5 @@ error: static `priv_static` is never used - --> $DIR/lint-dead-code-1.rs:20:8 + --> $DIR/lint-dead-code-1.rs:21:8 | LL | static priv_static: isize = 0; | ^^^^^^^^^^^ @@ -11,37 +11,43 @@ LL | #![deny(dead_code)] | ^^^^^^^^^ error: constant `priv_const` is never used - --> $DIR/lint-dead-code-1.rs:27:7 + --> $DIR/lint-dead-code-1.rs:28:7 | LL | const priv_const: isize = 0; | ^^^^^^^^^^ error: struct `PrivStruct` is never constructed - --> $DIR/lint-dead-code-1.rs:35:8 + --> $DIR/lint-dead-code-1.rs:36:8 | LL | struct PrivStruct; | ^^^^^^^^^^ error: struct `StructUsedAsField` is never constructed - --> $DIR/lint-dead-code-1.rs:49:8 + --> $DIR/lint-dead-code-1.rs:50:8 | LL | struct StructUsedAsField; | ^^^^^^^^^^^^^^^^^ error: struct `PubStruct2` is never constructed - --> $DIR/lint-dead-code-1.rs:52:12 + --> $DIR/lint-dead-code-1.rs:53:12 | LL | pub struct PubStruct2 { | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-dead-code-1.rs:6:9 + | +LL | #![deny(unconstructible_pub_struct)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: enum `priv_enum` is never used - --> $DIR/lint-dead-code-1.rs:63:6 + --> $DIR/lint-dead-code-1.rs:64:6 | LL | enum priv_enum { foo2, bar2 } | ^^^^^^^^^ error: variant `bar3` is never constructed - --> $DIR/lint-dead-code-1.rs:66:5 + --> $DIR/lint-dead-code-1.rs:67:5 | LL | enum used_enum { | --------- variant in this enum @@ -50,31 +56,31 @@ LL | bar3 | ^^^^ error: function `priv_fn` is never used - --> $DIR/lint-dead-code-1.rs:87:4 + --> $DIR/lint-dead-code-1.rs:88:4 | LL | fn priv_fn() { | ^^^^^^^ error: function `foo` is never used - --> $DIR/lint-dead-code-1.rs:92:4 + --> $DIR/lint-dead-code-1.rs:93:4 | LL | fn foo() { | ^^^ error: function `bar` is never used - --> $DIR/lint-dead-code-1.rs:97:4 + --> $DIR/lint-dead-code-1.rs:98:4 | LL | fn bar() { | ^^^ error: function `baz` is never used - --> $DIR/lint-dead-code-1.rs:101:4 + --> $DIR/lint-dead-code-1.rs:102:4 | LL | fn baz() -> impl Copy { | ^^^ error: struct `Bar` is never constructed - --> $DIR/lint-dead-code-1.rs:12:16 + --> $DIR/lint-dead-code-1.rs:13:16 | LL | pub struct Bar; | ^^^ diff --git a/tests/ui/lint/dead-code/unconstructible-pub-struct.rs b/tests/ui/lint/dead-code/unconstructible-pub-struct.rs index 2202cbb673098..f1999d4e30cfa 100644 --- a/tests/ui/lint/dead-code/unconstructible-pub-struct.rs +++ b/tests/ui/lint/dead-code/unconstructible-pub-struct.rs @@ -1,5 +1,5 @@ #![feature(never_type)] -#![deny(dead_code)] +#![deny(unconstructible_pub_struct)] pub struct T1(!); pub struct T2(()); @@ -32,4 +32,10 @@ pub struct T9 { //~ ERROR struct `T9` is never constructed _y: i32, } +pub struct _T10(i32); + +mod pri { + pub struct Unreachable(i32); +} + fn main() {} diff --git a/tests/ui/lint/dead-code/unconstructible-pub-struct.stderr b/tests/ui/lint/dead-code/unconstructible-pub-struct.stderr index a3dde042bbe15..9fcf45cb311c6 100644 --- a/tests/ui/lint/dead-code/unconstructible-pub-struct.stderr +++ b/tests/ui/lint/dead-code/unconstructible-pub-struct.stderr @@ -7,8 +7,8 @@ LL | pub struct T9 { note: the lint level is defined here --> $DIR/unconstructible-pub-struct.rs:2:9 | -LL | #![deny(dead_code)] - | ^^^^^^^^^ +LL | #![deny(unconstructible_pub_struct)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs index 658cc3d6c613a..af100872189b2 100644 --- a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs +++ b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs @@ -1,4 +1,4 @@ -#![deny(dead_code)] +#![deny(unused)] struct T1; //~ ERROR struct `T1` is never constructed struct T2; //~ ERROR struct `T2` is never constructed diff --git a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr index 08c7a5cb4b062..f478400a0c312 100644 --- a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr +++ b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr @@ -7,8 +7,9 @@ LL | struct T1; note: the lint level is defined here --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:1:9 | -LL | #![deny(dead_code)] - | ^^^^^^^^^ +LL | #![deny(unused)] + | ^^^^^^ + = note: `#[deny(dead_code)]` implied by `#[deny(unused)]` error: struct `T2` is never constructed --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:4:8 @@ -21,6 +22,8 @@ error: struct `T3` is never constructed | LL | pub struct T3(i32); | ^^ + | + = note: `#[deny(unconstructible_pub_struct)]` implied by `#[deny(unused)]` error: field `0` is never read --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:6:15 diff --git a/tests/ui/lint/dead-code/unused-pub-struct.rs b/tests/ui/lint/dead-code/unused-pub-struct.rs index aaf4dd612de4d..eb36231b55dc0 100644 --- a/tests/ui/lint/dead-code/unused-pub-struct.rs +++ b/tests/ui/lint/dead-code/unused-pub-struct.rs @@ -1,4 +1,4 @@ -#![deny(dead_code)] +#![deny(unused)] pub struct NotLint1(()); pub struct NotLint2(std::marker::PhantomData); diff --git a/tests/ui/lint/dead-code/unused-pub-struct.stderr b/tests/ui/lint/dead-code/unused-pub-struct.stderr index 3667ddb97bd3e..ebab9fb653c4b 100644 --- a/tests/ui/lint/dead-code/unused-pub-struct.stderr +++ b/tests/ui/lint/dead-code/unused-pub-struct.stderr @@ -7,8 +7,9 @@ LL | pub struct NeverConstructed(i32); note: the lint level is defined here --> $DIR/unused-pub-struct.rs:1:9 | -LL | #![deny(dead_code)] - | ^^^^^^^^^ +LL | #![deny(unused)] + | ^^^^^^ + = note: `#[deny(unconstructible_pub_struct)]` implied by `#[deny(unused)]` error: aborting due to 1 previous error diff --git a/tests/ui/regions/regions-issue-21422.rs b/tests/ui/regions/regions-issue-21422.rs index 67852a6f5de0d..54beed9b3ac2b 100644 --- a/tests/ui/regions/regions-issue-21422.rs +++ b/tests/ui/regions/regions-issue-21422.rs @@ -5,7 +5,6 @@ //@ pretty-expanded FIXME #23616 -#[allow(dead_code)] pub struct P<'a> { _ptr: *const &'a u8, } diff --git a/tests/ui/structs-enums/newtype-struct-with-dtor.rs b/tests/ui/structs-enums/newtype-struct-with-dtor.rs index 16439a7fedd3e..19672e41c9a30 100644 --- a/tests/ui/structs-enums/newtype-struct-with-dtor.rs +++ b/tests/ui/structs-enums/newtype-struct-with-dtor.rs @@ -3,10 +3,8 @@ #![allow(unused_variables)] //@ pretty-expanded FIXME #23616 -#[allow(dead_code)] pub struct Fd(u32); -#[allow(dead_code)] fn foo(a: u32) {} impl Drop for Fd { diff --git a/tests/ui/suggestions/option-content-move.fixed b/tests/ui/suggestions/option-content-move.fixed index ef07d55871e7e..259b1343b527d 100644 --- a/tests/ui/suggestions/option-content-move.fixed +++ b/tests/ui/suggestions/option-content-move.fixed @@ -1,5 +1,5 @@ //@ run-rustfix -#[allow(dead_code)] + pub struct LipogramCorpora { selections: Vec<(char, Option)>, } @@ -18,7 +18,6 @@ impl LipogramCorpora { } } -#[allow(dead_code)] pub struct LipogramCorpora2 { selections: Vec<(char, Result)>, } diff --git a/tests/ui/suggestions/option-content-move.rs b/tests/ui/suggestions/option-content-move.rs index 5be6358fd6a57..aa7ec0859ece9 100644 --- a/tests/ui/suggestions/option-content-move.rs +++ b/tests/ui/suggestions/option-content-move.rs @@ -1,5 +1,5 @@ //@ run-rustfix -#[allow(dead_code)] + pub struct LipogramCorpora { selections: Vec<(char, Option)>, } @@ -18,7 +18,6 @@ impl LipogramCorpora { } } -#[allow(dead_code)] pub struct LipogramCorpora2 { selections: Vec<(char, Result)>, } diff --git a/tests/ui/suggestions/option-content-move.stderr b/tests/ui/suggestions/option-content-move.stderr index b4ec5b180d210..b5237338bebdb 100644 --- a/tests/ui/suggestions/option-content-move.stderr +++ b/tests/ui/suggestions/option-content-move.stderr @@ -19,7 +19,7 @@ LL | if selection.1.clone().unwrap().contains(selection.0) { | ++++++++ error[E0507]: cannot move out of `selection.1` which is behind a shared reference - --> $DIR/option-content-move.rs:30:20 + --> $DIR/option-content-move.rs:29:20 | LL | if selection.1.unwrap().contains(selection.0) { | ^^^^^^^^^^^ -------- `selection.1` moved due to this method call diff --git a/tests/ui/traits/object/generics.rs b/tests/ui/traits/object/generics.rs index 0ae562c0d30a0..462b0bc5bb77f 100644 --- a/tests/ui/traits/object/generics.rs +++ b/tests/ui/traits/object/generics.rs @@ -7,7 +7,6 @@ pub trait Trait2 { fn doit(&self) -> A; } -#[allow(dead_code)] pub struct Impl { m1: marker::PhantomData<(A1,A2,A3)>, /*