1
- use clippy_utils:: diagnostics:: span_lint_and_note;
1
+ use clippy_utils:: diagnostics:: span_lint_and_then;
2
+ use rustc_data_structures:: fx:: FxHashSet ;
2
3
use rustc_hir:: { Item , ItemKind } ;
3
4
use rustc_lint:: { LateContext , LateLintPass } ;
4
- use rustc_middle:: ty:: { self , AdtDef , Ty , TyCtxt } ;
5
+ use rustc_middle:: ty:: { self , AdtDef , Ty , TyCtxt , TypeAndMut } ;
5
6
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
6
- use rustc_target :: abi :: Align ;
7
+ use rustc_span :: Span ;
7
8
use rustc_typeck:: hir_ty_to_ty;
8
9
9
10
pub struct StaticItemsLargeAlign {
@@ -46,16 +47,37 @@ impl LateLintPass<'_> for StaticItemsLargeAlign {
46
47
if_chain ! {
47
48
if let ItemKind :: Static ( hir_ty, _, _) = item. kind;
48
49
let ty = hir_ty_to_ty( cx. tcx, hir_ty) ;
49
- if let Some ( adt_ref) = self . check_ty_alignment( cx. tcx, ty) ;
50
+ let mut visited_tys = FxHashSet :: default ( ) ;
51
+ let mut intermediate_tys = Vec :: new( ) ;
52
+ if let Some ( la_ty) = self . check_ty_alignment( cx. tcx, ty, & mut visited_tys, & mut intermediate_tys) ;
50
53
then {
51
- span_lint_and_note(
54
+ let mut span_notes: Vec <( Span , String ) > = Vec :: new( ) ;
55
+ if !intermediate_tys. is_empty( ) {
56
+ let top_ty = intermediate_tys[ 0 ] . ty;
57
+ if !top_ty. is_adt( ) {
58
+ span_notes. push( (
59
+ hir_ty. span,
60
+ format!( "this {} contains an inner type with large alignment" , top_ty. prefix_string( cx. tcx) ) ,
61
+ ) ) ;
62
+ }
63
+ intermediate_tys. iter( )
64
+ . filter_map( |im_ty| Self :: report_im_ty( cx, im_ty) )
65
+ . for_each( |ss| span_notes. push( ss) ) ;
66
+ }
67
+ span_notes. push( self . report_la_ty( cx, & la_ty) ) ;
68
+
69
+ span_lint_and_then(
52
70
cx,
53
71
STATIC_ITEMS_LARGE_ALIGN ,
54
72
item. span,
55
- "this static item (itself or its subfield) has large type alignment, which may not be fulfilled,\n \
56
- for more information, see <https://github.com/rust-lang/rust/issues/70022>",
57
- Some ( cx. tcx. def_span( adt_ref. did( ) ) ) ,
58
- format!( "this type has an alignment larger than page size ({}KB)" , self . page_size/1024 ) . as_str( )
73
+ "this static item (itself or its subfield) has a type alignment,\n \
74
+ which is larger than page size and may not be fulfilled,\n \
75
+ for more information, see <https://github.com/rust-lang/rust/issues/70022>.",
76
+ move |diag| {
77
+ for ( span, s) in span_notes {
78
+ diag. span_note( span, s. as_str( ) ) ;
79
+ }
80
+ }
59
81
) ;
60
82
}
61
83
}
@@ -64,29 +86,100 @@ impl LateLintPass<'_> for StaticItemsLargeAlign {
64
86
65
87
impl StaticItemsLargeAlign {
66
88
/// It checks a type with the following steps:
67
- /// 1. picks out this type if its kind is among adt, array, tuple or ref to them;
68
- /// otherwise return None
69
- /// 2. check if its (or its inner fields') alignment are larger than page size
70
- /// 3. return one of them;
71
- /// otherwise return None
72
- fn check_ty_alignment < ' tcx > ( & self , tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> Option < AdtDef < ' tcx > > {
73
- match ty. kind ( ) {
74
- ty:: Adt ( adt_ref, subst_ref) => {
75
- if let Some ( align) = adt_ref. repr ( ) . align &&
76
- align > Align :: from_bytes ( self . page_size ) . unwrap ( )
89
+ /// 1. Check if the type is already visited (for a static item),
90
+ /// if not, continue;
91
+ /// otherwise, return `None` early.
92
+ /// 2. Push the type in the checked types.
93
+ /// 3. Pick out this type if its kind is among adt, tuple, array, ref or raw ptr to them;
94
+ /// otherwise return `None`.
95
+ /// 4. Check if its (or its inner fields') alignment are larger than page size.
96
+ /// 5. Return one of them;
97
+ /// otherwise pop the current checked type and return `None`.
98
+ fn check_ty_alignment < ' tcx > (
99
+ & self ,
100
+ tcx : TyCtxt < ' tcx > ,
101
+ ty : Ty < ' tcx > ,
102
+ visited_tys : & mut FxHashSet < Ty < ' tcx > > ,
103
+ intermediate_tys : & mut Vec < IntermediateTy < ' tcx > > ,
104
+ ) -> Option < LargeAlignmentTy < ' tcx > > {
105
+ if visited_tys. contains ( & ty) {
106
+ return None ;
107
+ }
108
+ visited_tys. insert ( ty) ;
109
+ intermediate_tys. push ( IntermediateTy { ty } ) ;
110
+
111
+ let ret = match ty. kind ( ) {
112
+ ty:: Adt ( adt_def, subst_ref) => {
113
+ if let Some ( align) = adt_def. repr ( ) . align &&
114
+ align. bytes ( ) > self . page_size
77
115
{
78
- Some ( * adt_ref)
116
+ intermediate_tys. pop ( ) ; // the last element is already in the return value
117
+ Some ( LargeAlignmentTy {
118
+ adt : * adt_def,
119
+ name : ty. sort_string ( tcx) . into_owned ( ) ,
120
+ align : align. bytes ( ) ,
121
+ } )
79
122
} else {
80
- adt_ref . all_fields ( )
123
+ adt_def . all_fields ( )
81
124
. map ( |field_ref| field_ref. ty ( tcx, subst_ref) )
82
- . find_map ( |ty| self . check_ty_alignment ( tcx, ty) )
125
+ . find_map ( |ty| self . check_ty_alignment ( tcx, ty, visited_tys , intermediate_tys ) )
83
126
}
84
127
} ,
85
- ty:: Array ( ty, _) => self . check_ty_alignment ( tcx, * ty) ,
86
- ty:: Tuple ( ty_list) => ty_list. iter ( )
87
- . find_map ( |ty| self . check_ty_alignment ( tcx, ty) ) ,
88
- ty:: Ref ( region, ty, _) if region. is_static ( ) => self . check_ty_alignment ( tcx, * ty) ,
128
+ ty:: Tuple ( ty_list) => ty_list
129
+ . iter ( )
130
+ . find_map ( |ty| self . check_ty_alignment ( tcx, ty, visited_tys, intermediate_tys) ) ,
131
+ ty:: Array ( ty, _) | ty:: Ref ( _, ty, _) | ty:: RawPtr ( TypeAndMut { ty, .. } ) => {
132
+ self . check_ty_alignment ( tcx, * ty, visited_tys, intermediate_tys)
133
+ } ,
89
134
_ => None ,
135
+ } ;
136
+
137
+ if ret. is_none ( ) {
138
+ intermediate_tys. pop ( ) ;
139
+ }
140
+ ret
141
+ }
142
+
143
+ fn report_im_ty ( cx : & LateContext < ' _ > , im_ty : & IntermediateTy < ' _ > ) -> Option < ( Span , String ) > {
144
+ let ty = im_ty. ty ;
145
+ if let ty:: Adt ( adt_def, substs_ref) = ty. kind ( ) {
146
+ Some ( (
147
+ cx. tcx . def_span ( adt_def. did ( ) ) ,
148
+ if substs_ref. is_empty ( ) {
149
+ format ! ( "{} contains an inner type with large alignment" , ty. sort_string( cx. tcx) )
150
+ } else {
151
+ // TODO - can we use :#?
152
+ format ! (
153
+ "{} with substitutions {:#?},\n \
154
+ contains an inner type with large alignment",
155
+ ty. sort_string( cx. tcx) ,
156
+ substs_ref
157
+ )
158
+ } ,
159
+ ) )
160
+ } else {
161
+ None
90
162
}
91
163
}
164
+
165
+ fn report_la_ty ( & self , cx : & LateContext < ' _ > , la_ty : & LargeAlignmentTy < ' _ > ) -> ( Span , String ) {
166
+ (
167
+ cx. tcx . def_span ( la_ty. adt . did ( ) ) ,
168
+ format ! (
169
+ "{} has alignment {:#x}, which is larger than {:#x},\n \
170
+ if you know what you are doing, config the default page size clippy uses in clippy.toml",
171
+ la_ty. name, la_ty. align, self . page_size,
172
+ ) ,
173
+ )
174
+ }
175
+ }
176
+
177
+ struct IntermediateTy < ' tcx > {
178
+ ty : Ty < ' tcx > ,
179
+ }
180
+
181
+ struct LargeAlignmentTy < ' tcx > {
182
+ adt : AdtDef < ' tcx > ,
183
+ name : String ,
184
+ align : u64 ,
92
185
}
0 commit comments