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