Skip to content

Commit 7976657

Browse files
Add missing_transmute_annotation lint
1 parent c9f2482 commit 7976657

File tree

4 files changed

+89
-0
lines changed

4 files changed

+89
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5426,6 +5426,7 @@ Released 2018-09-13
54265426
[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
54275427
[`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop
54285428
[`missing_trait_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_trait_methods
5429+
[`missing_transmute_annotations`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_transmute_annotations
54295430
[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
54305431
[`mixed_attributes_style`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_attributes_style
54315432
[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
677677
crate::trait_bounds::TYPE_REPETITION_IN_BOUNDS_INFO,
678678
crate::transmute::CROSSPOINTER_TRANSMUTE_INFO,
679679
crate::transmute::EAGER_TRANSMUTE_INFO,
680+
crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS_INFO,
680681
crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO,
681682
crate::transmute::TRANSMUTE_BYTES_TO_STR_INFO,
682683
crate::transmute::TRANSMUTE_FLOAT_TO_INT_INFO,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use rustc_errors::Applicability;
2+
use rustc_hir::{GenericArg, HirId, Node, Path, TyKind};
3+
use rustc_lint::LateContext;
4+
use rustc_middle::lint::in_external_macro;
5+
use rustc_middle::ty::Ty;
6+
7+
use clippy_utils::diagnostics::span_lint_and_sugg;
8+
9+
use crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS;
10+
11+
pub(super) fn check<'tcx>(
12+
cx: &LateContext<'tcx>,
13+
path: &Path<'tcx>,
14+
from_ty: Ty<'tcx>,
15+
to_ty: Ty<'tcx>,
16+
expr_hir_id: HirId,
17+
) -> bool {
18+
let last = path.segments.last().unwrap();
19+
if in_external_macro(cx.tcx.sess, last.ident.span) {
20+
// If it comes from a non-local macro, we ignore it.
21+
return false;
22+
}
23+
let args = last.args;
24+
let missing_generic = match args {
25+
Some(args) if !args.args.is_empty() => args.args.iter().any(|arg| match arg {
26+
GenericArg::Infer(_) => true,
27+
GenericArg::Type(ty) => matches!(ty.kind, TyKind::Infer),
28+
_ => false,
29+
}),
30+
_ => true,
31+
};
32+
if !missing_generic {
33+
return false;
34+
}
35+
// If it's being set as a local variable value...
36+
if let Some((_, node)) = cx.tcx.hir().parent_iter(expr_hir_id).next()
37+
&& let Node::Local(local) = node
38+
// ... which does have type annotations.
39+
&& local.ty.is_some()
40+
{
41+
return false;
42+
}
43+
span_lint_and_sugg(
44+
cx,
45+
MISSING_TRANSMUTE_ANNOTATIONS,
46+
last.ident.span.with_hi(path.span.hi()),
47+
"transmute used without annotations",
48+
"consider adding missing annotations",
49+
format!("{}::<{from_ty}, {to_ty}>", last.ident.as_str()),
50+
Applicability::MaybeIncorrect,
51+
);
52+
true
53+
}

clippy_lints/src/transmute/mod.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod crosspointer_transmute;
22
mod eager_transmute;
3+
mod missing_transmute_annotations;
34
mod transmute_float_to_int;
45
mod transmute_int_to_bool;
56
mod transmute_int_to_char;
@@ -520,6 +521,37 @@ declare_clippy_lint! {
520521
"eager evaluation of `transmute`"
521522
}
522523

524+
declare_clippy_lint! {
525+
/// ### What it does
526+
/// Checks if transmute calls have all generics specified.
527+
///
528+
/// ### Why is this bad?
529+
/// If not set, some unexpected output type could be retrieved instead of the expected one,
530+
/// potentially leading to invalid code.
531+
///
532+
/// This is particularly dangerous in case a seemingly innocent/unrelated change can cause type
533+
/// inference to start inferring a different type. E.g. the transmute is the tail expression of
534+
/// an `if` branch, and a different branches type changes, causing the transmute to silently
535+
/// have a different type, instead of a proper error.
536+
///
537+
/// ### Example
538+
/// ```no_run
539+
/// # unsafe {
540+
/// let x: i32 = std::mem::transmute([1u16, 2u16]);
541+
/// # }
542+
/// ```
543+
/// Use instead:
544+
/// ```no_run
545+
/// # unsafe {
546+
/// let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]);
547+
/// # }
548+
/// ```
549+
#[clippy::version = "1.77.0"]
550+
pub MISSING_TRANSMUTE_ANNOTATIONS,
551+
suspicious,
552+
"warns if a transmute call doesn't have all generics specified"
553+
}
554+
523555
pub struct Transmute {
524556
msrv: Msrv,
525557
}
@@ -542,6 +574,7 @@ impl_lint_pass!(Transmute => [
542574
TRANSMUTING_NULL,
543575
TRANSMUTE_NULL_TO_FN,
544576
EAGER_TRANSMUTE,
577+
MISSING_TRANSMUTE_ANNOTATIONS,
545578
]);
546579
impl Transmute {
547580
#[must_use]
@@ -579,6 +612,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
579612
| transmuting_null::check(cx, e, arg, to_ty)
580613
| transmute_null_to_fn::check(cx, e, arg, to_ty)
581614
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv)
615+
| missing_transmute_annotations::check(cx, path, from_ty, to_ty, e.hir_id)
582616
| transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
583617
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
584618
| transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)

0 commit comments

Comments
 (0)