Skip to content

Commit 351c18a

Browse files
authored
Unrolled build for #141470
Rollup merge of #141470 - GuillaumeGomez:function_casts_as_integer, r=urgau Add new `function_casts_as_integer` lint The `function_casts_as_integer` lint detects cases where users cast a function pointer into an integer. *warn-by-default* ### Example ```rust fn foo() {} let x = foo as usize; ``` ``` warning: casting a function into an integer implicitly --> $DIR/function_casts_as_integer.rs:9:17 | LL | let x = foo as usize; | ^^^^^^^^ | help: add `fn() as usize` | LL | let x = foo as fn() as usize; | +++++++ ``` ### Explanation You should never cast a function directly into an integer but go through a cast as `fn` first to make it obvious what's going on. It also allows to prevent confusion with (associated) constants. Related to #81686 and https://stackoverflow.com/questions/68701177/whats-the-meaning-of-casting-a-rust-enum-variant-to-a-numeric-data-type r? ````@urgau````
2 parents c8f22ca + 66b4e93 commit 351c18a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+316
-89
lines changed

compiler/rustc_driver_impl/src/signal_handler.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ pub(super) fn install() {
152152
libc::sigaltstack(&alt_stack, ptr::null_mut());
153153

154154
let mut sa: libc::sigaction = mem::zeroed();
155-
sa.sa_sigaction = print_stack_trace as libc::sighandler_t;
155+
sa.sa_sigaction =
156+
print_stack_trace as unsafe extern "C" fn(libc::c_int) as libc::sighandler_t;
156157
sa.sa_flags = libc::SA_NODEFER | libc::SA_RESETHAND | libc::SA_ONSTACK;
157158
libc::sigemptyset(&mut sa.sa_mask);
158159
for (signum, _signame) in KILL_SIGNALS {

compiler/rustc_lint/messages.ftl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,9 @@ lint_forgetting_copy_types = calls to `std::mem::forget` with a value that imple
265265
lint_forgetting_references = calls to `std::mem::forget` with a reference instead of an owned value does nothing
266266
.label = argument has type `{$arg_ty}`
267267
268+
lint_function_casts_as_integer = direct cast of function item into an integer
269+
.cast_as_fn = first cast to a pointer `as *const ()`
270+
268271
lint_hidden_glob_reexport = private item shadows public glob re-export
269272
.note_glob_reexport = the name `{$name}` in the {$namespace} namespace is supposed to be publicly re-exported here
270273
.note_private_item = but the private item here shadows it
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use rustc_hir as hir;
2+
use rustc_middle::ty;
3+
use rustc_session::{declare_lint, declare_lint_pass};
4+
use rustc_span::BytePos;
5+
6+
use crate::lints::{FunctionCastsAsIntegerDiag, FunctionCastsAsIntegerSugg};
7+
use crate::{LateContext, LateLintPass};
8+
9+
declare_lint! {
10+
/// The `function_casts_as_integer` lint detects cases where a function item is cast
11+
/// to an integer.
12+
///
13+
/// ### Example
14+
///
15+
/// ```rust
16+
/// fn foo() {}
17+
/// let x = foo as usize;
18+
/// ```
19+
///
20+
/// {{produces}}
21+
///
22+
/// ### Explanation
23+
///
24+
/// When casting a function item to an integer, it implicitly creates a
25+
/// function pointer that will in turn be cast to an integer. By making
26+
/// it explicit, it improves readability of the code and prevents bugs.
27+
pub FUNCTION_CASTS_AS_INTEGER,
28+
Warn,
29+
"casting a function into an integer",
30+
}
31+
32+
declare_lint_pass!(
33+
/// Lint for casts of functions into integers.
34+
FunctionCastsAsInteger => [FUNCTION_CASTS_AS_INTEGER]
35+
);
36+
37+
impl<'tcx> LateLintPass<'tcx> for FunctionCastsAsInteger {
38+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
39+
let hir::ExprKind::Cast(cast_from_expr, cast_to_expr) = expr.kind else { return };
40+
let cast_to_ty = cx.typeck_results().expr_ty(expr);
41+
// Casting to a function (pointer?), so all good.
42+
//
43+
// Normally, only casts to integers is possible, but if it ever changed, this condition
44+
// will likely need to be updated.
45+
if matches!(cast_to_ty.kind(), ty::FnDef(..) | ty::FnPtr(..) | ty::RawPtr(..)) {
46+
return;
47+
}
48+
let cast_from_ty = cx.typeck_results().expr_ty(cast_from_expr);
49+
if matches!(cast_from_ty.kind(), ty::FnDef(..)) {
50+
cx.tcx.emit_node_span_lint(
51+
FUNCTION_CASTS_AS_INTEGER,
52+
expr.hir_id,
53+
cast_to_expr.span.with_lo(cast_from_expr.span.hi() + BytePos(1)),
54+
FunctionCastsAsIntegerDiag {
55+
sugg: FunctionCastsAsIntegerSugg {
56+
suggestion: cast_from_expr.span.shrink_to_hi(),
57+
cast_to_ty,
58+
},
59+
},
60+
);
61+
}
62+
}
63+
}

compiler/rustc_lint/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ mod errors;
4545
mod expect;
4646
mod for_loops_over_fallibles;
4747
mod foreign_modules;
48+
mod function_cast_as_integer;
4849
mod if_let_rescope;
4950
mod impl_trait_overcaptures;
5051
mod internal;
@@ -89,6 +90,7 @@ use deref_into_dyn_supertrait::*;
8990
use drop_forget_useless::*;
9091
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
9192
use for_loops_over_fallibles::*;
93+
use function_cast_as_integer::*;
9294
use if_let_rescope::IfLetRescope;
9395
use impl_trait_overcaptures::ImplTraitOvercaptures;
9496
use internal::*;
@@ -241,6 +243,7 @@ late_lint_methods!(
241243
IfLetRescope: IfLetRescope::default(),
242244
StaticMutRefs: StaticMutRefs,
243245
UnqualifiedLocalImports: UnqualifiedLocalImports,
246+
FunctionCastsAsInteger: FunctionCastsAsInteger,
244247
CheckTransmutes: CheckTransmutes,
245248
LifetimeSyntax: LifetimeSyntax,
246249
]

compiler/rustc_lint/src/lints.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3019,6 +3019,26 @@ pub(crate) struct ReservedMultihash {
30193019
pub suggestion: Span,
30203020
}
30213021

3022+
#[derive(LintDiagnostic)]
3023+
#[diag(lint_function_casts_as_integer)]
3024+
pub(crate) struct FunctionCastsAsIntegerDiag<'tcx> {
3025+
#[subdiagnostic]
3026+
pub(crate) sugg: FunctionCastsAsIntegerSugg<'tcx>,
3027+
}
3028+
3029+
#[derive(Subdiagnostic)]
3030+
#[suggestion(
3031+
lint_cast_as_fn,
3032+
code = " as *const ()",
3033+
applicability = "machine-applicable",
3034+
style = "verbose"
3035+
)]
3036+
pub(crate) struct FunctionCastsAsIntegerSugg<'tcx> {
3037+
#[primary_span]
3038+
pub suggestion: Span,
3039+
pub cast_to_ty: Ty<'tcx>,
3040+
}
3041+
30223042
#[derive(Debug)]
30233043
pub(crate) struct MismatchedLifetimeSyntaxes {
30243044
pub inputs: LifetimeSyntaxCategories<Vec<Span>>,

compiler/rustc_session/src/filesearch.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ fn current_dll_path() -> Result<PathBuf, String> {
7272

7373
#[cfg(not(target_os = "aix"))]
7474
unsafe {
75-
let addr = current_dll_path as usize as *mut _;
75+
let addr = current_dll_path as fn() -> Result<PathBuf, String> as *mut _;
7676
let mut info = std::mem::zeroed();
7777
if libc::dladdr(addr, &mut info) == 0 {
7878
return Err("dladdr failed".into());
@@ -151,7 +151,10 @@ fn current_dll_path() -> Result<PathBuf, String> {
151151
unsafe {
152152
GetModuleHandleExW(
153153
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
154-
PCWSTR(current_dll_path as *mut u16),
154+
PCWSTR(
155+
current_dll_path as fn() -> Result<std::path::PathBuf, std::string::String>
156+
as *mut u16,
157+
),
155158
&mut module,
156159
)
157160
}

library/core/src/intrinsics/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ pub const fn forget<T: ?Sized>(_: T);
622622
/// // Crucially, we `as`-cast to a raw pointer before `transmute`ing to a function pointer.
623623
/// // This avoids an integer-to-pointer `transmute`, which can be problematic.
624624
/// // Transmuting between raw pointers and function pointers (i.e., two pointer types) is fine.
625-
/// let pointer = foo as *const ();
625+
/// let pointer = foo as fn() -> i32 as *const ();
626626
/// let function = unsafe {
627627
/// std::mem::transmute::<*const (), fn() -> i32>(pointer)
628628
/// };

library/coretests/tests/ptr.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,7 @@ fn ptr_metadata() {
565565

566566
#[test]
567567
fn ptr_metadata_bounds() {
568+
#[allow(unknown_lints, function_casts_as_integer)]
568569
fn metadata_eq_method_address<T: ?Sized>() -> usize {
569570
// The `Metadata` associated type has an `Ord` bound, so this is valid:
570571
<<T as Pointee>::Metadata as PartialEq>::eq as usize

library/panic_unwind/src/seh.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ unsafe fn throw_exception(data: Option<Box<dyn Any + Send>>) -> ! {
336336
// In any case, we basically need to do something like this until we can
337337
// express more operations in statics (and we may never be able to).
338338
unsafe {
339+
#[allow(function_casts_as_integer)]
339340
atomic_store::<_, { AtomicOrdering::SeqCst }>(
340341
(&raw mut THROW_INFO.pmfnUnwind).cast(),
341342
ptr_t::new(exception_cleanup as *mut u8).raw(),
@@ -352,6 +353,7 @@ unsafe fn throw_exception(data: Option<Box<dyn Any + Send>>) -> ! {
352353
(&raw mut CATCHABLE_TYPE.pType).cast(),
353354
ptr_t::new((&raw mut TYPE_DESCRIPTOR).cast()).raw(),
354355
);
356+
#[allow(function_casts_as_integer)]
355357
atomic_store::<_, { AtomicOrdering::SeqCst }>(
356358
(&raw mut CATCHABLE_TYPE.copyFunction).cast(),
357359
ptr_t::new(exception_copy as *mut u8).raw(),

library/std/src/backtrace.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ impl Backtrace {
293293
if !Backtrace::enabled() {
294294
return Backtrace { inner: Inner::Disabled };
295295
}
296-
Backtrace::create(Backtrace::capture as usize)
296+
Backtrace::create(Backtrace::capture as fn() -> Backtrace as usize)
297297
}
298298

299299
/// Forcibly captures a full backtrace, regardless of environment variable
@@ -309,7 +309,7 @@ impl Backtrace {
309309
#[stable(feature = "backtrace", since = "1.65.0")]
310310
#[inline(never)] // want to make sure there's a frame here to remove
311311
pub fn force_capture() -> Backtrace {
312-
Backtrace::create(Backtrace::force_capture as usize)
312+
Backtrace::create(Backtrace::force_capture as fn() -> Backtrace as usize)
313313
}
314314

315315
/// Forcibly captures a disabled backtrace, regardless of environment

0 commit comments

Comments
 (0)