about summary refs log tree commit diff
path: root/clippy_lints
diff options
context:
space:
mode:
authorJason Newcomb <jsnewcomb@pm.me>2022-06-03 22:12:26 -0400
committerJason Newcomb <jsnewcomb@pm.me>2022-06-25 07:55:30 -0400
commit7cdaabc9b7898fec15ee197ea32977c0ab4fd476 (patch)
treeacfd6c5a2b09132ddd1b1bb3a255362ef9d656fd /clippy_lints
parent93ebd0e2db0e7c316cca3d35b077d19a79b4e7b1 (diff)
downloadrust-7cdaabc9b7898fec15ee197ea32977c0ab4fd476.tar.gz
rust-7cdaabc9b7898fec15ee197ea32977c0ab4fd476.zip
Suggest `pointer::cast` when possible in `transmute_ptr_to_ref`
Defensively add a cast to any type with lifetimes.
Diffstat (limited to 'clippy_lints')
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/transmute/mod.rs25
-rw-r--r--clippy_lints/src/transmute/transmute_ptr_to_ref.rs56
-rw-r--r--clippy_lints/src/transmute/utils.rs28
4 files changed, 62 insertions, 49 deletions
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 70cf6be8b7c..172c08a8168 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -641,7 +641,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(borrow_deref_ref::BorrowDerefRef));
     store.register_late_pass(|| Box::new(no_effect::NoEffect));
     store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment));
