1
1
use rustc:: lint:: * ;
2
+ use rustc:: middle:: ty:: fast_reject:: simplify_type;
3
+ use rustc:: middle:: ty;
2
4
use rustc_front:: hir:: * ;
3
5
use syntax:: ast:: { Attribute , MetaItem_ } ;
6
+ use syntax:: codemap:: Span ;
7
+ use utils:: { CLONE_TRAIT_PATH , HASH_PATH } ;
4
8
use utils:: { match_path, span_lint_and_then} ;
5
- use utils:: HASH_PATH ;
6
-
7
- use rustc:: middle:: ty:: fast_reject:: simplify_type;
9
+ use rustc:: middle:: ty:: TypeVariants ;
8
10
9
11
/// **What it does:** This lint warns about deriving `Hash` but implementing `PartialEq`
10
- /// explicitely .
12
+ /// explicitly .
11
13
///
12
14
/// **Why is this bad?** The implementation of these traits must agree (for example for use with
13
15
/// `HashMap`) so it’s probably a bad idea to use a default-generated `Hash` implementation with
@@ -33,66 +35,145 @@ declare_lint! {
33
35
"deriving `Hash` but implementing `PartialEq` explicitly"
34
36
}
35
37
38
+ /// **What it does:** This lint warns about explicit `Clone` implementation for `Copy` types.
39
+ ///
40
+ /// **Why is this bad?** To avoid surprising behaviour, these traits should agree and the behaviour
41
+ /// of `Copy` cannot be overridden. In almost all situations a `Copy` type should have a `Clone`
42
+ /// implementation that does nothing more than copy the object, which is what
43
+ /// `#[derive(Copy, Clone)]` gets you.
44
+ ///
45
+ /// **Known problems:** None.
46
+ ///
47
+ /// **Example:**
48
+ /// ```rust
49
+ /// #[derive(Copy)]
50
+ /// struct Foo;
51
+ ///
52
+ /// impl Clone for Foo {
53
+ /// ..
54
+ /// }
55
+ declare_lint ! {
56
+ pub EXPL_IMPL_CLONE_ON_COPY ,
57
+ Warn ,
58
+ "implementing `Clone` explicitly on `Copy` types"
59
+ }
60
+
36
61
pub struct Derive ;
37
62
38
63
impl LintPass for Derive {
39
64
fn get_lints ( & self ) -> LintArray {
40
- lint_array ! ( DERIVE_HASH_NOT_EQ )
65
+ lint_array ! ( EXPL_IMPL_CLONE_ON_COPY , DERIVE_HASH_NOT_EQ )
41
66
}
42
67
}
43
68
44
69
impl LateLintPass for Derive {
45
70
fn check_item ( & mut self , cx : & LateContext , item : & Item ) {
46
- /// A `#[derive]`d implementation has a `#[automatically_derived]` attribute.
47
- fn is_automatically_derived ( attr : & Attribute ) -> bool {
48
- if let MetaItem_ :: MetaWord ( ref word) = attr. node . value . node {
49
- word == & "automatically_derived"
71
+ let ast_ty_to_ty_cache = cx. tcx . ast_ty_to_ty_cache . borrow ( ) ;
72
+
73
+ if_let_chain ! { [
74
+ let ItemImpl ( _, _, _, Some ( ref trait_ref) , ref ast_ty, _) = item. node,
75
+ let Some ( & ty) = ast_ty_to_ty_cache. get( & ast_ty. id)
76
+ ] , {
77
+ if item. attrs. iter( ) . any( is_automatically_derived) {
78
+ check_hash_peq( cx, item. span, trait_ref, ty) ;
50
79
}
51
80
else {
52
- false
81
+ check_copy_clone ( cx , item . span , trait_ref , ty ) ;
53
82
}
54
- }
83
+ } }
84
+ }
85
+ }
55
86
56
- // If `item` is an automatically derived `Hash` implementation
87
+ /// Implementation of the `DERIVE_HASH_NOT_EQ` lint.
88
+ fn check_hash_peq ( cx : & LateContext , span : Span , trait_ref : & TraitRef , ty : ty:: Ty ) {
89
+ // If `item` is an automatically derived `Hash` implementation
90
+ if_let_chain ! { [
91
+ match_path( & trait_ref. path, & HASH_PATH ) ,
92
+ let Some ( peq_trait_def_id) = cx. tcx. lang_items. eq_trait( )
93
+ ] , {
94
+ let peq_trait_def = cx. tcx. lookup_trait_def( peq_trait_def_id) ;
95
+
96
+ cx. tcx. populate_implementations_for_trait_if_necessary( peq_trait_def. trait_ref. def_id) ;
97
+ let peq_impls = peq_trait_def. borrow_impl_lists( cx. tcx) . 1 ;
98
+
99
+ // Look for the PartialEq implementations for `ty`
57
100
if_let_chain! { [
58
- let ItemImpl ( _, _, _, Some ( ref trait_ref) , ref ast_ty, _) = item. node,
59
- match_path( & trait_ref. path, & HASH_PATH ) ,
60
- item. attrs. iter( ) . any( is_automatically_derived) ,
61
- let Some ( peq_trait_def_id) = cx. tcx. lang_items. eq_trait( )
101
+ let Some ( simpl_ty) = simplify_type( cx. tcx, ty, false ) ,
102
+ let Some ( impl_ids) = peq_impls. get( & simpl_ty)
62
103
] , {
63
- let peq_trait_def = cx. tcx. lookup_trait_def( peq_trait_def_id) ;
104
+ for & impl_id in impl_ids {
105
+ let trait_ref = cx. tcx. impl_trait_ref( impl_id) . expect( "must be a trait implementation" ) ;
64
106
65
- cx. tcx. populate_implementations_for_trait_if_necessary( peq_trait_def. trait_ref. def_id) ;
66
- let peq_impls = peq_trait_def. borrow_impl_lists( cx. tcx) . 1 ;
67
- let ast_ty_to_ty_cache = cx. tcx. ast_ty_to_ty_cache. borrow( ) ;
107
+ // Only care about `impl PartialEq<Foo> for Foo`
108
+ if trait_ref. input_types( ) [ 0 ] == ty &&
109
+ !cx. tcx. get_attrs( impl_id) . iter( ) . any( is_automatically_derived) {
110
+ span_lint_and_then(
111
+ cx, DERIVE_HASH_NOT_EQ , span,
112
+ "you are deriving `Hash` but have implemented `PartialEq` explicitly" ,
113
+ |db| {
114
+ if let Some ( node_id) = cx. tcx. map. as_local_node_id( impl_id) {
115
+ db. span_note(
116
+ cx. tcx. map. span( node_id) ,
117
+ "`PartialEq` implemented here"
118
+ ) ;
119
+ }
120
+ } ) ;
121
+ }
122
+ }
123
+ } }
124
+ } }
125
+ }
68
126
127
+ /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
128
+ fn check_copy_clone < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , span : Span , trait_ref : & TraitRef , ty : ty:: Ty < ' tcx > ) {
129
+ if match_path ( & trait_ref. path , & CLONE_TRAIT_PATH ) {
130
+ let parameter_environment = cx. tcx . empty_parameter_environment ( ) ;
69
131
70
- // Look for the PartialEq implementations for `ty`
71
- if_let_chain! { [
72
- let Some ( ty) = ast_ty_to_ty_cache. get( & ast_ty. id) ,
73
- let Some ( simpl_ty) = simplify_type( cx. tcx, ty, false ) ,
74
- let Some ( impl_ids) = peq_impls. get( & simpl_ty)
75
- ] , {
76
- for & impl_id in impl_ids {
77
- let trait_ref = cx. tcx. impl_trait_ref( impl_id) . expect( "must be a trait implementation" ) ;
132
+ if ty. moves_by_default ( & parameter_environment, span) {
133
+ return ; // ty is not Copy
134
+ }
78
135
79
- // Only care about `impl PartialEq<Foo> for Foo`
80
- if trait_ref. input_types( ) [ 0 ] == * ty &&
81
- !cx. tcx. get_attrs( impl_id) . iter( ) . any( is_automatically_derived) {
82
- span_lint_and_then(
83
- cx, DERIVE_HASH_NOT_EQ , item. span,
84
- & format!( "you are deriving `Hash` but have implemented \
85
- `PartialEq` explicitely") , |db| {
86
- if let Some ( node_id) = cx. tcx. map. as_local_node_id( impl_id) {
87
- db. span_note(
88
- cx. tcx. map. span( node_id) ,
89
- "`PartialEq` implemented here"
90
- ) ;
136
+ // Some types are not Clone by default but could be cloned `by hand` if necessary
137
+ match ty. sty {
138
+ TypeVariants :: TyEnum ( def, substs) | TypeVariants :: TyStruct ( def, substs) => {
139
+ for variant in & def. variants {
140
+ for field in & variant. fields {
141
+ match field. ty ( cx. tcx , substs) . sty {
142
+ TypeVariants :: TyArray ( _, size) if size > 32 => {
143
+ return ;
91
144
}
92
- } ) ;
145
+ TypeVariants :: TyBareFn ( ..) => {
146
+ return ;
147
+ }
148
+ TypeVariants :: TyTuple ( ref tys) if tys. len ( ) > 12 => {
149
+ return ;
150
+ }
151
+ _ => ( ) ,
152
+ }
93
153
}
94
154
}
95
- } }
96
- } }
155
+ }
156
+ _ => ( ) ,
157
+ }
158
+
159
+ span_lint_and_then (
160
+ cx, DERIVE_HASH_NOT_EQ , span,
161
+ "you are implementing `Clone` explicitly on a `Copy` type" ,
162
+ |db| {
163
+ db. span_note (
164
+ span,
165
+ "consider deriving `Clone` or removing `Copy`"
166
+ ) ;
167
+ } ) ;
168
+ }
169
+ }
170
+
171
+ /// Checks for the `#[automatically_derived]` attribute all `#[derive]`d implementations have.
172
+ fn is_automatically_derived ( attr : & Attribute ) -> bool {
173
+ if let MetaItem_ :: MetaWord ( ref word) = attr. node . value . node {
174
+ word == & "automatically_derived"
175
+ }
176
+ else {
177
+ false
97
178
}
98
179
}
0 commit comments