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::{Span, Spanned, sym};
9
10use crate::errors;
11
12pub(super) struct FunctionItemReferences;
13
14impl<'tcx> crate::MirLint<'tcx> for FunctionItemReferences {
15 fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
16 let mut checker = FunctionItemRefChecker { tcx, body };
17 checker.visit_body(body);
18 }
19}
20
21struct FunctionItemRefChecker<'a, 'tcx> {
22 tcx: TyCtxt<'tcx>,
23 body: &'a Body<'tcx>,
24}
25
26impl<'tcx> Visitor<'tcx> for FunctionItemRefChecker<'_, 'tcx> {
27 fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
31 if let TerminatorKind::Call {
32 func,
33 args,
34 destination: _,
35 target: _,
36 unwind: _,
37 call_source: _,
38 fn_span: _,
39 } = &terminator.kind
40 {
41 let source_info = *self.body.source_info(location);
42 let func_ty = func.ty(self.body, self.tcx);
43 if let ty::FnDef(def_id, args_ref) = *func_ty.kind() {
44 if self.tcx.is_diagnostic_item(sym::transmute, def_id) {
46 let arg_ty = args[0].node.ty(self.body, self.tcx);
47 for inner_ty in arg_ty.walk().filter_map(|arg| arg.as_type()) {
48 if let Some((fn_id, fn_args)) = FunctionItemRefChecker::is_fn_ref(inner_ty)
49 {
50 let span = self.nth_arg_span(args, 0);
51 self.emit_lint(fn_id, fn_args, source_info, span);
52 }
53 }
54 } else {
55 self.check_bound_args(def_id, args_ref, args, source_info);
56 }
57 }
58 }
59 self.super_terminator(terminator, location);
60 }
61}
62
63impl<'tcx> FunctionItemRefChecker<'_, 'tcx> {
64 fn check_bound_args(
67 &self,
68 def_id: DefId,
69 args_ref: GenericArgsRef<'tcx>,
70 args: &[Spanned<Operand<'tcx>>],
71 source_info: SourceInfo,
72 ) {
73 let param_env = self.tcx.param_env(def_id);
74 let bounds = param_env.caller_bounds();
75 for bound in bounds {
76 if let Some(bound_ty) = self.is_pointer_trait(bound) {
77 let arg_defs =
79 self.tcx.fn_sig(def_id).instantiate_identity().skip_binder().inputs();
80 for (arg_num, arg_def) in arg_defs.iter().enumerate() {
81 for inner_ty in arg_def.walk().filter_map(|arg| arg.as_type()) {
83 if inner_ty == bound_ty {
85 let instantiated_ty = EarlyBinder::bind(inner_ty)
87 .instantiate(self.tcx, args_ref)
88 .skip_norm_wip();
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 args[n].node.span(&self.body.local_decls)
140 }
141
142 fn emit_lint(
143 &self,
144 fn_id: DefId,
145 fn_args: GenericArgsRef<'tcx>,
146 source_info: SourceInfo,
147 span: Span,
148 ) {
149 let lint_root = self.body.source_scopes[source_info.scope]
150 .local_data
151 .as_ref()
152 .unwrap_crate_local()
153 .lint_root;
154 let fn_sig = self.tcx.fn_sig(fn_id).instantiate(self.tcx, fn_args).skip_norm_wip();
156 let unsafety = fn_sig.safety().prefix_str();
157 let abi = match fn_sig.abi() {
158 ExternAbi::Rust => String::from(""),
159 other_abi => format!("extern {other_abi} "),
160 };
161 let ident = self.tcx.item_ident(fn_id);
162 let ty_params = fn_args.types().map(|ty| format!("{ty}"));
163 let const_params = fn_args.consts().map(|c| format!("{c}"));
164 let params = ty_params.chain(const_params).join(", ");
165 let num_args = fn_sig.inputs().map_bound(|inputs| inputs.len()).skip_binder();
166 let variadic = if fn_sig.c_variadic() { ", ..." } else { "" };
167 let ret = if fn_sig.output().skip_binder().is_unit() { "" } else { " -> _" };
168 let sugg = format!(
169 "{} as {}{}fn({}{}){}",
170 if params.is_empty() { ident.to_string() } else { format!("{ident}::<{params}>") },
171 unsafety,
172 abi,
173 vec!["_"; num_args].join(", "),
174 variadic,
175 ret,
176 );
177
178 self.tcx.emit_node_span_lint(
179 FUNCTION_ITEM_REFERENCES,
180 lint_root,
181 span,
182 errors::FnItemRef { span, sugg, ident },
183 );
184 }
185}