2 get_parent_expr, get_trait_def_id, implements_trait, method_calls, paths, snippet, span_lint_and_sugg,
4 use if_chain::if_chain;
5 use rustc_errors::Applicability;
7 use rustc_hir::{Expr, ExprKind, QPath, StmtKind};
8 use rustc_lint::{LateContext, LateLintPass};
9 use rustc_session::{declare_lint_pass, declare_tool_lint};
10 use rustc_span::source_map::Span;
12 declare_clippy_lint! {
13 /// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls.
15 /// **Why is this bad?** Derefencing by `&*x` or `&mut *x` is clearer and more concise,
16 /// when not part of a method chain.
20 /// use std::ops::Deref;
21 /// let a: &mut String = &mut String::from("foo");
22 /// let b: &str = a.deref();
24 /// Could be written as:
26 /// let a: &mut String = &mut String::from("foo");
30 /// This lint excludes
32 /// let _ = d.unwrap().deref();
34 pub EXPLICIT_DEREF_METHOD,
36 "Explicit use of deref or deref_mut method while not in a method chain."
39 declare_lint_pass!(Dereferencing => [
43 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Dereferencing {
44 fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx hir::Stmt<'_>) {
46 if let StmtKind::Local(ref local) = stmt.kind;
47 if let Some(ref init) = local.init;
51 ExprKind::Call(ref _method, args) => {
54 // Caller must call only one other function (deref or deref_mut)
55 // otherwise it can lead to error prone suggestions (ie: &*a.len())
56 let (method_names, arg_list, _) = method_calls(arg, 2);
57 if method_names.len() == 1;
58 // Caller must be a variable
59 let variables = arg_list[0];
60 if variables.len() == 1;
61 if let ExprKind::Path(QPath::Resolved(None, _)) = variables[0].kind;
64 let name = method_names[0].as_str();
65 lint_deref(cx, &*name, &variables[0], variables[0].span, arg.span);
70 ExprKind::MethodCall(ref method_name, _, ref args) => {
71 if init.span.from_expansion() {
76 if let ExprKind::Path(QPath::Resolved(None, _)) = args[0].kind;
77 // Caller must call only one other function (deref or deref_mut)
78 // otherwise it can lead to error prone suggestions (ie: &*a.len())
79 let (method_names, arg_list, _) = method_calls(init, 2);
80 if method_names.len() == 1;
81 // Caller must be a variable
82 let variables = arg_list[0];
83 if variables.len() == 1;
84 if let ExprKind::Path(QPath::Resolved(None, _)) = variables[0].kind;
87 let name = method_name.ident.as_str();
88 lint_deref(cx, &*name, init, args[0].span, init.span);
98 fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
100 if let ExprKind::MethodCall(ref method_name, _, ref args) = &expr.kind;
102 if let ExprKind::Path(QPath::Resolved(None, _)) = args[0].kind;
103 if let Some(parent) = get_parent_expr(cx, &expr);
107 // Already linted using statements
108 ExprKind::Call(_, _) | ExprKind::MethodCall(_, _, _) => (),
110 let name = method_name.ident.as_str();
111 lint_deref(cx, &*name, &args[0], args[0].span, expr.span);
119 fn lint_deref(cx: &LateContext<'_, '_>, fn_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) {
122 if get_trait_def_id(cx, &paths::DEREF_TRAIT)
123 .map_or(false, |id| implements_trait(cx, cx.tables.expr_ty(&call_expr), id, &[]))
127 EXPLICIT_DEREF_METHOD,
129 "explicit deref method call",
131 format!("&*{}", &snippet(cx, var_span, "..")),
132 Applicability::MachineApplicable,
137 if get_trait_def_id(cx, &paths::DEREF_MUT_TRAIT)
138 .map_or(false, |id| implements_trait(cx, cx.tables.expr_ty(&call_expr), id, &[]))
142 EXPLICIT_DEREF_METHOD,
144 "explicit deref_mut method call",
146 format!("&mut *{}", &snippet(cx, var_span, "..")),
147 Applicability::MachineApplicable,