]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_transform/src/function_item_references.rs
New upstream version 1.71.1+dfsg1
[rustc.git] / compiler / rustc_mir_transform / src / function_item_references.rs
CommitLineData
5099ac24 1use itertools::Itertools;
29967ef6
XL
2use rustc_hir::def_id::DefId;
3use rustc_middle::mir::visit::Visitor;
4use rustc_middle::mir::*;
49aad941 5use rustc_middle::ty::{self, EarlyBinder, PredicateKind, SubstsRef, Ty, TyCtxt};
29967ef6
XL
6use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES;
7use rustc_span::{symbol::sym, Span};
8use rustc_target::spec::abi::Abi;
9
49aad941 10use crate::{errors, MirLint};
29967ef6
XL
11
12pub struct FunctionItemReferences;
13
a2a8927a
XL
14impl<'tcx> MirLint<'tcx> for FunctionItemReferences {
15 fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
29967ef6
XL
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
a2a8927a 26impl<'tcx> Visitor<'tcx> for FunctionItemRefChecker<'_, 'tcx> {
29967ef6
XL
27 /// Emits a lint for function reference arguments bound by `fmt::Pointer` or passed to
28 /// `transmute`. This only handles arguments in calls outside macro expansions to avoid double
29 /// counting function references formatted as pointers by macros.
30 fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
31 if let TerminatorKind::Call {
32 func,
33 args,
34 destination: _,
923072b8 35 target: _,
353b0b11 36 unwind: _,
29967ef6
XL
37 from_hir_call: _,
38 fn_span: _,
39 } = &terminator.kind
40 {
41 let source_info = *self.body.source_info(location);
5099ac24
FG
42 let func_ty = func.ty(self.body, self.tcx);
43 if let ty::FnDef(def_id, substs_ref) = *func_ty.kind() {
44 // Handle calls to `transmute`
45 if self.tcx.is_diagnostic_item(sym::transmute, def_id) {
46 let arg_ty = args[0].ty(self.body, self.tcx);
49aad941
FG
47 for inner_ty in arg_ty.walk().filter_map(|arg| arg.as_type()) {
48 if let Some((fn_id, fn_substs)) =
49 FunctionItemRefChecker::is_fn_ref(inner_ty)
50 {
51 let span = self.nth_arg_span(&args, 0);
52 self.emit_lint(fn_id, fn_substs, source_info, span);
29967ef6 53 }
29967ef6 54 }
5099ac24
FG
55 } else {
56 self.check_bound_args(def_id, substs_ref, &args, source_info);
29967ef6
XL
57 }
58 }
59 }
60 self.super_terminator(terminator, location);
61 }
29967ef6
XL
62}
63
a2a8927a 64impl<'tcx> FunctionItemRefChecker<'_, 'tcx> {
29967ef6
XL
65 /// Emits a lint for function reference arguments bound by `fmt::Pointer` in calls to the
66 /// function defined by `def_id` with the substitutions `substs_ref`.
67 fn check_bound_args(
68 &self,
69 def_id: DefId,
70 substs_ref: SubstsRef<'tcx>,
5869c6ff 71 args: &[Operand<'tcx>],
29967ef6
XL
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 {
5869c6ff 77 if let Some(bound_ty) = self.is_pointer_trait(&bound.kind().skip_binder()) {
29967ef6 78 // Get the argument types as they appear in the function signature.
9ffffee4 79 let arg_defs = self.tcx.fn_sig(def_id).subst_identity().skip_binder().inputs();
29967ef6
XL
80 for (arg_num, arg_def) in arg_defs.iter().enumerate() {
81 // For all types reachable from the argument type in the fn sig
49aad941
FG
82 for inner_ty in arg_def.walk().filter_map(|arg| arg.as_type()) {
83 // If the inner type matches the type bound by `Pointer`
84 if inner_ty == bound_ty {
85 // Do a substitution using the parameters from the callsite
86 let subst_ty = EarlyBinder(inner_ty).subst(self.tcx, substs_ref);
87 if let Some((fn_id, fn_substs)) =
88 FunctionItemRefChecker::is_fn_ref(subst_ty)
89 {
90 let mut span = self.nth_arg_span(args, arg_num);
91 if span.from_expansion() {
92 // The operand's ctxt wouldn't display the lint since it's inside a macro so
93 // we have to use the callsite's ctxt.
94 let callsite_ctxt = span.source_callsite().ctxt();
95 span = span.with_ctxt(callsite_ctxt);
29967ef6 96 }
49aad941 97 self.emit_lint(fn_id, fn_substs, source_info, span);
29967ef6
XL
98 }
99 }
100 }
101 }
102 }
103 }
104 }
105
106 /// If the given predicate is the trait `fmt::Pointer`, returns the bound parameter type.
5869c6ff 107 fn is_pointer_trait(&self, bound: &PredicateKind<'tcx>) -> Option<Ty<'tcx>> {
487cf647 108 if let ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) = bound {
9ffffee4
FG
109 self.tcx
110 .is_diagnostic_item(sym::Pointer, predicate.def_id())
111 .then(|| predicate.trait_ref.self_ty())
29967ef6
XL
112 } else {
113 None
114 }
115 }
116
117 /// If a type is a reference or raw pointer to the anonymous type of a function definition,
118 /// returns that function's `DefId` and `SubstsRef`.
119 fn is_fn_ref(ty: Ty<'tcx>) -> Option<(DefId, SubstsRef<'tcx>)> {
120 let referent_ty = match ty.kind() {
121 ty::Ref(_, referent_ty, _) => Some(referent_ty),
122 ty::RawPtr(ty_and_mut) => Some(&ty_and_mut.ty),
123 _ => None,
124 };
125 referent_ty
126 .map(|ref_ty| {
127 if let ty::FnDef(def_id, substs_ref) = *ref_ty.kind() {
128 Some((def_id, substs_ref))
129 } else {
130 None
131 }
132 })
133 .unwrap_or(None)
134 }
135
5869c6ff 136 fn nth_arg_span(&self, args: &[Operand<'tcx>], n: usize) -> Span {
29967ef6
XL
137 match &args[n] {
138 Operand::Copy(place) | Operand::Move(place) => {
139 self.body.local_decls[place.local].source_info.span
140 }
141 Operand::Constant(constant) => constant.span,
142 }
143 }
144
145 fn emit_lint(
146 &self,
147 fn_id: DefId,
148 fn_substs: SubstsRef<'tcx>,
149 source_info: SourceInfo,
150 span: Span,
151 ) {
152 let lint_root = self.body.source_scopes[source_info.scope]
153 .local_data
154 .as_ref()
155 .assert_crate_local()
156 .lint_root;
9ffffee4
FG
157 // FIXME: use existing printing routines to print the function signature
158 let fn_sig = self.tcx.fn_sig(fn_id).subst(self.tcx, fn_substs);
29967ef6
XL
159 let unsafety = fn_sig.unsafety().prefix_str();
160 let abi = match fn_sig.abi() {
161 Abi::Rust => String::from(""),
162 other_abi => {
163 let mut s = String::from("extern \"");
164 s.push_str(other_abi.name());
165 s.push_str("\" ");
166 s
167 }
168 };
169 let ident = self.tcx.item_name(fn_id).to_ident_string();
170 let ty_params = fn_substs.types().map(|ty| format!("{}", ty));
171 let const_params = fn_substs.consts().map(|c| format!("{}", c));
5099ac24 172 let params = ty_params.chain(const_params).join(", ");
29967ef6
XL
173 let num_args = fn_sig.inputs().map_bound(|inputs| inputs.len()).skip_binder();
174 let variadic = if fn_sig.c_variadic() { ", ..." } else { "" };
175 let ret = if fn_sig.output().skip_binder().is_unit() { "" } else { " -> _" };
49aad941
FG
176 let sugg = format!(
177 "{} as {}{}fn({}{}){}",
178 if params.is_empty() { ident.clone() } else { format!("{}::<{}>", ident, params) },
179 unsafety,
180 abi,
181 vec!["_"; num_args].join(", "),
182 variadic,
183 ret,
184 );
185
186 self.tcx.emit_spanned_lint(
2b03887a
FG
187 FUNCTION_ITEM_REFERENCES,
188 lint_root,
189 span,
49aad941 190 errors::FnItemRef { span, sugg, ident },
2b03887a 191 );
29967ef6
XL
192 }
193}