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