Skip to content

Commit 2370123

Browse files
committed
Add a new lint for unconstructible pub structs
1 parent 7e3a971 commit 2370123

27 files changed

+106
-45
lines changed

compiler/rustc_lint/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ fn register_builtins(store: &mut LintStore) {
281281
UNUSED_VARIABLES,
282282
UNUSED_ASSIGNMENTS,
283283
DEAD_CODE,
284+
UNCONSTRUCTIBLE_PUB_STRUCT,
284285
UNUSED_MUT,
285286
UNREACHABLE_CODE,
286287
UNREACHABLE_PATTERNS,

compiler/rustc_lint_defs/src/builtin.rs

+30
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ declare_lint_pass! {
106106
TYVAR_BEHIND_RAW_POINTER,
107107
UNCONDITIONAL_PANIC,
108108
UNCONDITIONAL_RECURSION,
109+
UNCONSTRUCTIBLE_PUB_STRUCT,
109110
UNCOVERED_PARAM_IN_PROJECTION,
110111
UNDEFINED_NAKED_FUNCTION_ABI,
111112
UNEXPECTED_CFGS,
@@ -725,6 +726,35 @@ declare_lint! {
725726
"detect unused, unexported items"
726727
}
727728

729+
declare_lint! {
730+
/// The `unconstructible_pub_struct` lint detects public structs that
731+
/// are unused locally and cannot be constructed externally.
732+
///
733+
/// ### Example
734+
///
735+
/// ```rust
736+
/// pub struct Foo(i32);
737+
/// ```
738+
///
739+
/// {{produces}}
740+
///
741+
/// ### Explanation
742+
///
743+
/// Unconstructible pub structs may signal a mistake or unfinished code.
744+
/// To silence the warning for individual items, prefix the name with an
745+
/// underscore such as `_Foo`.
746+
///
747+
/// To preserve this lint, add a field with units or never types which
748+
/// indicates that the behaivor is intentional, or use `PhantomData` as
749+
/// fields' type if the struct is only used at the type level to check
750+
/// things like well-formedness.
751+
///
752+
/// Otherwise, consider removing it if the struct is no longer in use.
753+
pub UNCONSTRUCTIBLE_PUB_STRUCT,
754+
Warn,
755+
"detects pub structs that are unused locally and cannot be constructed externally"
756+
}
757+
728758
declare_lint! {
729759
/// The `unused_attributes` lint detects attributes that were not used by
730760
/// the compiler.

compiler/rustc_passes/src/dead.rs

+17-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_middle::query::Providers;
2020
use rustc_middle::ty::{self, AssocItemContainer, TyCtxt};
2121
use rustc_middle::{bug, span_bug};
2222
use rustc_session::lint;
23-
use rustc_session::lint::builtin::DEAD_CODE;
23+
use rustc_session::lint::builtin::{DEAD_CODE, UNCONSTRUCTIBLE_PUB_STRUCT};
2424
use rustc_span::symbol::{sym, Symbol};
2525
use rustc_target::abi::FieldIdx;
2626

@@ -739,6 +739,12 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
739739
}
740740
}
741741

742+
fn has_allow_unconstructible_pub_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
743+
let hir_id = tcx.local_def_id_to_hir_id(def_id);
744+
let lint_level = tcx.lint_level_at_node(lint::builtin::UNCONSTRUCTIBLE_PUB_STRUCT, hir_id).0;
745+
matches!(lint_level, lint::Allow | lint::Expect(_))
746+
}
747+
742748
fn has_allow_dead_code_or_lang_attr(
743749
tcx: TyCtxt<'_>,
744750
def_id: LocalDefId,
@@ -930,7 +936,7 @@ fn create_and_seed_worklist(
930936
match tcx.def_kind(id) {
931937
DefKind::Impl { .. } => false,
932938
DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer),
933-
DefKind::Struct => struct_all_fields_are_public(tcx, id) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(),
939+
DefKind::Struct => has_allow_unconstructible_pub_struct(tcx, id) || struct_all_fields_are_public(tcx, id),
934940
_ => true
935941
})
936942
.map(|id| (id, ComesFromAllowExpect::No))
@@ -1177,8 +1183,16 @@ impl<'tcx> DeadVisitor<'tcx> {
11771183
},
11781184
};
11791185

