11use 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+ } ;
35use 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+ } ;
511use rustc_lint:: { LateContext , LateLintPass } ;
12+ use rustc_middle:: hir:: map:: Map ;
613use rustc_middle:: ty:: { self , Ty } ;
714use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
815use rustc_span:: source_map:: Span ;
@@ -62,7 +69,41 @@ declare_clippy_lint! {
6269 "implementing `Clone` explicitly on `Copy` types"
6370}
6471
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 ] ) ;
66107
67108impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for Derive {
68109 fn check_item ( & mut self , cx : & LateContext < ' a , ' tcx > , item : & ' tcx Item < ' _ > ) {
@@ -76,7 +117,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Derive {
76117
77118 check_hash_peq ( cx, item. span , trait_ref, ty, is_automatically_derived) ;
78119
79- if !is_automatically_derived {
120+ if is_automatically_derived {
121+ check_unsafe_derive_deserialize ( cx, item, trait_ref, ty) ;
122+ } else {
80123 check_copy_clone ( cx, item, trait_ref, ty) ;
81124 }
82125 }
@@ -173,3 +216,90 @@ fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item: &Item<'_>, trait
173216 ) ;
174217 }
175218}
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