mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-28 01:34:21 +00:00
Auto merge of #5493 - ebroto:unsafe_derive_deserialize, r=flip1995
Implement unsafe_derive_deserialize lint Added `unsafe_derive_deserialize` lint to check for cases when automatically deriving `serde::Deserialize` can be problematic, i.e. with types that have methods using `unsafe`. Closes #5471 changelog: Add lint [`unsafe_derive_deserialize`]
This commit is contained in:
commit
1336558818
@ -1524,6 +1524,7 @@ Released 2018-09-13
|
||||
[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
|
||||
[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
|
||||
[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
|
||||
[`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize
|
||||
[`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name
|
||||
[`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization
|
||||
[`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix
|
||||
|
@ -1,8 +1,15 @@
|
||||
use crate::utils::paths;
|
||||
use crate::utils::{is_automatically_derived, is_copy, match_path, span_lint_and_note, span_lint_and_then};
|
||||
use crate::utils::{
|
||||
is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Item, ItemKind, TraitRef};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{
|
||||
BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, TraitRef, UnsafeSource, Unsafety,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
@ -62,7 +69,41 @@ declare_clippy_lint! {
|
||||
"implementing `Clone` explicitly on `Copy` types"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Derive => [EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ]);
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for deriving `serde::Deserialize` on a type that
|
||||
/// has methods using `unsafe`.
|
||||
///
|
||||
/// **Why is this bad?** Deriving `serde::Deserialize` will create a constructor
|
||||
/// that may violate invariants hold by another constructor.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use serde::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// pub struct Foo {
|
||||
/// // ..
|
||||
/// }
|
||||
///
|
||||
/// impl Foo {
|
||||
/// pub fn new() -> Self {
|
||||
/// // setup here ..
|
||||
/// }
|
||||
///
|
||||
/// pub unsafe fn parts() -> (&str, &str) {
|
||||
/// // assumes invariants hold
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub UNSAFE_DERIVE_DESERIALIZE,
|
||||
pedantic,
|
||||
"deriving `serde::Deserialize` on a type that has methods using `unsafe`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Derive => [EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, UNSAFE_DERIVE_DESERIALIZE]);
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Derive {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
|
||||
@ -76,7 +117,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Derive {
|
||||
|
||||
check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
|
||||
|
||||
if !is_automatically_derived {
|
||||
if is_automatically_derived {
|
||||
check_unsafe_derive_deserialize(cx, item, trait_ref, ty);
|
||||
} else {
|
||||
check_copy_clone(cx, item, trait_ref, ty);
|
||||
}
|
||||
}
|
||||
@ -173,3 +216,90 @@ fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item: &Item<'_>, trait
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
|
||||
fn check_unsafe_derive_deserialize<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
item: &Item<'_>,
|
||||
trait_ref: &TraitRef<'_>,
|
||||
ty: Ty<'tcx>,
|
||||
) {
|
||||
fn item_from_def_id<'tcx>(cx: &LateContext<'_, 'tcx>, def_id: DefId) -> &'tcx Item<'tcx> {
|
||||
let hir_id = cx.tcx.hir().as_local_hir_id(def_id).unwrap();
|
||||
cx.tcx.hir().expect_item(hir_id)
|
||||
}
|
||||
|
||||
fn has_unsafe<'tcx>(cx: &LateContext<'_, 'tcx>, item: &'tcx Item<'_>) -> bool {
|
||||
let mut visitor = UnsafeVisitor { cx, has_unsafe: false };
|
||||
walk_item(&mut visitor, item);
|
||||
visitor.has_unsafe
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE);
|
||||
if let ty::Adt(def, _) = ty.kind;
|
||||
if def.did.is_local();
|
||||
if cx.tcx.inherent_impls(def.did)
|
||||
.iter()
|
||||
.map(|imp_did| item_from_def_id(cx, *imp_did))
|
||||
.any(|imp| has_unsafe(cx, imp));
|
||||
then {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
UNSAFE_DERIVE_DESERIALIZE,
|
||||
item.span,
|
||||
"you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
|
||||
None,
|
||||
"consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct UnsafeVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
has_unsafe: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, span: Span, id: HirId) {
|
||||
if self.has_unsafe {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let Some(header) = kind.header();
|
||||
if let Unsafety::Unsafe = header.unsafety;
|
||||
then {
|
||||
self.has_unsafe = true;
|
||||
}
|
||||
}
|
||||
|
||||
walk_fn(self, kind, decl, body_id, span, id);
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if self.has_unsafe {
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Block(block, _) = expr.kind {
|
||||
match block.rules {
|
||||
BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
|
||||
| BlockCheckMode::PushUnsafeBlock(UnsafeSource::UserProvided)
|
||||
| BlockCheckMode::PopUnsafeBlock(UnsafeSource::UserProvided) => {
|
||||
self.has_unsafe = true;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::All(self.cx.tcx.hir())
|
||||
}
|
||||
}
|
||||
|
@ -520,6 +520,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&dereference::EXPLICIT_DEREF_METHODS,
|
||||
&derive::DERIVE_HASH_XOR_EQ,
|
||||
&derive::EXPL_IMPL_CLONE_ON_COPY,
|
||||
&derive::UNSAFE_DERIVE_DESERIALIZE,
|
||||
&doc::DOC_MARKDOWN,
|
||||
&doc::MISSING_ERRORS_DOC,
|
||||
&doc::MISSING_SAFETY_DOC,
|
||||
@ -1105,6 +1106,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS),
|
||||
LintId::of(&dereference::EXPLICIT_DEREF_METHODS),
|
||||
LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY),
|
||||
LintId::of(&derive::UNSAFE_DERIVE_DESERIALIZE),
|
||||
LintId::of(&doc::DOC_MARKDOWN),
|
||||
LintId::of(&doc::MISSING_ERRORS_DOC),
|
||||
LintId::of(&empty_enum::EMPTY_ENUM),
|
||||
|
@ -49,7 +49,7 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult
|
||||
/// Use this if you want to provide some general help but
|
||||
/// can't provide a specific machine applicable suggestion.
|
||||
///
|
||||
/// The `help` message is not attached to any `Span`.
|
||||
/// The `help` message can be optionally attached to a `Span`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
@ -241,7 +241,7 @@ pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust,ignore
|
||||
/// match_qpath(path, &["std", "rt", "begin_unwind"])
|
||||
/// match_path_ast(path, &["std", "rt", "begin_unwind"])
|
||||
/// ```
|
||||
pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool {
|
||||
path.segments
|
||||
|
@ -108,6 +108,7 @@ pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
|
||||
pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
|
||||
pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"];
|
||||
pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
|
||||
pub const SERDE_DESERIALIZE: [&str; 2] = ["_serde", "Deserialize"];
|
||||
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
|
||||
pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
|
||||
pub const SLICE_ITER: [&str; 3] = ["core", "slice", "Iter"];
|
||||
|
@ -2334,6 +2334,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
||||
deprecation: None,
|
||||
module: "literal_representation",
|
||||
},
|
||||
Lint {
|
||||
name: "unsafe_derive_deserialize",
|
||||
group: "pedantic",
|
||||
desc: "deriving `serde::Deserialize` on a type that has methods using `unsafe`",
|
||||
deprecation: None,
|
||||
module: "derive",
|
||||
},
|
||||
Lint {
|
||||
name: "unsafe_removed_from_name",
|
||||
group: "style",
|
||||
|
60
tests/ui/unsafe_derive_deserialize.rs
Normal file
60
tests/ui/unsafe_derive_deserialize.rs
Normal file
@ -0,0 +1,60 @@
|
||||
#![warn(clippy::unsafe_derive_deserialize)]
|
||||
#![allow(unused, clippy::missing_safety_doc)]
|
||||
|
||||
extern crate serde;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct A {}
|
||||
impl A {
|
||||
pub unsafe fn new(_a: i32, _b: i32) -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct B {}
|
||||
impl B {
|
||||
pub unsafe fn unsafe_method(&self) {}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct C {}
|
||||
impl C {
|
||||
pub fn unsafe_block(&self) {
|
||||
unsafe {}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct D {}
|
||||
impl D {
|
||||
pub fn inner_unsafe_fn(&self) {
|
||||
unsafe fn inner() {}
|
||||
}
|
||||
}
|
||||
|
||||
// Does not derive `Deserialize`, should be ignored
|
||||
pub struct E {}
|
||||
impl E {
|
||||
pub unsafe fn new(_a: i32, _b: i32) -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub unsafe fn unsafe_method(&self) {}
|
||||
|
||||
pub fn unsafe_block(&self) {
|
||||
unsafe {}
|
||||
}
|
||||
|
||||
pub fn inner_unsafe_fn(&self) {
|
||||
unsafe fn inner() {}
|
||||
}
|
||||
}
|
||||
|
||||
// Does not have methods using `unsafe`, should be ignored
|
||||
#[derive(Deserialize)]
|
||||
pub struct F {}
|
||||
|
||||
fn main() {}
|
39
tests/ui/unsafe_derive_deserialize.stderr
Normal file
39
tests/ui/unsafe_derive_deserialize.stderr
Normal file
@ -0,0 +1,39 @@
|
||||
error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
|
||||
--> $DIR/unsafe_derive_deserialize.rs:8:10
|
||||
|
|
||||
LL | #[derive(Deserialize)]
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::unsafe-derive-deserialize` implied by `-D warnings`
|
||||
= help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html
|
||||
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
|
||||
--> $DIR/unsafe_derive_deserialize.rs:16:10
|
||||
|
|
||||
LL | #[derive(Deserialize)]
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html
|
||||
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
|
||||
--> $DIR/unsafe_derive_deserialize.rs:22:10
|
||||
|
|
||||
LL | #[derive(Deserialize)]
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html
|
||||
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
|
||||
--> $DIR/unsafe_derive_deserialize.rs:30:10
|
||||
|
|
||||
LL | #[derive(Deserialize)]
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html
|
||||
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
Loading…
Reference in New Issue
Block a user