11use crate :: utils:: { is_direct_expn_of, span_lint} ;
2- use core:: ops:: Deref ;
32use if_chain:: if_chain;
43use 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 } ;
66use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
77use rustc:: { declare_lint_pass, declare_tool_lint, ty} ;
88use syntax_pos:: Span ;
@@ -32,72 +32,28 @@ declare_clippy_lint! {
3232
3333declare_lint_pass ! ( DebugAssertWithMutCall => [ DEBUG_ASSERT_WITH_MUT_CALL ] ) ;
3434
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+ }
4649 }
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+ }
9651 }
9752}
9853
54+ //HACK(hellow554): remove this when #4694 is implemented
9955#[ 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 > {
10157 if_chain ! {
10258 if let ExprKind :: Block ( ref block, _) = e. kind;
10359 if block. stmts. len( ) == 1 ;
@@ -109,7 +65,7 @@ fn extract_call(cx: &LateContext<'_, '_>, e: &Expr) -> Option<Span> {
10965 if let ExprKind :: Unary ( UnOp :: UnNot , ref condition) = droptmp. kind;
11066 then {
11167 // debug_assert
112- return check_for_mutable_call ( cx, condition. span , condition )
68+ return MutArgVisitor :: go ( cx, condition) ;
11369 } else {
11470 // debug_assert_{eq,ne}
11571 if_chain! {
@@ -120,12 +76,12 @@ fn extract_call(cx: &LateContext<'_, '_>, e: &Expr) -> Option<Span> {
12076 if conditions. len( ) == 2 ;
12177 then {
12278 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) {
12480 return Some ( span) ;
12581 }
12682 }
12783 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) {
12985 return Some ( span) ;
13086 }
13187 }
@@ -138,20 +94,62 @@ fn extract_call(cx: &LateContext<'_, '_>, e: &Expr) -> Option<Span> {
13894
13995 None
14096}
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 ,
155148 }
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 ( ) )
156154 }
157155}
0 commit comments