-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(cast_ptr_alignment): also lint to-types with unknown alignment #15879
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
use clippy_utils::diagnostics::span_lint; | ||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; | ||
use clippy_utils::ty::is_c_void; | ||
use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, sym}; | ||
use rustc_abi::Align; | ||
use rustc_hir::{Expr, ExprKind, GenericArg}; | ||
use rustc_lint::LateContext; | ||
use rustc_middle::ty::layout::LayoutOf; | ||
use rustc_middle::ty::layout::{LayoutError, LayoutOf}; | ||
use rustc_middle::ty::{self, Ty}; | ||
|
||
use super::CAST_PTR_ALIGNMENT; | ||
|
@@ -29,24 +30,50 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f | |
if let ty::RawPtr(from_ptr_ty, _) = *cast_from.kind() | ||
&& let ty::RawPtr(to_ptr_ty, _) = *cast_to.kind() | ||
&& let Ok(from_layout) = cx.layout_of(from_ptr_ty) | ||
&& let Ok(to_layout) = cx.layout_of(to_ptr_ty) | ||
&& from_layout.align.abi < to_layout.align.abi | ||
// with c_void, we inherently need to trust the user | ||
&& !is_c_void(cx, from_ptr_ty) | ||
// when casting from a ZST, we don't know enough to properly lint | ||
&& !from_layout.is_zst() | ||
&& !is_used_as_unaligned(cx, expr) | ||
{ | ||
span_lint( | ||
cx, | ||
CAST_PTR_ALIGNMENT, | ||
expr.span, | ||
format!( | ||
"casting from `{cast_from}` to a more-strictly-aligned pointer (`{cast_to}`) ({} < {} bytes)", | ||
from_layout.align.bytes(), | ||
to_layout.align.bytes(), | ||
), | ||
); | ||
// When the to-type has a lower alignment, it's always properly aligned when cast. Only when the | ||
// to-type has a higher alignment can it not be properly aligned (16 -> 32, 48 is not a multiple of | ||
// 32!) | ||
match cx.layout_of(to_ptr_ty) { | ||
Ok(to_layout) => { | ||
if from_layout.align.abi < to_layout.align.abi { | ||
span_lint( | ||
cx, | ||
CAST_PTR_ALIGNMENT, | ||
expr.span, | ||
format!( | ||
"casting from `{cast_from}` to a more-strictly-aligned pointer (`{cast_to}`) ({} < {} bytes)", | ||
from_layout.align.bytes(), | ||
to_layout.align.bytes(), | ||
), | ||
); | ||
} | ||
}, | ||
Err(LayoutError::TooGeneric(too_generic_ty)) => { | ||
// With the maximum possible alignment, there is no "higher alignment" case. | ||
if from_layout.align.abi != Align::MAX { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you elaborate on this check? I might just be missing something. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, that part was a bit awkward to explain -- how about something like this? // If the from-type already has the maximum possible alignment, then, whatever the unknown alignment
// of the to-type is, it can't possibly be stricter than that of the from-type There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ohh!! Okay I got it now ignore me 😅. How about something like this?: // When the to-type has a lower alignment, it's always properly aligned when cast. Only when the to-type has a higher alignment can it not be properly aligned (16 -> 32, 48 is not a multiple of 32!). With the maximum possible alignment, there is no "higher alignment" case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just found this a bit confusing and I think elaborating on why we're linting only the higher alignment case somewhere could be helpful to others There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, the first two sentences honestly apply to the whole lint, so maybe I could put them onto the diff --git i/clippy_lints/src/casts/cast_ptr_alignment.rs w/clippy_lints/src/casts/cast_ptr_alignment.rs
index 12057fd40..76f4ae00e 100644
--- i/clippy_lints/src/casts/cast_ptr_alignment.rs
+++ w/clippy_lints/src/casts/cast_ptr_alignment.rs
@@ -36,6 +36,9 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f
&& !from_layout.is_zst()
&& !is_used_as_unaligned(cx, expr)
{
+ // When the to-type has a lower alignment, it's always properly aligned when cast. Only when the
+ // to-type has a higher alignment can it not be properly aligned (16 -> 32, 48 is not a multiple of
+ // 32!)
match cx.layout_of(to_ptr_ty) {
Ok(to_layout) => {
if from_layout.align.abi < to_layout.align.abi {
@@ -52,6 +55,7 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f
}
},
Err(LayoutError::TooGeneric(too_generic_ty)) => {
- // a maximally aligned type can be aligned down to anything
+ // With the maximum possible alignment, there is no "higher alignment" case.
if from_layout.align.abi != Align::MAX {
span_lint_and_then(
cx, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
span_lint_and_then( | ||
cx, | ||
CAST_PTR_ALIGNMENT, | ||
expr.span, | ||
format!("casting from `{cast_from}` to a possibly more-strictly-aligned pointer (`{cast_to}`)"), | ||
|diag| { | ||
if too_generic_ty == to_ptr_ty { | ||
diag.note(format!("the alignment of `{too_generic_ty}` can vary")); | ||
} else { | ||
diag.note(format!("the alignment of the target pointer isn't known because the alignment of `{too_generic_ty}` can vary")); | ||
} | ||
}, | ||
); | ||
} | ||
}, | ||
_ => {}, | ||
} | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(It doesn't let me select lines above this): Would you find a let-else more readable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I generally liked them but it's up to you. I find this current version just fine too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean something like this?
I don't think that would be possible unfortunately, given that there are multiple kinds of
LayoutError
, and onlyLayoutError::TooGeneric
gives us access to the exact part of the to-type that made it impossible to calculate the layout and therefore the alignment.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope! I mean replacing the whole
if let
. This does require a ton ofreturn
s but I personally like it. Again, up to you—implementation wise, your code is good to meThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At first I thought that would only require putting
return
s on thelet
expressions, but really they would be required on the rest of the expression as well, meaning 6return
s in total, which.. I'm not sure is really desirable? It would lead to a lot of boilerplate inbetween the actual logic...