1
1
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
2
use clippy_utils:: eager_or_lazy:: switch_to_lazy_eval;
3
3
use clippy_utils:: source:: snippet_with_context;
4
- use clippy_utils:: ty:: { implements_trait, is_type_diagnostic_item} ;
5
- use clippy_utils:: { contains_return, is_trait_item , last_path_segment} ;
4
+ 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} ;
6
6
use if_chain:: if_chain;
7
7
use rustc_errors:: Applicability ;
8
- use rustc_hir as hir;
9
8
use rustc_lint:: LateContext ;
9
+ use rustc_middle:: ty;
10
10
use rustc_span:: source_map:: Span ;
11
- use rustc_span:: symbol:: { kw, sym, Symbol } ;
11
+ use rustc_span:: symbol:: { self , sym, Symbol } ;
12
+ use { rustc_ast as ast, rustc_hir as hir} ;
12
13
13
- use super :: OR_FUN_CALL ;
14
+ use super :: { OR_FUN_CALL , UNWRAP_OR_ELSE_DEFAULT } ;
14
15
15
16
/// Checks for the `OR_FUN_CALL` lint.
16
17
#[ allow( clippy:: too_many_lines) ]
@@ -24,44 +25,64 @@ pub(super) fn check<'tcx>(
24
25
) {
25
26
/// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`,
26
27
/// `or_insert(T::new())` or `or_insert(T::default())`.
28
+ /// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`,
29
+ /// `or_insert_with(T::new)` or `or_insert_with(T::default)`.
27
30
#[ allow( clippy:: too_many_arguments) ]
28
31
fn check_unwrap_or_default (
29
32
cx : & LateContext < ' _ > ,
30
33
name : & str ,
34
+ receiver : & hir:: Expr < ' _ > ,
31
35
fun : & hir:: Expr < ' _ > ,
32
- arg : & hir:: Expr < ' _ > ,
33
- or_has_args : bool ,
36
+ call_expr : Option < & hir:: Expr < ' _ > > ,
34
37
span : Span ,
35
38
method_span : Span ,
36
39
) -> bool {
37
- let is_default_default = || is_trait_item ( cx, fun, sym:: Default ) ;
40
+ if !expr_type_is_certain ( cx, receiver) {
41
+ return false ;
42
+ }
43
+
44
+ let is_new = |fun : & hir:: Expr < ' _ > | {
45
+ if let hir:: ExprKind :: Path ( ref qpath) = fun. kind {
46
+ let path = last_path_segment ( qpath) . ident . name ;
47
+ matches ! ( path, sym:: new)
48
+ } else {
49
+ false
50
+ }
51
+ } ;
38
52
39
- let implements_default = |arg, default_trait_id| {
40
- let arg_ty = cx. typeck_results ( ) . expr_ty ( arg) ;
41
- implements_trait ( cx, arg_ty, default_trait_id, & [ ] )
53
+ let output_type_implements_default = |fun| {
54
+ let fun_ty = cx. typeck_results ( ) . expr_ty ( fun) ;
55
+ if let ty:: FnDef ( def_id, substs) = fun_ty. kind ( ) {
56
+ let output_ty = cx. tcx . fn_sig ( def_id) . subst ( cx. tcx , substs) . skip_binder ( ) . output ( ) ;
57
+ cx. tcx
58
+ . get_diagnostic_item ( sym:: Default )
59
+ . map_or ( false , |default_trait_id| {
60
+ implements_trait ( cx, output_ty, default_trait_id, substs)
61
+ } )
62
+ } else {
63
+ false
64
+ }
42
65
} ;
43
66
44
67
if_chain ! {
45
- if !or_has_args;
46
- if let Some ( sugg) = match name {
47
- "unwrap_or" => Some ( "unwrap_or_default" ) ,
48
- "or_insert" => Some ( "or_default" ) ,
68
+ if let Some ( sugg) = match ( name, call_expr. is_some( ) ) {
69
+ ( "unwrap_or" , true ) | ( "unwrap_or_else" , false ) => Some ( "unwrap_or_default" ) ,
70
+ ( "or_insert" , true ) | ( "or_insert_with" , false ) => Some ( "or_default" ) ,
49
71
_ => None ,
50
72
} ;
51
- if let hir:: ExprKind :: Path ( ref qpath) = fun. kind;
52
- if let Some ( default_trait_id) = cx. tcx. get_diagnostic_item( sym:: Default ) ;
53
- let path = last_path_segment( qpath) . ident. name;
54
73
// needs to target Default::default in particular or be *::new and have a Default impl
55
74
// available
56
- if ( matches!( path, kw:: Default ) && is_default_default( ) )
57
- || ( matches!( path, sym:: new) && implements_default( arg, default_trait_id) ) ;
58
-
75
+ if ( is_new( fun) && output_type_implements_default( fun) )
76
+ || match call_expr {
77
+ Some ( call_expr) => is_default_equivalent( cx, call_expr) ,
78
+ None => is_default_equivalent_call( cx, fun) || closure_body_returns_empty_to_string( cx, fun) ,
79
+ } ;
59
80
then {
60
81
span_lint_and_sugg(
61
82
cx,
62
- OR_FUN_CALL ,
83
+ UNWRAP_OR_ELSE_DEFAULT ,
63
84
method_span. with_hi( span. hi( ) ) ,
64
- & format!( "use of `{name}` followed by a call to `{path}` " ) ,
85
+ & format!( "use of `{name}` to construct default value " ) ,
65
86
"try" ,
66
87
format!( "{sugg}()" ) ,
67
88
Applicability :: MachineApplicable ,
@@ -168,11 +189,16 @@ pub(super) fn check<'tcx>(
168
189
match inner_arg. kind {
169
190
hir:: ExprKind :: Call ( fun, or_args) => {
170
191
let or_has_args = !or_args. is_empty ( ) ;
171
- if !check_unwrap_or_default ( cx, name, fun, arg, or_has_args, expr. span , method_span) {
192
+ if or_has_args
193
+ || !check_unwrap_or_default ( cx, name, receiver, fun, Some ( inner_arg) , expr. span , method_span)
194
+ {
172
195
let fun_span = if or_has_args { None } else { Some ( fun. span ) } ;
173
196
check_general_case ( cx, name, method_span, receiver, arg, None , expr. span , fun_span) ;
174
197
}
175
198
} ,
199
+ hir:: ExprKind :: Path ( ..) | hir:: ExprKind :: Closure ( ..) => {
200
+ check_unwrap_or_default ( cx, name, receiver, inner_arg, None , expr. span , method_span) ;
201
+ } ,
176
202
hir:: ExprKind :: Index ( ..) | hir:: ExprKind :: MethodCall ( ..) => {
177
203
check_general_case ( cx, name, method_span, receiver, arg, None , expr. span , None ) ;
178
204
} ,
@@ -189,3 +215,22 @@ pub(super) fn check<'tcx>(
189
215
}
190
216
}
191
217
}
218
+
219
+ fn closure_body_returns_empty_to_string ( cx : & LateContext < ' _ > , e : & hir:: Expr < ' _ > ) -> bool {
220
+ if let hir:: ExprKind :: Closure ( & hir:: Closure { body, .. } ) = e. kind {
221
+ let body = cx. tcx . hir ( ) . body ( body) ;
222
+
223
+ if body. params . is_empty ( )
224
+ && let hir:: Expr { kind, .. } = & body. value
225
+ && let hir:: ExprKind :: MethodCall ( hir:: PathSegment { ident, ..} , self_arg, _, _) = kind
226
+ && ident == & symbol:: Ident :: from_str ( "to_string" )
227
+ && let hir:: Expr { kind, .. } = self_arg
228
+ && let hir:: ExprKind :: Lit ( lit) = kind
229
+ && let ast:: LitKind :: Str ( symbol:: kw:: Empty , _) = lit. node
230
+ {
231
+ return true ;
232
+ }
233
+ }
234
+
235
+ false
236
+ }
0 commit comments