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