11use clippy_utils:: diagnostics:: span_lint_and_sugg;
22use clippy_utils:: eager_or_lazy:: switch_to_lazy_eval;
33use clippy_utils:: source:: { snippet, snippet_with_macro_callsite} ;
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} ;
66use if_chain:: if_chain;
7+ use rustc_ast as ast;
78use rustc_errors:: Applicability ;
89use rustc_hir as hir;
910use rustc_lint:: LateContext ;
11+ use rustc_middle:: ty:: { self , EarlyBinder } ;
1012use rustc_span:: source_map:: Span ;
11- use rustc_span:: symbol:: { kw , sym, Symbol } ;
13+ use rustc_span:: symbol:: { self , sym, Symbol } ;
1214use std:: borrow:: Cow ;
1315
14- use super :: OR_FUN_CALL ;
16+ use super :: { OR_FUN_CALL , UNWRAP_OR_DEFAULT } ;
1517
1618/// Checks for the `OR_FUN_CALL` lint.
1719#[ allow( clippy:: too_many_lines) ]
@@ -25,45 +27,67 @@ pub(super) fn check<'tcx>(
2527) {
2628 /// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`,
2729 /// `or_insert(T::new())` or `or_insert(T::default())`.
30+ /// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`,
31+ /// `or_insert_with(T::new)` or `or_insert_with(T::default)`.
2832 #[ allow( clippy:: too_many_arguments) ]
2933 fn check_unwrap_or_default (
3034 cx : & LateContext < ' _ > ,
3135 name : & str ,
36+ receiver : & hir:: Expr < ' _ > ,
3237 fun : & hir:: Expr < ' _ > ,
33- arg : & hir:: Expr < ' _ > ,
34- or_has_args : bool ,
38+ call_expr : Option < & hir:: Expr < ' _ > > ,
3539 span : Span ,
3640 method_span : Span ,
3741 ) -> bool {
38- let is_default_default = || is_trait_item ( cx, fun, sym:: Default ) ;
42+ if !expr_type_is_certain ( cx, receiver) {
43+ return false ;
44+ }
45+
46+ let is_new = |fun : & hir:: Expr < ' _ > | {
47+ if let hir:: ExprKind :: Path ( ref qpath) = fun. kind {
48+ let path = last_path_segment ( qpath) . ident . name ;
49+ matches ! ( path, sym:: new)
50+ } else {
51+ false
52+ }
53+ } ;
3954
40- let implements_default = |arg, default_trait_id| {
41- let arg_ty = cx. typeck_results ( ) . expr_ty ( arg) ;
42- implements_trait ( cx, arg_ty, default_trait_id, & [ ] )
55+ let output_type_implements_default = |fun| {
56+ let fun_ty = cx. typeck_results ( ) . expr_ty ( fun) ;
57+ if let ty:: FnDef ( def_id, substs) = fun_ty. kind ( ) {
58+ let output_ty = EarlyBinder ( cx. tcx . fn_sig ( def_id) . output ( ) )
59+ . subst ( cx. tcx , substs)
60+ . skip_binder ( ) ;
61+ cx. tcx
62+ . get_diagnostic_item ( sym:: Default )
63+ . map_or ( false , |default_trait_id| {
64+ implements_trait ( cx, output_ty, default_trait_id, substs)
65+ } )
66+ } else {
67+ false
68+ }
4369 } ;
4470
4571 if_chain ! {
46- if !or_has_args;
47- if let Some ( sugg) = match name {
48- "unwrap_or" => Some ( "unwrap_or_default" ) ,
49- "or_insert" => Some ( "or_default" ) ,
72+ if let Some ( sugg) = match ( name, call_expr. is_some( ) ) {
73+ ( "unwrap_or" , true ) | ( "unwrap_or_else" , false ) => Some ( "unwrap_or_default" ) ,
74+ ( "or_insert" , true ) | ( "or_insert_with" , false ) => Some ( "or_default" ) ,
5075 _ => None ,
5176 } ;
52- if let hir:: ExprKind :: Path ( ref qpath) = fun. kind;
53- if let Some ( default_trait_id) = cx. tcx. get_diagnostic_item( sym:: Default ) ;
54- let path = last_path_segment( qpath) . ident. name;
5577 // needs to target Default::default in particular or be *::new and have a Default impl
5678 // available
57- if ( matches!( path, kw:: Default ) && is_default_default( ) )
58- || ( matches!( path, sym:: new) && implements_default( arg, default_trait_id) ) ;
59-
79+ if ( is_new( fun) && output_type_implements_default( fun) )
80+ || match call_expr {
81+ Some ( call_expr) => is_default_equivalent( cx, call_expr) ,
82+ None => is_default_equivalent_call( cx, fun) || closure_body_returns_empty_to_string( cx, fun) ,
83+ } ;
6084 then {
6185 span_lint_and_sugg(
6286 cx,
63- OR_FUN_CALL ,
87+ UNWRAP_OR_DEFAULT ,
6488 method_span. with_hi( span. hi( ) ) ,
65- & format!( "use of `{name}` followed by a call to `{path}` " ) ,
66- "try this " ,
89+ & format!( "use of `{name}` to construct default value " ) ,
90+ "try" ,
6791 format!( "{sugg}()" ) ,
6892 Applicability :: MachineApplicable ,
6993 ) ;
@@ -153,7 +177,7 @@ pub(super) fn check<'tcx>(
153177 OR_FUN_CALL ,
154178 span_replace_word,
155179 & format!( "use of `{name}` followed by a function call" ) ,
156- "try this " ,
180+ "try" ,
157181 format!( "{name}_{suffix}({sugg})" ) ,
158182 Applicability :: HasPlaceholders ,
159183 ) ;
@@ -182,11 +206,16 @@ pub(super) fn check<'tcx>(
182206 match inner_arg. kind {
183207 hir:: ExprKind :: Call ( fun, or_args) => {
184208 let or_has_args = !or_args. is_empty ( ) ;
185- if !check_unwrap_or_default ( cx, name, fun, arg, or_has_args, expr. span , method_span) {
209+ if or_has_args
210+ || !check_unwrap_or_default ( cx, name, receiver, fun, Some ( inner_arg) , expr. span , method_span)
211+ {
186212 let fun_span = if or_has_args { None } else { Some ( fun. span ) } ;
187213 check_general_case ( cx, name, method_span, receiver, arg, None , expr. span , fun_span) ;
188214 }
189215 } ,
216+ hir:: ExprKind :: Path ( ..) | hir:: ExprKind :: Closure ( ..) => {
217+ check_unwrap_or_default ( cx, name, receiver, inner_arg, None , expr. span , method_span) ;
218+ } ,
190219 hir:: ExprKind :: Index ( ..) | hir:: ExprKind :: MethodCall ( ..) => {
191220 check_general_case ( cx, name, method_span, receiver, arg, None , expr. span , None ) ;
192221 } ,
@@ -203,3 +232,22 @@ pub(super) fn check<'tcx>(
203232 }
204233 }
205234}
235+
236+ fn closure_body_returns_empty_to_string ( cx : & LateContext < ' _ > , e : & hir:: Expr < ' _ > ) -> bool {
237+ if let hir:: ExprKind :: Closure ( & hir:: Closure { body, .. } ) = e. kind {
238+ let body = cx. tcx . hir ( ) . body ( body) ;
239+
240+ if body. params . is_empty ( )
241+ && let hir:: Expr { kind, .. } = & body. value
242+ && let hir:: ExprKind :: MethodCall ( hir:: PathSegment { ident, ..} , self_arg, _, _) = kind
243+ && ident == & symbol:: Ident :: from_str ( "to_string" )
244+ && let hir:: Expr { kind, .. } = self_arg
245+ && let hir:: ExprKind :: Lit ( lit) = kind
246+ && let ast:: LitKind :: Str ( symbol:: kw:: Empty , _) = lit. node
247+ {
248+ return true ;
249+ }
250+ }
251+
252+ false
253+ }
0 commit comments