diff options
| author | y21 <30553356+y21@users.noreply.github.com> | 2023-06-18 22:20:10 +0200 |
|---|---|---|
| committer | y21 <30553356+y21@users.noreply.github.com> | 2023-06-18 22:20:10 +0200 |
| commit | c5a9adc2bef3afcaf8bcdb260e218a82911a034e (patch) | |
| tree | 333e3996cc064c45b5debdbc1ef835f4edda8026 | |
| parent | 3217f8aeaa754dde3baa078985518a219b18d04d (diff) | |
| download | rust-c5a9adc2bef3afcaf8bcdb260e218a82911a034e.tar.gz rust-c5a9adc2bef3afcaf8bcdb260e218a82911a034e.zip | |
new lint: `type_id_on_box`
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | clippy_lints/src/declared_lints.rs | 1 | ||||
| -rw-r--r-- | clippy_lints/src/methods/mod.rs | 35 | ||||
| -rw-r--r-- | clippy_lints/src/methods/type_id_on_box.rs | 64 | ||||
| -rw-r--r-- | tests/ui/type_id_on_box.fixed | 20 | ||||
| -rw-r--r-- | tests/ui/type_id_on_box.rs | 20 | ||||
| -rw-r--r-- | tests/ui/type_id_on_box.stderr | 25 |
7 files changed, 166 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 269be545b0a..f82a3739259 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5230,6 +5230,7 @@ Released 2018-09-13 [`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err [`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity +[`type_id_on_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_id_on_box [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds [`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction [`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 7690e8f7247..0a2db21ca95 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -403,6 +403,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::SUSPICIOUS_MAP_INFO, crate::methods::SUSPICIOUS_SPLITN_INFO, crate::methods::SUSPICIOUS_TO_OWNED_INFO, + crate::methods::TYPE_ID_ON_BOX_INFO, crate::methods::UNINIT_ASSUMED_INIT_INFO, crate::methods::UNIT_HASH_INFO, crate::methods::UNNECESSARY_FILTER_MAP_INFO, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 99c984ba65a..e98fe99eac7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -87,6 +87,7 @@ mod suspicious_command_arg_space; mod suspicious_map; mod suspicious_splitn; mod suspicious_to_owned; +mod type_id_on_box; mod uninit_assumed_init; mod unit_hash; mod unnecessary_filter_map; @@ -2924,6 +2925,37 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does + /// Looks for calls to `<Box<dyn Any> as Any>::type_id`. + /// + /// ### Why is this bad? + /// This most certainly does not do what the user expects and is very easy to miss. + /// Calling `type_id` on a `Box<dyn Any>` calls `type_id` on the `Box<..>` itself, + /// so this will return the `TypeId` of the `Box<dyn Any>` type (not the type id + /// of the value referenced by the box!). + /// + /// ### Example + /// ```rust,ignore + /// use std::any::{Any, TypeId}; + /// + /// let any_box: Box<dyn Any> = Box::new(42_i32); + /// assert_eq!(any_box.type_id(), TypeId::of::<i32>()); // ⚠️ this fails! + /// ``` + /// Use instead: + /// ```rust + /// use std::any::{Any, TypeId}; + /// + /// let any_box: Box<dyn Any> = Box::new(42_i32); + /// assert_eq!((*any_box).type_id(), TypeId::of::<i32>()); + /// // ^ dereference first, to call `type_id` on `dyn Any` + /// ``` + #[clippy::version = "1.47.0"] + pub TYPE_ID_ON_BOX, + suspicious, + "calling `.type_id()` on `Box<dyn Any>`" +} + +declare_clippy_lint! { + /// ### What it does /// Detects `().hash(_)`. /// /// ### Why is this bad? @@ -3878,6 +3910,9 @@ impl Methods { ("to_os_string" | "to_path_buf" | "to_vec", []) => { implicit_clone::check(cx, name, expr, recv); }, + ("type_id", []) => { + type_id_on_box::check(cx, recv, expr.span); + } ("unwrap", []) => { match method_call(recv) { Some(("get", recv, [get_arg], _, _)) => { diff --git a/clippy_lints/src/methods/type_id_on_box.rs b/clippy_lints/src/methods/type_id_on_box.rs new file mode 100644 index 00000000000..8ccbd0d8d1b --- /dev/null +++ b/clippy_lints/src/methods/type_id_on_box.rs @@ -0,0 +1,64 @@ +use crate::methods::TYPE_ID_ON_BOX; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::Adjustment; +use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, ExistentialPredicate}; +use rustc_span::{sym, Span}; + +fn is_dyn_trait(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + if let ty::Dynamic(preds, ..) = ty.kind() { + preds.iter().any(|p| match p.skip_binder() { + ExistentialPredicate::Trait(tr) => cx.tcx.is_diagnostic_item(sym::Any, tr.def_id), + _ => false, + }) + } else { + false + } +} + +pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span) { + let recv_adjusts = cx.typeck_results().expr_adjustments(receiver); + + if let Some(Adjustment { target: recv_ty, .. }) = recv_adjusts.last() + && let ty::Ref(_, ty, _) = recv_ty.kind() + && let ty::Adt(adt, substs) = ty.kind() + && adt.is_box() + && is_dyn_trait(cx, substs.type_at(0)) + { + span_lint_and_then( + cx, + TYPE_ID_ON_BOX, + call_span, + "calling `.type_id()` on a `Box<dyn Any>`", + |diag| { + let derefs = recv_adjusts + .iter() + .filter(|adj| matches!(adj.kind, Adjust::Deref(None))) + .count(); + + let mut sugg = "*".repeat(derefs + 1); + sugg += &snippet(cx, receiver.span, "<expr>"); + + diag.note( + "this returns the type id of the literal type `Box<dyn Any>` instead of the \ + type id of the boxed value, which is most likely not what you want" + ) + .note( + "if this is intentional, use `TypeId::of::<Box<dyn Any>>()` instead, \ + which makes it more clear" + ) + .span_suggestion( + receiver.span, + "consider dereferencing first", + format!("({sugg})"), + Applicability::MaybeIncorrect, + ); + }, + ); + } +} diff --git a/tests/ui/type_id_on_box.fixed b/tests/ui/type_id_on_box.fixed new file mode 100644 index 00000000000..fc177afacd4 --- /dev/null +++ b/tests/ui/type_id_on_box.fixed @@ -0,0 +1,20 @@ +//@run-rustfix + +#![warn(clippy::type_id_on_box)] + +use std::any::{Any, TypeId}; + +fn existential() -> impl Any { + Box::new(1) as Box<dyn Any> +} + +fn main() { + let any_box: Box<dyn Any> = Box::new(0usize); + let _ = (*any_box).type_id(); + let _ = TypeId::of::<Box<dyn Any>>(); // don't lint, user probably explicitly wants to do this + let _ = (*any_box).type_id(); + let any_box: &Box<dyn Any> = &(Box::new(0usize) as Box<dyn Any>); + let _ = (**any_box).type_id(); // 2 derefs are needed here + let b = existential(); + let _ = b.type_id(); // don't lint +} diff --git a/tests/ui/type_id_on_box.rs b/tests/ui/type_id_on_box.rs new file mode 100644 index 00000000000..36cf631a092 --- /dev/null +++ b/tests/ui/type_id_on_box.rs @@ -0,0 +1,20 @@ +//@run-rustfix + +#![warn(clippy::type_id_on_box)] + +use std::any::{Any, TypeId}; + +fn existential() -> impl Any { + Box::new(1) as Box<dyn Any> +} + +fn main() { + let any_box: Box<dyn Any> = Box::new(0usize); + let _ = any_box.type_id(); + let _ = TypeId::of::<Box<dyn Any>>(); // don't lint, user probably explicitly wants to do this + let _ = (*any_box).type_id(); + let any_box: &Box<dyn Any> = &(Box::new(0usize) as Box<dyn Any>); + let _ = any_box.type_id(); // 2 derefs are needed here + let b = existential(); + let _ = b.type_id(); // don't lint +} diff --git a/tests/ui/type_id_on_box.stderr b/tests/ui/type_id_on_box.stderr new file mode 100644 index 00000000000..47e0e814daf --- /dev/null +++ b/tests/ui/type_id_on_box.stderr @@ -0,0 +1,25 @@ +error: calling `.type_id()` on a `Box<dyn Any>` + --> $DIR/type_id_on_box.rs:13:13 + | +LL | let _ = any_box.type_id(); + | -------^^^^^^^^^^ + | | + | help: consider dereferencing first: `(*any_box)` + | + = note: this returns the type id of the literal type `Box<dyn Any>` instead of the type id of the boxed value, which is most likely not what you want + = note: if this is intentional, use `TypeId::of::<Box<dyn Any>>()` instead, which makes it more clear + = note: `-D clippy::type-id-on-box` implied by `-D warnings` + +error: calling `.type_id()` on a `Box<dyn Any>` + --> $DIR/type_id_on_box.rs:17:13 + | +LL | let _ = any_box.type_id(); // 2 derefs are needed here + | -------^^^^^^^^^^ + | | + | help: consider dereferencing first: `(**any_box)` + | + = note: this returns the type id of the literal type `Box<dyn Any>` instead of the type id of the boxed value, which is most likely not what you want + = note: if this is intentional, use `TypeId::of::<Box<dyn Any>>()` instead, which makes it more clear + +error: aborting due to 2 previous errors + |