1186+
let lint = if tcx.visibility(first_item.def_id).is_public()
1187+
&& matches!(tcx.def_kind(first_item.def_id), DefKind::Struct)
1188+
{
1189+
UNCONSTRUCTIBLE_PUB_STRUCT
1190+
} else {
1191+
DEAD_CODE
1192+
};
1193+
11801194
let hir_id = tcx.local_def_id_to_hir_id(first_item.def_id);
1181-
self.tcx.emit_node_span_lint(DEAD_CODE, hir_id, MultiSpan::from_spans(spans), diag);
1195+
self.tcx.emit_node_span_lint(lint, hir_id, MultiSpan::from_spans(spans), diag);
11821196
}
11831197

11841198
fn warn_multiple(

tests/ui/const-generics/defaults/repr-c-issue-82792.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
//@ run-pass
44

5-
#[allow(dead_code)]
5+
#[allow(unconstructible_pub_struct)]
66
#[repr(C)]
77
pub struct Loaf<T: Sized, const N: usize = 1> {
88
head: [T; N],

tests/ui/const-generics/generic_const_exprs/associated-consts.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ impl BlockCipher for BarCipher {
1616
const BLOCK_SIZE: usize = 32;
1717
}
1818

19-
#[allow(dead_code)]
19+
#[allow(unconstructible_pub_struct)]
2020
pub struct Block<C>(C);
2121

2222
pub fn test<C: BlockCipher, const M: usize>()

tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use std::mem::MaybeUninit;
88

9-
#[allow(dead_code)]
9+
#[allow(unconstructible_pub_struct)]
1010
#[repr(transparent)]
1111
pub struct MaybeUninitWrapper<const N: usize>(MaybeUninit<[u64; N]>);
1212

tests/ui/issues/issue-5708.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pub trait MyTrait<T> {
4444
fn dummy(&self, t: T) -> T { panic!() }
4545
}
4646

47-
#[allow(dead_code)]
47+
#[allow(unconstructible_pub_struct)]
4848
pub struct MyContainer<'a, T:'a> {
4949
foos: Vec<&'a (dyn MyTrait<T>+'a)> ,
5050
}

tests/ui/lint/dead-code/lint-dead-code-1.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![allow(non_camel_case_types)]
44
#![allow(non_upper_case_globals)]
55
#![deny(dead_code)]
6+
#![deny(unconstructible_pub_struct)]
67

78
#![crate_type="lib"]
89

tests/ui/lint/dead-code/lint-dead-code-1.stderr

+18-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: static `priv_static` is never used
2-
--> $DIR/lint-dead-code-1.rs:20:8
2+
--> $DIR/lint-dead-code-1.rs:21:8
33
|
44
LL | static priv_static: isize = 0;
55
| ^^^^^^^^^^^
@@ -11,37 +11,43 @@ LL | #![deny(dead_code)]
1111
| ^^^^^^^^^
1212

1313
error: constant `priv_const` is never used
14-
--> $DIR/lint-dead-code-1.rs:27:7
14+
--> $DIR/lint-dead-code-1.rs:28:7
1515
|
1616
LL | const priv_const: isize = 0;
1717
| ^^^^^^^^^^
1818

1919
error: struct `PrivStruct` is never constructed
20-
--> $DIR/lint-dead-code-1.rs:35:8
20+
--> $DIR/lint-dead-code-1.rs:36:8
2121
|
2222
LL | struct PrivStruct;
2323
| ^^^^^^^^^^
2424

2525
error: struct `StructUsedAsField` is never constructed
26-
--> $DIR/lint-dead-code-1.rs:49:8
26+
--> $DIR/lint-dead-code-1.rs:50:8
2727
|
2828
LL | struct StructUsedAsField;
2929
| ^^^^^^^^^^^^^^^^^
3030

3131
error: struct `PubStruct2` is never constructed
32-
--> $DIR/lint-dead-code-1.rs:52:12
32+
--> $DIR/lint-dead-code-1.rs:53:12
3333
|
3434
LL | pub struct PubStruct2 {
3535
| ^^^^^^^^^^
36+
|
37+
note: the lint level is defined here
38+
--> $DIR/lint-dead-code-1.rs:6:9
39+
|
40+
LL | #![deny(unconstructible_pub_struct)]
41+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
3642

3743
error: enum `priv_enum` is never used
38-
--> $DIR/lint-dead-code-1.rs:63:6
44+
--> $DIR/lint-dead-code-1.rs:64:6
3945
|
4046
LL | enum priv_enum { foo2, bar2 }
4147
| ^^^^^^^^^
4248

4349
error: variant `bar3` is never constructed
44-
--> $DIR/lint-dead-code-1.rs:66:5
50+
--> $DIR/lint-dead-code-1.rs:67:5
4551
|
4652
LL | enum used_enum {
4753
| --------- variant in this enum
@@ -50,31 +56,31 @@ LL | bar3
5056
| ^^^^
5157

5258
error: function `priv_fn` is never used
53-
--> $DIR/lint-dead-code-1.rs:87:4
59+
--> $DIR/lint-dead-code-1.rs:88:4
5460
|
5561
LL | fn priv_fn() {
5662
| ^^^^^^^
5763

5864
error: function `foo` is never used
59-
--> $DIR/lint-dead-code-1.rs:92:4
65+
--> $DIR/lint-dead-code-1.rs:93:4
6066
|
6167
LL | fn foo() {
6268
| ^^^
6369

6470
error: function `bar` is never used
65-
--> $DIR/lint-dead-code-1.rs:97:4
71+
--> $DIR/lint-dead-code-1.rs:98:4
6672
|
6773
LL | fn bar() {
6874
| ^^^
6975

7076
error: function `baz` is never used
71-
--> $DIR/lint-dead-code-1.rs:101:4
77+
--> $DIR/lint-dead-code-1.rs:102:4
7278
|
7379
LL | fn baz() -> impl Copy {
7480
| ^^^
7581

7682
error: struct `Bar` is never constructed
77-
--> $DIR/lint-dead-code-1.rs:12:16
83+
--> $DIR/lint-dead-code-1.rs:13:16
7884
|
7985
LL | pub struct Bar;
8086
| ^^^

tests/ui/lint/dead-code/unconstructible-pub-struct.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#![feature(never_type)]
2-
#![deny(dead_code)]
2+
#![deny(unconstructible_pub_struct)]
33

44
pub struct T1(!);
55
pub struct T2(());
@@ -32,4 +32,6 @@ pub struct T9<X> { //~ ERROR struct `T9` is never constructed
3232
_y: i32,
3333
}
3434

35+
pub struct _T10(i32);
36+
3537
fn main() {}

tests/ui/lint/dead-code/unconstructible-pub-struct.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ LL | pub struct T9<X> {
77
note: the lint level is defined here
88
--> $DIR/unconstructible-pub-struct.rs:2:9
99
|
10-
LL | #![deny(dead_code)]
11-
| ^^^^^^^^^
10+
LL | #![deny(unconstructible_pub_struct)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
1212

1313
error: aborting due to 1 previous error
1414

tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![deny(dead_code)]
1+
#![deny(unused)]
22

33
struct T1; //~ ERROR struct `T1` is never constructed
44
struct T2; //~ ERROR struct `T2` is never constructed

tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr

+5-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ LL | struct T1;
77
note: the lint level is defined here
88
--> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:1:9
99
|
10-
LL | #![deny(dead_code)]
11-
| ^^^^^^^^^
10+
LL | #![deny(unused)]
11+
| ^^^^^^
12+
= note: `#[deny(dead_code)]` implied by `#[deny(unused)]`
1213

1314
error: struct `T2` is never constructed
1415
--> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:4:8
@@ -21,6 +22,8 @@ error: struct `T3` is never constructed
2122
|
2223
LL | pub struct T3(i32);
2324
| ^^
25+
|
26+
= note: `#[deny(unconstructible_pub_struct)]` implied by `#[deny(unused)]`
2427

2528
error: field `0` is never read
2629
--> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:6:15

tests/ui/lint/dead-code/unused-pub-struct.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![deny(dead_code)]
1+
#![deny(unused)]
22

33
pub struct NotLint1(());
44
pub struct NotLint2(std::marker::PhantomData<i32>);

tests/ui/lint/dead-code/unused-pub-struct.stderr

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ LL | pub struct NeverConstructed(i32);
77
note: the lint level is defined here
88
--> $DIR/unused-pub-struct.rs:1:9
99
|
10-
LL | #![deny(dead_code)]
11-
| ^^^^^^^^^
10+
LL | #![deny(unused)]
11+
| ^^^^^^
12+
= note: `#[deny(unconstructible_pub_struct)]` implied by `#[deny(unused)]`
1213

1314
error: aborting due to 1 previous error
1415

tests/ui/macros/macro-pub-matcher.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//@ run-pass
2-
#![allow(dead_code, unused_imports, unused_macro_rules)]
2+
#![allow(dead_code, unused_imports, unused_macro_rules, unconstructible_pub_struct)]
33

44
/**
55
Ensure that `:vis` matches can be captured in existing positions, and passed

tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// Regression test for issues #100790 and #106439.
22
//@ run-rustfix
33

4-
#[allow(dead_code)]
4+
#![allow(unused)]
5+
56
pub struct Example(usize)
67
where
78
(): Sized;

tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// Regression test for issues #100790 and #106439.
22
//@ run-rustfix
33

4-
#[allow(dead_code)]
4+
#![allow(unused)]
5+
56
pub struct Example
67
where
78
(): Sized,

tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: where clauses are not allowed before tuple struct bodies
2-
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:6:1
2+
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:7:1
33
|
44
LL | pub struct Example
55
| ------- while parsing this tuple struct
@@ -17,7 +17,7 @@ LL ~ (): Sized;
1717
|
1818

1919
error: where clauses are not allowed before tuple struct bodies
20-
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:12:1
20+
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:13:1
2121
|
2222
LL | struct _Demo
2323
| ----- while parsing this tuple struct

tests/ui/pub/pub-ident-struct-4.fixed

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//@ run-rustfix
22

3-
#[allow(dead_code)]
3+
#[allow(unused)]
44
pub struct T(String);
55
//~^ ERROR missing `struct` for struct definition
66

tests/ui/pub/pub-ident-struct-4.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//@ run-rustfix
22

3-
#[allow(dead_code)]
3+
#[allow(unused)]
44
pub T(String);
55
//~^ ERROR missing `struct` for struct definition
66

tests/ui/regions/regions-issue-21422.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
//@ pretty-expanded FIXME #23616
77

8-
#[allow(dead_code)]
8+
#[allow(unconstructible_pub_struct)]
99
pub struct P<'a> {
1010
_ptr: *const &'a u8,
1111
}

tests/ui/structs-enums/newtype-struct-with-dtor.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
//@ run-pass
22
#![allow(unused_unsafe)]
33
#![allow(unused_variables)]
4+
#![allow(unconstructible_pub_struct)]
45
//@ pretty-expanded FIXME #23616
56

6-
#[allow(dead_code)]
77
pub struct Fd(u32);
88

9-
#[allow(dead_code)]
109
fn foo(a: u32) {}
1110

1211
impl Drop for Fd {

0 commit comments

Comments
 (0)