Skip to content

Commit 567b77f

Browse files
Add new function_casts_as_integer lint
1 parent 52bf0cf commit 567b77f

File tree

3 files changed

+93
-0
lines changed

3 files changed

+93
-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: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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+
if expr.span.in_external_macro(cx.tcx.sess.source_map()) {
40+
// We don't want to lint if it comes from an external proc-macro.
41+
return;
42+
}
43+
let hir::ExprKind::Cast(cast_from_expr, cast_to_expr) = expr.kind else { return };
44+
let cast_to_ty = cx.typeck_results().expr_ty(expr);
45+
// Casting to a function (pointer?), so all good.
46+
if matches!(cast_to_ty.kind(), ty::FnDef(..) | ty::FnPtr(..)) {
47+
return;
48+
}
49+
let cast_from_ty = cx.typeck_results().expr_ty(cast_from_expr);
50+
if matches!(cast_from_ty.kind(), ty::FnDef(..)) {
51+
cx.tcx.emit_node_span_lint(
52+
FUNCTION_CASTS_AS_INTEGER,
53+
expr.hir_id,
54+
cast_to_expr.span.with_lo(cast_from_expr.span.hi() + BytePos(1)),
55+
FunctionCastsAsIntegerMsg {
56+
sugg: FunctionCastsAsIntegerSugg {
57+
suggestion: cast_from_expr.span.shrink_to_hi(),
58+
// We get the function pointer to have a nice display.
59+
cast_from_ty: cx.typeck_results().expr_ty_adjusted(cast_from_expr),
60+
cast_to_ty,
61+
},
62+
},
63+
);
64+
}
65+
}
66+
}
67+
68+
#[derive(LintDiagnostic)]
69+
#[diag(lint_function_casts_as_integer)]
70+
struct FunctionCastsAsIntegerMsg<'tcx> {
71+
#[subdiagnostic]
72+
sugg: FunctionCastsAsIntegerSugg<'tcx>,
73+
}
74+
75+
#[derive(Subdiagnostic)]
76+
#[suggestion(
77+
lint_cast_as_fn,
78+
code = " as {cast_from_ty}",
79+
applicability = "machine-applicable",
80+
style = "verbose"
81+
)]
82+
struct FunctionCastsAsIntegerSugg<'tcx> {
83+
#[primary_span]
84+
pub suggestion: Span,
85+
pub cast_from_ty: Ty<'tcx>,
86+
pub cast_to_ty: Ty<'tcx>,
87+
}

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)