1
1
use crate :: utils:: { is_direct_expn_of, span_lint} ;
2
- use core:: ops:: Deref ;
3
2
use if_chain:: if_chain;
4
3
use matches:: matches;
5
- use rustc:: hir:: { Expr , ExprKind , Guard , Mutability , StmtKind , UnOp } ;
4
+ use rustc:: hir:: intravisit:: { walk_expr, NestedVisitorMap , Visitor } ;
5
+ use rustc:: hir:: { Expr , ExprKind , Mutability , StmtKind , UnOp } ;
6
6
use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
7
7
use rustc:: { declare_lint_pass, declare_tool_lint, ty} ;
8
8
use syntax_pos:: Span ;
@@ -32,72 +32,28 @@ declare_clippy_lint! {
32
32
33
33
declare_lint_pass ! ( DebugAssertWithMutCall => [ DEBUG_ASSERT_WITH_MUT_CALL ] ) ;
34
34
35
- fn check_for_mutable_call ( cx : & LateContext < ' _ , ' _ > , span : Span , e : & Expr ) -> Option < Span > {
36
- match & e. kind {
37
- ExprKind :: AddrOf ( Mutability :: MutMutable , _) => Some ( span) ,
38
- ExprKind :: Path ( _) => cx. tables . adjustments ( ) . get ( e. hir_id ) . and_then ( |adj| {
39
- if adj
40
- . iter ( )
41
- . any ( |a| matches ! ( a. target. kind, ty:: Ref ( _, _, Mutability :: MutMutable ) ) )
42
- {
43
- Some ( span)
44
- } else {
45
- None
35
+ const DEBUG_MACRO_NAMES : [ & str ; 3 ] = [ "debug_assert" , "debug_assert_eq" , "debug_assert_ne" ] ;
36
+
37
+ impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for DebugAssertWithMutCall {
38
+ fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , e : & ' tcx Expr ) {
39
+ for dmn in & DEBUG_MACRO_NAMES {
40
+ if is_direct_expn_of ( e. span , dmn) . is_some ( ) {
41
+ if let Some ( span) = extract_call ( cx, e) {
42
+ span_lint (
43
+ cx,
44
+ DEBUG_ASSERT_WITH_MUT_CALL ,
45
+ span,
46
+ & format ! ( "do not call a function with mutable arguments inside of `{}!`" , dmn) ,
47
+ ) ;
48
+ }
46
49
}
47
- } ) ,
48
- ExprKind :: AddrOf ( Mutability :: MutImmutable , expr)
49
- | ExprKind :: Assign ( _, expr)
50
- | ExprKind :: AssignOp ( _, _, expr)
51
- | ExprKind :: Box ( expr)
52
- | ExprKind :: Break ( _, Some ( expr) )
53
- | ExprKind :: Cast ( expr, _)
54
- | ExprKind :: DropTemps ( expr)
55
- | ExprKind :: Field ( expr, _)
56
- | ExprKind :: Repeat ( expr, _)
57
- | ExprKind :: Ret ( Some ( expr) )
58
- | ExprKind :: Type ( expr, _)
59
- | ExprKind :: Unary ( _, expr)
60
- | ExprKind :: Yield ( expr, _) => check_for_mutable_call ( cx, expr. span , expr) ,
61
- ExprKind :: Binary ( _, lhs, rhs) | ExprKind :: Index ( lhs, rhs) => {
62
- check_for_mutable_call ( cx, lhs. span , lhs) . or_else ( || check_for_mutable_call ( cx, rhs. span , rhs) )
63
- } ,
64
- ExprKind :: Array ( args) | ExprKind :: Call ( _, args) | ExprKind :: MethodCall ( _, _, args) | ExprKind :: Tup ( args) => {
65
- ( * args) . iter ( ) . find_map ( |a| check_for_mutable_call ( cx, span, a) )
66
- } ,
67
- ExprKind :: Match ( header, arm, _) => check_for_mutable_call ( cx, header. span , header) . or_else ( || {
68
- ( * arm) . iter ( ) . find_map ( |a| {
69
- check_for_mutable_call ( cx, a. body . span , & a. body ) . or_else ( || {
70
- a. guard . as_ref ( ) . and_then ( |g| {
71
- let Guard :: If ( e) = g;
72
- check_for_mutable_call ( cx, e. span , & e)
73
- } )
74
- } )
75
- } )
76
- } ) ,
77
- ExprKind :: Block ( block, _) | ExprKind :: Loop ( block, _, _) => block
78
- . stmts
79
- . iter ( )
80
- . filter_map ( |s| match & s. kind {
81
- StmtKind :: Local ( l) => l. init . as_ref ( ) . map ( |x| x. deref ( ) ) ,
82
- StmtKind :: Expr ( e) => Some ( e) ,
83
- StmtKind :: Semi ( e) => Some ( e) ,
84
- StmtKind :: Item ( _) => None ,
85
- } )
86
- . chain ( block. expr . as_ref ( ) . map ( |x| x. deref ( ) ) )
87
- . find_map ( |a| check_for_mutable_call ( cx, a. span , a) ) ,
88
- ExprKind :: Err
89
- | ExprKind :: Lit ( _)
90
- | ExprKind :: Continue ( _)
91
- | ExprKind :: InlineAsm ( _, _, _)
92
- | ExprKind :: Struct ( _, _, _)
93
- | ExprKind :: Closure ( _, _, _, _, _)
94
- | ExprKind :: Break ( _, None )
95
- | ExprKind :: Ret ( None ) => None ,
50
+ }
96
51
}
97
52
}
98
53
54
+ //HACK(hellow554): remove this when #4694 is implemented
99
55
#[ allow( clippy:: cognitive_complexity) ]
100
- fn extract_call ( cx : & LateContext < ' _ , ' _ > , e : & Expr ) -> Option < Span > {
56
+ fn extract_call < ' a , ' tcx > ( cx : & ' a LateContext < ' a , ' tcx > , e : & ' tcx Expr ) -> Option < Span > {
101
57
if_chain ! {
102
58
if let ExprKind :: Block ( ref block, _) = e. kind;
103
59
if block. stmts. len( ) == 1 ;
@@ -109,7 +65,7 @@ fn extract_call(cx: &LateContext<'_, '_>, e: &Expr) -> Option<Span> {
109
65
if let ExprKind :: Unary ( UnOp :: UnNot , ref condition) = droptmp. kind;
110
66
then {
111
67
// debug_assert
112
- return check_for_mutable_call ( cx, condition. span , condition )
68
+ return MutArgVisitor :: go ( cx, condition) ;
113
69
} else {
114
70
// debug_assert_{eq,ne}
115
71
if_chain! {
@@ -120,12 +76,12 @@ fn extract_call(cx: &LateContext<'_, '_>, e: &Expr) -> Option<Span> {
120
76
if conditions. len( ) == 2 ;
121
77
then {
122
78
if let ExprKind :: AddrOf ( _, ref lhs) = conditions[ 0 ] . kind {
123
- if let Some ( span) = check_for_mutable_call ( cx, lhs . span , lhs) {
79
+ if let Some ( span) = MutArgVisitor :: go ( cx, lhs) {
124
80
return Some ( span) ;
125
81
}
126
82
}
127
83
if let ExprKind :: AddrOf ( _, ref rhs) = conditions[ 1 ] . kind {
128
- if let Some ( span) = check_for_mutable_call ( cx, rhs . span , rhs) {
84
+ if let Some ( span) = MutArgVisitor :: go ( cx, rhs) {
129
85
return Some ( span) ;
130
86
}
131
87
}
@@ -138,20 +94,62 @@ fn extract_call(cx: &LateContext<'_, '_>, e: &Expr) -> Option<Span> {
138
94
139
95
None
140
96
}
141
- impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for DebugAssertWithMutCall {
142
- fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , e : & ' tcx Expr ) {
143
- if [ "debug_assert" , "debug_assert_eq" , "debug_assert_ne" ]
144
- . iter ( )
145
- . any ( |name| is_direct_expn_of ( e. span , name) . is_some ( ) )
146
- {
147
- if let Some ( span) = extract_call ( cx, e) {
148
- span_lint (
149
- cx,
150
- DEBUG_ASSERT_WITH_MUT_CALL ,
151
- span,
152
- "do not call functions with mutable arguments inside of a `debug_assert!`" ,
153
- ) ;
154
- }
97
+
98
+ struct MutArgVisitor < ' a , ' tcx > {
99
+ cx : & ' a LateContext < ' a , ' tcx > ,
100
+ expr_span : Option < Span > ,
101
+ found : bool ,
102
+ }
103
+
104
+ impl < ' a , ' tcx > MutArgVisitor < ' a , ' tcx > {
105
+ fn new ( cx : & ' a LateContext < ' a , ' tcx > ) -> Self {
106
+ Self {
107
+ cx,
108
+ expr_span : None ,
109
+ found : false ,
110
+ }
111
+ }
112
+
113
+ fn get_span ( & self ) -> Option < Span > {
114
+ if self . found {
115
+ self . expr_span
116
+ } else {
117
+ None
118
+ }
119
+ }
120
+
121
+ fn go ( cx : & ' a LateContext < ' a , ' tcx > , start : & ' tcx Expr ) -> Option < Span > {
122
+ let mut s = Self :: new ( cx) ;
123
+ s. visit_expr ( start) ;
124
+ s. get_span ( )
125
+ }
126
+ }
127
+
128
+ impl < ' a , ' tcx > Visitor < ' tcx > for MutArgVisitor < ' a , ' tcx > {
129
+ fn visit_expr ( & mut self , expr : & ' tcx Expr ) {
130
+ match expr. kind {
131
+ ExprKind :: AddrOf ( Mutability :: MutMutable , _) => {
132
+ self . found = true ;
133
+ return ;
134
+ } ,
135
+ ExprKind :: Path ( _) => {
136
+ if let Some ( adj) = self . cx . tables . adjustments ( ) . get ( expr. hir_id ) {
137
+ if adj
138
+ . iter ( )
139
+ . any ( |a| matches ! ( a. target. kind, ty:: Ref ( _, _, Mutability :: MutMutable ) ) )
140
+ {
141
+ self . found = true ;
142
+ return ;
143
+ }
144
+ }
145
+ } ,
146
+ _ if !self . found => self . expr_span = Some ( expr. span ) ,
147
+ _ => return ,
155
148
}
149
+ walk_expr ( self , expr)
150
+ }
151
+
152
+ fn nested_visit_map < ' this > ( & ' this mut self ) -> NestedVisitorMap < ' this , ' tcx > {
153
+ NestedVisitorMap :: All ( & self . cx . tcx . hir ( ) )
156
154
}
157
155
}
0 commit comments