-    store.register_late_pass(|| Box::new(transmute::Transmute));
+    store.register_late_pass(move || Box::new(transmute::Transmute::new(msrv)));
     let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
     store.register_late_pass(move || {
         Box::new(cognitive_complexity::CognitiveComplexity::new(
diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs
index cbe1406728b..5f3e98144f4 100644
--- a/clippy_lints/src/transmute/mod.rs
+++ b/clippy_lints/src/transmute/mod.rs
@@ -16,9 +16,10 @@ mod wrong_transmute;
 
 use clippy_utils::in_constant;
 use if_chain::if_chain;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::sym;
 
 declare_clippy_lint! {
@@ -385,7 +386,10 @@ declare_clippy_lint! {
     "transmute to or from a type with an undefined representation"
 }
 
-declare_lint_pass!(Transmute => [
+pub struct Transmute {
+    msrv: Option<RustcVersion>,
+}
+impl_lint_pass!(Transmute => [
     CROSSPOINTER_TRANSMUTE,
     TRANSMUTE_PTR_TO_REF,
     TRANSMUTE_PTR_TO_PTR,
@@ -401,13 +405,18 @@ declare_lint_pass!(Transmute => [
     TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
     TRANSMUTE_UNDEFINED_REPR,
 ]);
-
+impl Transmute {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
 impl<'tcx> LateLintPass<'tcx> for Transmute {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         if_chain! {
             if let ExprKind::Call(path_expr, [arg]) = e.kind;
-            if let ExprKind::Path(ref qpath) = path_expr.kind;
-            if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
+            if let ExprKind::Path(QPath::Resolved(None, path)) = path_expr.kind;
+            if let Some(def_id) = path.res.opt_def_id();
             if cx.tcx.is_diagnostic_item(sym::transmute, def_id);
             then {
                 // Avoid suggesting non-const operations in const contexts:
@@ -427,7 +436,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
 
                 let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
                     | crosspointer_transmute::check(cx, e, from_ty, to_ty)
-                    | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, qpath)
+                    | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
                     | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
                     | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
                     | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
@@ -446,4 +455,6 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
             }
         }
     }
+
+    extract_msrv_attr!(LateContext);
 }
diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
index f3653199b37..3ed5d5c6950 100644
--- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
+++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
@@ -1,11 +1,12 @@
-use super::utils::get_type_snippet;
 use super::TRANSMUTE_PTR_TO_REF;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{meets_msrv, msrvs, sugg};
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, Mutability, QPath};
+use rustc_hir::{self as hir, Expr, GenericArg, Mutability, Path, TyKind};
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, Ty, TypeFoldable};
+use rustc_semver::RustcVersion;
 
 /// Checks for `transmute_ptr_to_ref` lint.
 /// Returns `true` if it's triggered, otherwise returns `false`.
@@ -15,7 +16,8 @@ pub(super) fn check<'tcx>(
     from_ty: Ty<'tcx>,
     to_ty: Ty<'tcx>,
     arg: &'tcx Expr<'_>,
-    qpath: &'tcx QPath<'_>,
+    path: &'tcx Path<'_>,
+    msrv: Option<RustcVersion>,
 ) -> bool {
     match (&from_ty.kind(), &to_ty.kind()) {
         (ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => {
@@ -34,19 +36,34 @@ pub(super) fn check<'tcx>(
                     } else {
                         ("&*", "*const")
                     };
+                    let mut app = Applicability::MachineApplicable;
 
-                    let arg = if from_ptr_ty.ty == *to_ref_ty {
-                        arg
+                    let sugg = if let Some(ty) = get_explicit_type(path) {
+                        let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app);
+                        if meets_msrv(msrv, msrvs::POINTER_CAST) {
+                            format!("{}{}.cast::<{}>()", deref, arg.maybe_par(), ty_snip)
+                        } else if from_ptr_ty.has_erased_regions() {
+                            sugg::make_unop(deref, arg.as_ty(format!("{} () as {} {}", cast, cast, ty_snip)))
+                                .to_string()
+                        } else {
+                            sugg::make_unop(deref, arg.as_ty(format!("{} {}", cast, ty_snip))).to_string()
+                        }
+                    } else if from_ptr_ty.ty == *to_ref_ty {
+                        if from_ptr_ty.has_erased_regions() {
+                            if meets_msrv(msrv, msrvs::POINTER_CAST) {
+                                format!("{}{}.cast::<{}>()", deref, arg.maybe_par(), to_ref_ty)
+                            } else {
+                                sugg::make_unop(deref, arg.as_ty(format!("{} () as {} {}", cast, cast, to_ref_ty)))
+                                    .to_string()
+                            }
+                        } else {
+                            sugg::make_unop(deref, arg).to_string()
+                        }
                     } else {
-                        arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, *to_ref_ty)))
+                        sugg::make_unop(deref, arg.as_ty(format!("{} {}", cast, to_ref_ty))).to_string()
                     };
 
-                    diag.span_suggestion(
-                        e.span,
-                        "try",
-                        sugg::make_unop(deref, arg).to_string(),
-                        Applicability::Unspecified,
-                    );
+                    diag.span_suggestion(e.span, "try", sugg, app);
                 },
             );
             true
@@ -54,3 +71,14 @@ pub(super) fn check<'tcx>(
         _ => false,
     }
 }
+
+/// Gets the type `Bar` in `…::transmute<Foo, &Bar>`.
+fn get_explicit_type<'tcx>(path: &'tcx Path<'tcx>) -> Option<&'tcx hir::Ty<'tcx>> {
+    if let GenericArg::Type(ty) = path.segments.last()?.args?.args.get(1)?
+        && let TyKind::Rptr(_, ty) = &ty.kind
+    {
+        Some(ty.ty)
+    } else {
+        None
+    }
+}
diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs
index 0cbf5ccefa6..74927570b40 100644
--- a/clippy_lints/src/transmute/utils.rs
+++ b/clippy_lints/src/transmute/utils.rs
@@ -1,35 +1,9 @@
-use clippy_utils::last_path_segment;
-use clippy_utils::source::snippet;
-use if_chain::if_chain;
-use rustc_hir::{Expr, GenericArg, QPath, TyKind};
+use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{cast::CastKind, Ty};
 use rustc_span::DUMMY_SP;
 use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited};
 
-/// Gets the snippet of `Bar` in `…::transmute<Foo, &Bar>`. If that snippet is
-/// not available , use
-/// the type's `ToString` implementation. In weird cases it could lead to types
-/// with invalid `'_`
-/// lifetime, but it should be rare.
-pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty: Ty<'_>) -> String {
-    let seg = last_path_segment(path);
-    if_chain! {
-        if let Some(params) = seg.args;
-        if !params.parenthesized;
-        if let Some(to_ty) = params.args.iter().filter_map(|arg| match arg {
-            GenericArg::Type(ty) => Some(ty),
-            _ => None,
-        }).nth(1);
-        if let TyKind::Rptr(_, ref to_ty) = to_ty.kind;
-        then {
-            return snippet(cx, to_ty.ty.span, &to_ref_ty.to_string()).to_string();
-        }
-    }
-
-    to_ref_ty.to_string()
-}
-
 // check if the component types of the transmuted collection and the result have different ABI,
 // size or alignment
 pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {