1
1
//! lint when there is a large size difference between variants on an enum
2
2
3
- use clippy_utils:: diagnostics:: span_lint_and_then;
4
3
use clippy_utils:: source:: snippet_with_applicability;
4
+ use clippy_utils:: { diagnostics:: span_lint_and_then, ty:: is_copy} ;
5
5
use rustc_errors:: Applicability ;
6
6
use rustc_hir:: { Item , ItemKind } ;
7
7
use rustc_lint:: { LateContext , LateLintPass } ;
8
8
use rustc_middle:: lint:: in_external_macro;
9
9
use rustc_middle:: ty:: layout:: LayoutOf ;
10
+ use rustc_middle:: ty:: { Adt , Ty } ;
10
11
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
11
12
use rustc_span:: source_map:: Span ;
12
13
@@ -26,6 +27,15 @@ declare_clippy_lint! {
26
27
/// the overhead is negligible and the boxing is counter-productive. Always
27
28
/// measure the change this lint suggests.
28
29
///
30
+ /// For types that implement `Copy`, the suggestion to `Box` a variant's
31
+ /// data would require removing the trait impl. The types can of course
32
+ /// still be `Clone`, but that is worse ergonomically. Depending on the
33
+ /// use case it may be possible to store the large data in an auxillary
34
+ /// structure (e.g. Arena or ECS).
35
+ ///
36
+ /// The lint will ignore generic types if the layout depends on the
37
+ /// generics, even if the size difference will be large anyway.
38
+ ///
29
39
/// ### Example
30
40
/// ```rust
31
41
/// // Bad
@@ -74,7 +84,7 @@ struct VariantInfo {
74
84
impl_lint_pass ! ( LargeEnumVariant => [ LARGE_ENUM_VARIANT ] ) ;
75
85
76
86
impl < ' tcx > LateLintPass < ' tcx > for LargeEnumVariant {
77
- fn check_item ( & mut self , cx : & LateContext < ' _ > , item : & Item < ' _ > ) {
87
+ fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & Item < ' tcx > ) {
78
88
if in_external_macro ( cx. tcx . sess , item. span ) {
79
89
return ;
80
90
}
@@ -132,41 +142,57 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
132
142
let fields = def. variants [ variants_size[ 0 ] . ind ] . data . fields ( ) ;
133
143
variants_size[ 0 ] . fields_size . sort_by ( |a, b| ( a. size . cmp ( & b. size ) ) ) ;
134
144
let mut applicability = Applicability :: MaybeIncorrect ;
135
- let sugg: Vec < ( Span , String ) > = variants_size[ 0 ]
136
- . fields_size
137
- . iter ( )
138
- . rev ( )
139
- . map_while ( |val| {
140
- if difference > self . maximum_size_difference_allowed {
141
- difference = difference. saturating_sub ( val. size ) ;
142
- Some ( (
143
- fields[ val. ind ] . ty . span ,
144
- format ! (
145
- "Box<{}>" ,
146
- snippet_with_applicability(
147
- cx,
148
- fields[ val. ind] . ty. span,
149
- ".." ,
150
- & mut applicability
151
- )
152
- . into_owned( )
153
- ) ,
154
- ) )
155
- } else {
156
- None
157
- }
158
- } )
159
- . collect ( ) ;
145
+ if is_copy ( cx, ty) || maybe_copy ( cx, ty) {
146
+ diag. span_note (
147
+ item. ident . span ,
148
+ "boxing a variant would require the type no longer be `Copy`" ,
149
+ ) ;
150
+ } else {
151
+ let sugg: Vec < ( Span , String ) > = variants_size[ 0 ]
152
+ . fields_size
153
+ . iter ( )
154
+ . rev ( )
155
+ . map_while ( |val| {
156
+ if difference > self . maximum_size_difference_allowed {
157
+ difference = difference. saturating_sub ( val. size ) ;
158
+ Some ( (
159
+ fields[ val. ind ] . ty . span ,
160
+ format ! (
161
+ "Box<{}>" ,
162
+ snippet_with_applicability(
163
+ cx,
164
+ fields[ val. ind] . ty. span,
165
+ ".." ,
166
+ & mut applicability
167
+ )
168
+ . into_owned( )
169
+ ) ,
170
+ ) )
171
+ } else {
172
+ None
173
+ }
174
+ } )
175
+ . collect ( ) ;
160
176
161
- if !sugg. is_empty ( ) {
162
- diag. multipart_suggestion ( help_text, sugg, Applicability :: MaybeIncorrect ) ;
163
- return ;
177
+ if !sugg. is_empty ( ) {
178
+ diag. multipart_suggestion ( help_text, sugg, Applicability :: MaybeIncorrect ) ;
179
+ return ;
180
+ }
164
181
}
165
-
166
182
diag. span_help ( def. variants [ variants_size[ 0 ] . ind ] . span , help_text) ;
167
183
} ,
168
184
) ;
169
185
}
170
186
}
171
187
}
172
188
}
189
+
190
+ fn maybe_copy < ' tcx > ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> bool {
191
+ if let Adt ( _def, substs) = ty. kind ( )
192
+ && substs. types ( ) . next ( ) . is_some ( )
193
+ && let Some ( copy_trait) = cx. tcx . lang_items ( ) . copy_trait ( )
194
+ {
195
+ return cx. tcx . non_blanket_impls_for_ty ( copy_trait, ty) . next ( ) . is_some ( ) ;
196
+ }
197
+ false
198
+ }
0 commit comments