1
- use rustc_hir:: { Expr , ExprKind , QPath } ;
1
+ use crate :: utils:: { get_parent_expr, method_calls, snippet, span_lint_and_sugg} ;
2
+ use if_chain:: if_chain;
2
3
use rustc_errors:: Applicability ;
4
+ use rustc_hir as hir;
5
+ use rustc_hir:: { Expr , ExprKind , QPath , StmtKind } ;
3
6
use rustc_lint:: { LateContext , LateLintPass } ;
4
- use rustc_session:: { declare_tool_lint, declare_lint_pass} ;
5
- use crate :: utils:: { in_macro, span_lint_and_sugg} ;
6
- use if_chain:: if_chain;
7
+ use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
8
+ use rustc_span:: source_map:: Span ;
7
9
8
10
declare_clippy_lint ! {
9
11
/// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls.
@@ -21,7 +23,7 @@ declare_clippy_lint! {
21
23
/// let b = &*a;
22
24
/// let c = &mut *a;
23
25
/// ```
24
- ///
26
+ ///
25
27
/// This lint excludes
26
28
/// ```rust
27
29
/// let e = d.unwrap().deref();
@@ -36,45 +38,105 @@ declare_lint_pass!(Dereferencing => [
36
38
] ) ;
37
39
38
40
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for Dereferencing {
39
- fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr < ' _ > ) {
40
- if in_macro ( expr. span ) {
41
- return ;
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
+ }
42
92
}
93
+ }
43
94
95
+ fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr < ' _ > ) {
44
96
if_chain ! {
45
- // if this is a method call
46
97
if let ExprKind :: MethodCall ( ref method_name, _, ref args) = & expr. kind;
47
- // on a Path (i.e. a variable/name, not another method)
48
- if let ExprKind :: Path ( QPath :: Resolved ( None , path) ) = & args[ 0 ] . kind;
98
+ if args. len( ) == 1 ;
99
+ if let Some ( parent) = get_parent_expr( cx, & expr) ;
100
+
49
101
then {
50
- let name = method_name. ident. as_str( ) ;
51
- // alter help slightly to account for _mut
52
- match & * name {
53
- "deref" => {
54
- span_lint_and_sugg(
55
- cx,
56
- EXPLICIT_DEREF_METHOD ,
57
- expr. span,
58
- "explicit deref method call" ,
59
- "try this" ,
60
- format!( "&*{}" , path) ,
61
- Applicability :: MachineApplicable
62
- ) ;
63
- } ,
64
- "deref_mut" => {
65
- span_lint_and_sugg(
66
- cx,
67
- EXPLICIT_DEREF_METHOD ,
68
- expr. span,
69
- "explicit deref_mut method call" ,
70
- "try this" ,
71
- format!( "&mut *{}" , path) ,
72
- Applicability :: MachineApplicable
73
- ) ;
74
- } ,
75
- _ => ( )
76
- } ;
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
+ }
77
111
}
78
112
}
79
113
}
80
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
+ }
0 commit comments