]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | //! Checks for needless address of operations (`&`) |
2 | //! | |
3 | //! This lint is **warn** by default | |
4 | ||
5 | use crate::utils::{is_automatically_derived, snippet_opt, span_lint_and_then}; | |
6 | use if_chain::if_chain; | |
7 | use rustc_errors::Applicability; | |
8 | use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, Item, Mutability, Pat, PatKind}; | |
9 | use rustc_lint::{LateContext, LateLintPass}; | |
10 | use rustc_middle::ty; | |
11 | use rustc_middle::ty::adjustment::{Adjust, Adjustment}; | |
12 | use rustc_session::{declare_tool_lint, impl_lint_pass}; | |
13 | use rustc_span::def_id::LocalDefId; | |
14 | ||
15 | declare_clippy_lint! { | |
16 | /// **What it does:** Checks for address of operations (`&`) that are going to | |
17 | /// be dereferenced immediately by the compiler. | |
18 | /// | |
19 | /// **Why is this bad?** Suggests that the receiver of the expression borrows | |
20 | /// the expression. | |
21 | /// | |
22 | /// **Known problems:** None. | |
23 | /// | |
24 | /// **Example:** | |
25 | /// ```rust | |
26 | /// // Bad | |
27 | /// let x: &i32 = &&&&&&5; | |
28 | /// | |
29 | /// // Good | |
30 | /// let x: &i32 = &5; | |
31 | /// ``` | |
32 | pub NEEDLESS_BORROW, | |
33 | nursery, | |
34 | "taking a reference that is going to be automatically dereferenced" | |
35 | } | |
36 | ||
37 | #[derive(Default)] | |
38 | pub struct NeedlessBorrow { | |
39 | derived_item: Option<LocalDefId>, | |
40 | } | |
41 | ||
42 | impl_lint_pass!(NeedlessBorrow => [NEEDLESS_BORROW]); | |
43 | ||
44 | impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { | |
45 | fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { | |
46 | if e.span.from_expansion() || self.derived_item.is_some() { | |
47 | return; | |
48 | } | |
49 | if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind { | |
50 | if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() { | |
51 | for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) { | |
52 | if let [Adjustment { | |
53 | kind: Adjust::Deref(_), .. | |
54 | }, Adjustment { | |
55 | kind: Adjust::Deref(_), .. | |
56 | }, Adjustment { | |
57 | kind: Adjust::Borrow(_), | |
58 | .. | |
59 | }] = *adj3 | |
60 | { | |
61 | span_lint_and_then( | |
62 | cx, | |
63 | NEEDLESS_BORROW, | |
64 | e.span, | |
65 | &format!( | |
66 | "this expression borrows a reference (`&{}`) that is immediately dereferenced \ | |
67 | by the compiler", | |
68 | ty | |
69 | ), | |
70 | |diag| { | |
71 | if let Some(snippet) = snippet_opt(cx, inner.span) { | |
72 | diag.span_suggestion( | |
73 | e.span, | |
74 | "change this to", | |
75 | snippet, | |
76 | Applicability::MachineApplicable, | |
77 | ); | |
78 | } | |
79 | }, | |
80 | ); | |
81 | } | |
82 | } | |
83 | } | |
84 | } | |
85 | } | |
86 | fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { | |
87 | if pat.span.from_expansion() || self.derived_item.is_some() { | |
88 | return; | |
89 | } | |
90 | if_chain! { | |
91 | if let PatKind::Binding(BindingAnnotation::Ref, .., name, _) = pat.kind; | |
92 | if let ty::Ref(_, tam, mutbl) = *cx.typeck_results().pat_ty(pat).kind(); | |
93 | if mutbl == Mutability::Not; | |
94 | if let ty::Ref(_, _, mutbl) = *tam.kind(); | |
95 | // only lint immutable refs, because borrowed `&mut T` cannot be moved out | |
96 | if mutbl == Mutability::Not; | |
97 | then { | |
98 | span_lint_and_then( | |
99 | cx, | |
100 | NEEDLESS_BORROW, | |
101 | pat.span, | |
102 | "this pattern creates a reference to a reference", | |
103 | |diag| { | |
104 | if let Some(snippet) = snippet_opt(cx, name.span) { | |
105 | diag.span_suggestion( | |
106 | pat.span, | |
107 | "change this to", | |
108 | snippet, | |
109 | Applicability::MachineApplicable, | |
110 | ); | |
111 | } | |
112 | } | |
113 | ) | |
114 | } | |
115 | } | |
116 | } | |
117 | ||
118 | fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { | |
119 | let attrs = cx.tcx.hir().attrs(item.hir_id()); | |
120 | if is_automatically_derived(attrs) { | |
121 | debug_assert!(self.derived_item.is_none()); | |
122 | self.derived_item = Some(item.def_id); | |
123 | } | |
124 | } | |
125 | ||
126 | fn check_item_post(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) { | |
127 | if let Some(id) = self.derived_item { | |
128 | if item.def_id == id { | |
129 | self.derived_item = None; | |
130 | } | |
131 | } | |
132 | } | |
133 | } |