add lint for transmute from &T to &mut T of a ADT argument - #15992
add lint for transmute from &T to &mut T of a ADT argument#15992mendelsshop wants to merge 8 commits into
Conversation
4d4c366 to
61c208c
Compare
8b63f22 to
cbe29ff
Compare
27d8267 to
fb142f2
Compare
|
Edit: The way I am doing it now, I take the let _: Result<Option<&mut f32>, &usize> = std::mem::transmute(Option::<Result<&f32, usize>>::Some(Ok(&2f32)));Because in the order of the |
|
No changes for 7ac7844 |
9df378f to
6d0d072
Compare
This comment has been minimized.
This comment has been minimized.
053c5b8 to
1cc591c
Compare
|
r? clippy |
|
r? clippy |
There was a problem hiding this comment.
We should avoid false positives and get a better output, I would suggest that:
- When different ADT or different primitive types are detected, return
falseimmediately, as the types are structurally different - Instead of signaling the lint immediately (which will trigger several times if several references are cast into their mutable version), accumulate the pairs
(from_ty, to_ty)into a vector. At the end, if the vector is not empty, trigger the lint and add a note for every different pair (you can use.dedup_by()fromItertools) throughspan_lint_and_then(). - Add an example which checks that having several conversions is displayed properly (one lint, three notes), as in:
let _: Option<(&mut i32, &mut i32, &mut u32)> = std::mem::transmute(Some((&5i32, &10i32, &15u32)));
//~^ mutable_adt_argument_transmuteThis should print something like:
error: transmuting references into their mutable version is unsound
--> tests/ui/mutable_adt_argument_transmute.rs:17:57
|
LL | let _: Option<(&mut i32, &mut i32, &mut u32)> = std::mem::transmute(Some((&5i32, &10i32, &15u32)));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: transmuting from &i32 to &mut i32
= note: transmuting from &u32 to &mut u32| /// std::mem::transmute::<Option<&i32>, Option<&mut i32>>(Some(&5)); | ||
| /// } | ||
| /// ``` | ||
| #[clippy::version = "1.92.0"] |
There was a problem hiding this comment.
| #[clippy::version = "1.92.0"] | |
| #[clippy::version = "1.95.0"] |
| use rustc_middle::ty::{self, GenericArgKind, Ty}; | ||
|
|
||
| pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> bool { | ||
| // assumes walk will return all types in the same order |
There was a problem hiding this comment.
This should ensure that only adts are matched against, or it may also trigger for other types if the behavior of wrong_transmute is changed for example, won't it?
There was a problem hiding this comment.
Edit:
If you are talking about the arguments themselves, then they can be any type as long as they are both behind a reference, I currently do not check if the actual argument types that are behind the reference are the same.
I don't fully understand what you are asking, but it triggers for closure, fn, slice, reference, tuple, array and pointer types along with standard adts, basically any type that can have "arguments".
There was a problem hiding this comment.
Take for example the ty::FnPtr type: the type of the parameters may be safely transmuted from &T to &mut T, while the return type might not. In the two transmute examples below, only the second one should be linted:
// This one is not dangerous, as the function will not try
// to mutate its parameter anyway.
fn f(x: &i32) {}
let a: fn(&i32) = f;
let b: fn(&mut u32) = std::mem::transmute(a);
// This one should be linted because it would not be safe
// to assume that the return value can be mutated.
fn g() -> &'static i32 {
&0
}
let c: fn() -> &'static i32 = g;
let d: fn() -> &'static mut i32 = std::mem::transmute(c);The same thing probably happens in closures as well, and maybe others. In this case, you should either special-case the lint (such as linting only FnPtr and closures return types and ignore parameters), or not lint at all in those cases.
|
Reminder, once the PR becomes ready for a review, use |
1cc591c to
2318c90
Compare
| declare_clippy_lint! { | ||
| /// ### What it does | ||
| /// Checks for transmutes between the same adt, where at least one of the type argument goes from &T to &mut T. | ||
| /// This is an a more complicated version of https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#mutable-transmutes. |
There was a problem hiding this comment.
| /// This is an a more complicated version of https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#mutable-transmutes. | |
| /// This is a more complicated version of https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#mutable-transmutes. |
Also, maybe not "complicated".
There was a problem hiding this comment.
Also, maybe not "complicated".
Is "advanced" a better word?
This comment has been minimized.
This comment has been minimized.
1b2eb41 to
7b6cb4e
Compare
This comment has been minimized.
This comment has been minimized.
7b6cb4e to
07e26c6
Compare
|
Walking
Walking
Here you would miss a |
Does it make sense to duplicate |
You need to deduplicate to avoid infinite recursion. But you could walk two types in parallel and deduplicate by pairs. |
|
Infinite recursion only happens if you look at ADT fields. |
Ah, yes, that is what I had in mind, types can't be recursive even through pointers. |
You want to walk both types in lockstep with each other. This doesn't map well to an iterator and everything that does this has it's own rules for how to walk the structure. |
|
Ok, I moved away from |
| use rustc_middle::ty::{self, GenericArg, GenericArgKind, Ty}; | ||
|
|
||
| pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> bool { | ||
| // assumes walk will return all types in the same order |
There was a problem hiding this comment.
What is walk here? Is the comment out of date?
There was a problem hiding this comment.
It's an out of date comment.
7c4972b to
a065770
Compare
…t adts with different arities
…ly the return type matter for transmute
a065770 to
7ac7844
Compare
|
This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
View all comments
Checks transmutes between the same ADT, making sure type arguments don't go from
&Tto&mut T.Like it would catch something like:
changelog: new lint [
mutable_adt_argument_transmute]fixes #372
Caution
Concerns (1 active)
Managed by
@rustbot—see help for details.