Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 003972f

Browse files
committed
add multiple get_attrs and includes_repr and they all work!
1 parent b9948c4 commit 003972f

File tree

3 files changed

+123
-69
lines changed

3 files changed

+123
-69
lines changed

clippy_lints/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
2222
extern crate rustc_ast;
2323
extern crate rustc_ast_pretty;
24+
extern crate rustc_attr;
2425
extern crate rustc_data_structures;
2526
extern crate rustc_driver;
2627
extern crate rustc_errors;

clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
use clippy_utils::consts::{miri_to_const, Constant};
22
use clippy_utils::diagnostics::span_lint_and_sugg;
33
use clippy_utils::source::snippet;
4+
use rustc_ast::Attribute;
45
use rustc_errors::Applicability;
56
use rustc_hir::def_id::LocalDefId;
6-
use rustc_hir::{Item, ItemKind, TyKind, VariantData};
7+
use rustc_hir::{Item, ItemKind, VariantData};
78
use rustc_lint::{LateContext, LateLintPass};
9+
use rustc_middle::dep_graph::DepContext;
10+
use rustc_middle::ty as ty_mod;
11+
use rustc_middle::ty::ReprFlags;
812
use rustc_session::{declare_lint_pass, declare_tool_lint};
913
use rustc_span::sym;
1014

@@ -33,19 +37,16 @@ declare_clippy_lint! {
3337
/// ```
3438
pub TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C,
3539
nursery,
36-
"struct with a trailing zero-sized array but without `repr(C)`"
40+
"struct with a trailing zero-sized array but without `repr(C)` or another `repr` attribute"
3741
}
3842
declare_lint_pass!(TrailingZeroSizedArrayWithoutReprC => [TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C]);
3943

40-
//
41-
// TODO: Register the lint pass in `clippy_lints/src/lib.rs`,
42-
// e.g. store.register_early_pass(||
43-
// Box::new(trailing_zero_sized_array_without_repr_c::TrailingZeroSizedArrayWithoutReprC));
44-
// DONE!
44+
// TESTNAME=trailing_zero_sized_array_without_repr_c cargo uitest
4545

