]>
Commit | Line | Data |
---|---|---|
5099ac24 | 1 | use itertools::Itertools; |
29967ef6 XL |
2 | use rustc_hir::def_id::DefId; |
3 | use rustc_middle::mir::visit::Visitor; | |
4 | use rustc_middle::mir::*; | |
49aad941 | 5 | use rustc_middle::ty::{self, EarlyBinder, PredicateKind, SubstsRef, Ty, TyCtxt}; |
29967ef6 XL |
6 | use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES; |
7 | use rustc_span::{symbol::sym, Span}; | |
8 | use rustc_target::spec::abi::Abi; | |
9 | ||
49aad941 | 10 | use crate::{errors, MirLint}; |
29967ef6 XL |
11 | |
12 | pub struct FunctionItemReferences; | |
13 | ||
a2a8927a XL |
14 | impl<'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 | ||
21 | struct FunctionItemRefChecker<'a, 'tcx> { | |
22 | tcx: TyCtxt<'tcx>, | |
23 | body: &'a Body<'tcx>, | |
24 | } | |
25 | ||
a2a8927a | 26 | impl<'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 | 64 | impl<'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 | } |