Skip to content

Commit f30f382

Browse files
committed
Add transmute/utils.rs
1 parent 4d4a5e1 commit f30f382

File tree

2 files changed

+116
-103
lines changed

2 files changed

+116
-103
lines changed

clippy_lints/src/transmute/mod.rs

+7-103
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1+
mod utils;
2+
use utils::*;
3+
14
use crate::utils::{
2-
in_constant, is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg,
3-
span_lint_and_then, sugg,
5+
in_constant, match_def_path, paths, snippet, span_lint, span_lint_and_sugg, span_lint_and_then, sugg,
46
};
57
use if_chain::if_chain;
68
use rustc_ast as ast;
79
use rustc_errors::Applicability;
8-
use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, TyKind, UnOp};
10+
use rustc_hir::{Expr, ExprKind, Mutability, UnOp};
911
use rustc_lint::{LateContext, LateLintPass};
10-
use rustc_middle::ty::{self, cast::CastKind, Ty};
12+
use rustc_middle::ty;
1113
use rustc_session::{declare_lint_pass, declare_tool_lint};
12-
use rustc_span::DUMMY_SP;
13-
use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited};
1414
use std::borrow::Cow;
1515

1616
declare_clippy_lint! {
@@ -326,6 +326,7 @@ static COLLECTIONS: &[&[&str]] = &[
326326
&paths::HASHSET,
327327
&paths::HASHMAP,
328328
];
329+
329330
impl<'tcx> LateLintPass<'tcx> for Transmute {
330331
#[allow(clippy::similar_names, clippy::too_many_lines)]
331332
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
@@ -664,100 +665,3 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
664665
}
665666
}
666667
}
667-
668-
/// Gets the snippet of `Bar` in `…::transmute<Foo, &Bar>`. If that snippet is
669-
/// not available , use
670-
/// the type's `ToString` implementation. In weird cases it could lead to types
671-
/// with invalid `'_`
672-
/// lifetime, but it should be rare.
673-
fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty: Ty<'_>) -> String {
674-
let seg = last_path_segment(path);
675-
if_chain! {
676-
if let Some(ref params) = seg.args;
677-
if !params.parenthesized;
678-
if let Some(to_ty) = params.args.iter().filter_map(|arg| match arg {
679-
GenericArg::Type(ty) => Some(ty),
680-
_ => None,
681-
}).nth(1);
682-
if let TyKind::Rptr(_, ref to_ty) = to_ty.kind;
683-
then {
684-
return snippet(cx, to_ty.ty.span, &to_ref_ty.to_string()).to_string();
685-
}
686-
}
687-
688-
to_ref_ty.to_string()
689-
}
690-
691-
// check if the component types of the transmuted collection and the result have different ABI,
692-
// size or alignment
693-
fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
694-
let empty_param_env = ty::ParamEnv::empty();
695-
// check if `from` and `to` are normalizable to avoid ICE (#4968)
696-
if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) {
697-
return false;
698-
}
699-
let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from));
700-
let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to));
701-
if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) {
702-
from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi
703-
} else {
704-
// no idea about layout, so don't lint
705-
false
706-
}
707-
}
708-
709-
/// Check if the type conversion can be expressed as a pointer cast, instead of
710-
/// a transmute. In certain cases, including some invalid casts from array
711-
/// references to pointers, this may cause additional errors to be emitted and/or
712-
/// ICE error messages. This function will panic if that occurs.
713-
fn can_be_expressed_as_pointer_cast<'tcx>(
714-
cx: &LateContext<'tcx>,
715-
e: &'tcx Expr<'_>,
716-
from_ty: Ty<'tcx>,
717-
to_ty: Ty<'tcx>,
718-
) -> bool {
719-
use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast};
720-
matches!(
721-
check_cast(cx, e, from_ty, to_ty),
722-
Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast)
723-
)
724-
}
725-
726-
/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of
727-
/// the cast. In certain cases, including some invalid casts from array references
728-
/// to pointers, this may cause additional errors to be emitted and/or ICE error
729-
/// messages. This function will panic if that occurs.
730-
fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option<CastKind> {
731-
let hir_id = e.hir_id;
732-
let local_def_id = hir_id.owner;
733-
734-
Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
735-
let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id);
736-
737-
// If we already have errors, we can't be sure we can pointer cast.
738-
assert!(
739-
!fn_ctxt.errors_reported_since_creation(),
740-
"Newly created FnCtxt contained errors"
741-
);
742-
743-
if let Ok(check) = CastCheck::new(
744-
&fn_ctxt, e, from_ty, to_ty,
745-
// We won't show any error to the user, so we don't care what the span is here.
746-
DUMMY_SP, DUMMY_SP,
747-
) {
748-
let res = check.do_check(&fn_ctxt);
749-
750-
// do_check's documentation says that it might return Ok and create
751-
// errors in the fcx instead of returing Err in some cases. Those cases
752-
// should be filtered out before getting here.
753-
assert!(
754-
!fn_ctxt.errors_reported_since_creation(),
755-
"`fn_ctxt` contained errors after cast check!"
756-
);
757-
758-
res.ok()
759-
} else {
760-
None
761-
}
762-
})
763-
}

