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 as hir;
7+ use rustc_hir:: def_id:: DefId ;
8+ use rustc_hir:: intravisit:: { walk_expr, walk_fn, walk_item, FnKind , NestedVisitorMap , Visitor } ;
59use rustc_lint:: { LateContext , LateLintPass } ;
10+ use rustc_middle:: hir:: map:: Map ;
611use rustc_middle:: ty:: { self , Ty } ;
712use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
813use rustc_span:: source_map:: Span ;
@@ -62,11 +67,45 @@ declare_clippy_lint! {
6267 "implementing `Clone` explicitly on `Copy` types"
6368}
6469
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 ] ) ;
66105
67106impl < ' 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 {
70109 of_trait : Some ( ref trait_ref) ,
71110 ..
72111 } = item. kind
@@ -76,7 +115,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Derive {
76115
77116 check_hash_peq ( cx, item. span , trait_ref, ty, is_automatically_derived) ;
78117
79- if !is_automatically_derived {
118+ if is_automatically_derived {
119+ check_unsafe_derive_deserialize ( cx, item, trait_ref, ty) ;
120+ } else {
80121 check_copy_clone ( cx, item, trait_ref, ty) ;
81122 }
82123 }
@@ -87,7 +128,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Derive {
87128fn check_hash_peq < ' a , ' tcx > (
88129 cx : & LateContext < ' a , ' tcx > ,
89130 span : Span ,
90- trait_ref : & TraitRef < ' _ > ,
131+ trait_ref : & hir :: TraitRef < ' _ > ,
91132 ty : Ty < ' tcx > ,
92133 hash_is_automatically_derived : bool ,
93134) {
@@ -134,7 +175,12 @@ fn check_hash_peq<'a, 'tcx>(
134175}
135176
136177/// 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+ ) {
138184 if match_path ( & trait_ref. path , & paths:: CLONE_TRAIT ) {
139185 if !is_copy ( cx, ty) {
140186 return ;
@@ -173,3 +219,99 @@ fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item: &Item<'_>, trait
173219 ) ;
174220 }
175221}
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