| use rustc_hir::{BinOpKind, Body, Expr, ExprKind, ImplItemKind, ItemKind, Node, TraitFn, TraitItem, TraitItemKind}; | |
| use rustc_lint::{LateContext, LateLintPass}; | |
| use rustc_session::declare_lint_pass; | |
| mod cmp_null; | |
| mod mut_from_ref; | |
| mod ptr_arg; | |
| mod ptr_eq; | |
| declare_clippy_lint! { | |
| /// ### What it does | |
| /// This lint checks for equality comparisons with `ptr::null` or `ptr::null_mut` | |
| /// | |
| /// ### Why is this bad? | |
| /// It's easier and more readable to use the inherent | |
| /// `.is_null()` | |
| /// method instead | |
| /// | |
| /// ### Example | |
| /// ```rust,ignore | |
| /// use std::ptr; | |
| /// | |
| /// if x == ptr::null() { | |
| /// // .. | |
| /// } | |
| /// ``` | |
| /// | |
| /// Use instead: | |
| /// ```rust,ignore | |
| /// if x.is_null() { | |
| /// // .. | |
| /// } | |
| /// ``` | |
| #[clippy::version = "pre 1.29.0"] | |
| pub CMP_NULL, | |
| style, | |
| "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead" | |
| } | |
| declare_clippy_lint! { | |
| /// ### What it does | |
| /// This lint checks for functions that take immutable references and return | |
| /// mutable ones. This will not trigger if no unsafe code exists as there | |
| /// are multiple safe functions which will do this transformation | |
| /// | |
| /// To be on the conservative side, if there's at least one mutable | |
| /// reference with the output lifetime, this lint will not trigger. | |
| /// | |
| /// ### Why is this bad? | |
| /// Creating a mutable reference which can be repeatably derived from an | |
| /// immutable reference is unsound as it allows creating multiple live | |
| /// mutable references to the same object. | |
| /// | |
| /// This [error](https://github.com/rust-lang/rust/issues/39465) actually | |
| /// lead to an interim Rust release 1.15.1. | |
| /// | |
| /// ### Known problems | |
| /// This pattern is used by memory allocators to allow allocating multiple | |
| /// objects while returning mutable references to each one. So long as | |
| /// different mutable references are returned each time such a function may | |
| /// be safe. | |
| /// | |
| /// ### Example | |
| /// ```ignore | |
| /// fn foo(&Foo) -> &mut Bar { .. } | |
| /// ``` | |
| #[clippy::version = "pre 1.29.0"] | |
| pub MUT_FROM_REF, | |
| correctness, | |
| "fns that create mutable refs from immutable ref args" | |
| } | |
| declare_clippy_lint! { | |
| /// ### What it does | |
| /// This lint checks for function arguments of type `&String`, `&Vec`, | |
| /// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls | |
| /// with the appropriate `.to_owned()`/`to_string()` calls. | |
| /// | |
| /// ### Why is this bad? | |
| /// Requiring the argument to be of the specific type | |
| /// makes the function less useful for no benefit; slices in the form of `&[T]` | |
| /// or `&str` usually suffice and can be obtained from other types, too. | |
| /// | |
| /// ### Known problems | |
| /// There may be `fn(&Vec)`-typed references pointing to your function. | |
| /// If you have them, you will get a compiler error after applying this lint's | |
| /// suggestions. You then have the choice to undo your changes or change the | |
| /// type of the reference. | |
| /// | |
| /// Note that if the function is part of your public interface, there may be | |
| /// other crates referencing it, of which you may not be aware. Carefully | |
| /// deprecate the function before applying the lint suggestions in this case. | |
| /// | |
| /// ### Example | |
| /// ```ignore | |
| /// fn foo(&Vec<u32>) { .. } | |
| /// ``` | |
| /// | |
| /// Use instead: | |
| /// ```ignore | |
| /// fn foo(&[u32]) { .. } | |
| /// ``` | |
| #[clippy::version = "pre 1.29.0"] | |
| pub PTR_ARG, | |
| style, | |
| "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively" | |
| } | |
| declare_clippy_lint! { | |
| /// ### What it does | |
| /// Use `std::ptr::eq` when applicable | |
| /// | |
| /// ### Why is this bad? | |
| /// `ptr::eq` can be used to compare `&T` references | |
| /// (which coerce to `*const T` implicitly) by their address rather than | |
| /// comparing the values they point to. | |
| /// | |
| /// ### Example | |
| /// ```no_run | |
| /// let a = &[1, 2, 3]; | |
| /// let b = &[1, 2, 3]; | |
| /// | |
| /// assert!(a as *const _ as usize == b as *const _ as usize); | |
| /// ``` | |
| /// Use instead: | |
| /// ```no_run | |
| /// let a = &[1, 2, 3]; | |
| /// let b = &[1, 2, 3]; | |
| /// | |
| /// assert!(std::ptr::eq(a, b)); | |
| /// ``` | |
| #[clippy::version = "1.49.0"] | |
| pub PTR_EQ, | |
| style, | |
| "use `std::ptr::eq` when comparing raw pointers" | |
| } | |
| declare_lint_pass!(Ptr => [CMP_NULL, MUT_FROM_REF, PTR_ARG, PTR_EQ]); | |
| impl<'tcx> LateLintPass<'tcx> for Ptr { | |
| fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { | |
| if let TraitItemKind::Fn(sig, trait_method) = &item.kind { | |
| if matches!(trait_method, TraitFn::Provided(_)) { | |
| // Handled by `check_body`. | |
| return; | |
| } | |
| mut_from_ref::check(cx, sig, None); | |
| ptr_arg::check_trait_item(cx, item.owner_id, sig); | |
| } | |
| } | |
| fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) { | |
| let mut parents = cx.tcx.hir_parent_iter(body.value.hir_id); | |
| let (item_id, sig, is_trait_item) = match parents.next() { | |
| Some((_, Node::Item(i))) => { | |
| if let ItemKind::Fn { sig, .. } = &i.kind { | |
| (i.owner_id, sig, false) | |
| } else { | |
| return; | |
| } | |
| }, | |
| Some((_, Node::ImplItem(i))) => { | |
| if !matches!(parents.next(), | |
| Some((_, Node::Item(i))) if matches!(&i.kind, ItemKind::Impl(i) if i.of_trait.is_none()) | |
| ) { | |
| return; | |
| } | |
| if let ImplItemKind::Fn(sig, _) = &i.kind { | |
| (i.owner_id, sig, false) | |
| } else { | |
| return; | |
| } | |
| }, | |
| Some((_, Node::TraitItem(i))) => { | |
| if let TraitItemKind::Fn(sig, _) = &i.kind { | |
| (i.owner_id, sig, true) | |
| } else { | |
| return; | |
| } | |
| }, | |
| _ => return, | |
| }; | |
| mut_from_ref::check(cx, sig, Some(body)); | |
| ptr_arg::check_body(cx, body, item_id, sig, is_trait_item); | |
| } | |
| fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | |
| if let ExprKind::Binary(op, l, r) = expr.kind | |
| && (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) | |
| { | |
| #[expect( | |
| clippy::collapsible_if, | |
| reason = "the outer `if`s check the HIR, the inner ones run lints" | |
| )] | |
| if !cmp_null::check(cx, expr, op.node, l, r) { | |
| ptr_eq::check(cx, op.node, l, r, expr.span); | |
| } | |
| } | |
| } | |
| } |