rust/clippy_lints/src/trailing_empty_array.rs

79 lines
2.9 KiB
Rust
Raw Normal View History

2021-10-18 19:41:27 -05:00
use clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir::{HirId, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
2021-10-18 19:41:27 -05:00
use rustc_middle::ty::Const;
2021-10-14 22:08:38 -05:00
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
2021-10-14 22:08:38 -05:00
declare_clippy_lint! {
/// ### What it does
2021-10-18 05:45:08 -05:00
/// Displays a warning when a struct with a trailing zero-sized array is declared without a `repr` attribute.
2021-10-14 22:08:38 -05:00
///
/// ### Why is this bad?
2021-10-18 05:45:08 -05:00
/// Zero-sized arrays aren't very useful in Rust itself, so such a struct is likely being created to pass to C code or in some other situation where control over memory layout matters (for example, in conjuction with manual allocation to make it easy to compute the offset of the array). Either way, `#[repr(C)]` (or another `repr` attribute) is needed.
2021-10-14 22:08:38 -05:00
///
/// ### Example
/// ```rust
/// struct RarelyUseful {
2021-10-18 20:32:00 -05:00
/// some_field: u32,
/// last: [u32; 0],
2021-10-14 22:08:38 -05:00
/// }
/// ```
///
2021-10-18 20:42:01 -05:00
/// Use instead:
2021-10-14 22:08:38 -05:00
/// ```rust
/// #[repr(C)]
2021-10-16 04:26:08 -05:00
/// struct MoreOftenUseful {
2021-10-14 22:08:38 -05:00
/// some_field: usize,
2021-10-18 20:32:00 -05:00
/// last: [u32; 0],
2021-10-14 22:08:38 -05:00
/// }
/// ```
Added `clippy::version` attribute to all normal lints So, some context for this, well, more a story. I'm not used to scripting, I've never really scripted anything, even if it's a valuable skill. I just never really needed it. Now, `@flip1995` correctly suggested using a script for this in `rust-clippy#7813`... And I decided to write a script using nushell because why not? This was a mistake... I spend way more time on this than I would like to admit. It has definitely been more than 4 hours. It shouldn't take that long, but me being new to scripting and nushell just wasn't a good mixture... Anyway, here is the script that creates another script which adds the versions. Fun... Just execute this on the `gh-pages` branch and the resulting `replacer.sh` in `clippy_lints` and it should all work. ```nu mv v0.0.212 rust-1.00.0; mv beta rust-1.57.0; mv master rust-1.58.0; let paths = (open ./rust-1.58.0/lints.json | select id id_span | flatten | select id path); let versions = ( ls | where name =~ "rust-" | select name | format {name}/lints.json | each { open $it | select id | insert version $it | str substring "5,11" version} | group-by id | rotate counter-clockwise id version | update version {get version | first 1} | flatten | select id version); $paths | each { |row| let version = ($versions | where id == ($row.id) | format {version}) let idu = ($row.id | str upcase) $"sed -i '0,/($idu),/{s/pub ($idu),/#[clippy::version = "($version)"]\n pub ($idu),/}' ($row.path)" } | str collect ";" | str find-replace --all '1.00.0' 'pre 1.29.0' | save "replacer.sh"; ``` And this still has some problems, but at this point I just want to be done -.-
2021-10-21 14:06:26 -05:00
#[clippy::version = "1.58.0"]
2021-10-19 16:33:43 -05:00
pub TRAILING_EMPTY_ARRAY,
2021-10-14 22:08:38 -05:00
nursery,
2021-10-18 05:52:57 -05:00
"struct with a trailing zero-sized array but without `#[repr(C)]` or another `repr` attribute"
2021-10-14 22:08:38 -05:00
}
2021-10-19 16:33:43 -05:00
declare_lint_pass!(TrailingEmptyArray => [TRAILING_EMPTY_ARRAY]);
2021-10-14 22:08:38 -05:00
2021-10-19 16:33:43 -05:00
impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
2021-10-18 18:53:05 -05:00
if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.hir_id()) {
span_lint_and_help(
cx,
2021-10-19 16:33:43 -05:00
TRAILING_EMPTY_ARRAY,
item.span,
"trailing zero-sized array in a struct which is not marked with a `repr` attribute",
None,
2021-10-18 19:41:27 -05:00
&format!(
"consider annotating `{}` with `#[repr(C)]` or another `repr` attribute",
cx.tcx.def_path_str(item.def_id.to_def_id())
),
);
}
}
}
fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool {
2021-10-18 19:41:27 -05:00
if_chain! {
// First check if last field is an array
if let ItemKind::Struct(data, _) = &item.kind;
if let Some(last_field) = data.fields().last();
if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind;
2021-10-18 05:45:08 -05:00
2021-10-18 19:41:27 -05:00
// Then check if that that array zero-sized
let length_ldid = cx.tcx.hir().local_def_id(length.hir_id);
let length = Const::from_anon_const(cx.tcx, length_ldid);
let length = length.try_eval_usize(cx.tcx, cx.param_env);
if let Some(length) = length;
then {
length == 0
} else {
false
}
}
}
2021-10-18 20:20:35 -05:00
fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2021-10-18 18:53:05 -05:00
cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::repr))
}