Skip to content

Commit 511fe04

Browse files
committed
Changed lint to check for std::fmt::Pointer and transmute
The lint checks arguments in calls to `transmute` or functions that have `Pointer` as a trait bound and displays a warning if the argument is a function reference. Also checks for `std::fmt::Pointer::fmt` to handle formatting macros although it doesn't depend on the exact expansion of the macro or formatting internals. `std::fmt::Pointer` and `std::fmt::Pointer::fmt` were also added as diagnostic items and symbols.
1 parent 3214de7 commit 511fe04

File tree

7 files changed

+324
-168
lines changed

7 files changed

+324
-168
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,134 +1,154 @@
11
use rustc_hir::def_id::DefId;
22
use rustc_middle::mir::visit::Visitor;
33
use rustc_middle::mir::*;
4-
use rustc_middle::ty::{self, Ty, TyCtxt};
5-
use rustc_session::lint::builtin::FUNCTION_REFERENCES;
6-
use rustc_span::Span;
4+
use rustc_middle::ty::{self, subst::GenericArgKind, PredicateAtom, Ty, TyCtxt, TyS};
5+
use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES;
6+
use rustc_span::{symbol::sym, Span};
77
use rustc_target::spec::abi::Abi;
88

9-
use crate::transform::{MirPass, MirSource};
9+
use crate::transform::MirPass;
1010

11-
pub struct FunctionReferences;
11+
pub struct FunctionItemReferences;
1212

13-
impl<'tcx> MirPass<'tcx> for FunctionReferences {
14-
fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
15-
let source_info = SourceInfo::outermost(body.span);
16-
let mut checker = FunctionRefChecker {
17-
tcx,
18-
body,
19-
potential_lints: Vec::new(),
20-
casts: Vec::new(),
21-
calls: Vec::new(),
22-
source_info,
23-
};
13+
impl<'tcx> MirPass<'tcx> for FunctionItemReferences {
14+
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
15+
let mut checker = FunctionItemRefChecker { tcx, body };
2416
checker.visit_body(&body);
2517
}
2618
}
2719

