1
1
use crate :: methods:: SelfKind ;
2
2
use clippy_utils:: diagnostics:: span_lint_and_help;
3
+ use clippy_utils:: ty:: is_copy;
3
4
use rustc_lint:: LateContext ;
4
5
use rustc_middle:: ty:: TyS ;
5
6
use rustc_span:: source_map:: Span ;
@@ -9,43 +10,58 @@ use super::WRONG_PUB_SELF_CONVENTION;
9
10
use super :: WRONG_SELF_CONVENTION ;
10
11
11
12
#[ rustfmt:: skip]
12
- const CONVENTIONS : [ ( & [ Convention ] , & [ SelfKind ] ) ; 8 ] = [
13
+ const CONVENTIONS : [ ( & [ Convention ] , & [ SelfKind ] ) ; 9 ] = [
13
14
( & [ Convention :: Eq ( "new" ) ] , & [ SelfKind :: No ] ) ,
14
15
( & [ Convention :: StartsWith ( "as_" ) ] , & [ SelfKind :: Ref , SelfKind :: RefMut ] ) ,
15
16
( & [ Convention :: StartsWith ( "from_" ) ] , & [ SelfKind :: No ] ) ,
16
17
( & [ Convention :: StartsWith ( "into_" ) ] , & [ SelfKind :: Value ] ) ,
17
18
( & [ Convention :: StartsWith ( "is_" ) ] , & [ SelfKind :: Ref , SelfKind :: No ] ) ,
18
19
( & [ Convention :: Eq ( "to_mut" ) ] , & [ SelfKind :: RefMut ] ) ,
19
20
( & [ Convention :: StartsWith ( "to_" ) , Convention :: EndsWith ( "_mut" ) ] , & [ SelfKind :: RefMut ] ) ,
20
- ( & [ Convention :: StartsWith ( "to_" ) , Convention :: NotEndsWith ( "_mut" ) ] , & [ SelfKind :: Ref ] ) ,
21
+
22
+ // Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types).
23
+ // Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
24
+ ( & [ Convention :: StartsWith ( "to_" ) , Convention :: NotEndsWith ( "_mut" ) , Convention :: IsSelfTypeCopy ( false ) , Convention :: ImplementsTrait ( false ) ] , & [ SelfKind :: Ref ] ) ,
25
+ ( & [ Convention :: StartsWith ( "to_" ) , Convention :: NotEndsWith ( "_mut" ) , Convention :: IsSelfTypeCopy ( true ) , Convention :: ImplementsTrait ( false ) ] , & [ SelfKind :: Value ] ) ,
21
26
] ;
22
27
23
28
enum Convention {
24
29
Eq ( & ' static str ) ,
25
30
StartsWith ( & ' static str ) ,
26
31
EndsWith ( & ' static str ) ,
27
32
NotEndsWith ( & ' static str ) ,
33
+ IsSelfTypeCopy ( bool ) ,
34
+ ImplementsTrait ( bool ) ,
28
35
}
29
36
30
37
impl Convention {
31
38
#[ must_use]
32
- fn check ( & self , other : & str ) -> bool {
39
+ fn check < ' tcx > ( & self , cx : & LateContext < ' tcx > , self_ty : & ' tcx TyS < ' tcx > , other : & str , is_trait_def : bool ) -> bool {
33
40
match * self {
34
41
Self :: Eq ( this) => this == other,
35
42
Self :: StartsWith ( this) => other. starts_with ( this) && this != other,
36
43
Self :: EndsWith ( this) => other. ends_with ( this) && this != other,
37
- Self :: NotEndsWith ( this) => !Self :: EndsWith ( this) . check ( other) ,
44
+ Self :: NotEndsWith ( this) => !Self :: EndsWith ( this) . check ( cx, self_ty, other, is_trait_def) ,
45
+ Self :: IsSelfTypeCopy ( is_true) => is_true == is_copy ( cx, self_ty) ,
46
+ Self :: ImplementsTrait ( is_true) => is_true == is_trait_def,
38
47
}
39
48
}
40
49
}
41
50
42
51
impl fmt:: Display for Convention {
43
52
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> Result < ( ) , fmt:: Error > {
44
53
match * self {
45
- Self :: Eq ( this) => this. fmt ( f) ,
46
- Self :: StartsWith ( this) => this. fmt ( f) . and_then ( |_| '*' . fmt ( f) ) ,
47
- Self :: EndsWith ( this) => '*' . fmt ( f) . and_then ( |_| this. fmt ( f) ) ,
48
- Self :: NotEndsWith ( this) => '~' . fmt ( f) . and_then ( |_| this. fmt ( f) ) ,
54
+ Self :: Eq ( this) => format ! ( "`{}`" , this) . fmt ( f) ,
55
+ Self :: StartsWith ( this) => format ! ( "`{}*`" , this) . fmt ( f) ,
56
+ Self :: EndsWith ( this) => format ! ( "`*{}`" , this) . fmt ( f) ,
57
+ Self :: NotEndsWith ( this) => format ! ( "`~{}`" , this) . fmt ( f) ,
58
+ Self :: IsSelfTypeCopy ( is_true) => {
59
+ format ! ( "`self` type is{} `Copy`" , if is_true { "" } else { " not" } ) . fmt ( f)
60
+ } ,
61
+ Self :: ImplementsTrait ( is_true) => {
62
+ let ( negation, s_suffix) = if is_true { ( "" , "s" ) } else { ( " does not" , "" ) } ;
63
+ format ! ( "Method{} implement{} a trait" , negation, s_suffix) . fmt ( f)
64
+ } ,
49
65
}
50
66
}
51
67
}
@@ -57,47 +73,44 @@ pub(super) fn check<'tcx>(
57
73
self_ty : & ' tcx TyS < ' tcx > ,
58
74
first_arg_ty : & ' tcx TyS < ' tcx > ,
59
75
first_arg_span : Span ,
76
+ is_trait_item : bool ,
60
77
) {
61
78
let lint = if is_pub {
62
79
WRONG_PUB_SELF_CONVENTION
63
80
} else {
64
81
WRONG_SELF_CONVENTION
65
82
} ;
66
- if let Some ( ( conventions, self_kinds) ) = & CONVENTIONS
67
- . iter ( )
68
- . find ( |( convs, _) | convs. iter ( ) . all ( |conv| conv. check ( item_name) ) )
69
- {
83
+ if let Some ( ( conventions, self_kinds) ) = & CONVENTIONS . iter ( ) . find ( |( convs, _) | {
84
+ convs
85
+ . iter ( )
86
+ . all ( |conv| conv. check ( cx, self_ty, item_name, is_trait_item) )
87
+ } ) {
70
88
if !self_kinds. iter ( ) . any ( |k| k. matches ( cx, self_ty, first_arg_ty) ) {
71
89
let suggestion = {
72
90
if conventions. len ( ) > 1 {
73
- let special_case = {
74
- // Don't mention `NotEndsWith` when there is also `StartsWith` convention present
75
- if conventions. len ( ) == 2 {
76
- match conventions {
77
- [ Convention :: StartsWith ( starts_with) , Convention :: NotEndsWith ( _) ]
78
- | [ Convention :: NotEndsWith ( _) , Convention :: StartsWith ( starts_with) ] => {
79
- Some ( format ! ( "methods called `{}`" , Convention :: StartsWith ( starts_with) ) )
80
- } ,
81
- _ => None ,
82
- }
83
- } else {
84
- None
85
- }
86
- } ;
87
-
88
- if let Some ( suggestion) = special_case {
89
- suggestion
90
- } else {
91
- let s = conventions
91
+ // Don't mention `NotEndsWith` when there is also `StartsWith` convention present
92
+ let cut_ends_with_conv = conventions. iter ( ) . any ( |conv| matches ! ( conv, Convention :: StartsWith ( _) ) )
93
+ && conventions
92
94
. iter ( )
93
- . map ( |c| format ! ( "`{}`" , & c. to_string( ) ) )
94
- . collect :: < Vec < _ > > ( )
95
- . join ( " and " ) ;
95
+ . any ( |conv| matches ! ( conv, Convention :: NotEndsWith ( _) ) ) ;
96
+
97
+ let s = conventions
98
+ . iter ( )
99
+ . filter_map ( |conv| {
100
+ if ( cut_ends_with_conv && matches ! ( conv, Convention :: NotEndsWith ( _) ) )
101
+ || matches ! ( conv, Convention :: ImplementsTrait ( _) )
102
+ {
103
+ None
104
+ } else {
105
+ Some ( conv. to_string ( ) )
106
+ }
107
+ } )
108
+ . collect :: < Vec < _ > > ( )
109
+ . join ( " and " ) ;
96
110
97
- format ! ( "methods called like this: ({})" , & s)
98
- }
111
+ format ! ( "methods with the following characteristics: ({})" , & s)
99
112
} else {
100
- format ! ( "methods called `{}` " , & conventions[ 0 ] )
113
+ format ! ( "methods called {} " , & conventions[ 0 ] )
101
114
}
102
115
} ;
103
116
0 commit comments