Skip to content

Commit 8ec61a6

Browse files
authored
Merge pull request #2661 from devonhollowood/ptr-ptr-casts
Replace `misaligned_transmute` lint
2 parents c5f3107 + b77d740 commit 8ec61a6

File tree

11 files changed

+198
-36
lines changed

11 files changed

+198
-36
lines changed

clippy_lints/src/deprecated_lints.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,14 @@ declare_deprecated_lint! {
7171
pub STRING_TO_STRING,
7272
"using `string::to_string` is common even today and specialization will likely happen soon"
7373
}
74+
75+
/// **What it does:** Nothing. This lint has been deprecated.
76+
///
77+
/// **Deprecation reason:** This lint should never have applied to non-pointer types, as transmuting
78+
/// between non-pointer types of differing alignment is well-defined behavior (it's semantically
79+
/// equivalent to a memcpy). This lint has thus been refactored into two separate lints:
80+
/// cast_ptr_alignment and transmute_ptr_to_ptr.
81+
declare_deprecated_lint! {
82+
pub MISALIGNED_TRANSMUTE,
83+
"this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr"
84+
}

clippy_lints/src/lib.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
276276
"string_to_string",
277277
"using `string::to_string` is common even today and specialization will likely happen soon",
278278
);
279+
store.register_removed(
280+
"misaligned_transmute",
281+
"this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
282+
);
279283
// end deprecated lints, do not remove this comment, it’s used in `update_lints`
280284

281285
reg.register_late_lint_pass(box serde_api::Serde);
@@ -633,18 +637,19 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
633637
swap::MANUAL_SWAP,
634638
temporary_assignment::TEMPORARY_ASSIGNMENT,
635639
transmute::CROSSPOINTER_TRANSMUTE,
636-
transmute::MISALIGNED_TRANSMUTE,
637640
transmute::TRANSMUTE_BYTES_TO_STR,
638641
transmute::TRANSMUTE_INT_TO_BOOL,
639642
transmute::TRANSMUTE_INT_TO_CHAR,
640643
transmute::TRANSMUTE_INT_TO_FLOAT,
641644
transmute::TRANSMUTE_PTR_TO_REF,
645+
transmute::TRANSMUTE_PTR_TO_PTR,
642646
transmute::USELESS_TRANSMUTE,
643647
transmute::WRONG_TRANSMUTE,
644648
types::ABSURD_EXTREME_COMPARISONS,
645649
types::BORROWED_BOX,
646650
types::BOX_VEC,
647651
types::CAST_LOSSLESS,
652+
types::CAST_PTR_ALIGNMENT,
648653
types::CHAR_LIT_AS_U8,
649654
types::IMPLICIT_HASHER,
650655
types::LET_UNIT_VALUE,
@@ -782,12 +787,12 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
782787
swap::MANUAL_SWAP,
783788
temporary_assignment::TEMPORARY_ASSIGNMENT,
784789
transmute::CROSSPOINTER_TRANSMUTE,
785-
transmute::MISALIGNED_TRANSMUTE,
786790
transmute::TRANSMUTE_BYTES_TO_STR,
787791
transmute::TRANSMUTE_INT_TO_BOOL,
788792
transmute::TRANSMUTE_INT_TO_CHAR,
789793
transmute::TRANSMUTE_INT_TO_FLOAT,
790794
transmute::TRANSMUTE_PTR_TO_REF,
795+
transmute::TRANSMUTE_PTR_TO_PTR,
791796
transmute::USELESS_TRANSMUTE,
792797
types::BORROWED_BOX,
793798
types::CAST_LOSSLESS,
@@ -845,6 +850,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
845850
swap::ALMOST_SWAPPED,
846851
transmute::WRONG_TRANSMUTE,
847852
types::ABSURD_EXTREME_COMPARISONS,
853+
types::CAST_PTR_ALIGNMENT,
848854
types::UNIT_CMP,
849855
unicode::ZERO_WIDTH_SPACE,
850856
unused_io_amount::UNUSED_IO_AMOUNT,

clippy_lints/src/transmute.rs

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use rustc::lint::*;
22
use rustc::ty::{self, Ty};
33
use rustc::hir::*;
4-
use rustc::ty::layout::LayoutOf;
54
use std::borrow::Cow;
65
use syntax::ast;
76
use utils::{last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_then};
@@ -169,21 +168,31 @@ declare_clippy_lint! {
169168
"transmutes from an integer to a float"
170169
}
171170