4646
impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC {
4747
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
48-
if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_c(cx, item.def_id) {
48+
dbg!(item.ident);
49+
if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.def_id) {
4950
span_lint_and_sugg(
5051
cx,
5152
TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C,
@@ -64,7 +65,7 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx
6465
if let ItemKind::Struct(data, _generics) = &item.kind;
6566
if let VariantData::Struct(field_defs, _) = data;
6667
if let Some(last_field) = field_defs.last();
67-
if let TyKind::Array(_, aconst) = last_field.ty.kind;
68+
if let rustc_hir::TyKind::Array(_, aconst) = last_field.ty.kind;
6869
let aconst_def_id = cx.tcx.hir().body_owner_def_id(aconst.body).to_def_id();
6970
let ty = cx.tcx.type_of(aconst_def_id);
7071
let constant = cx
@@ -83,17 +84,50 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx
8384
}
8485
}
8586

86-
fn has_repr_c(cx: &LateContext<'tcx>, def_id: LocalDefId) -> bool {
87+
fn has_repr_attr(cx: &LateContext<'tcx>, def_id: LocalDefId) -> bool {
88+
let attrs_get_attrs = get_attrs_get_attrs(cx, def_id);
89+
let attrs_hir_map = get_attrs_hir_map(cx, def_id);
90+
let b11 = dbg!(includes_repr_attr_using_sym(attrs_get_attrs));
91+
let b12 = dbg!(includes_repr_attr_using_sym(attrs_hir_map));
92+
let b21 = dbg!(includes_repr_attr_using_helper(cx, attrs_get_attrs));
93+
let b22 = dbg!(includes_repr_attr_using_helper(cx, attrs_hir_map));
94+
let b3 = dbg!(has_repr_attr_using_adt(cx, def_id));
95+
let all_same = b11 && b12 && b21 && b22 && b3;
96+
dbg!(all_same);
97+
98+
b11
99+
}
100+
101+
fn get_attrs_get_attrs(cx: &LateContext<'tcx>, def_id: LocalDefId) -> &'tcx [Attribute] {
102+
cx.tcx.get_attrs(def_id.to_def_id())
103+
}
104+
105+
fn get_attrs_hir_map(cx: &LateContext<'tcx>, def_id: LocalDefId) -> &'tcx [Attribute] {
87106
let hir_map = cx.tcx.hir();
88107
let hir_id = hir_map.local_def_id_to_hir_id(def_id);
89-
let attrs = hir_map.attrs(hir_id);
108+
hir_map.attrs(hir_id)
109+
}
90110

91-
// NOTE: Can there ever be more than one `repr` attribute?
92-
// other `repr` syms: repr, repr128, repr_align, repr_align_enum, repr_no_niche, repr_packed,
93-
// repr_simd, repr_transparent
94-
if let Some(_attr) = attrs.iter().find(|attr| attr.has_name(sym::repr)) {
95-
true
111+
// Don't like this because it's so dependent on the current list of `repr` flags and it would have to be manually updated if that ever expanded. idk if there's any mechanism in `bitflag!` or elsewhere for requiring that sort of exhaustiveness
112+
fn has_repr_attr_using_adt(cx: &LateContext<'tcx>, def_id: LocalDefId) -> bool {
113+
let ty = cx.tcx.type_of(def_id.to_def_id());
114+
if let ty_mod::Adt(adt, _) = ty.kind() {
115+
if adt.is_struct() {
116+
let repr = adt.repr;
117+
let repr_attr = ReprFlags::IS_C | ReprFlags::IS_TRANSPARENT | ReprFlags::IS_SIMD | ReprFlags::IS_LINEAR;
118+
repr.int.is_some() || repr.align.is_some() || repr.pack.is_some() || repr.flags.intersects(repr_attr)
119+
} else {
120+
false
121+
}
96122
} else {
97123
false
98124
}
99125
}
126+
127+
fn includes_repr_attr_using_sym(attrs: &'tcx [Attribute]) -> bool {
128+
attrs.iter().any(|attr| attr.has_name(sym::repr))
129+
}
130+
131+
fn includes_repr_attr_using_helper(cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) -> bool {
132+
attrs.iter().any(|attr| !rustc_attr::find_repr_attrs(cx.tcx.sess(), attr).is_empty())
133+
}
Lines changed: 72 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
#![warn(clippy::trailing_zero_sized_array_without_repr_c)]
22
// #![feature(const_generics_defaults)] // see below
33

4-
struct RarelyUseful {
5-
field: i32,
6-
last: [usize; 0],
7-
}
4+
// Do lint:
85

9-
#[repr(C)]
10-
struct GoodReason {
6+
struct RarelyUseful {
117
field: i32,
128
last: [usize; 0],
139
}
@@ -21,24 +17,25 @@ struct GenericArrayType<T> {
2117
last: [T; 0],
2218
}
2319

24-
struct SizedArray {
20+
#[derive(Debug)]
21+
struct PlayNiceWithOtherAttributesDerive {
2522
field: i32,
26-
last: [usize; 1],
23+
last: [usize; 0]
2724
}
2825

29-
const ZERO: usize = 0;
30-
struct ZeroSizedFromExternalConst {
26+
#[must_use]
27+
struct PlayNiceWithOtherAttributesMustUse {
3128
field: i32,
32-
last: [usize; ZERO],
29+
last: [usize; 0]
3330
}
3431

35-
const ONE: usize = 1;
36-
struct NonZeroSizedFromExternalConst {
32+
const ZERO: usize = 0;
33+
struct ZeroSizedFromExternalConst {
3734
field: i32,
38-
last: [usize; ONE],
35+
last: [usize; ZERO],
3936
}
4037

41-
#[allow(clippy::eq_op)] // lmao im impressed
38+
#[allow(clippy::eq_op)]
4239
const fn compute_zero() -> usize {
4340
(4 + 6) - (2 * 5)
4441
}
@@ -47,36 +44,62 @@ struct UsingFunction {
4744
last: [usize; compute_zero()],
4845
}
4946

50-
// NOTE: including these (along with the required feature) triggers an ICE. Should make sure the
51-
// const generics people are aware of that if they weren't already.
47+
struct LotsOfFields {
48+
f1: u32,
49+
f2: u32,
50+
f3: u32,
51+
f4: u32,
52+
f5: u32,
53+
f6: u32,
54+
f7: u32,
55+
f8: u32,
56+
f9: u32,
57+
f10: u32,
58+
f11: u32,
59+
f12: u32,
60+
f13: u32,
61+
f14: u32,
62+
f15: u32,
63+
f16: u32,
64+
last: [usize; 0],
65+
}
5266

53-
// #[repr(C)]
54-
// struct ConstParamOk<const N: usize = 0> {
55-
// field: i32,
56-
// last: [usize; N]
57-
// }
67+
// Don't lint
5868

59-
// struct ConstParamLint<const N: usize = 0> {
60-
// field: i32,
61-
// last: [usize; N]
62-
// }
69+
#[repr(C)]
70+
struct GoodReason {
71+
field: i32,
72+
last: [usize; 0],
73+
}
74+
75+
struct SizedArray {
76+
field: i32,
77+
last: [usize; 1],
78+
}
79+
80+
const ONE: usize = 1;
81+
struct NonZeroSizedFromExternalConst {
82+
field: i32,
83+
last: [usize; ONE],
84+
}
6385

64-
// TODO: actually, uh,, no idea what behavior here would be
6586
#[repr(packed)]
6687
struct ReprPacked {
67-
small: u8,
68-
medium: i32,
69-
weird: [u64; 0],
88+
field: i32,
89+
last: [usize; 0],
90+
}
91+
92+
#[repr(C, packed)]
93+
struct ReprCPacked {
94+
field: i32,
95+
last: [usize; 0],
7096
}
7197

72-
// TODO: clarify expected behavior
7398
#[repr(align(64))]
7499
struct ReprAlign {
75100
field: i32,
76101
last: [usize; 0],
77102
}
78-
79-
// TODO: clarify expected behavior
80103
#[repr(C, align(64))]
81104
struct ReprCAlign {
82105
field: i32,
@@ -91,24 +114,20 @@ enum DontLintAnonymousStructsFromDesuraging {
91114
C { x: u32, y: [u64; 0] },
92115
}
93116

94-
struct LotsOfFields {
95-
f1: u32,
96-
f2: u32,
97-
f3: u32,
98-
f4: u32,
99-
f5: u32,
100-
f6: u32,
101-
f7: u32,
102-
f8: u32,
103-
f9: u32,
104-
f10: u32,
105-
f11: u32,
106-
f12: u32,
107-
f13: u32,
108-
f14: u32,
109-
f15: u32,
110-
f16: u32,
111-
last: [usize; 0],
112-
}
117+
// NOTE: including these (along with the required feature) triggers an ICE. Should make sure the
118+
// const generics people are aware of that if they weren't already.
119+
120+
// #[repr(C)]
121+
// struct ConstParamOk<const N: usize = 0> {
122+
// field: i32,
123+
// last: [usize; N]
124+
// }
113125

114-
fn main() {}
126+
// struct ConstParamLint<const N: usize = 0> {
127+
// field: i32,
128+
// last: [usize; N]
129+
// }
130+
131+
fn main() {
132+
let _ = PlayNiceWithOtherAttributesMustUse {field: 0, last: []};
133+
}

0 commit comments

Comments
 (0)