Skip to content

Commit 973f5a4

Browse files
Add new function_casts_as_integer lint
1 parent 0fc6f16 commit 973f5a4

File tree

4 files changed

+89
-1
lines changed

4 files changed

+89
-1
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,9 @@ lint_forgetting_copy_types = calls to `std::mem::forget` with a value that imple
288288
lint_forgetting_references = calls to `std::mem::forget` with a reference instead of an owned value does nothing
289289
.label = argument has type `{$arg_ty}`
290290
291+
lint_function_casts_as_integer = direct cast of function item into an integer
292+
.cast_as_fn = first cast to a function pointer `{$cast_from_ty}`
293+
291294
lint_hidden_glob_reexport = private item shadows public glob re-export
292295
.note_glob_reexport = the name `{$name}` in the {$namespace} namespace is supposed to be publicly re-exported here
293296
.note_private_item = but the private item here shadows it
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use rustc_hir as hir;
2+
use rustc_macros::{LintDiagnostic, Subdiagnostic};
3+
use rustc_middle::ty::{self, Ty};
4+
use rustc_session::{declare_lint, declare_lint_pass};
5+
use rustc_span::{BytePos, Span};
6+
7+
use crate::{LateContext, LateLintPass};
8+
9+
declare_lint! {
10+
/// The lint detects cases where users cast a function into an integer.
11+
///
12+
/// ### Example
13+
///
14+
/// ```rust
15+
/// fn foo() {}
16+
/// let x = foo as usize;
17+
/// ```
18+
///
19+
/// {{produces}}
20+
///
21+
/// ### Explanation
22+
///
23+
/// You should never cast a function directly into an integer but go through
24+
/// a cast as `fn` first to make it obvious what's going on. It also allows
25+
/// to prevent confusion with (associated) constants.
26+
pub FUNCTION_CASTS_AS_INTEGER,
27+
Warn,
28+
"Casting a function into an integer",
29+
}
30+
31+
declare_lint_pass!(
32+
/// Lint for casts of functions into integers.
33+
FunctionCastsAsInteger => [FUNCTION_CASTS_AS_INTEGER]
34+
);
35+
36+
impl<'tcx> LateLintPass<'tcx> for FunctionCastsAsInteger {
37+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
38+
let hir::ExprKind::Cast(cast_from_expr, cast_to_expr) = expr.kind else { return };
39+
let cast_to_ty = cx.typeck_results().expr_ty(expr);
40+
// Casting to a function (pointer?), so all good.
41+
if matches!(cast_to_ty.kind(), ty::FnDef(..) | ty::FnPtr(..)) {
42+
return;
43+
}
44+
let cast_from_ty = cx.typeck_results().expr_ty(cast_from_expr);
45+
if matches!(cast_from_ty.kind(), ty::FnDef(..)) {
46+
cx.tcx.emit_node_span_lint(
47+
FUNCTION_CASTS_AS_INTEGER,
48+
expr.hir_id,
49+
cast_to_expr.span.with_lo(cast_from_expr.span.hi() + BytePos(1)),
50+
FunctionCastsAsIntegerMsg {
51+
sugg: FunctionCastsAsIntegerSugg {
52+
suggestion: cast_from_expr.span.shrink_to_hi(),
53+
// We get the function pointer to have a nice display.
54+
cast_from_ty: cx.typeck_results().expr_ty_adjusted(cast_from_expr),
55+
cast_to_ty,
56+
},
57+
},
58+
);
59+
}
60+
}
61+
}
62+
63+
#[derive(LintDiagnostic)]
64+
#[diag(lint_function_casts_as_integer)]
65+
struct FunctionCastsAsIntegerMsg<'tcx> {
66+
#[subdiagnostic]
67+
sugg: FunctionCastsAsIntegerSugg<'tcx>,
68+
}
69+
70+
#[derive(Subdiagnostic)]
71+
#[suggestion(
72+
lint_cast_as_fn,
73+
code = " as {cast_from_ty}",
74+
applicability = "machine-applicable",
75+
style = "verbose"
76+
)]
77+
struct FunctionCastsAsIntegerSugg<'tcx> {
78+
#[primary_span]
79+
pub suggestion: Span,
80+
pub cast_from_ty: Ty<'tcx>,
81+
pub cast_to_ty: Ty<'tcx>,
82+
}

compiler/rustc_lint/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ mod errors;
4848
mod expect;
4949
mod for_loops_over_fallibles;
5050
mod foreign_modules;
51+
mod function_cast_as_integer;
5152
pub mod hidden_unicode_codepoints;
5253
mod if_let_rescope;
5354
mod impl_trait_overcaptures;
@@ -91,6 +92,7 @@ use deref_into_dyn_supertrait::*;
9192
use drop_forget_useless::*;
9293
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
9394
use for_loops_over_fallibles::*;
95+
use function_cast_as_integer::*;
9496
use hidden_unicode_codepoints::*;
9597
use if_let_rescope::IfLetRescope;
9698
use impl_trait_overcaptures::ImplTraitOvercaptures;
@@ -245,6 +247,7 @@ late_lint_methods!(
245247
IfLetRescope: IfLetRescope::default(),
246248
StaticMutRefs: StaticMutRefs,
247249
UnqualifiedLocalImports: UnqualifiedLocalImports,
250+
FunctionCastsAsInteger: FunctionCastsAsInteger,
248251
]
249252
]
250253
);

compiler/rustc_session/src/filesearch.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ fn current_dll_path() -> Result<PathBuf, String> {
7373

7474
#[cfg(not(target_os = "aix"))]
7575
unsafe {
76-
let addr = current_dll_path as usize as *mut _;
76+
let addr = current_dll_path as fn() -> Result<PathBuf, String> as *mut _;
7777
let mut info = std::mem::zeroed();
7878
if libc::dladdr(addr, &mut info) == 0 {
7979
return Err("dladdr failed".into());

0 commit comments

Comments
 (0)