Skip to content

Commit 22ea47b

Browse files
Add new function_casts_as_integer lint
1 parent 52bf0cf commit 22ea47b

File tree

3 files changed

+89
-0
lines changed

3 files changed

+89
-0
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 = casting a function into an integer implicitly
292+
.cast_as_fn = add `{$cast_from_ty} as {$cast_to_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: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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+
/// ```
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+
if matches!(cast_to_ty.kind(), ty::FnDef(..) | ty::FnPtr(..)) {
43+
return;
44+
}
45+
let cast_from_ty = cx.typeck_results().expr_ty(cast_from_expr);
46+
if matches!(cast_from_ty.kind(), ty::FnDef(..)) {
47+
cx.tcx.emit_node_span_lint(
48+
FUNCTION_CASTS_AS_INTEGER,
49+
expr.hir_id,
50+
cast_to_expr.span.with_lo(cast_from_expr.span.hi() + BytePos(1)),
51+
FunctionCastsAsIntegerMsg {
52+
sugg: FunctionCastsAsIntegerSugg {
53+
suggestion: cast_from_expr.span.shrink_to_hi(),
54+
// We get the function pointer to have a nice display.
55+
cast_from_ty: cx.typeck_results().expr_ty_adjusted(cast_from_expr),
56+
cast_to_ty,
57+
},
58+
},
59+
);
60+
}
61+
}
62+
}
63+
64+
#[derive(LintDiagnostic)]
65+
#[diag(lint_function_casts_as_integer)]
66+
struct FunctionCastsAsIntegerMsg<'tcx> {
67+
#[subdiagnostic]
68+
sugg: FunctionCastsAsIntegerSugg<'tcx>,
69+
}
70+
71+
#[derive(Subdiagnostic)]
72+
#[suggestion(
73+
lint_cast_as_fn,
74+
code = " as {cast_from_ty}",
75+
applicability = "machine-applicable",
76+
style = "verbose"
77+
)]
78+
struct FunctionCastsAsIntegerSugg<'tcx> {
79+
#[primary_span]
80+
pub suggestion: Span,
81+
pub cast_from_ty: Ty<'tcx>,
82+
pub cast_to_ty: Ty<'tcx>,
83+
}

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
);

0 commit comments

Comments
 (0)