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 =
87 EarlyBinder::bind(inner_ty).instantiate(self.tcx, args_ref);
88 if let Some((fn_id, fn_args)) =
89 FunctionItemRefChecker::is_fn_ref(instantiated_ty)
90 {
91 let mut span = self.nth_arg_span(args, arg_num);
92 if span.from_expansion() {
93 let callsite_ctxt = span.source_callsite().ctxt();
96 span = span.with_ctxt(callsite_ctxt);
97 }
98 self.emit_lint(fn_id, fn_args, source_info, span);
99 }
100 }
101 }
102 }
103 }
104 }
105 }
106
107 fn is_pointer_trait(&self, bound: ty::Clause<'tcx>) -> Option<Ty<'tcx>> {
109 if let ty::ClauseKind::Trait(predicate) = bound.kind().skip_binder() {
110 self.tcx
111 .is_diagnostic_item(sym::Pointer, predicate.def_id())
112 .then(|| predicate.trait_ref.self_ty())
113 } else {
114 None
115 }
116 }
117
118 fn is_fn_ref(ty: Ty<'tcx>) -> Option<(DefId, GenericArgsRef<'tcx>)> {
121 let referent_ty = match ty.kind() {
122 ty::Ref(_, referent_ty, _) => Some(referent_ty),
123 ty::RawPtr(referent_ty, _) => Some(referent_ty),
124 _ => None,
125 };
126 referent_ty
127 .map(|ref_ty| {
128 if let ty::FnDef(def_id, args_ref) = *ref_ty.kind() {
129 Some((def_id, args_ref))
130 } else {
131 None
132 }
133 })
134 .unwrap_or(None)
135 }
136
137 fn nth_arg_span(&self, args: &[Spanned<Operand<'tcx>>], n: usize) -> Span {
138 args[n].node.span(&self.body.local_decls)
139 }
140
141 fn emit_lint(
142 &self,
143 fn_id: DefId,
144 fn_args: GenericArgsRef<'tcx>,
145 source_info: SourceInfo,
146 span: Span,
147 ) {
148 let lint_root = self.body.source_scopes[source_info.scope]
149 .local_data
150 .as_ref()
151 .unwrap_crate_local()
152 .lint_root;
153 let fn_sig = self.tcx.fn_sig(fn_id).instantiate(self.tcx, fn_args);
155 let unsafety = fn_sig.safety().prefix_str();
156 let abi = match fn_sig.abi() {
157 ExternAbi::Rust => String::from(""),
158 other_abi => format!("extern {other_abi} "),
159 };
160 let ident = self.tcx.item_ident(fn_id);
161 let ty_params = fn_args.types().map(|ty| format!("{ty}"));
162 let const_params = fn_args.consts().map(|c| format!("{c}"));
163 let params = ty_params.chain(const_params).join(", ");
164 let num_args = fn_sig.inputs().map_bound(|inputs| inputs.len()).skip_binder();
165 let variadic = if fn_sig.c_variadic() { ", ..." } else { "" };
166 let ret = if fn_sig.output().skip_binder().is_unit() { "" } else { " -> _" };
167 let sugg = format!(
168 "{} as {}{}fn({}{}){}",
169 if params.is_empty() { ident.to_string() } else { format!("{ident}::<{params}>") },
170 unsafety,
171 abi,
172 vec!["_"; num_args].join(", "),
173 variadic,
174 ret,
175 );
176
177 self.tcx.emit_node_span_lint(
178 FUNCTION_ITEM_REFERENCES,
179 lint_root,
180 span,
181 errors::FnItemRef { span, sugg, ident },
182 );
183 }
184}