28-
struct FunctionRefChecker<'a, 'tcx> {
20+
struct FunctionItemRefChecker<'a, 'tcx> {
2921
tcx: TyCtxt<'tcx>,
3022
body: &'a Body<'tcx>,
31-
potential_lints: Vec<FunctionRefLint>,
32-
casts: Vec<Span>,
33-
calls: Vec<Span>,
34-
source_info: SourceInfo,
3523
}
3624

37-
impl<'a, 'tcx> Visitor<'tcx> for FunctionRefChecker<'a, 'tcx> {
38-
fn visit_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) {
39-
self.super_basic_block_data(block, data);
40-
for cast_span in self.casts.drain(..) {
41-
self.potential_lints.retain(|lint| lint.source_info.span != cast_span);
42-
}
43-
for call_span in self.calls.drain(..) {
44-
self.potential_lints.retain(|lint| lint.source_info.span != call_span);
45-
}
46-
for lint in self.potential_lints.drain(..) {
47-
lint.emit(self.tcx, self.body);
48-
}
49-
}
50-
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
51-
self.source_info = statement.source_info;
52-
self.super_statement(statement, location);
53-
}
25+
impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
5426
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
55-
self.source_info = terminator.source_info;
5627
if let TerminatorKind::Call {
5728
func,
58-
args: _,
29+
args,
5930
destination: _,
6031
cleanup: _,
6132
from_hir_call: _,
6233
fn_span: _,
6334
} = &terminator.kind
6435
{
65-
let span = match func {
66-
Operand::Copy(place) | Operand::Move(place) => {
67-
self.body.local_decls[place.local].source_info.span
36+
let func_ty = func.ty(self.body, self.tcx);
37+
if let ty::FnDef(def_id, substs_ref) = *func_ty.kind() {
38+
//check arguments for `std::mem::transmute`
39+
if self.tcx.is_diagnostic_item(sym::transmute, def_id) {
40+
let arg_ty = args[0].ty(self.body, self.tcx);
41+
for generic_inner_ty in arg_ty.walk() {
42+
if let GenericArgKind::Type(inner_ty) = generic_inner_ty.unpack() {
43+
if let Some(fn_id) = FunctionItemRefChecker::is_fn_ref(inner_ty) {
44+
let ident = self.tcx.item_name(fn_id).to_ident_string();
45+
let source_info = *self.body.source_info(location);
46+
let span = self.nth_arg_span(&args, 0);
47+
self.emit_lint(ident, fn_id, source_info, span);
48+
}
49+
}
50+
}
51+
} else {
52+
//check arguments for any function with `std::fmt::Pointer` as a bound trait
53+
let param_env = self.tcx.param_env(def_id);
54+
let bounds = param_env.caller_bounds();
55+
for bound in bounds {
56+
if let Some(bound_ty) = self.is_pointer_trait(&bound.skip_binders()) {
57+
let arg_defs = self.tcx.fn_sig(def_id).skip_binder().inputs();
58+
for (arg_num, arg_def) in arg_defs.iter().enumerate() {
59+
for generic_inner_ty in arg_def.walk() {
60+
if let GenericArgKind::Type(inner_ty) =
61+
generic_inner_ty.unpack()
62+
{
63+
//if any type reachable from the argument types in the fn sig matches the type bound by `Pointer`
64+
if TyS::same_type(inner_ty, bound_ty) {
65+
//check if this type is a function reference in the function call
66+
let norm_ty =
67+
self.tcx.subst_and_normalize_erasing_regions(
68+
substs_ref, param_env, &inner_ty,
69+
);
70+
if let Some(fn_id) =
71+
FunctionItemRefChecker::is_fn_ref(norm_ty)
72+
{
73+
let ident =
74+
self.tcx.item_name(fn_id).to_ident_string();
75+
let source_info = *self.body.source_info(location);
76+
let span = self.nth_arg_span(&args, arg_num);
77+
self.emit_lint(ident, fn_id, source_info, span);
78+
}
79+
}
80+
}
81+
}
82+
}
83+
}
84+
}
6885
}
69-
Operand::Constant(constant) => constant.span,
70-
};
71-
self.calls.push(span);
72-
};
86+
}
87+
}
7388
self.super_terminator(terminator, location);
7489
}
75-
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
76-
match rvalue {
77-
Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
78-
let decl = &self.body.local_decls[place.local];
79-
if let ty::FnDef(def_id, _) = decl.ty.kind {
80-
let ident = self
81-
.body
82-
.var_debug_info
83-
.iter()
84-
.find(|info| info.source_info.span == decl.source_info.span)
85-
.map(|info| info.name.to_ident_string())
86-
.unwrap_or(self.tcx.def_path_str(def_id));
87-
let lint = FunctionRefLint { ident, def_id, source_info: self.source_info };
88-
self.potential_lints.push(lint);
89-
}
90-
}
91-
Rvalue::Cast(_, op, _) => {
92-
let op_ty = op.ty(self.body, self.tcx);
93-
if self.is_fn_ref(op_ty) {
94-
self.casts.push(self.source_info.span);
90+
//check for `std::fmt::Pointer::<T>::fmt` where T is a function reference
91+
//this is used in formatting macros, but doesn't rely on the specific expansion
92+
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
93+
let op_ty = operand.ty(self.body, self.tcx);
94+
if let ty::FnDef(def_id, substs_ref) = *op_ty.kind() {
95+
if self.tcx.is_diagnostic_item(sym::pointer_trait_fmt, def_id) {
96+
let param_ty = substs_ref.type_at(0);
97+
if let Some(fn_id) = FunctionItemRefChecker::is_fn_ref(param_ty) {
98+
let source_info = *self.body.source_info(location);
99+
let callsite_ctxt = source_info.span.source_callsite().ctxt();
100+
let span = source_info.span.with_ctxt(callsite_ctxt);
101+
let ident = self.tcx.item_name(fn_id).to_ident_string();
102+
self.emit_lint(ident, fn_id, source_info, span);
95103
}
96104
}
97-
_ => {}
98105
}
99-
self.super_rvalue(rvalue, location);
106+
self.super_operand(operand, location);
100107
}
101108
}
102109

