Skip to content

Commit a4ff175

Browse files
committed
Add lint for publically constructable Unsafe types`
1 parent f712eb5 commit a4ff175

File tree

9 files changed

+131
-9
lines changed

9 files changed

+131
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5409,6 +5409,7 @@ Released 2018-09-13
54095409
[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
54105410
[`const_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_is_empty
54115411
[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
5412+
[`constructable_unsafe_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#constructable_unsafe_type
54125413
[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
54135414
[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def
54145415
[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use clippy_utils::diagnostics::span_lint_and_help;
2+
use rustc_hir::{Item, ItemKind};
3+
use rustc_lint::{LateContext, LateLintPass};
4+
use rustc_session::declare_lint_pass;
5+
6+
declare_clippy_lint! {
7+
/// ### What it does
8+
///
9+
/// Detects types with `Unsafe` in the name that are publically constructable.
10+
///
11+
/// ### Why is this bad?
12+
///
13+
/// `Unsafe` in the name of a type implies that there is some kind of safety invariant
14+
/// being held by constructing said type, however, this invariant may not be checked
15+
/// if a user can safely publically construct it.
16+
///
17+
/// ### Example
18+
/// ```no_run
19+
/// pub struct UnsafeToken {}
20+
/// ```
21+
/// Use instead:
22+
/// ```no_run
23+
/// pub struct UnsafeToken {
24+
/// _private: ()
25+
/// }
26+
/// ```
27+
#[clippy::version = "1.84.0"]
28+
pub CONSTRUCTABLE_UNSAFE_TYPE,
29+
suspicious,
30+
"`Unsafe` types that are publically constructable"
31+
}
32+
33+
declare_lint_pass!(ConstructableUnsafeType => [CONSTRUCTABLE_UNSAFE_TYPE]);
34+
35+
impl LateLintPass<'_> for ConstructableUnsafeType {
36+
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
37+
if let ItemKind::Struct(variant, generics) = item.kind
38+
&& item.ident.as_str().contains("Unsafe")
39+
&& generics.params.is_empty()
40+
&& cx.effective_visibilities.is_reachable(item.owner_id.def_id)
41+
&& variant
42+
.fields()
43+
.iter()
44+
.all(|f| cx.effective_visibilities.is_exported(f.def_id))
45+
{
46+
span_lint_and_help(
47+
cx,
48+
CONSTRUCTABLE_UNSAFE_TYPE,
49+
item.span,
50+
"`Unsafe` type is publically constructable",
51+
None,
52+
"give this type a private field, or make it private",
53+
);
54+
}
55+
}
56+
}

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
106106
crate::collapsible_if::COLLAPSIBLE_IF_INFO,
107107
crate::collection_is_never_read::COLLECTION_IS_NEVER_READ_INFO,
108108
crate::comparison_chain::COMPARISON_CHAIN_INFO,
109+
crate::constructable_unsafe_type::CONSTRUCTABLE_UNSAFE_TYPE_INFO,
109110
crate::copies::BRANCHES_SHARING_CODE_INFO,
110111
crate::copies::IFS_SAME_COND_INFO,
111112
crate::copies::IF_SAME_THEN_ELSE_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ mod cognitive_complexity;
9898
mod collapsible_if;
9999
mod collection_is_never_read;
100100
mod comparison_chain;
101+
mod constructable_unsafe_type;
101102
mod copies;
102103
mod copy_iterator;
103104
mod crate_in_macro_def;
@@ -963,5 +964,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
963964
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
964965
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
965966
store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf)));
967+
store.register_late_pass(|_| Box::new(constructable_unsafe_type::ConstructableUnsafeType));
966968
// add lints here, do not remove this comment, it's used in `new_lint`
967969
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#![warn(clippy::constructable_unsafe_type)]
2+
3+
struct PrivateUnsafeToken;
4+
pub struct GoodUnsafeToken {
5+
_private: (),
6+
}
7+
8+
pub struct DangerousUnsafeToken1;
9+
//~^ error: `Unsafe` type is publically constructable
10+
pub struct DangerousUnsafeToken2();
11+
//~^ error: `Unsafe` type is publically constructable
12+
pub struct DangerousUnsafeToken3 {}
13+
//~^ error: `Unsafe` type is publically constructable
14+
pub struct DangerousUnsafeToken4 {
15+
//~^ error: `Unsafe` type is publically constructable
16+
pub public: (),
17+
}
18+
19+
fn main() {}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error: `Unsafe` type is publically constructable
2+
--> tests/ui/constructable_unsafe_type.rs:8:1
3+
|
4+
LL | pub struct DangerousUnsafeToken1;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= help: give this type a private field, or make it private
8+
= note: `-D clippy::constructable-unsafe-type` implied by `-D warnings`
9+
= help: to override `-D warnings` add `#[allow(clippy::constructable_unsafe_type)]`
10+
11+
error: `Unsafe` type is publically constructable
12+
--> tests/ui/constructable_unsafe_type.rs:10:1
13+
|
14+
LL | pub struct DangerousUnsafeToken2();
15+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16+
|
17+
= help: give this type a private field, or make it private
18+
19+
error: `Unsafe` type is publically constructable
20+
--> tests/ui/constructable_unsafe_type.rs:12:1
21+
|
22+
LL | pub struct DangerousUnsafeToken3 {}
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
24+
|
25+
= help: give this type a private field, or make it private
26+
27+
error: `Unsafe` type is publically constructable
28+
--> tests/ui/constructable_unsafe_type.rs:14:1
29+
|
30+
LL | / pub struct DangerousUnsafeToken4 {
31+
LL | |
32+
LL | | pub public: (),
33+
LL | | }
34+
| |_^
35+
|
36+
= help: give this type a private field, or make it private
37+
38+
error: aborting due to 4 previous errors
39+

