1
+ use std:: ops:: ControlFlow ;
2
+
1
3
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
4
use clippy_utils:: eager_or_lazy:: switch_to_lazy_eval;
3
5
use clippy_utils:: source:: snippet_with_context;
4
6
use clippy_utils:: ty:: { expr_type_is_certain, implements_trait, is_type_diagnostic_item} ;
5
- use clippy_utils:: { contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment} ;
7
+ use clippy_utils:: visitors:: for_each_expr;
8
+ use clippy_utils:: {
9
+ contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment, peel_blocks,
10
+ } ;
6
11
use rustc_errors:: Applicability ;
7
12
use rustc_lint:: LateContext ;
8
13
use rustc_middle:: ty;
@@ -13,7 +18,7 @@ use {rustc_ast as ast, rustc_hir as hir};
13
18
use super :: { OR_FUN_CALL , UNWRAP_OR_DEFAULT } ;
14
19
15
20
/// Checks for the `OR_FUN_CALL` lint.
16
- #[ allow ( clippy:: too_many_lines) ]
21
+ #[ expect ( clippy:: too_many_lines) ]
17
22
pub ( super ) fn check < ' tcx > (
18
23
cx : & LateContext < ' tcx > ,
19
24
expr : & hir:: Expr < ' _ > ,
@@ -26,7 +31,6 @@ pub(super) fn check<'tcx>(
26
31
/// `or_insert(T::new())` or `or_insert(T::default())`.
27
32
/// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`,
28
33
/// `or_insert_with(T::new)` or `or_insert_with(T::default)`.
29
- #[ allow( clippy:: too_many_arguments) ]
30
34
fn check_unwrap_or_default (
31
35
cx : & LateContext < ' _ > ,
32
36
name : & str ,
@@ -123,8 +127,8 @@ pub(super) fn check<'tcx>(
123
127
}
124
128
125
129
/// Checks for `*or(foo())`.
126
- #[ allow ( clippy:: too_many_arguments) ]
127
- fn check_general_case < ' tcx > (
130
+ #[ expect ( clippy:: too_many_arguments) ]
131
+ fn check_or_fn_call < ' tcx > (
128
132
cx : & LateContext < ' tcx > ,
129
133
name : & str ,
130
134
method_span : Span ,
@@ -135,7 +139,7 @@ pub(super) fn check<'tcx>(
135
139
span : Span ,
136
140
// None if lambda is required
137
141
fun_span : Option < Span > ,
138
- ) {
142
+ ) -> bool {
139
143
// (path, fn_has_argument, methods, suffix)
140
144
const KNOW_TYPES : [ ( Symbol , bool , & [ & str ] , & str ) ; 4 ] = [
141
145
( sym:: BTreeEntry , false , & [ "or_insert" ] , "with" ) ,
@@ -185,54 +189,68 @@ pub(super) fn check<'tcx>(
185
189
format ! ( "{name}_{suffix}({sugg})" ) ,
186
190
app,
187
191
) ;
188
- }
189
- }
190
-
191
- let extract_inner_arg = |arg : & ' tcx hir:: Expr < ' _ > | {
192
- if let hir:: ExprKind :: Block (
193
- hir:: Block {
194
- stmts : [ ] ,
195
- expr : Some ( expr) ,
196
- ..
197
- } ,
198
- _,
199
- ) = arg. kind
200
- {
201
- expr
192
+ true
202
193
} else {
203
- arg
194
+ false
204
195
}
205
- } ;
196
+ }
206
197
207
198
if let [ arg] = args {
208
- let inner_arg = extract_inner_arg ( arg) ;
209
- match inner_arg. kind {
210
- hir:: ExprKind :: Call ( fun, or_args) => {
211
- let or_has_args = !or_args. is_empty ( ) ;
212
- if or_has_args
213
- || !check_unwrap_or_default ( cx, name, receiver, fun, Some ( inner_arg) , expr. span , method_span)
214
- {
215
- let fun_span = if or_has_args { None } else { Some ( fun. span ) } ;
216
- check_general_case ( cx, name, method_span, receiver, arg, None , expr. span , fun_span) ;
217
- }
218
- } ,
219
- hir:: ExprKind :: Path ( ..) | hir:: ExprKind :: Closure ( ..) => {
220
- check_unwrap_or_default ( cx, name, receiver, inner_arg, None , expr. span , method_span) ;
221
- } ,
222
- hir:: ExprKind :: Index ( ..) | hir:: ExprKind :: MethodCall ( ..) => {
223
- check_general_case ( cx, name, method_span, receiver, arg, None , expr. span , None ) ;
224
- } ,
225
- _ => ( ) ,
226
- }
199
+ let inner_arg = peel_blocks ( arg) ;
200
+ for_each_expr ( cx, inner_arg, |ex| {
201
+ // `or_fun_call` lint needs to take nested expr into account,
202
+ // but `unwrap_or_default` lint doesn't, we don't want something like:
203
+ // `opt.unwrap_or(Foo { inner: String::default(), other: 1 })` to get replaced by
204
+ // `opt.unwrap_or_default()`.
205
+ let is_nested_expr = ex. hir_id != inner_arg. hir_id ;
206
+
207
+ let is_triggered = match ex. kind {
208
+ hir:: ExprKind :: Call ( fun, fun_args) => {
209
+ let inner_fun_has_args = !fun_args. is_empty ( ) ;
210
+ let fun_span = if inner_fun_has_args || is_nested_expr {
211
+ None
212
+ } else {
213
+ Some ( fun. span )
214
+ } ;
215
+ ( !inner_fun_has_args
216
+ && !is_nested_expr
217
+ && check_unwrap_or_default ( cx, name, receiver, fun, Some ( ex) , expr. span , method_span) )
218
+ || check_or_fn_call ( cx, name, method_span, receiver, arg, None , expr. span , fun_span)
219
+ } ,
220
+ hir:: ExprKind :: Path ( ..) | hir:: ExprKind :: Closure ( ..) if !is_nested_expr => {
221
+ check_unwrap_or_default ( cx, name, receiver, ex, None , expr. span , method_span)
222
+ } ,
223
+ hir:: ExprKind :: Index ( ..) | hir:: ExprKind :: MethodCall ( ..) => {
224
+ check_or_fn_call ( cx, name, method_span, receiver, arg, None , expr. span , None )
225
+ } ,
226
+ _ => false ,
227
+ } ;
228
+
229
+ if is_triggered {
230
+ ControlFlow :: Break ( ( ) )
231
+ } else {
232
+ ControlFlow :: Continue ( ( ) )
233
+ }
234
+ } ) ;
227
235
}
228
236
229
237
// `map_or` takes two arguments
230
238
if let [ arg, lambda] = args {
231
- let inner_arg = extract_inner_arg ( arg) ;
232
- if let hir:: ExprKind :: Call ( fun, or_args) = inner_arg. kind {
233
- let fun_span = if or_args. is_empty ( ) { Some ( fun. span ) } else { None } ;
234
- check_general_case ( cx, name, method_span, receiver, arg, Some ( lambda) , expr. span , fun_span) ;
235
- }
239
+ let inner_arg = peel_blocks ( arg) ;
240
+ for_each_expr ( cx, inner_arg, |ex| {
241
+ let is_top_most_expr = ex. hir_id == inner_arg. hir_id ;
242
+ if let hir:: ExprKind :: Call ( fun, fun_args) = ex. kind {
243
+ let fun_span = if fun_args. is_empty ( ) && is_top_most_expr {
244
+ Some ( fun. span )
245
+ } else {
246
+ None
247
+ } ;
248
+ if check_or_fn_call ( cx, name, method_span, receiver, arg, Some ( lambda) , expr. span , fun_span) {
249
+ return ControlFlow :: Break ( ( ) ) ;
250
+ }
251
+ }
252
+ ControlFlow :: Continue ( ( ) )
253
+ } ) ;
236
254
}
237
255
}
238
256
0 commit comments