103-
impl<'a, 'tcx> FunctionRefChecker<'a, 'tcx> {
104-
fn is_fn_ref(&self, ty: Ty<'tcx>) -> bool {
105-
let referent_ty = match ty.kind {
110+
impl<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> {
111+
//return the bound parameter type if the trait is `std::fmt::Pointer`
112+
fn is_pointer_trait(&self, bound: &PredicateAtom<'tcx>) -> Option<Ty<'tcx>> {
113+
if let ty::PredicateAtom::Trait(predicate, _) = bound {
114+
if self.tcx.is_diagnostic_item(sym::pointer_trait, predicate.def_id()) {
115+
Some(predicate.trait_ref.self_ty())
116+
} else {
117+
None
118+
}
119+
} else {
120+
None
121+
}
122+
}
123+
fn is_fn_ref(ty: Ty<'tcx>) -> Option<DefId> {
124+
let referent_ty = match ty.kind() {
106125
ty::Ref(_, referent_ty, _) => Some(referent_ty),
107-
ty::RawPtr(ty_and_mut) => Some(ty_and_mut.ty),
126+
ty::RawPtr(ty_and_mut) => Some(&ty_and_mut.ty),
108127
_ => None,
109128
};
110129
referent_ty
111-
.map(|ref_ty| if let ty::FnDef(..) = ref_ty.kind { true } else { false })
112-
.unwrap_or(false)
130+
.map(
131+
|ref_ty| {
132+
if let ty::FnDef(def_id, _) = *ref_ty.kind() { Some(def_id) } else { None }
133+
},
134+
)
135+
.unwrap_or(None)
113136
}
114-
}
115-
116-
struct FunctionRefLint {
117-
ident: String,
118-
def_id: DefId,
119-
source_info: SourceInfo,
120-
}
121-
122-
impl<'tcx> FunctionRefLint {
123-
fn emit(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
124-
let def_id = self.def_id;
125-
let source_info = self.source_info;
126-
let lint_root = body.source_scopes[source_info.scope]
137+
fn nth_arg_span(&self, args: &Vec<Operand<'tcx>>, n: usize) -> Span {
138+
match &args[n] {
139+
Operand::Copy(place) | Operand::Move(place) => {
140+
self.body.local_decls[place.local].source_info.span
141+
}
142+
Operand::Constant(constant) => constant.span,
143+
}
144+
}
145+
fn emit_lint(&self, ident: String, fn_id: DefId, source_info: SourceInfo, span: Span) {
146+
let lint_root = self.body.source_scopes[source_info.scope]
127147
.local_data
128148
.as_ref()
129149
.assert_crate_local()
130150
.lint_root;
131-
let fn_sig = tcx.fn_sig(def_id);
151+
let fn_sig = self.tcx.fn_sig(fn_id);
132152
let unsafety = fn_sig.unsafety().prefix_str();
133153
let abi = match fn_sig.abi() {
134154
Abi::Rust => String::from(""),
@@ -142,17 +162,17 @@ impl<'tcx> FunctionRefLint {
142162
let num_args = fn_sig.inputs().map_bound(|inputs| inputs.len()).skip_binder();
143163
let variadic = if fn_sig.c_variadic() { ", ..." } else { "" };
144164
let ret = if fn_sig.output().skip_binder().is_unit() { "" } else { " -> _" };
145-
tcx.struct_span_lint_hir(FUNCTION_REFERENCES, lint_root, source_info.span, |lint| {
165+
self.tcx.struct_span_lint_hir(FUNCTION_ITEM_REFERENCES, lint_root, span, |lint| {
146166
lint.build(&format!(
147167
"cast `{}` with `as {}{}fn({}{}){}` to use it as a pointer",
148-
self.ident,
168+
ident,
149169
unsafety,
150170
abi,
151171
vec!["_"; num_args].join(", "),
152172
variadic,
153173
ret,
154174
))
155-
.emit()
175+
.emit();
156176
});
157177
}
158178
}

compiler/rustc_mir/src/transform/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ fn mir_const<'tcx>(
267267
// MIR-level lints.
268268
&check_packed_ref::CheckPackedRef,
269269
&check_const_item_mutation::CheckConstItemMutation,
270-
&function_references::FunctionReferences,
270+
&function_references::FunctionItemReferences,
271271
// What we need to do constant evaluation.
272272
&simplify::SimplifyCfg::new("initial"),
273273
&rustc_peek::SanityCheck,

compiler/rustc_session/src/lint/builtin.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2645,9 +2645,10 @@ declare_lint! {
26452645
reference: "issue #76200 <https://github.com/rust-lang/rust/issues/76200>",
26462646
edition: None,
26472647
};
2648+
}
26482649

26492650
declare_lint! {
2650-
pub FUNCTION_REFERENCES,
2651+
pub FUNCTION_ITEM_REFERENCES,
26512652
Warn,
26522653
"suggest casting functions to pointers when attempting to take references",
26532654
}
@@ -2767,7 +2768,7 @@ declare_lint_pass! {
27672768
CONST_EVALUATABLE_UNCHECKED,
27682769
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
27692770
UNINHABITED_STATIC,
2770-
FUNCTION_REFERENCES,
2771+
FUNCTION_ITEM_REFERENCES,
27712772
]
27722773
}
27732774

compiler/rustc_span/src/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,8 @@ symbols! {
796796
plugin_registrar,
797797
plugins,
798798
pointer,
799+
pointer_trait,
800+
pointer_trait_fmt,
799801
poll,
800802
position,
801803
post_dash_lto: "post-lto",

library/core/src/fmt/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -920,9 +920,11 @@ pub trait UpperHex {
920920
/// assert_eq!(&l_ptr[..2], "0x");
921921
/// ```
922922
#[stable(feature = "rust1", since = "1.0.0")]
923+
#[rustc_diagnostic_item = "pointer_trait"]
923924
pub trait Pointer {
924925
/// Formats the value using the given formatter.
925926
#[stable(feature = "rust1", since = "1.0.0")]
927+
#[rustc_diagnostic_item = "pointer_trait_fmt"]
926928
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
927929
}
928930

0 commit comments

Comments
 (0)