11use clippy_utils:: diagnostics:: span_lint_and_sugg;
22use clippy_utils:: eager_or_lazy:: switch_to_lazy_eval;
33use 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} ;
66use if_chain:: if_chain;
77use rustc_errors:: Applicability ;
8- use rustc_hir as hir;
98use rustc_lint:: LateContext ;
9+ use rustc_middle:: ty;
1010use 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} ;
1213
13- use super :: OR_FUN_CALL ;
14+ use super :: { OR_FUN_CALL , UNWRAP_OR_ELSE_DEFAULT } ;
1415
1516/// Checks for the `OR_FUN_CALL` lint.
1617#[ allow( clippy:: too_many_lines) ]
@@ -24,44 +25,64 @@ pub(super) fn check<'tcx>(
2425) {
2526 /// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`,
2627 /// `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)`.
2730 #[ allow( clippy:: too_many_arguments) ]
2831 fn check_unwrap_or_default (
2932 cx : & LateContext < ' _ > ,
3033 name : & str ,
34+ receiver : & hir:: Expr < ' _ > ,
3135 fun : & hir:: Expr < ' _ > ,
32- arg : & hir:: Expr < ' _ > ,
33- or_has_args : bool ,
36+ call_expr : Option < & hir:: Expr < ' _ > > ,
3437 span : Span ,
3538 method_span : Span ,
3639 ) -> 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+ } ;
3852
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+ }
4265 } ;
4366
4467 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" ) ,
4971 _ => None ,
5072 } ;
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;
5473 // needs to target Default::default in particular or be *::new and have a Default impl
5574 // 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+ } ;
5980 then {
6081 span_lint_and_sugg(
6182 cx,
62- OR_FUN_CALL ,
83+ UNWRAP_OR_ELSE_DEFAULT ,
6384 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 " ) ,
6586 "try" ,
6687 format!( "{sugg}()" ) ,
6788 Applicability :: MachineApplicable ,
@@ -168,11 +189,16 @@ pub(super) fn check<'tcx>(
168189 match inner_arg. kind {
169190 hir:: ExprKind :: Call ( fun, or_args) => {
170191 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+ {
172195 let fun_span = if or_has_args { None } else { Some ( fun. span ) } ;
173196 check_general_case ( cx, name, method_span, receiver, arg, None , expr. span , fun_span) ;
174197 }
175198 } ,
199+ hir:: ExprKind :: Path ( ..) | hir:: ExprKind :: Closure ( ..) => {
200+ check_unwrap_or_default ( cx, name, receiver, inner_arg, None , expr. span , method_span) ;
201+ } ,
176202 hir:: ExprKind :: Index ( ..) | hir:: ExprKind :: MethodCall ( ..) => {
177203 check_general_case ( cx, name, method_span, receiver, arg, None , expr. span , None ) ;
178204 } ,
@@ -189,3 +215,22 @@ pub(super) fn check<'tcx>(
189215 }
190216 }
191217}
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