1
1
use clippy_utils:: diagnostics:: span_lint_and_then;
2
- use clippy_utils:: { is_res_lang_ctor, is_trait_method, match_trait_method, paths} ;
2
+ use clippy_utils:: macros:: { is_panic, root_macro_call_first_node} ;
3
+ use clippy_utils:: { is_res_lang_ctor, is_trait_method, match_trait_method, paths, peel_blocks} ;
3
4
use hir:: { ExprKind , PatKind } ;
4
5
use rustc_hir as hir;
5
6
use rustc_lint:: { LateContext , LateLintPass } ;
@@ -82,37 +83,72 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
82
83
}
83
84
84
85
if let Some ( exp) = block. expr
85
- && matches ! ( exp. kind, hir:: ExprKind :: If ( _, _, _) | hir:: ExprKind :: Match ( _, _, _) )
86
+ && matches ! (
87
+ exp. kind,
88
+ hir:: ExprKind :: If ( _, _, _) | hir:: ExprKind :: Match ( _, _, hir:: MatchSource :: Normal )
89
+ )
86
90
{
87
91
check_expr ( cx, exp) ;
88
92
}
89
93
}
90
94
}
91
95
96
+ fn non_consuming_err_arm < ' a > ( cx : & LateContext < ' a > , arm : & hir:: Arm < ' a > ) -> bool {
97
+ // if there is a guard, we consider the result to be consumed
98
+ if arm. guard . is_some ( ) {
99
+ return false ;
100
+ }
101
+ if is_unreachable_or_panic ( cx, arm. body ) {
102
+ // if the body is unreachable or there is a panic,
103
+ // we consider the result to be consumed
104
+ return false ;
105
+ }
106
+
107
+ if let PatKind :: TupleStruct ( ref path, [ inner_pat] , _) = arm. pat . kind {
108
+ return is_res_lang_ctor ( cx, cx. qpath_res ( path, inner_pat. hir_id ) , hir:: LangItem :: ResultErr ) ;
109
+ }
110
+
111
+ false
112
+ }
113
+
114
+ fn non_consuming_ok_arm < ' a > ( cx : & LateContext < ' a > , arm : & hir:: Arm < ' a > ) -> bool {
115
+ // if there is a guard, we consider the result to be consumed
116
+ if arm. guard . is_some ( ) {
117
+ return false ;
118
+ }
119
+ if is_unreachable_or_panic ( cx, arm. body ) {
120
+ // if the body is unreachable or there is a panic,
121
+ // we consider the result to be consumed
122
+ return false ;
123
+ }
124
+
125
+ if is_ok_wild_or_dotdot_pattern ( cx, arm. pat ) {
126
+ return true ;
127
+ }
128
+ false
129
+ }
130
+
92
131
fn check_expr < ' a > ( cx : & LateContext < ' a > , expr : & ' a hir:: Expr < ' a > ) {
93
132
match expr. kind {
94
133
hir:: ExprKind :: If ( cond, _, _)
95
134
if let ExprKind :: Let ( hir:: Let { pat, init, .. } ) = cond. kind
96
- && pattern_is_ignored_ok ( cx, pat)
135
+ && is_ok_wild_or_dotdot_pattern ( cx, pat)
97
136
&& let Some ( op) = should_lint ( cx, init) =>
98
137
{
99
138
emit_lint ( cx, cond. span , op, & [ pat. span ] ) ;
100
139
} ,
101
- hir:: ExprKind :: Match ( expr, arms, hir:: MatchSource :: Normal ) if let Some ( op) = should_lint ( cx, expr) => {
102
- let found_arms: Vec < _ > = arms
103
- . iter ( )
104
- . filter_map ( |arm| {
105
- if pattern_is_ignored_ok ( cx, arm. pat ) {
106
- Some ( arm. span )
107
- } else {
108
- None
109
- }
110
- } )
111
- . collect ( ) ;
112
- if !found_arms. is_empty ( ) {
113
- emit_lint ( cx, expr. span , op, found_arms. as_slice ( ) ) ;
140
+ // we will capture only the case where the match is Ok( ) or Err( )
141
+ // prefer to match the minimum possible, and expand later if needed
142
+ // to avoid false positives on something as used as this
143
+ hir:: ExprKind :: Match ( expr, [ arm1, arm2] , hir:: MatchSource :: Normal ) if let Some ( op) = should_lint ( cx, expr) => {
144
+ if non_consuming_ok_arm ( cx, arm1) && non_consuming_err_arm ( cx, arm2) {
145
+ emit_lint ( cx, expr. span , op, & [ arm1. pat . span ] ) ;
146
+ }
147
+ if non_consuming_ok_arm ( cx, arm2) && non_consuming_err_arm ( cx, arm1) {
148
+ emit_lint ( cx, expr. span , op, & [ arm2. pat . span ] ) ;
114
149
}
115
150
} ,
151
+ hir:: ExprKind :: Match ( _, _, hir:: MatchSource :: Normal ) => { } ,
116
152
_ if let Some ( op) = should_lint ( cx, expr) => {
117
153
emit_lint ( cx, expr. span , op, & [ ] ) ;
118
154
} ,
@@ -130,25 +166,40 @@ fn should_lint<'a>(cx: &LateContext<'a>, mut inner: &'a hir::Expr<'a>) -> Option
130
166
check_io_mode ( cx, inner)
131
167
}
132
168
133
- fn pattern_is_ignored_ok ( cx : & LateContext < ' _ > , pat : & hir:: Pat < ' _ > ) -> bool {
169
+ fn is_ok_wild_or_dotdot_pattern < ' a > ( cx : & LateContext < ' a > , pat : & hir:: Pat < ' a > ) -> bool {
134
170
// the if checks whether we are in a result Ok( ) pattern
135
171
// and the return checks whether it is unhandled
136
172
137
- if let PatKind :: TupleStruct ( ref path, inner_pat, ddp ) = pat. kind
173
+ if let PatKind :: TupleStruct ( ref path, inner_pat, _ ) = pat. kind
138
174
// we check against Result::Ok to avoid linting on Err(_) or something else.
139
175
&& is_res_lang_ctor ( cx, cx. qpath_res ( path, pat. hir_id ) , hir:: LangItem :: ResultOk )
140
176
{
141
- return match ( inner_pat, ddp. as_opt_usize ( ) ) {
142
- // Ok(_) pattern
143
- ( [ inner_pat] , None ) if matches ! ( inner_pat. kind, PatKind :: Wild ) => true ,
144
- // Ok(..) pattern
145
- ( [ ] , Some ( 0 ) ) => true ,
146
- _ => false ,
147
- } ;
177
+ if matches ! ( inner_pat, [ ] ) {
178
+ return true ;
179
+ }
180
+
181
+ if let [ cons_pat] = inner_pat
182
+ && matches ! ( cons_pat. kind, PatKind :: Wild )
183
+ {
184
+ return true ;
185
+ }
186
+ return false ;
148
187
}
149
188
false
150
189
}
151
190
191
+ // this is partially taken from panic_unimplemented
192
+ fn is_unreachable_or_panic ( cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) -> bool {
193
+ let expr = peel_blocks ( expr) ;
194
+ let Some ( macro_call) = root_macro_call_first_node ( cx, expr) else {
195
+ return false ;
196
+ } ;
197
+ if is_panic ( cx, macro_call. def_id ) {
198
+ return !cx. tcx . hir ( ) . is_inside_const_context ( expr. hir_id ) ;
199
+ }
200
+ matches ! ( cx. tcx. item_name( macro_call. def_id) . as_str( ) , "unreachable" )
201
+ }
202
+
152
203
fn unpack_call_chain < ' a > ( mut expr : & ' a hir:: Expr < ' a > ) -> & ' a hir:: Expr < ' a > {
153
204
while let hir:: ExprKind :: MethodCall ( path, receiver, ..) = expr. kind {
154
205
if matches ! (
0 commit comments