]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use crate::utils::{get_parent_expr, implements_trait, snippet, span_lint_and_sugg}; |
2 | use if_chain::if_chain; | |
3 | use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX, PREC_PREFIX}; | |
4 | use rustc_errors::Applicability; | |
5 | use rustc_hir::{Expr, ExprKind}; | |
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?** Dereferencing by `&*x` or `&mut *x` is clearer and more concise, | |
14 | /// when not part of a method chain. | |
15 | /// | |
16 | /// **Example:** | |
17 | /// ```rust | |
18 | /// use std::ops::Deref; | |
19 | /// let a: &mut String = &mut String::from("foo"); | |
20 | /// let b: &str = a.deref(); | |
21 | /// ``` | |
22 | /// Could be written as: | |
23 | /// ```rust | |
24 | /// let a: &mut String = &mut String::from("foo"); | |
25 | /// let b = &*a; | |
26 | /// ``` | |
27 | /// | |
28 | /// This lint excludes | |
29 | /// ```rust,ignore | |
30 | /// let _ = d.unwrap().deref(); | |
31 | /// ``` | |
32 | pub EXPLICIT_DEREF_METHODS, | |
33 | pedantic, | |
34 | "Explicit use of deref or deref_mut method while not in a method chain." | |
35 | } | |
36 | ||
37 | declare_lint_pass!(Dereferencing => [ | |
38 | EXPLICIT_DEREF_METHODS | |
39 | ]); | |
40 | ||
41 | impl<'tcx> LateLintPass<'tcx> for Dereferencing { | |
42 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | |
43 | if_chain! { | |
44 | if !expr.span.from_expansion(); | |
45 | if let ExprKind::MethodCall(ref method_name, _, ref args, _) = &expr.kind; | |
46 | if args.len() == 1; | |
47 | ||
48 | then { | |
49 | if let Some(parent_expr) = get_parent_expr(cx, expr) { | |
50 | // Check if we have the whole call chain here | |
51 | if let ExprKind::MethodCall(..) = parent_expr.kind { | |
52 | return; | |
53 | } | |
54 | // Check for Expr that we don't want to be linted | |
55 | let precedence = parent_expr.precedence(); | |
56 | match precedence { | |
57 | // Lint a Call is ok though | |
58 | ExprPrecedence::Call | ExprPrecedence::AddrOf => (), | |
59 | _ => { | |
60 | if precedence.order() >= PREC_PREFIX && precedence.order() <= PREC_POSTFIX { | |
61 | return; | |
62 | } | |
63 | } | |
64 | } | |
65 | } | |
66 | let name = method_name.ident.as_str(); | |
67 | lint_deref(cx, &*name, &args[0], args[0].span, expr.span); | |
68 | } | |
69 | } | |
70 | } | |
71 | } | |
72 | ||
73 | fn lint_deref(cx: &LateContext<'_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) { | |
74 | match method_name { | |
75 | "deref" => { | |
76 | let impls_deref_trait = cx.tcx.lang_items().deref_trait().map_or(false, |id| { | |
77 | implements_trait(cx, cx.typeck_results().expr_ty(&call_expr), id, &[]) | |
78 | }); | |
79 | if impls_deref_trait { | |
80 | span_lint_and_sugg( | |
81 | cx, | |
82 | EXPLICIT_DEREF_METHODS, | |
83 | expr_span, | |
84 | "explicit deref method call", | |
85 | "try this", | |
86 | format!("&*{}", &snippet(cx, var_span, "..")), | |
87 | Applicability::MachineApplicable, | |
88 | ); | |
89 | } | |
90 | }, | |
91 | "deref_mut" => { | |
92 | let impls_deref_mut_trait = cx.tcx.lang_items().deref_mut_trait().map_or(false, |id| { | |
93 | implements_trait(cx, cx.typeck_results().expr_ty(&call_expr), id, &[]) | |
94 | }); | |
95 | if impls_deref_mut_trait { | |
96 | span_lint_and_sugg( | |
97 | cx, | |
98 | EXPLICIT_DEREF_METHODS, | |
99 | expr_span, | |
100 | "explicit deref_mut method call", | |
101 | "try this", | |
102 | format!("&mut *{}", &snippet(cx, var_span, "..")), | |
103 | Applicability::MachineApplicable, | |
104 | ); | |
105 | } | |
106 | }, | |
107 | _ => (), | |
108 | } | |
109 | } |