Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lint against &T to &mut T and &T to &UnsafeCell<T> transmutes #128351

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ lint_builtin_missing_doc = missing documentation for {$article} {$desc}

lint_builtin_mutable_transmutes =
transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell
.note = transmute from `{$from}` to `{$to}`

lint_builtin_no_mangle_fn = declaration of a `no_mangle` function
lint_builtin_no_mangle_generic = functions generic over types or consts must be mangled
Expand Down Expand Up @@ -888,6 +889,11 @@ lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe
.label = usage of unsafe attribute
lint_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)`

lint_unsafe_cell_conversions =
converting &T to &UnsafeCell<T> is error-prone, rarely intentional and may cause undefined behavior
.label = conversion happend here
.note = conversion from `{$from}` to `{$to}`

lint_unsupported_group = `{$lint_group}` lint group is not supported with ´--force-warn´

lint_untranslatable_diag = diagnostics should be created using translatable messages
Expand Down
78 changes: 5 additions & 73 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,11 @@ use crate::lints::{
BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures,
BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents,
BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinMutablesTransmutes,
BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed,
BuiltinTrivialBounds, BuiltinTypeAliasBounds, BuiltinUngatedAsyncFnTrackCaller,
BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub,
BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
BuiltinWhileTrue, InvalidAsmLabel,
BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinNoMangleGeneric,
BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds,
BuiltinTypeAliasBounds, BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit,
BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures,
BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel,
};
use crate::nonstandard_style::{MethodLateContext, method_context};
use crate::{
Expand Down Expand Up @@ -1076,72 +1075,6 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
}
}

declare_lint! {
/// The `mutable_transmutes` lint catches transmuting from `&T` to `&mut
/// T` because it is [undefined behavior].
///
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
///
/// ### Example
///
/// ```rust,compile_fail
/// unsafe {
/// let y = std::mem::transmute::<&i32, &mut i32>(&5);
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Certain assumptions are made about aliasing of data, and this transmute
/// violates those assumptions. Consider using [`UnsafeCell`] instead.
///
/// [`UnsafeCell`]: https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html
MUTABLE_TRANSMUTES,
Deny,
"transmuting &T to &mut T is undefined behavior, even if the reference is unused"
}

declare_lint_pass!(MutableTransmutes => [MUTABLE_TRANSMUTES]);

impl<'tcx> LateLintPass<'tcx> for MutableTransmutes {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
if let Some((&ty::Ref(_, _, from_mutbl), &ty::Ref(_, _, to_mutbl))) =
get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind()))
{
if from_mutbl < to_mutbl {
cx.emit_span_lint(MUTABLE_TRANSMUTES, expr.span, BuiltinMutablesTransmutes);
}
}

fn get_transmute_from_to<'tcx>(
cx: &LateContext<'tcx>,
expr: &hir::Expr<'_>,
) -> Option<(Ty<'tcx>, Ty<'tcx>)> {
let def = if let hir::ExprKind::Path(ref qpath) = expr.kind {
cx.qpath_res(qpath, expr.hir_id)
} else {
return None;
};
if let Res::Def(DefKind::Fn, did) = def {
if !def_id_is_transmute(cx, did) {
return None;
}
let sig = cx.typeck_results().node_type(expr.hir_id).fn_sig(cx.tcx);
let from = sig.inputs().skip_binder()[0];
let to = sig.output().skip_binder();
return Some((from, to));
}
None
}

fn def_id_is_transmute(cx: &LateContext<'_>, def_id: DefId) -> bool {
cx.tcx.is_intrinsic(def_id, sym::transmute)
}
}
}

declare_lint! {
/// The `unstable_features` lint detects uses of `#![feature]`.
///
Expand Down Expand Up @@ -1595,7 +1528,6 @@ declare_lint_pass!(
UNUSED_DOC_COMMENTS,
NO_MANGLE_CONST_ITEMS,
NO_MANGLE_GENERIC_ITEMS,
MUTABLE_TRANSMUTES,
UNSTABLE_FEATURES,
UNREACHABLE_PUB,
TYPE_ALIAS_BOUNDS,
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ mod lints;
mod macro_expr_fragment_specifier_2024_migration;
mod map_unit_fn;
mod multiple_supertrait_upcastable;
mod mutable_transmutes;
mod non_ascii_idents;
mod non_fmt_panic;
mod non_local_def;
Expand Down Expand Up @@ -98,6 +99,7 @@ use let_underscore::*;
use macro_expr_fragment_specifier_2024_migration::*;
use map_unit_fn::*;
use multiple_supertrait_upcastable::*;
use mutable_transmutes::*;
use non_ascii_idents::*;
use non_fmt_panic::NonPanicFmt;
use non_local_def::*;
Expand Down
37 changes: 35 additions & 2 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use std::num::NonZero;
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, ElidedLifetimeInPathSubdiag,
EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp, Subdiagnostic, SuggestionStyle,
EmissionGuarantee, IntoDiagArg, LintDiagnostic, MultiSpan, SubdiagMessageOp, Subdiagnostic,
SuggestionStyle,
};
use rustc_hir::def::Namespace;
use rustc_hir::def_id::DefId;
Expand Down Expand Up @@ -224,9 +225,41 @@ pub(crate) struct BuiltinConstNoMangle {
pub suggestion: Span,
}

// This would be more convenient as `from: String` and `to: String` instead of `from` and `to`
// being `ConversionBreadcrumbs`, but that'll mean we'll have to format the `Ty`s in the lint code,
// and then we'll ICE when the lint is allowed, because formatting a `Ty` is expensive and so the
// compiler panics if it is done when there aren't diagnostics.
pub(crate) struct ConversionBreadcrumbs<'a> {
pub before_ty: &'static str,
pub ty: Ty<'a>,
pub after_ty: String,
}

impl IntoDiagArg for ConversionBreadcrumbs<'_> {
fn into_diag_arg(self) -> DiagArgValue {
format!("{}{}{}", self.before_ty, self.ty, self.after_ty).into_diag_arg()
}
}

// mutable_transmutes.rs
#[derive(LintDiagnostic)]
#[diag(lint_builtin_mutable_transmutes)]
pub(crate) struct BuiltinMutablesTransmutes;
#[note]
pub(crate) struct BuiltinMutablesTransmutes<'a> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this one have a Builtin prefix but UnsafeCellTransmutes does not?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the Builtin prefix means (some lints include it while some don't), so I just randomly chose not to include it for the new lint, but not change the existing one.

pub from: ConversionBreadcrumbs<'a>,
pub to: ConversionBreadcrumbs<'a>,
}

// mutable_transmutes.rs
#[derive(LintDiagnostic)]
#[diag(lint_unsafe_cell_conversions)]
#[note]
pub(crate) struct UnsafeCellConversions<'a> {
#[label]
pub orig_cast: Option<Span>,
pub from: ConversionBreadcrumbs<'a>,
pub to: ConversionBreadcrumbs<'a>,
}

#[derive(LintDiagnostic)]
#[diag(lint_builtin_unstable_features)]
Expand Down
Loading
Loading