1
- use crate :: utils:: { higher:: if_block, is_type_diagnostic_item, span_lint_and_then, usage:: is_potentially_mutated} ;
1
+ use crate :: utils:: {
2
+ differing_macro_contexts, higher:: if_block, is_type_diagnostic_item, span_lint_and_then,
3
+ usage:: is_potentially_mutated,
4
+ } ;
2
5
use if_chain:: if_chain;
3
6
use rustc_hir:: intravisit:: { walk_expr, walk_fn, FnKind , NestedVisitorMap , Visitor } ;
4
7
use rustc_hir:: { BinOpKind , Body , Expr , ExprKind , FnDecl , HirId , Path , QPath , UnOp } ;
@@ -73,6 +76,8 @@ struct UnwrapInfo<'tcx> {
73
76
ident : & ' tcx Path < ' tcx > ,
74
77
/// The check, like `x.is_ok()`
75
78
check : & ' tcx Expr < ' tcx > ,
79
+ /// The branch where the check takes place, like `if x.is_ok() { .. }`
80
+ branch : & ' tcx Expr < ' tcx > ,
76
81
/// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`).
77
82
safe_to_unwrap : bool ,
78
83
}
@@ -82,19 +87,20 @@ struct UnwrapInfo<'tcx> {
82
87
fn collect_unwrap_info < ' a , ' tcx > (
83
88
cx : & ' a LateContext < ' a , ' tcx > ,
84
89
expr : & ' tcx Expr < ' _ > ,
90
+ branch : & ' tcx Expr < ' _ > ,
85
91
invert : bool ,
86
92
) -> Vec < UnwrapInfo < ' tcx > > {
87
93
if let ExprKind :: Binary ( op, left, right) = & expr. kind {
88
94
match ( invert, op. node ) {
89
95
( false , BinOpKind :: And ) | ( false , BinOpKind :: BitAnd ) | ( true , BinOpKind :: Or ) | ( true , BinOpKind :: BitOr ) => {
90
- let mut unwrap_info = collect_unwrap_info ( cx, left, invert) ;
91
- unwrap_info. append ( & mut collect_unwrap_info ( cx, right, invert) ) ;
96
+ let mut unwrap_info = collect_unwrap_info ( cx, left, branch , invert) ;
97
+ unwrap_info. append ( & mut collect_unwrap_info ( cx, right, branch , invert) ) ;
92
98
return unwrap_info;
93
99
} ,
94
100
_ => ( ) ,
95
101
}
96
102
} else if let ExprKind :: Unary ( UnOp :: UnNot , expr) = & expr. kind {
97
- return collect_unwrap_info ( cx, expr, !invert) ;
103
+ return collect_unwrap_info ( cx, expr, branch , !invert) ;
98
104
} else {
99
105
if_chain ! {
100
106
if let ExprKind :: MethodCall ( method_name, _, args) = & expr. kind;
@@ -111,7 +117,7 @@ fn collect_unwrap_info<'a, 'tcx>(
111
117
_ => unreachable!( ) ,
112
118
} ;
113
119
let safe_to_unwrap = unwrappable != invert;
114
- return vec![ UnwrapInfo { ident: path, check: expr, safe_to_unwrap } ] ;
120
+ return vec![ UnwrapInfo { ident: path, check: expr, branch , safe_to_unwrap } ] ;
115
121
}
116
122
}
117
123
}
@@ -121,7 +127,7 @@ fn collect_unwrap_info<'a, 'tcx>(
121
127
impl < ' a , ' tcx > UnwrappableVariablesVisitor < ' a , ' tcx > {
122
128
fn visit_branch ( & mut self , cond : & ' tcx Expr < ' _ > , branch : & ' tcx Expr < ' _ > , else_branch : bool ) {
123
129
let prev_len = self . unwrappables . len ( ) ;
124
- for unwrap_info in collect_unwrap_info ( self . cx , cond, else_branch) {
130
+ for unwrap_info in collect_unwrap_info ( self . cx , cond, branch , else_branch) {
125
131
if is_potentially_mutated ( unwrap_info. ident , cond, self . cx )
126
132
|| is_potentially_mutated ( unwrap_info. ident , branch, self . cx )
127
133
{
@@ -158,6 +164,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
158
164
let call_to_unwrap = method_name. ident. name == sym!( unwrap) ;
159
165
if let Some ( unwrappable) = self . unwrappables. iter( )
160
166
. find( |u| u. ident. res == path. res) ;
167
+ // Span contexts should not differ with the conditional branch
168
+ if !differing_macro_contexts( unwrappable. branch. span, expr. span) ;
169
+ if !differing_macro_contexts( unwrappable. branch. span, unwrappable. check. span) ;
161
170
then {
162
171
if call_to_unwrap == unwrappable. safe_to_unwrap {
163
172
span_lint_and_then(
0 commit comments