Initial impl of repr_packed_without_abi (#13398)
Fixes #13375 I've added the lint next to the other attribute-related ones. Not sure if this is the correct place, since while we are looking after the `packed`-attribute (there is nothing we can do about types defined elsewhere), we are more concerned about the type's representation set by the attribute (instead of "duplicate attributes" and such). The lint simply looks at the attributes themselves without concern for the item-kind, since items where `repr` is not allowed end up in a compile-error anyway. I'm somewhat concerned about the level of noise this lint would cause if/when it goes into stable, although it does _not_ come up in `lintcheck`. ``` changelog: [`repr_packed_without_abi`]: Initial implementation ```
This commit is contained in:
commit
8da8da8428
13 changed files with 187 additions and 25 deletions
|
|
@ -7,6 +7,7 @@ mod duplicated_attributes;
|
|||
mod inline_always;
|
||||
mod mixed_attributes_style;
|
||||
mod non_minimal_cfg;
|
||||
mod repr_attributes;
|
||||
mod should_panic_without_expect;
|
||||
mod unnecessary_clippy_cfg;
|
||||
mod useless_attribute;
|
||||
|
|
@ -272,6 +273,44 @@ declare_clippy_lint! {
|
|||
"ensures that all `should_panic` attributes specify its expected panic message"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for items with `#[repr(packed)]`-attribute without ABI qualification
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Without qualification, `repr(packed)` implies `repr(Rust)`. The Rust-ABI is inherently unstable.
|
||||
/// While this is fine as long as the type is accessed correctly within Rust-code, most uses
|
||||
/// of `#[repr(packed)]` involve FFI and/or data structures specified by network-protocols or
|
||||
/// other external specifications. In such situations, the unstable Rust-ABI implied in
|
||||
/// `#[repr(packed)]` may lead to future bugs should the Rust-ABI change.
|
||||
///
|
||||
/// In case you are relying on a well defined and stable memory layout, qualify the type's
|
||||
/// representation using the `C`-ABI. Otherwise, if the type in question is only ever
|
||||
/// accessed from Rust-code according to Rust's rules, use the `Rust`-ABI explicitly.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[repr(packed)]
|
||||
/// struct NetworkPacketHeader {
|
||||
/// header_length: u8,
|
||||
/// header_version: u16
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[repr(C, packed)]
|
||||
/// struct NetworkPacketHeader {
|
||||
/// header_length: u8,
|
||||
/// header_version: u16
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.84.0"]
|
||||
pub REPR_PACKED_WITHOUT_ABI,
|
||||
suspicious,
|
||||
"ensures that `repr(packed)` always comes with a qualified ABI"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `any` and `all` combinators in `cfg` with only one condition.
|
||||
|
|
@ -415,6 +454,7 @@ pub struct Attributes {
|
|||
|
||||
impl_lint_pass!(Attributes => [
|
||||
INLINE_ALWAYS,
|
||||
REPR_PACKED_WITHOUT_ABI,
|
||||
]);
|
||||
|
||||
impl Attributes {
|
||||
|
|
@ -431,6 +471,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|
|||
if is_relevant_item(cx, item) {
|
||||
inline_always::check(cx, item.span, item.ident.name, attrs);
|
||||
}
|
||||
repr_attributes::check(cx, item.span, attrs, &self.msrv);
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
|
||||
|
|
|
|||
43
clippy_lints/src/attrs/repr_attributes.rs
Normal file
43
clippy_lints/src/attrs/repr_attributes.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
use rustc_ast::Attribute;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs;
|
||||
|
||||
use super::REPR_PACKED_WITHOUT_ABI;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute], msrv: &msrvs::Msrv) {
|
||||
if msrv.meets(msrvs::REPR_RUST) {
|
||||
check_packed(cx, item_span, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_packed(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) {
|
||||
if let Some(items) = attrs.iter().find_map(|attr| {
|
||||
if attr.ident().is_some_and(|ident| matches!(ident.name, sym::repr)) {
|
||||
attr.meta_item_list()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) && let Some(packed) = items
|
||||
.iter()
|
||||
.find(|item| item.ident().is_some_and(|ident| matches!(ident.name, sym::packed)))
|
||||
&& !items.iter().any(|item| {
|
||||
item.ident()
|
||||
.is_some_and(|ident| matches!(ident.name, sym::C | sym::Rust))
|
||||
})
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
REPR_PACKED_WITHOUT_ABI,
|
||||
item_span,
|
||||
"item uses `packed` representation without ABI-qualification",
|
||||
|diag| {
|
||||
diag.warn("unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI")
|
||||
.help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`")
|
||||
.span_label(packed.span(), "`packed` representation set here");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -55,6 +55,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::attrs::INLINE_ALWAYS_INFO,
|
||||
crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO,
|
||||
crate::attrs::NON_MINIMAL_CFG_INFO,
|
||||
crate::attrs::REPR_PACKED_WITHOUT_ABI_INFO,
|
||||
crate::attrs::SHOULD_PANIC_WITHOUT_EXPECT_INFO,
|
||||
crate::attrs::UNNECESSARY_CLIPPY_CFG_INFO,
|
||||
crate::attrs::USELESS_ATTRIBUTE_INFO,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue