|
1 | 1 | use clippy_utils::consts::{miri_to_const, Constant};
|
2 | 2 | use clippy_utils::diagnostics::span_lint_and_help;
|
3 | 3 | use rustc_ast::Attribute;
|
4 |
| -use rustc_hir::def_id::LocalDefId; |
5 | 4 | use rustc_hir::{Item, ItemKind, VariantData};
|
6 | 5 | use rustc_lint::{LateContext, LateLintPass};
|
7 | 6 | use rustc_middle::dep_graph::DepContext;
|
8 |
| -use rustc_middle::ty as ty_mod; |
9 |
| -use rustc_middle::ty::ReprFlags; |
| 7 | +use rustc_middle::ty::Const; |
10 | 8 | use rustc_session::{declare_lint_pass, declare_tool_lint};
|
11 |
| -use rustc_span::sym; |
12 | 9 |
|
13 | 10 | declare_clippy_lint! {
|
14 | 11 | /// ### What it does
|
@@ -43,93 +40,55 @@ declare_lint_pass!(TrailingZeroSizedArrayWithoutReprC => [TRAILING_ZERO_SIZED_AR
|
43 | 40 |
|
44 | 41 | impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC {
|
45 | 42 | fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
46 |
| - dbg!(item.ident); |
47 |
| - if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.def_id) { |
48 |
| - // span_lint_and_help( |
49 |
| - // cx, |
50 |
| - // TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C, |
51 |
| - // item.span, |
52 |
| - // "trailing zero-sized array in a struct which is not marked `#[repr(C)]`", |
53 |
| - // None, |
54 |
| - // "consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute)", |
55 |
| - // ); |
56 |
| - eprintln!("— consider yourself linted — 🦀") |
| 43 | + if is_struct_with_trailing_zero_sized_array(cx, item) { |
| 44 | + let attrs = cx.tcx.get_attrs(item.def_id.to_def_id()); |
| 45 | + let first_attr = attrs.first(); // Actually, I've no idea if this is guaranteed to be the first one in the source code. |
| 46 | + |
| 47 | + let lint_span = if let Some(first_attr) = first_attr { |
| 48 | + first_attr.span.until(item.span) |
| 49 | + } else { |
| 50 | + item.span |
| 51 | + }; |
| 52 | + |
| 53 | + if !has_repr_attr(cx, attrs) { |
| 54 | + span_lint_and_help( |
| 55 | + cx, |
| 56 | + TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C, |
| 57 | + lint_span, |
| 58 | + "trailing zero-sized array in a struct which is not marked `#[repr(C)]`", |
| 59 | + None, |
| 60 | + "consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute)", |
| 61 | + ); |
| 62 | + } |
57 | 63 | }
|
58 | 64 | }
|
59 | 65 | }
|
60 | 66 |
|
61 | 67 | fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool {
|
62 | 68 | if_chain! {
|
63 |
| - if let ItemKind::Struct(data, _generics) = &item.kind; |
| 69 | + // Check if last field is an array |
| 70 | + if let ItemKind::Struct(data, _) = &item.kind; |
64 | 71 | if let VariantData::Struct(field_defs, _) = data;
|
65 | 72 | if let Some(last_field) = field_defs.last();
|
66 |
| - if let rustc_hir::TyKind::Array(_, aconst) = last_field.ty.kind; |
67 |
| - let aconst_def_id = cx.tcx.hir().body_owner_def_id(aconst.body).to_def_id(); |
68 |
| - let ty = cx.tcx.type_of(aconst_def_id); |
69 |
| - let constant = cx |
70 |
| - .tcx |
71 |
| - // NOTE: maybe const_eval_resolve? |
72 |
| - .const_eval_poly(aconst_def_id) |
73 |
| - .ok() |
74 |
| - .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)); |
75 |
| - if let Some(Constant::Int(val)) = constant.and_then(miri_to_const); |
76 |
| - if val == 0; |
77 |
| - then { |
78 |
| - true |
79 |
| - } else { |
80 |
| - false |
81 |
| - } |
82 |
| - } |
83 |
| -} |
84 |
| - |
85 |
| -fn has_repr_attr(cx: &LateContext<'tcx>, def_id: LocalDefId) -> bool { |
86 |
| - let attrs_get_attrs = get_attrs_get_attrs(cx, def_id); |
87 |
| - let attrs_hir_map = get_attrs_hir_map(cx, def_id); |
| 73 | + if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind; |
88 | 74 |
|
89 |
| - let b11 = includes_repr_attr_using_sym(attrs_get_attrs); |
90 |
| - let b12 = includes_repr_attr_using_sym(attrs_hir_map); |
91 |
| - let b21 = includes_repr_attr_using_helper(cx, attrs_get_attrs); |
92 |
| - let b22 = includes_repr_attr_using_helper(cx, attrs_hir_map); |
93 |
| - let b3 = has_repr_attr_using_adt(cx, def_id); |
94 |
| - let all_same = (b11 && b12 && b21 && b22 && b3) || (!b11 && !b12 && !b21 && !b22 && !b3); |
95 |
| - dbg!(all_same); |
| 75 | + // Check if that that array zero-sized. |
| 76 | + let length_ldid = cx.tcx.hir().local_def_id(length.hir_id); |
| 77 | + let length = Const::from_anon_const(cx.tcx, length_ldid); |
| 78 | + if let Some(Constant::Int(length)) = miri_to_const(length); |
| 79 | + if length == 0; |
96 | 80 |
|
97 |
| - b21 |
98 |
| -} |
99 |
| - |
100 |
| -fn get_attrs_get_attrs(cx: &LateContext<'tcx>, def_id: LocalDefId) -> &'tcx [Attribute] { |
101 |
| - cx.tcx.get_attrs(def_id.to_def_id()) |
102 |
| -} |
103 |
| - |
104 |
| -fn get_attrs_hir_map(cx: &LateContext<'tcx>, def_id: LocalDefId) -> &'tcx [Attribute] { |
105 |
| - let hir_map = cx.tcx.hir(); |
106 |
| - let hir_id = hir_map.local_def_id_to_hir_id(def_id); |
107 |
| - hir_map.attrs(hir_id) |
108 |
| -} |
109 |
| - |
110 |
| -// Don't like this because it's so dependent on the current list of `repr` flags and it would have |
111 |
| -// to be manually updated if that ever expanded. idk if there's any mechanism in `bitflag!` or |
112 |
| -// elsewhere for requiring that sort of exhaustiveness |
113 |
| -fn has_repr_attr_using_adt(cx: &LateContext<'tcx>, def_id: LocalDefId) -> bool { |
114 |
| - let ty = cx.tcx.type_of(def_id.to_def_id()); |
115 |
| - if let ty_mod::Adt(adt, _) = ty.kind() { |
116 |
| - if adt.is_struct() { |
117 |
| - let repr = adt.repr; |
118 |
| - let repr_attr = ReprFlags::IS_C | ReprFlags::IS_TRANSPARENT | ReprFlags::IS_SIMD | ReprFlags::IS_LINEAR; |
119 |
| - repr.int.is_some() || repr.align.is_some() || repr.pack.is_some() || repr.flags.intersects(repr_attr) |
| 81 | + then { |
| 82 | + true |
120 | 83 | } else {
|
121 | 84 | false
|
122 | 85 | }
|
123 |
| - } else { |
124 |
| - false |
125 | 86 | }
|
126 | 87 | }
|
127 | 88 |
|
128 |
| -fn includes_repr_attr_using_sym(attrs: &'tcx [Attribute]) -> bool { |
129 |
| - attrs.iter().any(|attr| attr.has_name(sym::repr)) |
130 |
| -} |
131 |
| - |
132 |
| -fn includes_repr_attr_using_helper(cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) -> bool { |
| 89 | +fn has_repr_attr(cx: &LateContext<'tcx>, attrs: &[Attribute]) -> bool { |
| 90 | + // NOTE: there's at least four other ways to do this but I liked this one the best. (All five agreed |
| 91 | + // on all testcases.) Happy to use another; they're in the commit history. |
133 | 92 | attrs
|
134 | 93 | .iter()
|
135 | 94 | .any(|attr| !rustc_attr::find_repr_attrs(cx.tcx.sess(), attr).is_empty())
|
|
0 commit comments