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