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;
23use rustc_errors:: Applicability ;
4+ use rustc_hir as hir;
5+ use rustc_hir:: { Expr , ExprKind , QPath , StmtKind } ;
36use 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 ;
79
810declare_clippy_lint ! {
911 /// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls.
@@ -21,7 +23,7 @@ declare_clippy_lint! {
2123 /// let b = &*a;
2224 /// let c = &mut *a;
2325 /// ```
24- ///
26+ ///
2527 /// This lint excludes
2628 /// ```rust
2729 /// let e = d.unwrap().deref();
@@ -36,45 +38,105 @@ declare_lint_pass!(Dereferencing => [
3638] ) ;
3739
3840impl < ' 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+ }
4292 }
93+ }
4394
95+ fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr < ' _ > ) {
4496 if_chain ! {
45- // if this is a method call
4697 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+
49101 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+ }
77111 }
78112 }
79113 }
80114}
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