diff options
Diffstat (limited to 'src/tools/clippy/clippy_lints/src/transmute')
4 files changed, 271 insertions, 219 deletions
diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index 5f3e98144..424a6e926 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -9,6 +9,7 @@ mod transmute_ptr_to_ref; mod transmute_ref_to_ref; mod transmute_undefined_repr; mod transmutes_expressible_as_ptr_casts; +mod transmuting_null; mod unsound_collection_transmute; mod useless_transmute; mod utils; @@ -386,6 +387,28 @@ declare_clippy_lint! { "transmute to or from a type with an undefined representation" } +declare_clippy_lint! { + /// ### What it does + /// Checks for transmute calls which would receive a null pointer. + /// + /// ### Why is this bad? + /// Transmuting a null pointer is undefined behavior. + /// + /// ### Known problems + /// Not all cases can be detected at the moment of this writing. + /// For example, variables which hold a null pointer and are then fed to a `transmute` + /// call, aren't detectable yet. + /// + /// ### Example + /// ```rust + /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) }; + /// ``` + #[clippy::version = "1.35.0"] + pub TRANSMUTING_NULL, + correctness, + "transmutes from a null pointer to a reference, which is undefined behavior" +} + pub struct Transmute { msrv: Option<RustcVersion>, } @@ -404,6 +427,7 @@ impl_lint_pass!(Transmute => [ UNSOUND_COLLECTION_TRANSMUTE, TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, TRANSMUTE_UNDEFINED_REPR, + TRANSMUTING_NULL, ]); impl Transmute { #[must_use] @@ -436,6 +460,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { let linted = wrong_transmute::check(cx, e, from_ty, to_ty) | crosspointer_transmute::check(cx, e, from_ty, to_ty) + | transmuting_null::check(cx, e, arg, to_ty) | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv) | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs index 20b348fc1..b6d7d9f5b 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -5,9 +5,9 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::subst::{Subst, SubstsRef}; use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy}; -use rustc_span::Span; +use rustc_span::DUMMY_SP; -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, @@ -18,116 +18,89 @@ pub(super) fn check<'tcx>( let mut to_ty = cx.tcx.erase_regions(to_ty_orig); while from_ty != to_ty { - match reduce_refs(cx, e.span, from_ty, to_ty) { - ReducedTys::FromFatPtr { - unsized_ty, - to_ty: to_sub_ty, - } => match reduce_ty(cx, to_sub_ty) { - ReducedTy::TypeErasure => break, - ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break, - ReducedTy::Ref(to_sub_ty) => { - from_ty = unsized_ty; - to_ty = to_sub_ty; - continue; - }, - _ => { - span_lint_and_then( - cx, - TRANSMUTE_UNDEFINED_REPR, - e.span, - &format!("transmute from `{}` which has an undefined layout", from_ty_orig), - |diag| { - if from_ty_orig.peel_refs() != unsized_ty { - diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty)); - } - }, - ); - return true; - }, + let reduced_tys = reduce_refs(cx, from_ty, to_ty); + match (reduce_ty(cx, reduced_tys.from_ty), reduce_ty(cx, reduced_tys.to_ty)) { + // Various forms of type erasure. + (ReducedTy::TypeErasure { raw_ptr_only: false }, _) + | (_, ReducedTy::TypeErasure { raw_ptr_only: false }) => return false, + (ReducedTy::TypeErasure { .. }, _) if reduced_tys.from_raw_ptr => return false, + (_, ReducedTy::TypeErasure { .. }) if reduced_tys.to_raw_ptr => return false, + + // `Repr(C)` <-> unordered type. + // If the first field of the `Repr(C)` type matches then the transmute is ok + (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::UnorderedFields(to_sub_ty)) + | (ReducedTy::UnorderedFields(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty))) => { + from_ty = from_sub_ty; + to_ty = to_sub_ty; + continue; }, - ReducedTys::ToFatPtr { - unsized_ty, - from_ty: from_sub_ty, - } => match reduce_ty(cx, from_sub_ty) { - ReducedTy::TypeErasure => break, - ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break, - ReducedTy::Ref(from_sub_ty) => { - from_ty = from_sub_ty; - to_ty = unsized_ty; - continue; - }, - _ => { - span_lint_and_then( - cx, - TRANSMUTE_UNDEFINED_REPR, - e.span, - &format!("transmute to `{}` which has an undefined layout", to_ty_orig), - |diag| { - if to_ty_orig.peel_refs() != unsized_ty { - diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty)); - } - }, - ); - return true; - }, + (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) if reduced_tys.to_fat_ptr => { + from_ty = from_sub_ty; + to_ty = to_sub_ty; + continue; }, - ReducedTys::ToPtr { - from_ty: from_sub_ty, - to_ty: to_sub_ty, - } => match reduce_ty(cx, from_sub_ty) { - ReducedTy::UnorderedFields(from_ty) => { - span_lint_and_then( - cx, - TRANSMUTE_UNDEFINED_REPR, - e.span, - &format!("transmute from `{}` which has an undefined layout", from_ty_orig), - |diag| { - if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); - } - }, - ); - return true; - }, - ReducedTy::Ref(from_sub_ty) => { - from_ty = from_sub_ty; - to_ty = to_sub_ty; - continue; - }, - _ => break, + (ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty))) + if reduced_tys.from_fat_ptr => + { + from_ty = from_sub_ty; + to_ty = to_sub_ty; + continue; }, - ReducedTys::FromPtr { - from_ty: from_sub_ty, - to_ty: to_sub_ty, - } => match reduce_ty(cx, to_sub_ty) { - ReducedTy::UnorderedFields(to_ty) => { - span_lint_and_then( - cx, - TRANSMUTE_UNDEFINED_REPR, - e.span, - &format!("transmute to `{}` which has an undefined layout", to_ty_orig), - |diag| { - if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); - } - }, - ); - return true; - }, - ReducedTy::Ref(to_sub_ty) => { - from_ty = from_sub_ty; - to_ty = to_sub_ty; - continue; - }, - _ => break, + + // ptr <-> ptr + (ReducedTy::Other(from_sub_ty), ReducedTy::Other(to_sub_ty)) + if matches!(from_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_)) + && matches!(to_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_)) => + { + from_ty = from_sub_ty; + to_ty = to_sub_ty; + continue; }, - ReducedTys::Other { - from_ty: from_sub_ty, - to_ty: to_sub_ty, - } => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) { - (ReducedTy::TypeErasure, _) | (_, ReducedTy::TypeErasure) => return false, - (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => { - let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs)) + + // fat ptr <-> (*size, *size) + (ReducedTy::Other(_), ReducedTy::UnorderedFields(to_ty)) + if reduced_tys.from_fat_ptr && is_size_pair(to_ty) => + { + return false; + }, + (ReducedTy::UnorderedFields(from_ty), ReducedTy::Other(_)) + if reduced_tys.to_fat_ptr && is_size_pair(from_ty) => + { + return false; + }, + + // fat ptr -> some struct | some struct -> fat ptr + (ReducedTy::Other(_), _) if reduced_tys.from_fat_ptr => { + span_lint_and_then( + cx, + TRANSMUTE_UNDEFINED_REPR, + e.span, + &format!("transmute from `{}` which has an undefined layout", from_ty_orig), + |diag| { + if from_ty_orig.peel_refs() != from_ty.peel_refs() { + diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); + } + }, + ); + return true; + }, + (_, ReducedTy::Other(_)) if reduced_tys.to_fat_ptr => { + span_lint_and_then( + cx, + TRANSMUTE_UNDEFINED_REPR, + e.span, + &format!("transmute to `{}` which has an undefined layout", to_ty_orig), + |diag| { + if to_ty_orig.peel_refs() != to_ty.peel_refs() { + diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); + } + }, + ); + return true; + }, + + (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => { + let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs)) = (from_ty.kind(), to_ty.kind()) && from_def == to_def { @@ -138,79 +111,72 @@ pub(super) fn check<'tcx>( } else { None }; - span_lint_and_then( - cx, - TRANSMUTE_UNDEFINED_REPR, - e.span, - &format!( - "transmute from `{}` to `{}`, both of which have an undefined layout", - from_ty_orig, to_ty_orig - ), - |diag| { - if let Some(same_adt_did) = same_adt_did { - diag.note(&format!( - "two instances of the same generic type (`{}`) may have different layouts", - cx.tcx.item_name(same_adt_did) - )); - } else { - if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); - } - if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); - } - } - }, - ); - return true; - }, - ( - ReducedTy::UnorderedFields(from_ty), - ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_), - ) => { - span_lint_and_then( - cx, - TRANSMUTE_UNDEFINED_REPR, - e.span, - &format!("transmute from `{}` which has an undefined layout", from_ty_orig), - |diag| { + span_lint_and_then( + cx, + TRANSMUTE_UNDEFINED_REPR, + e.span, + &format!( + "transmute from `{}` to `{}`, both of which have an undefined layout", + from_ty_orig, to_ty_orig + ), + |diag| { + if let Some(same_adt_did) = same_adt_did { + diag.note(&format!( + "two instances of the same generic type (`{}`) may have different layouts", + cx.tcx.item_name(same_adt_did) + )); + } else { if from_ty_orig.peel_refs() != from_ty { diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); } - }, - ); - return true; - }, - ( - ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_), - ReducedTy::UnorderedFields(to_ty), - ) => { - span_lint_and_then( - cx, - TRANSMUTE_UNDEFINED_REPR, - e.span, - &format!("transmute into `{}` which has an undefined layout", to_ty_orig), - |diag| { if to_ty_orig.peel_refs() != to_ty { diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); } - }, - ); - return true; - }, - (ReducedTy::Ref(from_sub_ty), ReducedTy::Ref(to_sub_ty)) => { - from_ty = from_sub_ty; - to_ty = to_sub_ty; - continue; - }, - ( - ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param, - ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param, - ) - | ( - ReducedTy::UnorderedFields(_) | ReducedTy::Param, - ReducedTy::UnorderedFields(_) | ReducedTy::Param, - ) => break, + } + }, + ); + return true; + }, + ( + ReducedTy::UnorderedFields(from_ty), + ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true }, + ) => { + span_lint_and_then( + cx, + TRANSMUTE_UNDEFINED_REPR, + e.span, + &format!("transmute from `{}` which has an undefined layout", from_ty_orig), + |diag| { + if from_ty_orig.peel_refs() != from_ty { + diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); + } + }, + ); + return true; + }, + ( + ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true }, + ReducedTy::UnorderedFields(to_ty), + ) => { + span_lint_and_then( + cx, + TRANSMUTE_UNDEFINED_REPR, + e.span, + &format!("transmute into `{}` which has an undefined layout", to_ty_orig), + |diag| { + if to_ty_orig.peel_refs() != to_ty { + diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); + } + }, + ); + return true; + }, + ( + ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true }, + ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true }, + ) + | (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => { + break; }, } } @@ -218,64 +184,64 @@ pub(super) fn check<'tcx>( false } -enum ReducedTys<'tcx> { - FromFatPtr { unsized_ty: Ty<'tcx>, to_ty: Ty<'tcx> }, - ToFatPtr { unsized_ty: Ty<'tcx>, from_ty: Ty<'tcx> }, - ToPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> }, - FromPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> }, - Other { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> }, +#[expect(clippy::struct_excessive_bools)] +struct ReducedTys<'tcx> { + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + from_raw_ptr: bool, + to_raw_ptr: bool, + from_fat_ptr: bool, + to_fat_ptr: bool, } /// Remove references so long as both types are references. -fn reduce_refs<'tcx>( - cx: &LateContext<'tcx>, - span: Span, - mut from_ty: Ty<'tcx>, - mut to_ty: Ty<'tcx>, -) -> ReducedTys<'tcx> { - loop { - return match (from_ty.kind(), to_ty.kind()) { +fn reduce_refs<'tcx>(cx: &LateContext<'tcx>, mut from_ty: Ty<'tcx>, mut to_ty: Ty<'tcx>) -> ReducedTys<'tcx> { + let mut from_raw_ptr = false; + let mut to_raw_ptr = false; + let (from_fat_ptr, to_fat_ptr) = loop { + break match (from_ty.kind(), to_ty.kind()) { ( &(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. })), &(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. })), ) => { + from_raw_ptr = matches!(*from_ty.kind(), ty::RawPtr(_)); from_ty = from_sub_ty; + to_raw_ptr = matches!(*to_ty.kind(), ty::RawPtr(_)); to_ty = to_sub_ty; continue; }, (&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), _) - if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) => + if !unsized_ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env) => { - ReducedTys::FromFatPtr { unsized_ty, to_ty } + (true, false) }, (_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }))) - if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) => + if !unsized_ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env) => { - ReducedTys::ToFatPtr { unsized_ty, from_ty } + (false, true) }, - (&(ty::Ref(_, from_ty, _) | ty::RawPtr(TypeAndMut { ty: from_ty, .. })), _) => { - ReducedTys::FromPtr { from_ty, to_ty } - }, - (_, &(ty::Ref(_, to_ty, _) | ty::RawPtr(TypeAndMut { ty: to_ty, .. }))) => { - ReducedTys::ToPtr { from_ty, to_ty } - }, - _ => ReducedTys::Other { from_ty, to_ty }, + _ => (false, false), }; + }; + ReducedTys { + from_ty, + to_ty, + from_raw_ptr, + to_raw_ptr, + from_fat_ptr, + to_fat_ptr, } } enum ReducedTy<'tcx> { /// The type can be used for type erasure. - TypeErasure, + TypeErasure { raw_ptr_only: bool }, /// The type is a struct containing either zero non-zero sized fields, or multiple non-zero /// sized fields with a defined order. - OrderedFields(Ty<'tcx>), + /// The second value is the first non-zero sized type. + OrderedFields(Ty<'tcx>, Option<Ty<'tcx>>), /// The type is a struct containing multiple non-zero sized fields with no defined order. UnorderedFields(Ty<'tcx>), - /// The type is a reference to the contained type. - Ref(Ty<'tcx>), - /// The type is a generic parameter. - Param, /// Any other type. Other(Ty<'tcx>), } @@ -285,16 +251,18 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> loop { ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty); return match *ty.kind() { - ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::TypeErasure, + ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => { + ReducedTy::TypeErasure { raw_ptr_only: false } + }, ty::Array(sub_ty, _) | ty::Slice(sub_ty) => { ty = sub_ty; continue; }, - ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure, + ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure { raw_ptr_only: false }, ty::Tuple(args) => { let mut iter = args.iter(); let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else { - return ReducedTy::OrderedFields(ty); + return ReducedTy::OrderedFields(ty, None); }; if iter.all(|ty| is_zero_sized_ty(cx, ty)) { ty = sized_ty; @@ -309,27 +277,25 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> .iter() .map(|f| cx.tcx.bound_type_of(f.did).subst(cx.tcx, substs)); let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else { - return ReducedTy::TypeErasure; + return ReducedTy::TypeErasure { raw_ptr_only: false }; }; if iter.all(|ty| is_zero_sized_ty(cx, ty)) { ty = sized_ty; continue; } if def.repr().inhibit_struct_field_reordering_opt() { - ReducedTy::OrderedFields(ty) + ReducedTy::OrderedFields(ty, Some(sized_ty)) } else { ReducedTy::UnorderedFields(ty) } }, ty::Adt(def, _) if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => { - ReducedTy::TypeErasure + ReducedTy::TypeErasure { raw_ptr_only: false } }, // TODO: Check if the conversion to or from at least one of a union's fields is valid. - ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure, - ty::Foreign(_) => ReducedTy::TypeErasure, - ty::Ref(_, ty, _) => ReducedTy::Ref(ty), - ty::RawPtr(ty) => ReducedTy::Ref(ty.ty), - ty::Param(_) => ReducedTy::Param, + ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure { raw_ptr_only: false }, + ty::Foreign(_) | ty::Param(_) => ReducedTy::TypeErasure { raw_ptr_only: false }, + ty::Int(_) | ty::Uint(_) => ReducedTy::TypeErasure { raw_ptr_only: true }, _ => ReducedTy::Other(ty), }; } diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs b/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs new file mode 100644 index 000000000..d8e349af7 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs @@ -0,0 +1,61 @@ +use clippy_utils::consts::{constant_context, Constant}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_path_diagnostic_item; +use if_chain::if_chain; +use rustc_ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::symbol::sym; + +use super::TRANSMUTING_NULL; + +const LINT_MSG: &str = "transmuting a known null pointer into a reference"; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool { + if !to_ty.is_ref() { + return false; + } + + // Catching transmute over constants that resolve to `null`. + let mut const_eval_context = constant_context(cx, cx.typeck_results()); + if_chain! { + if let ExprKind::Path(ref _qpath) = arg.kind; + if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg); + if x == 0; + then { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); + return true; + } + } + + // Catching: + // `std::mem::transmute(0 as *const i32)` + if_chain! { + if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind; + if let ExprKind::Lit(ref lit) = inner_expr.kind; + if let LitKind::Int(0, _) = lit.node; + then { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); + return true; + } + } + + // Catching: + // `std::mem::transmute(std::ptr::null::<i32>())` + if_chain! { + if let ExprKind::Call(func1, []) = arg.kind; + if is_path_diagnostic_item(cx, func1, sym::ptr_null); + then { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); + return true; + } + } + + // FIXME: + // Also catch transmutations of variables which are known nulls. + // To do this, MIR const propagation seems to be the better tool. + // Whenever MIR const prop routines are more developed, this will + // become available. As of this writing (25/03/19) it is not yet. + false +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/utils.rs b/src/tools/clippy/clippy_lints/src/transmute/utils.rs index 74927570b..8bdadf244 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/utils.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/utils.rs @@ -2,7 +2,7 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; -use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited}; +use rustc_typeck::check::{cast::{self, CastCheckResult}, FnCtxt, Inherited}; // check if the component types of the transmuted collection and the result have different ABI, // size or alignment @@ -53,7 +53,7 @@ fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx> "Newly created FnCtxt contained errors" ); - if let Ok(check) = CastCheck::new( + if let CastCheckResult::Deferred(check) = cast::check_cast( &fn_ctxt, e, from_ty, to_ty, // We won't show any error to the user, so we don't care what the span is here. DUMMY_SP, DUMMY_SP, |