1+ use super :: implicit_clone:: is_clone_like;
12use clippy_utils:: diagnostics:: span_lint_and_sugg;
23use clippy_utils:: source:: snippet_opt;
34use clippy_utils:: ty:: { implements_trait, is_copy, peel_mid_ty_refs} ;
4- use clippy_utils:: { get_parent_expr, match_def_path , paths } ;
5+ use clippy_utils:: { get_parent_expr, is_diag_item_method , is_diag_trait_item } ;
56use rustc_errors:: Applicability ;
67use rustc_hir:: { def_id:: DefId , BorrowKind , Expr , ExprKind } ;
78use rustc_lint:: LateContext ;
@@ -14,28 +15,17 @@ use std::cmp::max;
1415
1516use super :: UNNECESSARY_TO_OWNED ;
1617
17- const TO_OWNED_LIKE_PATHS : & [ & [ & str ] ] = & [
18- & paths:: COW_INTO_OWNED ,
19- & paths:: OS_STR_TO_OS_STRING ,
20- & paths:: PATH_TO_PATH_BUF ,
21- & paths:: SLICE_TO_VEC ,
22- & paths:: TO_OWNED_METHOD ,
23- & paths:: TO_STRING_METHOD ,
24- ] ;
25-
2618pub fn check ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > , method_name : Symbol , args : & ' tcx [ Expr < ' tcx > ] ) {
2719 if_chain ! {
2820 if let Some ( method_def_id) = cx. typeck_results( ) . type_dependent_def_id( expr. hir_id) ;
29- if TO_OWNED_LIKE_PATHS
30- . iter( )
31- . any( |path| match_def_path( cx, method_def_id, path) ) ;
21+ if is_to_owned_like( cx, method_name, method_def_id) ;
3222 if let [ receiver] = args;
3323 then {
3424 // At this point, we know the call is of a `to_owned`-like function. The functions
3525 // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary
3626 // based on its context, that is, whether it is a referent in an `AddrOf` expression or
3727 // an argument in a function call.
38- if check_addr_of_expr( cx, expr, method_name, receiver) {
28+ if check_addr_of_expr( cx, expr, method_name, method_def_id , receiver) {
3929 return ;
4030 }
4131 check_call_arg( cx, expr, method_name, receiver) ;
@@ -45,10 +35,12 @@ pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol
4535
4636/// Checks whether `expr` is a referent in an `AddrOf` expression and, if so, determines whether its
4737/// call of a `to_owned`-like function is unnecessary.
38+ #[ allow( clippy:: too_many_lines) ]
4839fn check_addr_of_expr (
4940 cx : & LateContext < ' tcx > ,
5041 expr : & ' tcx Expr < ' tcx > ,
5142 method_name : Symbol ,
43+ method_def_id : DefId ,
5244 receiver : & ' tcx Expr < ' tcx > ,
5345) -> bool {
5446 if_chain ! {
@@ -100,14 +92,17 @@ fn check_addr_of_expr(
10092 ] => Some ( target_ty) ,
10193 _ => None ,
10294 } ;
95+ let receiver_ty = cx. typeck_results( ) . expr_ty( receiver) ;
96+ // Only flag cases where the receiver is copyable or the method is `Cow::into_owned`. This
97+ // restriction is to ensure there is not overlap between `redundant_clone` and this lint.
98+ if is_copy( cx, receiver_ty) || is_cow_into_owned( cx, method_name, method_def_id) ;
99+ if let Some ( receiver_snippet) = snippet_opt( cx, receiver. span) ;
103100 then {
104101 let ( target_ty, n_target_refs) = peel_mid_ty_refs( target_ty) ;
105- let receiver_ty = cx. typeck_results( ) . expr_ty( receiver) ;
106102 let ( receiver_ty, n_receiver_refs) = peel_mid_ty_refs( receiver_ty) ;
107103 if_chain! {
108104 if receiver_ty == target_ty;
109105 if n_target_refs >= n_receiver_refs;
110- if let Some ( receiver_snippet) = snippet_opt( cx, receiver. span) ;
111106 then {
112107 span_lint_and_sugg(
113108 cx,
@@ -122,21 +117,32 @@ fn check_addr_of_expr(
122117 }
123118 }
124119 if implements_deref_trait( cx, receiver_ty, target_ty) {
125- span_lint_and_sugg(
126- cx,
127- UNNECESSARY_TO_OWNED ,
128- expr. span. with_lo( receiver. span. hi( ) ) ,
129- & format!( "unnecessary use of `{}`" , method_name) ,
130- "remove this" ,
131- String :: new( ) ,
132- Applicability :: MachineApplicable ,
133- ) ;
120+ if n_receiver_refs > 0 {
121+ span_lint_and_sugg(
122+ cx,
123+ UNNECESSARY_TO_OWNED ,
124+ parent. span,
125+ & format!( "unnecessary use of `{}`" , method_name) ,
126+ "use" ,
127+ receiver_snippet,
128+ Applicability :: MachineApplicable ,
129+ ) ;
130+ } else {
131+ span_lint_and_sugg(
132+ cx,
133+ UNNECESSARY_TO_OWNED ,
134+ expr. span. with_lo( receiver. span. hi( ) ) ,
135+ & format!( "unnecessary use of `{}`" , method_name) ,
136+ "remove this" ,
137+ String :: new( ) ,
138+ Applicability :: MachineApplicable ,
139+ ) ;
140+ }
134141 return true ;
135142 }
136143 if_chain! {
137144 if let Some ( as_ref_trait_id) = cx. tcx. get_diagnostic_item( sym:: AsRef ) ;
138145 if implements_trait( cx, receiver_ty, as_ref_trait_id, & [ GenericArg :: from( target_ty) ] ) ;
139- if let Some ( receiver_snippet) = snippet_opt( cx, receiver. span) ;
140146 then {
141147 span_lint_and_sugg(
142148 cx,
@@ -326,3 +332,21 @@ fn implements_deref_trait(cx: &LateContext<'tcx>, ty: Ty<'tcx>, deref_target_ty:
326332 }
327333 }
328334}
335+
336+ /// Returns true if the named method can be used to convert the receiver to its "owned"
337+ /// representation.
338+ fn is_to_owned_like ( cx : & LateContext < ' _ > , method_name : Symbol , method_def_id : DefId ) -> bool {
339+ is_clone_like ( cx, & * method_name. as_str ( ) , method_def_id)
340+ || is_cow_into_owned ( cx, method_name, method_def_id)
341+ || is_to_string ( cx, method_name, method_def_id)
342+ }
343+
344+ /// Returns true if the named method is `Cow::into_owned`.
345+ fn is_cow_into_owned ( cx : & LateContext < ' _ > , method_name : Symbol , method_def_id : DefId ) -> bool {
346+ method_name. as_str ( ) == "into_owned" && is_diag_item_method ( cx, method_def_id, sym:: Cow )
347+ }
348+
349+ /// Returns true if the named method is `ToString::to_string`.
350+ fn is_to_string ( cx : & LateContext < ' _ > , method_name : Symbol , method_def_id : DefId ) -> bool {
351+ method_name. as_str ( ) == "to_string" && is_diag_trait_item ( cx, method_def_id, sym:: ToString )
352+ }
0 commit comments