clippy_lints/src/transmute/utils.rs

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
use crate::utils::{is_normalizable, last_path_segment, snippet};
2+
use if_chain::if_chain;
3+
use rustc_hir::{Expr, GenericArg, QPath, TyKind};
4+
use rustc_lint::LateContext;
5+
use rustc_middle::ty::{self, cast::CastKind, Ty};
6+
use rustc_span::DUMMY_SP;
7+
use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited};
8+
9+
/// Gets the snippet of `Bar` in `…::transmute<Foo, &Bar>`. If that snippet is
10+
/// not available , use
11+
/// the type's `ToString` implementation. In weird cases it could lead to types
12+
/// with invalid `'_`
13+
/// lifetime, but it should be rare.
14+
pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty: Ty<'_>) -> String {
15+
let seg = last_path_segment(path);
16+
if_chain! {
17+
if let Some(ref params) = seg.args;
18+
if !params.parenthesized;
19+
if let Some(to_ty) = params.args.iter().filter_map(|arg| match arg {
20+
GenericArg::Type(ty) => Some(ty),
21+
_ => None,
22+
}).nth(1);
23+
if let TyKind::Rptr(_, ref to_ty) = to_ty.kind;
24+
then {
25+
return snippet(cx, to_ty.ty.span, &to_ref_ty.to_string()).to_string();
26+
}
27+
}
28+
29+
to_ref_ty.to_string()
30+
}
31+
32+
// check if the component types of the transmuted collection and the result have different ABI,
33+
// size or alignment
34+
pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
35+
let empty_param_env = ty::ParamEnv::empty();
36+
// check if `from` and `to` are normalizable to avoid ICE (#4968)
37+
if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) {
38+
return false;
39+
}
40+
let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from));
41+
let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to));
42+
if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) {
43+
from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi
44+
} else {
45+
// no idea about layout, so don't lint
46+
false
47+
}
48+
}
49+
50+
/// Check if the type conversion can be expressed as a pointer cast, instead of
51+
/// a transmute. In certain cases, including some invalid casts from array
52+
/// references to pointers, this may cause additional errors to be emitted and/or
53+
/// ICE error messages. This function will panic if that occurs.
54+
pub(super) fn can_be_expressed_as_pointer_cast<'tcx>(
55+
cx: &LateContext<'tcx>,
56+
e: &'tcx Expr<'_>,
57+
from_ty: Ty<'tcx>,
58+
to_ty: Ty<'tcx>,
59+
) -> bool {
60+
use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast};
61+
matches!(
62+
check_cast(cx, e, from_ty, to_ty),
63+
Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast)
64+
)
65+
}
66+
67+
/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of
68+
/// the cast. In certain cases, including some invalid casts from array references
69+
/// to pointers, this may cause additional errors to be emitted and/or ICE error
70+
/// messages. This function will panic if that occurs.
71+
pub(super) fn check_cast<'tcx>(
72+
cx: &LateContext<'tcx>,
73+
e: &'tcx Expr<'_>,
74+
from_ty: Ty<'tcx>,
75+
to_ty: Ty<'tcx>,
76+
) -> Option<CastKind> {
77+
let hir_id = e.hir_id;
78+
let local_def_id = hir_id.owner;
79+
80+
Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
81+
let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id);
82+
83+
// If we already have errors, we can't be sure we can pointer cast.
84+
assert!(
85+
!fn_ctxt.errors_reported_since_creation(),
86+
"Newly created FnCtxt contained errors"
87+
);
88+
89+
if let Ok(check) = CastCheck::new(
90+
&fn_ctxt, e, from_ty, to_ty,
91+
// We won't show any error to the user, so we don't care what the span is here.
92+
DUMMY_SP, DUMMY_SP,
93+
) {
94+
let res = check.do_check(&fn_ctxt);
95+
96+
// do_check's documentation says that it might return Ok and create
97+
// errors in the fcx instead of returing Err in some cases. Those cases
98+
// should be filtered out before getting here.
99+
assert!(
100+
!fn_ctxt.errors_reported_since_creation(),
101+
"`fn_ctxt` contained errors after cast check!"
102+
);
103+
104+
res.ok()
105+
} else {
106+
None
107+
}
108+
})
109+
}

0 commit comments

Comments
 (0)