rustc_mir_transform/
function_item_references.rs1use itertools::Itertools;
2use rustc_abi::ExternAbi;
3use rustc_hir::def_id::DefId;
4use rustc_middle::mir::visit::Visitor;
5use rustc_middle::mir::*;
6use rustc_middle::ty::{self, EarlyBinder, GenericArgsRef, Ty, TyCtxt};
7use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES;
8use rustc_span::source_map::Spanned;
9use rustc_span::{Span, sym};
10
11use crate::errors;
12
13pub(super) struct FunctionItemReferences;
14
15impl<'tcx> crate::MirLint<'tcx> for FunctionItemReferences {
16 fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
17 let mut checker = FunctionItemRefChecker { tcx, body };
18 checker.visit_body(body);
19 }
20}
21
22struct FunctionItemRefChecker<'a, 'tcx> {
23 tcx: TyCtxt<'tcx>,
24 body: &'a Body<'tcx>,
25}
26
27impl<'tcx> Visitor<'tcx> for FunctionItemRefChecker<'_, 'tcx> {
28 fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
32 if let TerminatorKind::Call {
33 func,
34 args,
35 destination: _,
36 target: _,
37 unwind: _,
38 call_source: _,
39 fn_span: _,
40 } = &terminator.kind
41 {
42 let source_info = *self.body.source_info(location);
43 let func_ty = func.ty(self.body, self.tcx);
44 if let ty::FnDef(def_id, args_ref) = *func_ty.kind() {
45 if self.tcx.is_diagnostic_item(sym::transmute, def_id) {
47 let arg_ty = args[0].node.ty(self.body, self.tcx);
48 for inner_ty in arg_ty.walk().filter_map(|arg| arg.as_type()) {
49 if let Some((fn_id, fn_args)) = FunctionItemRefChecker::is_fn_ref(inner_ty)
50 {
51 let span = self.nth_arg_span(args, 0);
52 self.emit_lint(fn_id, fn_args, source_info, span);
53 }
54 }
55 } else {
56 self.check_bound_args(def_id, args_ref, args, source_info);
57 }
58 }
59 }
60 self.super_terminator(terminator, location);
61 }
62}
63
64impl<'tcx> FunctionItemRefChecker<'_, 'tcx> {
65 fn check_bound_args(
68 &self,
69 def_id: DefId,
70 args_ref: GenericArgsRef<'tcx>,
71 args: &[Spanned<Operand<'tcx>>],
72 source_info: SourceInfo,
73 ) {
74 let param_env = self.tcx.param_env(def_id);
75 let bounds = param_env.caller_bounds();
76 for bound in bounds {
77 if let Some(bound_ty) = self.is_pointer_trait(bound) {
78 let arg_defs =
80 self.tcx.fn_sig(def_id).instantiate_identity().skip_binder().inputs();
81 for (arg_num, arg_def) in arg_defs.iter().enumerate() {
82 for inner_ty in arg_def.walk().filter_map(|arg| arg.as_type()) {
84 if inner_ty == bound_ty {
86 let instantiated_ty =
88 EarlyBinder::bind(inner_ty).instantiate(self.tcx, args_ref);
89 if let Some((fn_id, fn_args)) =
90 FunctionItemRefChecker::is_fn_ref(instantiated_ty)
91 {
92 let mut span = self.nth_arg_span(args, arg_num);
93 if span.from_expansion() {
94 let callsite_ctxt = span.source_callsite().ctxt();
97 span = span.with_ctxt(callsite_ctxt);
98 }
99 self.emit_lint(fn_id, fn_args, source_info, span);
100 }
101 }
102 }
103 }
104 }
105 }
106 }
107
108 fn is_pointer_trait(&self, bound: ty::Clause<'tcx>) -> Option<Ty<'tcx>> {
110 if let ty::ClauseKind::Trait(predicate) = bound.kind().skip_binder() {
111 self.tcx
112 .is_diagnostic_item(sym::Pointer, predicate.def_id())
113 .then(|| predicate.trait_ref.self_ty())
114 } else {
115 None
116 }
117 }
118
119 fn is_fn_ref(ty: Ty<'tcx>) -> Option<(DefId, GenericArgsRef<'tcx>)> {
122 let referent_ty = match ty.kind() {
123 ty::Ref(_, referent_ty, _) => Some(referent_ty),
124 ty::RawPtr(referent_ty, _) => Some(referent_ty),
125 _ => None,
126 };
127 referent_ty
128 .map(|ref_ty| {
129 if let ty::FnDef(def_id, args_ref) = *ref_ty.kind() {
130 Some((def_id, args_ref))
131 } else {
132 None
133 }
134 })
135 .unwrap_or(None)
136 }
137
138 fn nth_arg_span(&self, args: &[Spanned<Operand<'tcx>>], n: usize) -> Span {
139 match &args[n].node {
140 Operand::Copy(place) | Operand::Move(place) => {
141 self.body.local_decls[place.local].source_info.span
142 }
143 Operand::Constant(constant) => constant.span,
144 }
145 }
146
147 fn emit_lint(
148 &self,
149 fn_id: DefId,
150 fn_args: GenericArgsRef<'tcx>,
151 source_info: SourceInfo,
152 span: Span,
153 ) {
154 let lint_root = self.body.source_scopes[source_info.scope]
155 .local_data
156 .as_ref()
157 .assert_crate_local()
158 .lint_root;
159 let fn_sig = self.tcx.fn_sig(fn_id).instantiate(self.tcx, fn_args);
161 let unsafety = fn_sig.safety().prefix_str();
162 let abi = match fn_sig.abi() {
163 ExternAbi::Rust => String::from(""),
164 other_abi => format!("extern {other_abi} "),
165 };
166 let ident = self.tcx.item_ident(fn_id);
167 let ty_params = fn_args.types().map(|ty| format!("{ty}"));
168 let const_params = fn_args.consts().map(|c| format!("{c}"));
169 let params = ty_params.chain(const_params).join(", ");
170 let num_args = fn_sig.inputs().map_bound(|inputs| inputs.len()).skip_binder();
171 let variadic = if fn_sig.c_variadic() { ", ..." } else { "" };
172 let ret = if fn_sig.output().skip_binder().is_unit() { "" } else { " -> _" };
173 let sugg = format!(
174 "{} as {}{}fn({}{}){}",
175 if params.is_empty() { ident.to_string() } else { format!("{ident}::<{params}>") },
176 unsafety,
177 abi,
178 vec!["_"; num_args].join(", "),
179 variadic,
180 ret,
181 );
182
183 self.tcx.emit_node_span_lint(
184 FUNCTION_ITEM_REFERENCES,
185 lint_root,
186 span,
187 errors::FnItemRef { span, sugg, ident },
188 );
189 }
190}