172-
/// **What it does:** Checks for transmutes to a potentially less-aligned type.
171+
/// **What it does:** Checks for transmutes from a pointer to a pointer, or
172+
/// from a reference to a reference.
173173
///
174-
/// **Why is this bad?** This might result in undefined behavior.
174+
/// **Why is this bad?** Transmutes are dangerous, and these can instead be
175+
/// written as casts.
175176
///
176177
/// **Known problems:** None.
177178
///
178179
/// **Example:**
179180
/// ```rust
180-
/// // u32 is 32-bit aligned; u8 is 8-bit aligned
181-
/// let _: u32 = unsafe { std::mem::transmute([0u8; 4]) };
181+
/// let ptr = &1u32 as *const u32;
182+
/// unsafe {
183+
/// // pointer-to-pointer transmute
184+
/// let _: *const f32 = std::mem::transmute(ptr);
185+
/// // ref-ref transmute
186+
/// let _: &f32 = std::mem::transmute(&1u32);
187+
/// }
188+
/// // These can be respectively written:
189+
/// let _ = ptr as *const f32
190+
/// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
182191
/// ```
183192
declare_clippy_lint! {
184-
pub MISALIGNED_TRANSMUTE,
193+
pub TRANSMUTE_PTR_TO_PTR,
185194
complexity,
186-
"transmutes to a potentially less-aligned type"
195+
"transmutes from a pointer to a reference type"
187196
}
188197

189198
pub struct Transmute;
@@ -193,13 +202,13 @@ impl LintPass for Transmute {
193202
lint_array!(
194203
CROSSPOINTER_TRANSMUTE,
195204
TRANSMUTE_PTR_TO_REF,
205+
TRANSMUTE_PTR_TO_PTR,
196206
USELESS_TRANSMUTE,
197207
WRONG_TRANSMUTE,
198208
TRANSMUTE_INT_TO_CHAR,
199209
TRANSMUTE_BYTES_TO_STR,
200210
TRANSMUTE_INT_TO_BOOL,
201211
TRANSMUTE_INT_TO_FLOAT,
202-
MISALIGNED_TRANSMUTE
203212
)
204213
}
205214
}
@@ -220,18 +229,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
220229
e.span,
221230
&format!("transmute from a type (`{}`) to itself", from_ty),
222231
),
223-
_ if cx.layout_of(from_ty).ok().map(|a| a.align.abi())
224-
< cx.layout_of(to_ty).ok().map(|a| a.align.abi())
225-
=> span_lint(
226-
cx,
227-
MISALIGNED_TRANSMUTE,
228-
e.span,
229-
&format!(
230-
"transmute from `{}` to a less-aligned type (`{}`)",
231-
from_ty,
232-
to_ty,
233-
)
234-
),
235232
(&ty::TyRef(_, rty), &ty::TyRawPtr(ptr_ty)) => span_lint_and_then(
236233
cx,
237234
USELESS_TRANSMUTE,
@@ -363,9 +360,35 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
363360
);
364361
}
365362
)
363+
} else {
364+
span_lint_and_then(
365+
cx,
366+
TRANSMUTE_PTR_TO_PTR,
367+
e.span,
368+
"transmute from a reference to a reference",
369+
|db| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
370+
let sugg_paren = arg.as_ty(cx.tcx.mk_ptr(*ref_from)).as_ty(cx.tcx.mk_ptr(*ref_to));
371+
let sugg = if ref_to.mutbl == Mutability::MutMutable {
372+
sugg_paren.mut_addr_deref()
373+
} else {
374+
sugg_paren.addr_deref()
375+
};
376+
db.span_suggestion(e.span, "try", sugg.to_string());
377+
},
378+
)
366379
}
367380
}
368381
},
382+
(&ty::TyRawPtr(_), &ty::TyRawPtr(to_ty)) => span_lint_and_then(
383+
cx,
384+
TRANSMUTE_PTR_TO_PTR,
385+
e.span,
386+
"transmute from a pointer to a pointer",
387+
|db| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
388+
let sugg = arg.as_ty(cx.tcx.mk_ptr(to_ty));
389+
db.span_suggestion(e.span, "try", sugg.to_string());
390+
},
391+
),
369392
(&ty::TyInt(ast::IntTy::I8), &ty::TyBool) | (&ty::TyUint(ast::UintTy::U8), &ty::TyBool) => {
370393
span_lint_and_then(
371394
cx,

clippy_lints/src/types.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,25 @@ declare_clippy_lint! {
679679
"cast to the same type, e.g. `x as i32` where `x: i32`"
680680
}
681681

682+
/// **What it does:** Checks for casts from a more-strictly-aligned pointer to a
683+
/// less-strictly-aligned pointer
684+
///
685+
/// **Why is this bad?** Dereferencing the resulting pointer is undefined
686+
/// behavior.
687+
///
688+
/// **Known problems:** None.
689+
///
690+
/// **Example:**
691+
/// ```rust
692+
/// let _ = (&1u8 as *const u8) as *const u16;
693+
/// let _ = (&mut 1u8 as *mut u8) as *mut u16;
694+
/// ```
695+
declare_clippy_lint! {
696+
pub CAST_PTR_ALIGNMENT,
697+
correctness,
698+
"cast from a pointer to a less-strictly-aligned pointer"
699+
}
700+
682701
/// Returns the size in bits of an integral type.
683702
/// Will return 0 if the type is not an int or uint variant
684703
fn int_ty_to_nbits(typ: Ty, tcx: TyCtxt) -> u64 {
@@ -871,7 +890,8 @@ impl LintPass for CastPass {
871890
CAST_POSSIBLE_TRUNCATION,
872891
CAST_POSSIBLE_WRAP,
873892
CAST_LOSSLESS,
874-
UNNECESSARY_CAST
893+
UNNECESSARY_CAST,
894+
CAST_PTR_ALIGNMENT
875895
)
876896
}
877897
}
@@ -955,6 +975,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CastPass {
955975
},
956976
}
957977
}
978+
if_chain!{
979+
if let ty::TyRawPtr(from_ptr_ty) = &cast_from.sty;
980+
if let ty::TyRawPtr(to_ptr_ty) = &cast_to.sty;
981+
if let Some(from_align) = cx.layout_of(from_ptr_ty.ty).ok().map(|a| a.align.abi());
982+
if let Some(to_align) = cx.layout_of(to_ptr_ty.ty).ok().map(|a| a.align.abi());
983+
if from_align < to_align;
984+
then {
985+
span_lint(
986+
cx,
987+
CAST_PTR_ALIGNMENT,
988+
expr.span,
989+
&format!("casting from `{}` to a less-strictly-aligned pointer (`{}`)", cast_from, cast_to)
990+
);
991+
}
992+
}
958993
}
959994
}
960995
}

