]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/dereference.rs
Report using stmts and expr + tests
[rust.git] / clippy_lints / src / dereference.rs
1 use crate::utils::{get_parent_expr, method_calls, snippet, span_lint_and_sugg};
2 use if_chain::if_chain;
3 use rustc_errors::Applicability;
4 use rustc_hir as hir;
5 use rustc_hir::{Expr, ExprKind, QPath, StmtKind};
6 use rustc_lint::{LateContext, LateLintPass};
7 use rustc_session::{declare_lint_pass, declare_tool_lint};
8 use rustc_span::source_map::Span;
9
10 declare_clippy_lint! {
11     /// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls.
12     ///
13     /// **Why is this bad?** Derefencing by `&*x` or `&mut *x` is clearer and more concise,
14     /// when not part of a method chain.
15     ///
16     /// **Example:**
17     /// ```rust
18     /// let b = a.deref();
19     /// let c = a.deref_mut();
20     /// ```
21     /// Could be written as:
22     /// ```rust
23     /// let b = &*a;
24     /// let c = &mut *a;
25     /// ```
26     ///
27     /// This lint excludes
28     /// ```rust
29     /// let e = d.unwrap().deref();
30     /// ```
31     pub EXPLICIT_DEREF_METHOD,
32     pedantic,
33     "Explicit use of deref or deref_mut method while not in a method chain."
34 }
35
36 declare_lint_pass!(Dereferencing => [
37     EXPLICIT_DEREF_METHOD
38 ]);
39
40 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Dereferencing {
41     fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx hir::Stmt<'_>) {
42         if_chain! {
43             if let StmtKind::Local(ref local) = stmt.kind;
44             if let Some(ref init) = local.init;
45
46             then {
47                 match init.kind {
48                     ExprKind::Call(ref _method, args) => {
49                         for arg in args {
50                             if_chain! {
51                                 // Caller must call only one other function (deref or deref_mut)
52                                 // otherwise it can lead to error prone suggestions (ex: &*a.len())
53                                 let (method_names, arg_list, _) = method_calls(arg, 2);
54                                 if method_names.len() == 1;
55                                 // Caller must be a variable
56                                 let variables = arg_list[0];
57                                 if variables.len() == 1;
58                                 if let ExprKind::Path(QPath::Resolved(None, _)) = variables[0].kind;
59
60                                 then {
61                                     let name = method_names[0].as_str();
62                                     lint_deref(cx, &*name, variables[0].span, arg.span);
63                                 }
64                             }
65                         }
66                     }
67                     ExprKind::MethodCall(ref method_name, _, ref args) => {
68                         if init.span.from_expansion() {
69                             return;
70                         }
71                         if_chain! {
72                             if args.len() == 1;
73                             if let ExprKind::Path(QPath::Resolved(None, _)) = args[0].kind;
74                             // Caller must call only one other function (deref or deref_mut)
75                             // otherwise it can lead to error prone suggestions (ex: &*a.len())
76                             let (method_names, arg_list, _) = method_calls(init, 2);
77                             if method_names.len() == 1;
78                             // Caller must be a variable
79                             let variables = arg_list[0];
80                             if variables.len() == 1;
81                             if let ExprKind::Path(QPath::Resolved(None, _)) = variables[0].kind;
82
83                             then {
84                                 let name = method_name.ident.as_str();
85                                 lint_deref(cx, &*name, args[0].span, init.span);
86                             }
87                         }
88                     }
89                     _ => ()
90                 }
91             }
92         }
93     }
94
95     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
96         if_chain! {
97             if let ExprKind::MethodCall(ref method_name, _, ref args) = &expr.kind;
98             if args.len() == 1;
99             if let Some(parent) = get_parent_expr(cx, &expr);
100
101             then {
102                 // Call and MethodCall exprs are better reported using statements
103                 match parent.kind {
104                     ExprKind::Call(_, _) => return,
105                     ExprKind::MethodCall(_, _, _) => return,
106                     _ => {
107                         let name = method_name.ident.as_str();
108                         lint_deref(cx, &*name, args[0].span, expr.span);
109                     }
110                 }
111             }
112         }
113     }
114 }
115
116 fn lint_deref(cx: &LateContext<'_, '_>, fn_name: &str, var_span: Span, expr_span: Span) {
117     match fn_name {
118         "deref" => {
119             span_lint_and_sugg(
120                 cx,
121                 EXPLICIT_DEREF_METHOD,
122                 expr_span,
123                 "explicit deref method call",
124                 "try this",
125                 format!("&*{}", &snippet(cx, var_span, "..")),
126                 Applicability::MachineApplicable,
127             );
128         },
129         "deref_mut" => {
130             span_lint_and_sugg(
131                 cx,
132                 EXPLICIT_DEREF_METHOD,
133                 expr_span,
134                 "explicit deref_mut method call",
135                 "try this",
136                 format!("&mut *{}", &snippet(cx, var_span, "..")),
137                 Applicability::MachineApplicable,
138             );
139         },
140         _ => (),
141     }
142 }