1use 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::diagnostics;
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(
56 def_id,
57 args_ref.no_bound_vars().unwrap(),
58 args,
59 source_info,
60 );
61 }
62 }
63 }
64 self.super_terminator(terminator, location);
65 }
66}
67
68impl<'tcx> FunctionItemRefChecker<'_, 'tcx> {
69 fn check_bound_args(
72 &self,
73 def_id: DefId,
74 args_ref: GenericArgsRef<'tcx>,
75 args: &[Spanned<Operand<'tcx>>],
76 source_info: SourceInfo,
77 ) {
78 let param_env = self.tcx.param_env(def_id);
79 let bounds = param_env.caller_bounds();
80 for bound in bounds {
81 if let Some(bound_ty) = self.is_pointer_trait(bound) {
82 let arg_defs =
84 self.tcx.fn_sig(def_id).instantiate_identity().skip_binder().inputs();
85 for (arg_num, arg_def) in arg_defs.iter().enumerate() {
86 for inner_ty in arg_def.walk().filter_map(|arg| arg.as_type()) {
88 if inner_ty == bound_ty {
90 let instantiated_ty = EarlyBinder::bind(self.tcx, inner_ty)
92 .instantiate(self.tcx, args_ref)
93 .skip_norm_wip();
94 if let Some((fn_id, fn_args)) =
95 FunctionItemRefChecker::is_fn_ref(instantiated_ty)
96 {
97 let mut span = self.nth_arg_span(args, arg_num);
98 if span.from_expansion() {
99 let callsite_ctxt = span.source_callsite().ctxt();
102 span = span.with_ctxt(callsite_ctxt);
103 }
104 self.emit_lint(fn_id, fn_args, source_info, span);
105 }
106 }
107 }
108 }
109 }
110 }
111 }
112
113 fn is_pointer_trait(&self, bound: ty::Clause<'tcx>) -> Option<Ty<'tcx>> {
115 if let ty::ClauseKind::Trait(predicate) = bound.kind().skip_binder() {
116 self.tcx
117 .is_diagnostic_item(sym::Pointer, predicate.def_id())
118 .then(|| predicate.trait_ref.self_ty())
119 } else {
120 None
121 }
122 }
123
124 fn is_fn_ref(ty: Ty<'tcx>) -> Option<(DefId, GenericArgsRef<'tcx>)> {
127 let referent_ty = match ty.kind() {
128 ty::Ref(_, referent_ty, _) => Some(referent_ty),
129 ty::RawPtr(referent_ty, _) => Some(referent_ty),
130 _ => None,
131 };
132 referent_ty
133 .map(|ref_ty| {
134 if let ty::FnDef(def_id, args_ref) = *ref_ty.kind() {
135 Some((def_id, args_ref.no_bound_vars().unwrap()))
136 } else {
137 None
138 }
139 })
140 .unwrap_or(None)
141 }
142
143 fn nth_arg_span(&self, args: &[Spanned<Operand<'tcx>>], n: usize) -> Span {
144 args[n].node.span(&self.body.local_decls)
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 .unwrap_crate_local()
158 .lint_root;
159 let fn_sig = self.tcx.fn_sig(fn_id).instantiate(self.tcx, fn_args).skip_norm_wip();
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 diagnostics::FnItemRef { span, sugg, ident },
188 );
189 }
190}