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