11use crate :: methods:: SelfKind ;
22use clippy_utils:: diagnostics:: span_lint_and_help;
3+ use clippy_utils:: ty:: is_copy;
34use rustc_lint:: LateContext ;
45use rustc_middle:: ty:: TyS ;
56use rustc_span:: source_map:: Span ;
@@ -9,43 +10,58 @@ use super::WRONG_PUB_SELF_CONVENTION;
910use super :: WRONG_SELF_CONVENTION ;
1011
1112#[ rustfmt:: skip]
12- const CONVENTIONS : [ ( & [ Convention ] , & [ SelfKind ] ) ; 8 ] = [
13+ const CONVENTIONS : [ ( & [ Convention ] , & [ SelfKind ] ) ; 9 ] = [
1314 ( & [ Convention :: Eq ( "new" ) ] , & [ SelfKind :: No ] ) ,
1415 ( & [ Convention :: StartsWith ( "as_" ) ] , & [ SelfKind :: Ref , SelfKind :: RefMut ] ) ,
1516 ( & [ Convention :: StartsWith ( "from_" ) ] , & [ SelfKind :: No ] ) ,
1617 ( & [ Convention :: StartsWith ( "into_" ) ] , & [ SelfKind :: Value ] ) ,
1718 ( & [ Convention :: StartsWith ( "is_" ) ] , & [ SelfKind :: Ref , SelfKind :: No ] ) ,
1819 ( & [ Convention :: Eq ( "to_mut" ) ] , & [ SelfKind :: RefMut ] ) ,
1920 ( & [ 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 ] ) ,
2126] ;
2227
2328enum Convention {
2429 Eq ( & ' static str ) ,
2530 StartsWith ( & ' static str ) ,
2631 EndsWith ( & ' static str ) ,
2732 NotEndsWith ( & ' static str ) ,
33+ IsSelfTypeCopy ( bool ) ,
34+ ImplementsTrait ( bool ) ,
2835}
2936
3037impl Convention {
3138 #[ 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 {
3340 match * self {
3441 Self :: Eq ( this) => this == other,
3542 Self :: StartsWith ( this) => other. starts_with ( this) && this != other,
3643 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,
3847 }
3948 }
4049}
4150
4251impl fmt:: Display for Convention {
4352 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> Result < ( ) , fmt:: Error > {
4453 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+ } ,
4965 }
5066 }
5167}
@@ -57,47 +73,44 @@ pub(super) fn check<'tcx>(
5773 self_ty : & ' tcx TyS < ' tcx > ,
5874 first_arg_ty : & ' tcx TyS < ' tcx > ,
5975 first_arg_span : Span ,
76+ is_trait_item : bool ,
6077) {
6178 let lint = if is_pub {
6279 WRONG_PUB_SELF_CONVENTION
6380 } else {
6481 WRONG_SELF_CONVENTION
6582 } ;
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+ } ) {
7088 if !self_kinds. iter ( ) . any ( |k| k. matches ( cx, self_ty, first_arg_ty) ) {
7189 let suggestion = {
7290 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
9294 . 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 " ) ;
96110
97- format ! ( "methods called like this: ({})" , & s)
98- }
111+ format ! ( "methods with the following characteristics: ({})" , & s)
99112 } else {
100- format ! ( "methods called `{}` " , & conventions[ 0 ] )
113+ format ! ( "methods called {} " , & conventions[ 0 ] )
101114 }
102115 } ;
103116
0 commit comments