@@ -18,7 +18,6 @@ use {rustc_ast as ast, rustc_hir as hir};
18
18
use super :: { OR_FUN_CALL , UNWRAP_OR_DEFAULT } ;
19
19
20
20
/// Checks for the `OR_FUN_CALL` lint.
21
- #[ expect( clippy:: too_many_lines) ]
22
21
pub ( super ) fn check < ' tcx > (
23
22
cx : & LateContext < ' tcx > ,
24
23
expr : & hir:: Expr < ' _ > ,
@@ -27,184 +26,6 @@ pub(super) fn check<'tcx>(
27
26
receiver : & ' tcx hir:: Expr < ' _ > ,
28
27
args : & ' tcx [ hir:: Expr < ' _ > ] ,
29
28
) {
30
- /// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`,
31
- /// `or_insert(T::new())` or `or_insert(T::default())`.
32
- /// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`,
33
- /// `or_insert_with(T::new)` or `or_insert_with(T::default)`.
34
- fn check_unwrap_or_default (
35
- cx : & LateContext < ' _ > ,
36
- name : Symbol ,
37
- receiver : & hir:: Expr < ' _ > ,
38
- fun : & hir:: Expr < ' _ > ,
39
- call_expr : Option < & hir:: Expr < ' _ > > ,
40
- span : Span ,
41
- method_span : Span ,
42
- ) -> bool {
43
- if !expr_type_is_certain ( cx, receiver) {
44
- return false ;
45
- }
46
-
47
- let is_new = |fun : & hir:: Expr < ' _ > | {
48
- if let hir:: ExprKind :: Path ( ref qpath) = fun. kind {
49
- let path = last_path_segment ( qpath) . ident . name ;
50
- matches ! ( path, sym:: new)
51
- } else {
52
- false
53
- }
54
- } ;
55
-
56
- let output_type_implements_default = |fun| {
57
- let fun_ty = cx. typeck_results ( ) . expr_ty ( fun) ;
58
- if let ty:: FnDef ( def_id, args) = fun_ty. kind ( ) {
59
- let output_ty = cx. tcx . fn_sig ( def_id) . instantiate ( cx. tcx , args) . skip_binder ( ) . output ( ) ;
60
- cx. tcx
61
- . get_diagnostic_item ( sym:: Default )
62
- . is_some_and ( |default_trait_id| implements_trait ( cx, output_ty, default_trait_id, & [ ] ) )
63
- } else {
64
- false
65
- }
66
- } ;
67
-
68
- let sugg = match ( name, call_expr. is_some ( ) ) {
69
- ( sym:: unwrap_or, true ) | ( sym:: unwrap_or_else, false ) => sym:: unwrap_or_default,
70
- ( sym:: or_insert, true ) | ( sym:: or_insert_with, false ) => sym:: or_default,
71
- _ => return false ,
72
- } ;
73
-
74
- let receiver_ty = cx. typeck_results ( ) . expr_ty_adjusted ( receiver) . peel_refs ( ) ;
75
- let Some ( suggested_method_def_id) = receiver_ty. ty_adt_def ( ) . and_then ( |adt_def| {
76
- cx. tcx
77
- . inherent_impls ( adt_def. did ( ) )
78
- . iter ( )
79
- . flat_map ( |impl_id| cx. tcx . associated_items ( impl_id) . filter_by_name_unhygienic ( sugg) )
80
- . find_map ( |assoc| {
81
- if assoc. is_method ( ) && cx. tcx . fn_sig ( assoc. def_id ) . skip_binder ( ) . inputs ( ) . skip_binder ( ) . len ( ) == 1
82
- {
83
- Some ( assoc. def_id )
84
- } else {
85
- None
86
- }
87
- } )
88
- } ) else {
89
- return false ;
90
- } ;
91
- let in_sugg_method_implementation = {
92
- matches ! (
93
- suggested_method_def_id. as_local( ) ,
94
- Some ( local_def_id) if local_def_id == cx. tcx. hir_get_parent_item( receiver. hir_id) . def_id
95
- )
96
- } ;
97
- if in_sugg_method_implementation {
98
- return false ;
99
- }
100
-
101
- // `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. And if the expression is a
102
- // non-empty `Vec`, then it will not be a default value anyway. Bail out in all cases.
103
- if call_expr. and_then ( |call_expr| VecArgs :: hir ( cx, call_expr) ) . is_some ( ) {
104
- return false ;
105
- }
106
-
107
- // needs to target Default::default in particular or be *::new and have a Default impl
108
- // available
109
- if ( is_new ( fun) && output_type_implements_default ( fun) )
110
- || match call_expr {
111
- Some ( call_expr) => is_default_equivalent ( cx, call_expr) ,
112
- None => is_default_equivalent_call ( cx, fun, None ) || closure_body_returns_empty_to_string ( cx, fun) ,
113
- }
114
- {
115
- span_lint_and_sugg (
116
- cx,
117
- UNWRAP_OR_DEFAULT ,
118
- method_span. with_hi ( span. hi ( ) ) ,
119
- format ! ( "use of `{name}` to construct default value" ) ,
120
- "try" ,
121
- format ! ( "{sugg}()" ) ,
122
- Applicability :: MachineApplicable ,
123
- ) ;
124
-
125
- true
126
- } else {
127
- false
128
- }
129
- }
130
-
131
- /// Checks for `*or(foo())`.
132
- #[ expect( clippy:: too_many_arguments) ]
133
- fn check_or_fn_call < ' tcx > (
134
- cx : & LateContext < ' tcx > ,
135
- name : Symbol ,
136
- method_span : Span ,
137
- self_expr : & hir:: Expr < ' _ > ,
138
- arg : & ' tcx hir:: Expr < ' _ > ,
139
- // `Some` if fn has second argument
140
- second_arg : Option < & hir:: Expr < ' _ > > ,
141
- span : Span ,
142
- // None if lambda is required
143
- fun_span : Option < Span > ,
144
- ) -> bool {
145
- // (path, fn_has_argument, methods, suffix)
146
- const KNOW_TYPES : [ ( Symbol , bool , & [ Symbol ] , & str ) ; 7 ] = [
147
- ( sym:: BTreeEntry , false , & [ sym:: or_insert] , "with" ) ,
148
- ( sym:: HashMapEntry , false , & [ sym:: or_insert] , "with" ) ,
149
- (
150
- sym:: Option ,
151
- false ,
152
- & [ sym:: map_or, sym:: ok_or, sym:: or, sym:: unwrap_or] ,
153
- "else" ,
154
- ) ,
155
- ( sym:: Option , false , & [ sym:: get_or_insert] , "with" ) ,
156
- ( sym:: Option , true , & [ sym:: and] , "then" ) ,
157
- ( sym:: Result , true , & [ sym:: map_or, sym:: or, sym:: unwrap_or] , "else" ) ,
158
- ( sym:: Result , true , & [ sym:: and] , "then" ) ,
159
- ] ;
160
-
161
- if KNOW_TYPES . iter ( ) . any ( |k| k. 2 . contains ( & name) )
162
- && switch_to_lazy_eval ( cx, arg)
163
- && !contains_return ( arg)
164
- && let self_ty = cx. typeck_results ( ) . expr_ty ( self_expr)
165
- && let Some ( & ( _, fn_has_arguments, _, suffix) ) = KNOW_TYPES
166
- . iter ( )
167
- . find ( |& & i| is_type_diagnostic_item ( cx, self_ty, i. 0 ) && i. 2 . contains ( & name) )
168
- {
169
- let ctxt = span. ctxt ( ) ;
170
- let mut app = Applicability :: HasPlaceholders ;
171
- let sugg = {
172
- let ( snippet_span, use_lambda) = match ( fn_has_arguments, fun_span) {
173
- ( false , Some ( fun_span) ) => ( fun_span, false ) ,
174
- _ => ( arg. span , true ) ,
175
- } ;
176
-
177
- let snip = snippet_with_context ( cx, snippet_span, ctxt, ".." , & mut app) . 0 ;
178
- let snip = if use_lambda {
179
- let l_arg = if fn_has_arguments { "_" } else { "" } ;
180
- format ! ( "|{l_arg}| {snip}" )
181
- } else {
182
- snip. into_owned ( )
183
- } ;
184
-
185
- if let Some ( f) = second_arg {
186
- let f = snippet_with_context ( cx, f. span , ctxt, ".." , & mut app) . 0 ;
187
- format ! ( "{snip}, {f}" )
188
- } else {
189
- snip
190
- }
191
- } ;
192
- let span_replace_word = method_span. with_hi ( span. hi ( ) ) ;
193
- span_lint_and_sugg (
194
- cx,
195
- OR_FUN_CALL ,
196
- span_replace_word,
197
- format ! ( "function call inside of `{name}`" ) ,
198
- "try" ,
199
- format ! ( "{name}_{suffix}({sugg})" ) ,
200
- app,
201
- ) ;
202
- true
203
- } else {
204
- false
205
- }
206
- }
207
-
208
29
if let [ arg] = args {
209
30
let inner_arg = peel_blocks ( arg) ;
210
31
for_each_expr ( cx, inner_arg, |ex| {
@@ -272,6 +93,183 @@ pub(super) fn check<'tcx>(
272
93
}
273
94
}
274
95
96
+ /// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`,
97
+ /// `or_insert(T::new())` or `or_insert(T::default())`.
98
+ /// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`,
99
+ /// `or_insert_with(T::new)` or `or_insert_with(T::default)`.
100
+ fn check_unwrap_or_default (
101
+ cx : & LateContext < ' _ > ,
102
+ name : Symbol ,
103
+ receiver : & hir:: Expr < ' _ > ,
104
+ fun : & hir:: Expr < ' _ > ,
105
+ call_expr : Option < & hir:: Expr < ' _ > > ,
106
+ span : Span ,
107
+ method_span : Span ,
108
+ ) -> bool {
109
+ if !expr_type_is_certain ( cx, receiver) {
110
+ return false ;
111
+ }
112
+
113
+ let is_new = |fun : & hir:: Expr < ' _ > | {
114
+ if let hir:: ExprKind :: Path ( ref qpath) = fun. kind {
115
+ let path = last_path_segment ( qpath) . ident . name ;
116
+ matches ! ( path, sym:: new)
117
+ } else {
118
+ false
119
+ }
120
+ } ;
121
+
122
+ let output_type_implements_default = |fun| {
123
+ let fun_ty = cx. typeck_results ( ) . expr_ty ( fun) ;
124
+ if let ty:: FnDef ( def_id, args) = fun_ty. kind ( ) {
125
+ let output_ty = cx. tcx . fn_sig ( def_id) . instantiate ( cx. tcx , args) . skip_binder ( ) . output ( ) ;
126
+ cx. tcx
127
+ . get_diagnostic_item ( sym:: Default )
128
+ . is_some_and ( |default_trait_id| implements_trait ( cx, output_ty, default_trait_id, & [ ] ) )
129
+ } else {
130
+ false
131
+ }
132
+ } ;
133
+
134
+ let sugg = match ( name, call_expr. is_some ( ) ) {
135
+ ( sym:: unwrap_or, true ) | ( sym:: unwrap_or_else, false ) => sym:: unwrap_or_default,
136
+ ( sym:: or_insert, true ) | ( sym:: or_insert_with, false ) => sym:: or_default,
137
+ _ => return false ,
138
+ } ;
139
+
140
+ let receiver_ty = cx. typeck_results ( ) . expr_ty_adjusted ( receiver) . peel_refs ( ) ;
141
+ let Some ( suggested_method_def_id) = receiver_ty. ty_adt_def ( ) . and_then ( |adt_def| {
142
+ cx. tcx
143
+ . inherent_impls ( adt_def. did ( ) )
144
+ . iter ( )
145
+ . flat_map ( |impl_id| cx. tcx . associated_items ( impl_id) . filter_by_name_unhygienic ( sugg) )
146
+ . find_map ( |assoc| {
147
+ if assoc. is_method ( ) && cx. tcx . fn_sig ( assoc. def_id ) . skip_binder ( ) . inputs ( ) . skip_binder ( ) . len ( ) == 1 {
148
+ Some ( assoc. def_id )
149
+ } else {
150
+ None
151
+ }
152
+ } )
153
+ } ) else {
154
+ return false ;
155
+ } ;
156
+ let in_sugg_method_implementation = {
157
+ matches ! (
158
+ suggested_method_def_id. as_local( ) ,
159
+ Some ( local_def_id) if local_def_id == cx. tcx. hir_get_parent_item( receiver. hir_id) . def_id
160
+ )
161
+ } ;
162
+ if in_sugg_method_implementation {
163
+ return false ;
164
+ }
165
+
166
+ // `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. And if the expression is a
167
+ // non-empty `Vec`, then it will not be a default value anyway. Bail out in all cases.
168
+ if call_expr. and_then ( |call_expr| VecArgs :: hir ( cx, call_expr) ) . is_some ( ) {
169
+ return false ;
170
+ }
171
+
172
+ // needs to target Default::default in particular or be *::new and have a Default impl
173
+ // available
174
+ if ( is_new ( fun) && output_type_implements_default ( fun) )
175
+ || match call_expr {
176
+ Some ( call_expr) => is_default_equivalent ( cx, call_expr) ,
177
+ None => is_default_equivalent_call ( cx, fun, None ) || closure_body_returns_empty_to_string ( cx, fun) ,
178
+ }
179
+ {
180
+ span_lint_and_sugg (
181
+ cx,
182
+ UNWRAP_OR_DEFAULT ,
183
+ method_span. with_hi ( span. hi ( ) ) ,
184
+ format ! ( "use of `{name}` to construct default value" ) ,
185
+ "try" ,
186
+ format ! ( "{sugg}()" ) ,
187
+ Applicability :: MachineApplicable ,
188
+ ) ;
189
+
190
+ true
191
+ } else {
192
+ false
193
+ }
194
+ }
195
+
196
+ /// Checks for `*or(foo())`.
197
+ #[ expect( clippy:: too_many_arguments) ]
198
+ fn check_or_fn_call < ' tcx > (
199
+ cx : & LateContext < ' tcx > ,
200
+ name : Symbol ,
201
+ method_span : Span ,
202
+ self_expr : & hir:: Expr < ' _ > ,
203
+ arg : & ' tcx hir:: Expr < ' _ > ,
204
+ // `Some` if fn has second argument
205
+ second_arg : Option < & hir:: Expr < ' _ > > ,
206
+ span : Span ,
207
+ // None if lambda is required
208
+ fun_span : Option < Span > ,
209
+ ) -> bool {
210
+ // (path, fn_has_argument, methods, suffix)
211
+ const KNOW_TYPES : [ ( Symbol , bool , & [ Symbol ] , & str ) ; 7 ] = [
212
+ ( sym:: BTreeEntry , false , & [ sym:: or_insert] , "with" ) ,
213
+ ( sym:: HashMapEntry , false , & [ sym:: or_insert] , "with" ) ,
214
+ (
215
+ sym:: Option ,
216
+ false ,
217
+ & [ sym:: map_or, sym:: ok_or, sym:: or, sym:: unwrap_or] ,
218
+ "else" ,
219
+ ) ,
220
+ ( sym:: Option , false , & [ sym:: get_or_insert] , "with" ) ,
221
+ ( sym:: Option , true , & [ sym:: and] , "then" ) ,
222
+ ( sym:: Result , true , & [ sym:: map_or, sym:: or, sym:: unwrap_or] , "else" ) ,
223
+ ( sym:: Result , true , & [ sym:: and] , "then" ) ,
224
+ ] ;
225
+
226
+ if KNOW_TYPES . iter ( ) . any ( |k| k. 2 . contains ( & name) )
227
+ && switch_to_lazy_eval ( cx, arg)
228
+ && !contains_return ( arg)
229
+ && let self_ty = cx. typeck_results ( ) . expr_ty ( self_expr)
230
+ && let Some ( & ( _, fn_has_arguments, _, suffix) ) = KNOW_TYPES
231
+ . iter ( )
232
+ . find ( |& & i| is_type_diagnostic_item ( cx, self_ty, i. 0 ) && i. 2 . contains ( & name) )
233
+ {
234
+ let ctxt = span. ctxt ( ) ;
235
+ let mut app = Applicability :: HasPlaceholders ;
236
+ let sugg = {
237
+ let ( snippet_span, use_lambda) = match ( fn_has_arguments, fun_span) {
238
+ ( false , Some ( fun_span) ) => ( fun_span, false ) ,
239
+ _ => ( arg. span , true ) ,
240
+ } ;
241
+
242
+ let snip = snippet_with_context ( cx, snippet_span, ctxt, ".." , & mut app) . 0 ;
243
+ let snip = if use_lambda {
244
+ let l_arg = if fn_has_arguments { "_" } else { "" } ;
245
+ format ! ( "|{l_arg}| {snip}" )
246
+ } else {
247
+ snip. into_owned ( )
248
+ } ;
249
+
250
+ if let Some ( f) = second_arg {
251
+ let f = snippet_with_context ( cx, f. span , ctxt, ".." , & mut app) . 0 ;
252
+ format ! ( "{snip}, {f}" )
253
+ } else {
254
+ snip
255
+ }
256
+ } ;
257
+ let span_replace_word = method_span. with_hi ( span. hi ( ) ) ;
258
+ span_lint_and_sugg (
259
+ cx,
260
+ OR_FUN_CALL ,
261
+ span_replace_word,
262
+ format ! ( "function call inside of `{name}`" ) ,
263
+ "try" ,
264
+ format ! ( "{name}_{suffix}({sugg})" ) ,
265
+ app,
266
+ ) ;
267
+ true
268
+ } else {
269
+ false
270
+ }
271
+ }
272
+
275
273
fn closure_body_returns_empty_to_string ( cx : & LateContext < ' _ > , e : & hir:: Expr < ' _ > ) -> bool {
276
274
if let hir:: ExprKind :: Closure ( & hir:: Closure { body, .. } ) = e. kind {
277
275
let body = cx. tcx . hir_body ( body) ;
0 commit comments