Skip to content

Commit

Permalink
Add a new lint for unconstructible pub structs
Browse files Browse the repository at this point in the history
  • Loading branch information
mu001999 committed Jul 31, 2024
1 parent 1ddedba commit 4e0311e
Show file tree
Hide file tree
Showing 21 changed files with 87 additions and 37 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
32 changes: 32 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -725,6 +726,37 @@ 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
/// #![deny(unconstructible_pub_struct)]
///
/// pub struct Foo(i32);
/// ```
///
/// {{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.
Expand Down
20 changes: 17 additions & 3 deletions compiler/rustc_passes/src/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -1177,8 +1183,16 @@ impl<'tcx> DeadVisitor<'tcx> {
},
};

let lint = if tcx.visibility(first_item.def_id).is_public()
&& 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(
Expand Down
1 change: 0 additions & 1 deletion tests/ui/const-generics/defaults/repr-c-issue-82792.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

//@ run-pass

#[allow(dead_code)]
#[repr(C)]
pub struct Loaf<T: Sized, const N: usize = 1> {
head: [T; N],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ impl BlockCipher for BarCipher {
const BLOCK_SIZE: usize = 32;
}

#[allow(dead_code)]
pub struct Block<C>(C);

pub fn test<C: BlockCipher, const M: usize>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use std::mem::MaybeUninit;

#[allow(dead_code)]
#[repr(transparent)]
pub struct MaybeUninitWrapper<const N: usize>(MaybeUninit<[u64; N]>);

Expand Down
1 change: 0 additions & 1 deletion tests/ui/issues/issue-5708.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ pub trait MyTrait<T> {
fn dummy(&self, t: T) -> T { panic!() }
}

#[allow(dead_code)]
pub struct MyContainer<'a, T:'a> {
foos: Vec<&'a (dyn MyTrait<T>+'a)> ,
}
Expand Down
1 change: 1 addition & 0 deletions tests/ui/lint/dead-code/lint-dead-code-1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![deny(dead_code)]
#![deny(unconstructible_pub_struct)]

#![crate_type="lib"]

Expand Down
30 changes: 18 additions & 12 deletions tests/ui/lint/dead-code/lint-dead-code-1.stderr
Original file line number Diff line number Diff line change
@@ -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;
| ^^^^^^^^^^^
Expand All @@ -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
Expand All @@ -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;
| ^^^
Expand Down
4 changes: 3 additions & 1 deletion tests/ui/lint/dead-code/unconstructible-pub-struct.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![feature(never_type)]
#![deny(dead_code)]
#![deny(unconstructible_pub_struct)]

pub struct T1(!);
pub struct T2(());
Expand Down Expand Up @@ -32,4 +32,6 @@ pub struct T9<X> { //~ ERROR struct `T9` is never constructed
_y: i32,
}

pub struct _T10(i32);

fn main() {}
4 changes: 2 additions & 2 deletions tests/ui/lint/dead-code/unconstructible-pub-struct.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ LL | pub struct T9<X> {
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

Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/lint/dead-code/unused-pub-struct.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![deny(dead_code)]
#![deny(unused)]

pub struct NotLint1(());
pub struct NotLint2(std::marker::PhantomData<i32>);
Expand Down
5 changes: 3 additions & 2 deletions tests/ui/lint/dead-code/unused-pub-struct.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

1 change: 0 additions & 1 deletion tests/ui/regions/regions-issue-21422.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

//@ pretty-expanded FIXME #23616

#[allow(dead_code)]
pub struct P<'a> {
_ptr: *const &'a u8,
}
Expand Down
2 changes: 0 additions & 2 deletions tests/ui/structs-enums/newtype-struct-with-dtor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 1 addition & 2 deletions tests/ui/suggestions/option-content-move.fixed
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//@ run-rustfix
#[allow(dead_code)]

pub struct LipogramCorpora {
selections: Vec<(char, Option<String>)>,
}
Expand All @@ -18,7 +18,6 @@ impl LipogramCorpora {
}
}

#[allow(dead_code)]
pub struct LipogramCorpora2 {
selections: Vec<(char, Result<String, String>)>,
}
Expand Down
3 changes: 1 addition & 2 deletions tests/ui/suggestions/option-content-move.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//@ run-rustfix
#[allow(dead_code)]

pub struct LipogramCorpora {
selections: Vec<(char, Option<String>)>,
}
Expand All @@ -18,7 +18,6 @@ impl LipogramCorpora {
}
}

#[allow(dead_code)]
pub struct LipogramCorpora2 {
selections: Vec<(char, Result<String, String>)>,
}
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/suggestions/option-content-move.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion tests/ui/traits/object/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ pub trait Trait2<A> {
fn doit(&self) -> A;
}

#[allow(dead_code)]
pub struct Impl<A1, A2, A3> {
m1: marker::PhantomData<(A1,A2,A3)>,
/*
Expand Down

0 comments on commit 4e0311e

Please sign in to comment.