1
1
use crate :: utils:: paths;
2
- use crate :: utils:: { is_automatically_derived, is_copy, match_path, span_lint_and_note, span_lint_and_then} ;
2
+ use crate :: utils:: {
3
+ is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then,
4
+ } ;
3
5
use if_chain:: if_chain;
4
- use rustc_hir:: { Item , ItemKind , TraitRef } ;
6
+ use rustc_hir as hir;
7
+ use rustc_hir:: def_id:: DefId ;
8
+ use rustc_hir:: intravisit:: { walk_expr, walk_fn, walk_item, FnKind , NestedVisitorMap , Visitor } ;
5
9
use rustc_lint:: { LateContext , LateLintPass } ;
10
+ use rustc_middle:: hir:: map:: Map ;
6
11
use rustc_middle:: ty:: { self , Ty } ;
7
12
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
8
13
use rustc_span:: source_map:: Span ;
@@ -62,11 +67,45 @@ declare_clippy_lint! {
62
67
"implementing `Clone` explicitly on `Copy` types"
63
68
}
64
69
65
- declare_lint_pass ! ( Derive => [ EXPL_IMPL_CLONE_ON_COPY , DERIVE_HASH_XOR_EQ ] ) ;
70
+ declare_clippy_lint ! {
71
+ /// **What it does:** Checks for deriving `serde::Deserialize` on a type that
72
+ /// has methods using `unsafe`.
73
+ ///
74
+ /// **Why is this bad?** Deriving `serde::Deserialize` will create a constructor
75
+ /// that may violate invariants hold by another constructor.
76
+ ///
77
+ /// **Known problems:** None.
78
+ ///
79
+ /// **Example:**
80
+ ///
81
+ /// ```rust,ignore
82
+ /// use serde::Deserialize;
83
+ ///
84
+ /// #[derive(Deserialize)]
85
+ /// pub struct Foo {
86
+ /// // ..
87
+ /// }
88
+ ///
89
+ /// impl Foo {
90
+ /// pub fn new() -> Self {
91
+ /// // setup here ..
92
+ /// }
93
+ ///
94
+ /// pub unsafe fn parts() -> (&str, &str) {
95
+ /// // assumes invariants hold
96
+ /// }
97
+ /// }
98
+ /// ```
99
+ pub UNSAFE_DERIVE_DESERIALIZE ,
100
+ correctness,
101
+ "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
102
+ }
103
+
104
+ declare_lint_pass ! ( Derive => [ EXPL_IMPL_CLONE_ON_COPY , DERIVE_HASH_XOR_EQ , UNSAFE_DERIVE_DESERIALIZE ] ) ;
66
105
67
106
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for Derive {
68
- fn check_item ( & mut self , cx : & LateContext < ' a , ' tcx > , item : & ' tcx Item < ' _ > ) {
69
- if let ItemKind :: Impl {
107
+ fn check_item ( & mut self , cx : & LateContext < ' a , ' tcx > , item : & ' tcx hir :: Item < ' _ > ) {
108
+ if let hir :: ItemKind :: Impl {
70
109
of_trait : Some ( ref trait_ref) ,
71
110
..
72
111
} = item. kind
@@ -76,7 +115,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Derive {
76
115
77
116
check_hash_peq ( cx, item. span , trait_ref, ty, is_automatically_derived) ;
78
117
79
- if !is_automatically_derived {
118
+ if is_automatically_derived {
119
+ check_unsafe_derive_deserialize ( cx, item, trait_ref, ty) ;
120
+ } else {
80
121
check_copy_clone ( cx, item, trait_ref, ty) ;
81
122
}
82
123
}
@@ -87,7 +128,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Derive {
87
128
fn check_hash_peq < ' a , ' tcx > (
88
129
cx : & LateContext < ' a , ' tcx > ,
89
130
span : Span ,
90
- trait_ref : & TraitRef < ' _ > ,
131
+ trait_ref : & hir :: TraitRef < ' _ > ,
91
132
ty : Ty < ' tcx > ,
92
133
hash_is_automatically_derived : bool ,
93
134
) {
@@ -134,7 +175,12 @@ fn check_hash_peq<'a, 'tcx>(
134
175
}
135
176
136
177
/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
137
- fn check_copy_clone < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , item : & Item < ' _ > , trait_ref : & TraitRef < ' _ > , ty : Ty < ' tcx > ) {
178
+ fn check_copy_clone < ' a , ' tcx > (
179
+ cx : & LateContext < ' a , ' tcx > ,
180
+ item : & hir:: Item < ' _ > ,
181
+ trait_ref : & hir:: TraitRef < ' _ > ,
182
+ ty : Ty < ' tcx > ,
183
+ ) {
138
184
if match_path ( & trait_ref. path , & paths:: CLONE_TRAIT ) {
139
185
if !is_copy ( cx, ty) {
140
186
return ;
@@ -173,3 +219,99 @@ fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item: &Item<'_>, trait
173
219
) ;
174
220
}
175
221
}
222
+
223
+ /// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
224
+ fn check_unsafe_derive_deserialize < ' a , ' tcx > (
225
+ cx : & LateContext < ' a , ' tcx > ,
226
+ item : & hir:: Item < ' _ > ,
227
+ trait_ref : & hir:: TraitRef < ' _ > ,
228
+ ty : Ty < ' tcx > ,
229
+ ) {
230
+ fn item_from_def_id < ' tcx > ( cx : & LateContext < ' _ , ' tcx > , def_id : DefId ) -> & ' tcx hir:: Item < ' tcx > {
231
+ let hir_id = cx. tcx . hir ( ) . as_local_hir_id ( def_id) . unwrap ( ) ;
232
+ cx. tcx . hir ( ) . expect_item ( hir_id)
233
+ }
234
+
235
+ fn has_unsafe < ' tcx > ( cx : & LateContext < ' _ , ' tcx > , item : & ' tcx hir:: Item < ' _ > ) -> bool {
236
+ let mut visitor = UnsafeVisitor { cx, has_unsafe : false } ;
237
+ walk_item ( & mut visitor, item) ;
238
+ visitor. has_unsafe
239
+ }
240
+
241
+ if_chain ! {
242
+ if match_path( & trait_ref. path, & paths:: SERDE_DESERIALIZE ) ;
243
+ if let ty:: Adt ( def, _) = ty. kind;
244
+ if def. did. is_local( ) ;
245
+ if cx. tcx. inherent_impls( def. did)
246
+ . iter( )
247
+ . map( |imp_did| item_from_def_id( cx, * imp_did) )
248
+ . any( |imp| has_unsafe( cx, imp) ) ;
249
+ then {
250
+ span_lint_and_help(
251
+ cx,
252
+ UNSAFE_DERIVE_DESERIALIZE ,
253
+ item. span,
254
+ "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`" ,
255
+ None ,
256
+ "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html"
257
+ ) ;
258
+ }
259
+ }
260
+ }
261
+
262
+ struct UnsafeVisitor < ' a , ' tcx > {
263
+ cx : & ' a LateContext < ' a , ' tcx > ,
264
+ has_unsafe : bool ,
265
+ }
266
+
267
+ impl < ' tcx > Visitor < ' tcx > for UnsafeVisitor < ' _ , ' tcx > {
268
+ type Map = Map < ' tcx > ;
269
+
270
+ fn visit_fn (
271
+ & mut self ,
272
+ kind : FnKind < ' tcx > ,
273
+ decl : & ' tcx hir:: FnDecl < ' _ > ,
274
+ body_id : hir:: BodyId ,
275
+ span : Span ,
276
+ id : hir:: HirId ,
277
+ ) {
278
+ if self . has_unsafe {
279
+ return ;
280
+ }
281
+
282
+ if_chain ! {
283
+ if let Some ( header) = kind. header( ) ;
284
+ if let hir:: Unsafety :: Unsafe = header. unsafety;
285
+ then {
286
+ self . has_unsafe = true ;
287
+ }
288
+ }
289
+
290
+ walk_fn ( self , kind, decl, body_id, span, id) ;
291
+ }
292
+
293
+ fn visit_expr ( & mut self , expr : & ' tcx hir:: Expr < ' _ > ) {
294
+ if self . has_unsafe {
295
+ return ;
296
+ }
297
+
298
+ if let hir:: ExprKind :: Block ( block, _) = expr. kind {
299
+ use hir:: { BlockCheckMode , UnsafeSource } ;
300
+
301
+ match block. rules {
302
+ BlockCheckMode :: UnsafeBlock ( UnsafeSource :: UserProvided )
303
+ | BlockCheckMode :: PushUnsafeBlock ( UnsafeSource :: UserProvided )
304
+ | BlockCheckMode :: PopUnsafeBlock ( UnsafeSource :: UserProvided ) => {
305
+ self . has_unsafe = true ;
306
+ } ,
307
+ _ => { } ,
308
+ }
309
+ }
310
+
311
+ walk_expr ( self , expr) ;
312
+ }
313
+
314
+ fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
315
+ NestedVisitorMap :: All ( self . cx . tcx . hir ( ) )
316
+ }
317
+ }
0 commit comments