]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/reference.rs
Don't lint `deref_addrof` when the two operations occur in different expansions
[rust.git] / clippy_lints / src / reference.rs
1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::source::{snippet_opt, snippet_with_applicability};
3 use clippy_utils::sugg::Sugg;
4 use if_chain::if_chain;
5 use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp};
6 use rustc_errors::Applicability;
7 use rustc_lint::{EarlyContext, EarlyLintPass};
8 use rustc_session::{declare_lint_pass, declare_tool_lint};
9 use rustc_span::BytePos;
10
11 declare_clippy_lint! {
12     /// ### What it does
13     /// Checks for usage of `*&` and `*&mut` in expressions.
14     ///
15     /// ### Why is this bad?
16     /// Immediately dereferencing a reference is no-op and
17     /// makes the code less clear.
18     ///
19     /// ### Known problems
20     /// Multiple dereference/addrof pairs are not handled so
21     /// the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect.
22     ///
23     /// ### Example
24     /// ```rust,ignore
25     /// // Bad
26     /// let a = f(*&mut b);
27     /// let c = *&d;
28     ///
29     /// // Good
30     /// let a = f(b);
31     /// let c = d;
32     /// ```
33     #[clippy::version = "pre 1.29.0"]
34     pub DEREF_ADDROF,
35     complexity,
36     "use of `*&` or `*&mut` in an expression"
37 }
38
39 declare_lint_pass!(DerefAddrOf => [DEREF_ADDROF]);
40
41 fn without_parens(mut e: &Expr) -> &Expr {
42     while let ExprKind::Paren(ref child_e) = e.kind {
43         e = child_e;
44     }
45     e
46 }
47
48 impl EarlyLintPass for DerefAddrOf {
49     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
50         if_chain! {
51             if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind;
52             if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind;
53             if deref_target.span.ctxt() == e.span.ctxt();
54             if !addrof_target.span.from_expansion();
55             then {
56                 let mut applicability = Applicability::MachineApplicable;
57                 let sugg = if e.span.from_expansion() {
58                     #[allow(clippy::option_if_let_else)]
59                     if let Some(macro_source) = snippet_opt(cx, e.span) {
60                         // Remove leading whitespace from the given span
61                         // e.g: ` $visitor` turns into `$visitor`
62                         let trim_leading_whitespaces = |span| {
63                             snippet_opt(cx, span).and_then(|snip| {
64                                 #[allow(clippy::cast_possible_truncation)]
65                                 snip.find(|c: char| !c.is_whitespace()).map(|pos| {
66                                     span.lo() + BytePos(pos as u32)
67                                 })
68                             }).map_or(span, |start_no_whitespace| e.span.with_lo(start_no_whitespace))
69                         };
70
71                         let mut generate_snippet = |pattern: &str| {
72                             #[allow(clippy::cast_possible_truncation)]
73                             macro_source.rfind(pattern).map(|pattern_pos| {
74                                 let rpos = pattern_pos + pattern.len();
75                                 let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32));
76                                 let span = trim_leading_whitespaces(span_after_ref);
77                                 snippet_with_applicability(cx, span, "_", &mut applicability)
78                             })
79                         };
80
81                         if *mutability == Mutability::Mut {
82                             generate_snippet("mut")
83                         } else {
84                             generate_snippet("&")
85                         }
86                     } else {
87                         Some(snippet_with_applicability(cx, e.span, "_", &mut applicability))
88                     }
89                 } else {
90                     Some(snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability))
91                 };
92                 if let Some(sugg) = sugg {
93                     span_lint_and_sugg(
94                         cx,
95                         DEREF_ADDROF,
96                         e.span,
97                         "immediately dereferencing a reference",
98                         "try this",
99                         sugg.to_string(),
100                         applicability,
101                     );
102                 }
103             }
104         }
105     }
106 }
107
108 declare_clippy_lint! {
109     /// ### What it does
110     /// Checks for references in expressions that use
111     /// auto dereference.
112     ///
113     /// ### Why is this bad?
114     /// The reference is a no-op and is automatically
115     /// dereferenced by the compiler and makes the code less clear.
116     ///
117     /// ### Example
118     /// ```rust
119     /// struct Point(u32, u32);
120     /// let point = Point(30, 20);
121     /// let x = (&point).0;
122     /// ```
123     /// Use instead:
124     /// ```rust
125     /// # struct Point(u32, u32);
126     /// # let point = Point(30, 20);
127     /// let x = point.0;
128     /// ```
129     #[clippy::version = "pre 1.29.0"]
130     pub REF_IN_DEREF,
131     complexity,
132     "Use of reference in auto dereference expression."
133 }
134
135 declare_lint_pass!(RefInDeref => [REF_IN_DEREF]);
136
137 impl EarlyLintPass for RefInDeref {
138     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
139         if_chain! {
140             if let ExprKind::Field(ref object, _) = e.kind;
141             if let ExprKind::Paren(ref parened) = object.kind;
142             if let ExprKind::AddrOf(_, _, ref inner) = parened.kind;
143             then {
144                 let applicability = if inner.span.from_expansion() {
145                     Applicability::MaybeIncorrect
146                 } else {
147                     Applicability::MachineApplicable
148                 };
149                 let sugg = Sugg::ast(cx, inner, "_").maybe_par();
150                 span_lint_and_sugg(
151                     cx,
152                     REF_IN_DEREF,
153                     object.span,
154                     "creating a reference that is immediately dereferenced",
155                     "try this",
156                     sugg.to_string(),
157                     applicability,
158                 );
159             }
160         }
161     }
162 }