]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
New upstream version 1.72.1+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / borrow_deref_ref.rs
1 use crate::reference::DEREF_ADDROF;
2 use clippy_utils::diagnostics::span_lint_and_then;
3 use clippy_utils::is_from_proc_macro;
4 use clippy_utils::source::snippet_opt;
5 use clippy_utils::ty::implements_trait;
6 use clippy_utils::{get_parent_expr, is_lint_allowed};
7 use rustc_errors::Applicability;
8 use rustc_hir::{ExprKind, UnOp};
9 use rustc_lint::{LateContext, LateLintPass};
10 use rustc_middle::mir::Mutability;
11 use rustc_middle::ty;
12 use rustc_session::{declare_lint_pass, declare_tool_lint};
13
14 declare_clippy_lint! {
15 /// ### What it does
16 /// Checks for `&*(&T)`.
17 ///
18 /// ### Why is this bad?
19 /// Dereferencing and then borrowing a reference value has no effect in most cases.
20 ///
21 /// ### Known problems
22 /// False negative on such code:
23 /// ```
24 /// let x = &12;
25 /// let addr_x = &x as *const _ as usize;
26 /// let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggered.
27 /// // But if we fix it, assert will fail.
28 /// assert_ne!(addr_x, addr_y);
29 /// ```
30 ///
31 /// ### Example
32 /// ```rust
33 /// let s = &String::new();
34 ///
35 /// let a: &String = &* s;
36 /// ```
37 ///
38 /// Use instead:
39 /// ```rust
40 /// # let s = &String::new();
41 /// let a: &String = s;
42 /// ```
43 #[clippy::version = "1.63.0"]
44 pub BORROW_DEREF_REF,
45 complexity,
46 "deref on an immutable reference returns the same type as itself"
47 }
48
49 declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]);
50
51 impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
52 fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) {
53 if_chain! {
54 if !e.span.from_expansion();
55 if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind;
56 if !addrof_target.span.from_expansion();
57 if let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind;
58 if !deref_target.span.from_expansion();
59 if !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..) );
60 let ref_ty = cx.typeck_results().expr_ty(deref_target);
61 if let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind();
62 if !is_from_proc_macro(cx, e);
63 then{
64
65 if let Some(parent_expr) = get_parent_expr(cx, e){
66 if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) &&
67 !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) {
68 return;
69 }
70
71 // modification to `&mut &*x` is different from `&mut x`
72 if matches!(deref_target.kind, ExprKind::Path(..)
73 | ExprKind::Field(..)
74 | ExprKind::Index(..)
75 | ExprKind::Unary(UnOp::Deref, ..))
76 && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) {
77 return;
78 }
79 }
80
81 span_lint_and_then(
82 cx,
83 BORROW_DEREF_REF,
84 e.span,
85 "deref on an immutable reference",
86 |diag| {
87 diag.span_suggestion(
88 e.span,
89 "if you would like to reborrow, try removing `&*`",
90 snippet_opt(cx, deref_target.span).unwrap(),
91 Applicability::MachineApplicable
92 );
93
94 // has deref trait -> give 2 help
95 // doesn't have deref trait -> give 1 help
96 if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait(){
97 if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) {
98 return;
99 }
100 }
101
102 diag.span_suggestion(
103 e.span,
104 "if you would like to deref, try using `&**`",
105 format!(
106 "&**{}",
107 &snippet_opt(cx, deref_target.span).unwrap(),
108 ),
109 Applicability::MaybeIncorrect
110 );
111
112 }
113 );
114
115 }
116 }
117 }
118 }