tests/ui/new_without_default.fixed

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,13 @@ pub trait TraitWithNew: Sized {
161161
}
162162
}
163163

164-
pub struct IgnoreUnsafeNew;
164+
pub struct IgnoreUnsafeNew {
165+
_private: (),
166+
}
165167

166168
impl IgnoreUnsafeNew {
167169
pub unsafe fn new() -> Self {
168-
IgnoreUnsafeNew
170+
IgnoreUnsafeNew { _private: () }
169171
}
170172
}
171173

tests/ui/new_without_default.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,13 @@ pub trait TraitWithNew: Sized {
137137
}
138138
}
139139

140-
pub struct IgnoreUnsafeNew;
140+
pub struct IgnoreUnsafeNew {
141+
_private: (),
142+
}
141143

142144
impl IgnoreUnsafeNew {
143145
pub unsafe fn new() -> Self {
144-
IgnoreUnsafeNew
146+
IgnoreUnsafeNew { _private: () }
145147
}
146148
}
147149

tests/ui/new_without_default.stderr

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ LL + }
7373
|
7474

7575
error: you should consider adding a `Default` implementation for `NewNotEqualToDerive`
76-
--> tests/ui/new_without_default.rs:181:5
76+
--> tests/ui/new_without_default.rs:183:5
7777
|
7878
LL | / pub fn new() -> Self {
7979
LL | |
@@ -91,7 +91,7 @@ LL + }
9191
|
9292

9393
error: you should consider adding a `Default` implementation for `FooGenerics<T>`
94-
--> tests/ui/new_without_default.rs:190:5
94+
--> tests/ui/new_without_default.rs:192:5
9595
|
9696
LL | / pub fn new() -> Self {
9797
LL | |
@@ -109,7 +109,7 @@ LL + }
109109
|
110110

111111
error: you should consider adding a `Default` implementation for `BarGenerics<T>`
112-
--> tests/ui/new_without_default.rs:198:5
112+
--> tests/ui/new_without_default.rs:200:5
113113
|
114114
LL | / pub fn new() -> Self {
115115
LL | |
@@ -127,7 +127,7 @@ LL + }
127127
|
128128

129129
error: you should consider adding a `Default` implementation for `Foo<T>`
130-
--> tests/ui/new_without_default.rs:210:9
130+
--> tests/ui/new_without_default.rs:212:9
131131
|
132132
LL | / pub fn new() -> Self {
133133
LL | |
@@ -147,7 +147,7 @@ LL ~ impl<T> Foo<T> {
147147
|
148148

149149
error: you should consider adding a `Default` implementation for `MyStruct<K, V>`
150-
--> tests/ui/new_without_default.rs:256:5
150+
--> tests/ui/new_without_default.rs:258:5
151151
|
152152
LL | / pub fn new() -> Self {
153153
LL | | Self { _kv: None }

0 commit comments

Comments
 (0)