Skip to content

Commit 4dcafd0

Browse files
committed
Report the intermediate spans
1 parent 2f34c33 commit 4dcafd0

File tree

4 files changed

+337
-65
lines changed

4 files changed

+337
-65
lines changed

clippy_lints/src/static_items_large_align.rs

Lines changed: 119 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
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;
23
use rustc_hir::{Item, ItemKind};
34
use rustc_lint::{LateContext, LateLintPass};
4-
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
5+
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeAndMut};
56
use rustc_session::{declare_tool_lint, impl_lint_pass};
6-
use rustc_target::abi::Align;
7+
use rustc_span::Span;
78
use rustc_typeck::hir_ty_to_ty;
89

910
pub struct StaticItemsLargeAlign {
@@ -46,16 +47,37 @@ impl LateLintPass<'_> for StaticItemsLargeAlign {
4647
if_chain! {
4748
if let ItemKind::Static(hir_ty, _, _) = item.kind;
4849
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);
5053
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(
5270
cx,
5371
STATIC_ITEMS_LARGE_ALIGN,
5472
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+
}
5981
);
6082
}
6183
}
@@ -64,29 +86,100 @@ impl LateLintPass<'_> for StaticItemsLargeAlign {
6486

6587
impl StaticItemsLargeAlign {
6688
/// 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
77115
{
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+
})
79122
} else {
80-
adt_ref.all_fields()
123+
adt_def.all_fields()
81124
.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))
83126
}
84127
},
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+
},
89134
_ => 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
90162
}
91163
}
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,
92185
}

clippy_lints/src/utils/conf.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ define_Conf! {
314314
///
315315
/// The page size of the target platform. It is useful when we know the exact page size and know that
316316
/// it could be fulfilled (e.g., when we are targeting embedded platforms).
317-
(page_size: u64 = 4096),
317+
(page_size: u64 = 0x1000),
318318
}
319319

320320
/// Search for the configuration file.

tests/ui/static_items_large_align.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,61 @@ enum AlignedEnum {
1515
A(Aligned),
1616
}
1717

18+
struct AlignedPtr(*const Aligned);
19+
20+
unsafe impl Sync for AlignedPtr {}
21+
1822
struct AlignedGeneric<T>(T);
1923

2024
static X: Aligned = Aligned(0);
2125

2226
static X_REF: &Aligned = &Aligned(0);
2327

24-
static ARRAY: [Aligned; 10] = [Aligned(0); 10];
25-
26-
static TUPLE: (u8, Aligned) = (0, Aligned(0));
28+
static X_PTR: AlignedPtr = AlignedPtr(&Aligned(0) as *const _);
2729

2830
static XW: AlignedWrapper = AlignedWrapper { f: 0, g: Aligned(0) };
2931

32+
static ARRAY: [Aligned; 10] = [Aligned(0); 10];
33+
34+
static TUPLE: (u8, (Aligned, u8)) = (0, (Aligned(0), 0));
35+
3036
static XE: AlignedEnum = AlignedEnum::A(Aligned(0));
3137

3238
static XG: AlignedGeneric<Aligned> = AlignedGeneric(Aligned(0));
3339

40+
static XGW: AlignedGeneric<AlignedWrapper> = AlignedGeneric(AlignedWrapper { f: 0, g: Aligned(0) });
41+
3442
fn main() {
3543
let x = Aligned(0);
3644
println!("{:#x}", Box::into_raw(Box::new(Aligned(0))) as usize);
3745
}
46+
47+
////////////////////////////////////////////////////////////////
48+
/////////////// below is a more involved example ///////////////
49+
////////////////////////////////////////////////////////////////
50+
51+
#[repr(align(0x100000))]
52+
struct AlignedA(u8);
53+
54+
#[repr(align(0x100000))]
55+
struct AlignedB(u8);
56+
57+
struct FnPtr<T>(fn() -> Box<T>);
58+
59+
struct AG<T>(T);
60+
61+
type AGAlias<T> = AG<T>;
62+
63+
struct AGWithArgs<A, B> {
64+
not_aligned: FnPtr<A>,
65+
aligned: AGAlias<B>,
66+
}
67+
68+
static XG_ARGS: AGWithArgs<AlignedA, AlignedB> = AGWithArgs {
69+
not_aligned: FnPtr(box_aligned),
70+
aligned: AG(AlignedB(0)),
71+
};
72+
73+
fn box_aligned() -> Box<AlignedA> {
74+
Box::new(AlignedA(0))
75+
}

0 commit comments

Comments
 (0)