1+ use std:: ops:: ControlFlow ;
2+
13use clippy_utils:: diagnostics:: span_lint_and_sugg;
24use clippy_utils:: eager_or_lazy:: switch_to_lazy_eval;
35use clippy_utils:: source:: snippet_with_context;
46use 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+ } ;
611use rustc_errors:: Applicability ;
712use rustc_lint:: LateContext ;
813use rustc_middle:: ty;
@@ -13,7 +18,7 @@ use {rustc_ast as ast, rustc_hir as hir};
1318use super :: { OR_FUN_CALL , UNWRAP_OR_DEFAULT } ;
1419
1520/// Checks for the `OR_FUN_CALL` lint.
16- #[ allow ( clippy:: too_many_lines) ]
21+ #[ expect ( clippy:: too_many_lines) ]
1722pub ( super ) fn check < ' tcx > (
1823 cx : & LateContext < ' tcx > ,
1924 expr : & hir:: Expr < ' _ > ,
@@ -26,7 +31,6 @@ pub(super) fn check<'tcx>(
2631 /// `or_insert(T::new())` or `or_insert(T::default())`.
2732 /// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`,
2833 /// `or_insert_with(T::new)` or `or_insert_with(T::default)`.
29- #[ allow( clippy:: too_many_arguments) ]
3034 fn check_unwrap_or_default (
3135 cx : & LateContext < ' _ > ,
3236 name : & str ,
@@ -123,8 +127,8 @@ pub(super) fn check<'tcx>(
123127 }
124128
125129 /// 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 > (
128132 cx : & LateContext < ' tcx > ,
129133 name : & str ,
130134 method_span : Span ,
@@ -135,7 +139,7 @@ pub(super) fn check<'tcx>(
135139 span : Span ,
136140 // None if lambda is required
137141 fun_span : Option < Span > ,
138- ) {
142+ ) -> bool {
139143 // (path, fn_has_argument, methods, suffix)
140144 const KNOW_TYPES : [ ( Symbol , bool , & [ & str ] , & str ) ; 4 ] = [
141145 ( sym:: BTreeEntry , false , & [ "or_insert" ] , "with" ) ,
@@ -185,54 +189,68 @@ pub(super) fn check<'tcx>(
185189 format ! ( "{name}_{suffix}({sugg})" ) ,
186190 app,
187191 ) ;
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
202193 } else {
203- arg
194+ false
204195 }
205- } ;
196+ }
206197
207198 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+ } ) ;
227235 }
228236
229237 // `map_or` takes two arguments
230238 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+ } ) ;
236254 }
237255}
238256
0 commit comments