clippy_lints/src/utils/sugg.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,20 @@ impl<'a> Sugg<'a> {
160160
make_unop("*", self)
161161
}
162162

163+
/// Convenience method to create the `&*<expr>` suggestion. Currently this
164+
/// is needed because `sugg.deref().addr()` produces an unnecessary set of
165+
/// parentheses around the deref.
166+
pub fn addr_deref(self) -> Sugg<'static> {
167+
make_unop("&*", self)
168+
}
169+
170+
/// Convenience method to create the `&mut *<expr>` suggestion. Currently
171+
/// this is needed because `sugg.deref().mut_addr()` produces an unnecessary
172+
/// set of parentheses around the deref.
173+
pub fn mut_addr_deref(self) -> Sugg<'static> {
174+
make_unop("&mut *", self)
175+
}
176+
163177
/// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>`
164178
/// suggestion.
165179
pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> {

tests/ui/cast_alignment.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//! Test casts for alignment issues
2+
3+
#[warn(cast_ptr_alignment)]
4+
#[allow(no_effect, unnecessary_operation, cast_lossless)]
5+
fn main() {
6+
/* These should be warned against */
7+
8+
// cast to more-strictly-aligned type
9+
(&1u8 as *const u8) as *const u16;
10+
(&mut 1u8 as *mut u8) as *mut u16;
11+
12+
/* These should be okay */
13+
14+
// not a pointer type
15+
1u8 as u16;
16+
// cast to less-strictly-aligned type
17+
(&1u16 as *const u16) as *const u8;
18+
(&mut 1u16 as *mut u16) as *mut u8;
19+
}

tests/ui/cast_alignment.stderr

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: casting from `*const u8` to a less-strictly-aligned pointer (`*const u16`)
2+
--> $DIR/cast_alignment.rs:9:5
3+
|
4+
9 | (&1u8 as *const u8) as *const u16;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D cast-ptr-alignment` implied by `-D warnings`
8+
9+
error: casting from `*mut u8` to a less-strictly-aligned pointer (`*mut u16`)
10+
--> $DIR/cast_alignment.rs:10:5
11+
|
12+
10 | (&mut 1u8 as *mut u8) as *mut u16;
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
15+
error: aborting due to 2 previous errors
16+

tests/ui/deprecated.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@
99

1010
#[warn(unstable_as_mut_slice)]
1111

12+
#[warn(misaligned_transmute)]
13+
1214
fn main() {}

tests/ui/deprecated.stderr

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,11 @@ error: lint unstable_as_mut_slice has been removed: `Vec::as_mut_slice` has been
2424
10 | #[warn(unstable_as_mut_slice)]
2525
| ^^^^^^^^^^^^^^^^^^^^^
2626

27-
error: aborting due to 4 previous errors
27+
error: lint misaligned_transmute has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr
28+
--> $DIR/deprecated.rs:12:8
29+
|
30+
12 | #[warn(misaligned_transmute)]
31+
| ^^^^^^^^^^^^^^^^^^^^
32+
33+
error: aborting due to 5 previous errors
2834

tests/ui/transmute.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fn my_vec() -> MyVec<i32> {
1616
vec![]
1717
}
1818

19-
#[allow(needless_lifetimes)]
19+
#[allow(needless_lifetimes, transmute_ptr_to_ptr)]
2020
#[warn(useless_transmute)]
2121
unsafe fn _generic<'a, T, U: 'a>(t: &'a T) {
2222
let _: &'a T = core::intrinsics::transmute(t);
@@ -140,11 +140,23 @@ fn bytes_to_str(b: &[u8], mb: &mut [u8]) {
140140
let _: &mut str = unsafe { std::mem::transmute(mb) };
141141
}
142142

143-
#[warn(misaligned_transmute)]
144-
fn misaligned_transmute() {
145-
let _: u32 = unsafe { std::mem::transmute([0u8; 4]) }; // err
146-
let _: u32 = unsafe { std::mem::transmute(0f32) }; // ok (alignment-wise)
147-
let _: [u8; 4] = unsafe { std::mem::transmute(0u32) }; // ok (alignment-wise)
143+
#[warn(transmute_ptr_to_ptr)]
144+
fn transmute_ptr_to_ptr() {
145+
let ptr = &1u32 as *const u32;
146+
let mut_ptr = &mut 1u32 as *mut u32;
147+
unsafe {
148+
// pointer-to-pointer transmutes; bad
149+
let _: *const f32 = std::mem::transmute(ptr);
150+
let _: *mut f32 = std::mem::transmute(mut_ptr);
151+
// ref-ref transmutes; bad
152+
let _: &f32 = std::mem::transmute(&1u32);
153+
let _: &mut f32 = std::mem::transmute(&mut 1u32);
154+
}
155+
// These should be fine
156+
let _ = ptr as *const f32;
157+
let _ = mut_ptr as *mut f32;
158+
let _ = unsafe { &*(&1u32 as *const u32 as *const f32) };
159+
let _ = unsafe { &mut *(&mut 1u32 as *mut u32 as *mut f32) };
148160
}
149161

150162
fn main() { }

tests/ui/transmute.stderr

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,13 +204,31 @@ error: transmute from a `&mut [u8]` to a `&mut str`
204204
140 | let _: &mut str = unsafe { std::mem::transmute(mb) };
205205
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()`
206206

207-
error: transmute from `[u8; 4]` to a less-aligned type (`u32`)
208-
--> $DIR/transmute.rs:145:27
207+
error: transmute from a pointer to a pointer
208+
--> $DIR/transmute.rs:149:29
209209
|
210-
145 | let _: u32 = unsafe { std::mem::transmute([0u8; 4]) }; // err
211-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
210+
149 | let _: *const f32 = std::mem::transmute(ptr);
211+
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr as *const f32`
212212
|
213-
= note: `-D misaligned-transmute` implied by `-D warnings`
213+
= note: `-D transmute-ptr-to-ptr` implied by `-D warnings`
214214

215-
error: aborting due to 33 previous errors
215+
error: transmute from a pointer to a pointer
216+
--> $DIR/transmute.rs:150:27
217+
|
218+
150 | let _: *mut f32 = std::mem::transmute(mut_ptr);
219+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `mut_ptr as *mut f32`
220+
221+
error: transmute from a reference to a reference
222+
--> $DIR/transmute.rs:152:23
223+
|
224+
152 | let _: &f32 = std::mem::transmute(&1u32);
225+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)`
226+
227+
error: transmute from a reference to a reference
228+
--> $DIR/transmute.rs:153:27
229+
|
230+
153 | let _: &mut f32 = std::mem::transmute(&mut 1u32);
231+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)`
232+
233+
error: aborting due to 36 previous errors
216234

0 commit comments

Comments
 (0)