1
+ use super :: implicit_clone:: is_clone_like;
1
2
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
3
use clippy_utils:: source:: snippet_opt;
3
4
use 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 } ;
5
6
use rustc_errors:: Applicability ;
6
7
use rustc_hir:: { def_id:: DefId , BorrowKind , Expr , ExprKind } ;
7
8
use rustc_lint:: LateContext ;
@@ -14,28 +15,17 @@ use std::cmp::max;
14
15
15
16
use super :: UNNECESSARY_TO_OWNED ;
16
17
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
-
26
18
pub fn check ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > , method_name : Symbol , args : & ' tcx [ Expr < ' tcx > ] ) {
27
19
if_chain ! {
28
20
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) ;
32
22
if let [ receiver] = args;
33
23
then {
34
24
// At this point, we know the call is of a `to_owned`-like function. The functions
35
25
// `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary
36
26
// based on its context, that is, whether it is a referent in an `AddrOf` expression or
37
27
// 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) {
39
29
return ;
40
30
}
41
31
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
45
35
46
36
/// Checks whether `expr` is a referent in an `AddrOf` expression and, if so, determines whether its
47
37
/// call of a `to_owned`-like function is unnecessary.
38
+ #[ allow( clippy:: too_many_lines) ]
48
39
fn check_addr_of_expr (
49
40
cx : & LateContext < ' tcx > ,
50
41
expr : & ' tcx Expr < ' tcx > ,
51
42
method_name : Symbol ,
43
+ method_def_id : DefId ,
52
44
receiver : & ' tcx Expr < ' tcx > ,
53
45
) -> bool {
54
46
if_chain ! {
@@ -100,14 +92,17 @@ fn check_addr_of_expr(
100
92
] => Some ( target_ty) ,
101
93
_ => None ,
102
94
} ;
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) ;
103
100
then {
104
101
let ( target_ty, n_target_refs) = peel_mid_ty_refs( target_ty) ;
105
- let receiver_ty = cx. typeck_results( ) . expr_ty( receiver) ;
106
102
let ( receiver_ty, n_receiver_refs) = peel_mid_ty_refs( receiver_ty) ;
107
103
if_chain! {
108
104
if receiver_ty == target_ty;
109
105
if n_target_refs >= n_receiver_refs;
110
- if let Some ( receiver_snippet) = snippet_opt( cx, receiver. span) ;
111
106
then {
112
107
span_lint_and_sugg(
113
108
cx,
@@ -122,21 +117,32 @@ fn check_addr_of_expr(
122
117
}
123
118
}
124
119
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
+ }
134
141
return true ;
135
142
}
136
143
if_chain! {
137
144
if let Some ( as_ref_trait_id) = cx. tcx. get_diagnostic_item( sym:: AsRef ) ;
138
145
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) ;
140
146
then {
141
147
span_lint_and_sugg(
142
148
cx,
@@ -326,3 +332,21 @@ fn implements_deref_trait(cx: &LateContext<'tcx>, ty: Ty<'tcx>, deref_target_ty:
326
332
}
327
333
}
328
334
}
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