1 use rustc_errors
::Applicability
;
2 use rustc_hir
::def_id
::DefId
;
3 use rustc_middle
::mir
::visit
::Visitor
;
4 use rustc_middle
::mir
::*;
5 use rustc_middle
::ty
::{
7 subst
::{GenericArgKind, Subst, SubstsRef}
,
8 PredicateKind
, Ty
, TyCtxt
, TyS
,
10 use rustc_session
::lint
::builtin
::FUNCTION_ITEM_REFERENCES
;
11 use rustc_span
::{symbol::sym, Span}
;
12 use rustc_target
::spec
::abi
::Abi
;
14 use crate::transform
::MirPass
;
16 pub struct FunctionItemReferences
;
18 impl<'tcx
> MirPass
<'tcx
> for FunctionItemReferences
{
19 fn run_pass(&self, tcx
: TyCtxt
<'tcx
>, body
: &mut Body
<'tcx
>) {
20 let mut checker
= FunctionItemRefChecker { tcx, body }
;
21 checker
.visit_body(&body
);
25 struct FunctionItemRefChecker
<'a
, 'tcx
> {
30 impl<'a
, 'tcx
> Visitor
<'tcx
> for FunctionItemRefChecker
<'a
, 'tcx
> {
31 /// Emits a lint for function reference arguments bound by `fmt::Pointer` or passed to
32 /// `transmute`. This only handles arguments in calls outside macro expansions to avoid double
33 /// counting function references formatted as pointers by macros.
34 fn visit_terminator(&mut self, terminator
: &Terminator
<'tcx
>, location
: Location
) {
35 if let TerminatorKind
::Call
{
44 let source_info
= *self.body
.source_info(location
);
45 // Only handle function calls outside macros
46 if !source_info
.span
.from_expansion() {
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(self.tcx
) {
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
)
57 let span
= self.nth_arg_span(&args
, 0);
58 self.emit_lint(fn_id
, fn_substs
, source_info
, span
);
63 self.check_bound_args(def_id
, substs_ref
, &args
, source_info
);
68 self.super_terminator(terminator
, location
);
71 /// Emits a lint for function references formatted with `fmt::Pointer::fmt` by macros. These
72 /// cases are handled as operands instead of call terminators to avoid any dependence on
73 /// unstable, internal formatting details like whether `fmt` is called directly or not.
74 fn visit_operand(&mut self, operand
: &Operand
<'tcx
>, location
: Location
) {
75 let source_info
= *self.body
.source_info(location
);
76 if source_info
.span
.from_expansion() {
77 let op_ty
= operand
.ty(self.body
, self.tcx
);
78 if let ty
::FnDef(def_id
, substs_ref
) = *op_ty
.kind() {
79 if self.tcx
.is_diagnostic_item(sym
::pointer_trait_fmt
, def_id
) {
80 let param_ty
= substs_ref
.type_at(0);
81 if let Some((fn_id
, fn_substs
)) = FunctionItemRefChecker
::is_fn_ref(param_ty
) {
82 // The operand's ctxt wouldn't display the lint since it's inside a macro so
83 // we have to use the callsite's ctxt.
84 let callsite_ctxt
= source_info
.span
.source_callsite().ctxt();
85 let span
= source_info
.span
.with_ctxt(callsite_ctxt
);
86 self.emit_lint(fn_id
, fn_substs
, source_info
, span
);
91 self.super_operand(operand
, location
);
95 impl<'a
, 'tcx
> FunctionItemRefChecker
<'a
, 'tcx
> {
96 /// Emits a lint for function reference arguments bound by `fmt::Pointer` in calls to the
97 /// function defined by `def_id` with the substitutions `substs_ref`.
101 substs_ref
: SubstsRef
<'tcx
>,
102 args
: &[Operand
<'tcx
>],
103 source_info
: SourceInfo
,
105 let param_env
= self.tcx
.param_env(def_id
);
106 let bounds
= param_env
.caller_bounds();
107 for bound
in bounds
{
108 if let Some(bound_ty
) = self.is_pointer_trait(&bound
.kind().skip_binder()) {
109 // Get the argument types as they appear in the function signature.
110 let arg_defs
= self.tcx
.fn_sig(def_id
).skip_binder().inputs();
111 for (arg_num
, arg_def
) in arg_defs
.iter().enumerate() {
112 // For all types reachable from the argument type in the fn sig
113 for generic_inner_ty
in arg_def
.walk(self.tcx
) {
114 if let GenericArgKind
::Type(inner_ty
) = generic_inner_ty
.unpack() {
115 // If the inner type matches the type bound by `Pointer`
116 if TyS
::same_type(inner_ty
, bound_ty
) {
117 // Do a substitution using the parameters from the callsite
118 let subst_ty
= inner_ty
.subst(self.tcx
, substs_ref
);
119 if let Some((fn_id
, fn_substs
)) =
120 FunctionItemRefChecker
::is_fn_ref(subst_ty
)
122 let span
= self.nth_arg_span(args
, arg_num
);
123 self.emit_lint(fn_id
, fn_substs
, source_info
, span
);
133 /// If the given predicate is the trait `fmt::Pointer`, returns the bound parameter type.
134 fn is_pointer_trait(&self, bound
: &PredicateKind
<'tcx
>) -> Option
<Ty
<'tcx
>> {
135 if let ty
::PredicateKind
::Trait(predicate
) = bound
{
136 if self.tcx
.is_diagnostic_item(sym
::pointer_trait
, predicate
.def_id()) {
137 Some(predicate
.trait_ref
.self_ty())
146 /// If a type is a reference or raw pointer to the anonymous type of a function definition,
147 /// returns that function's `DefId` and `SubstsRef`.
148 fn is_fn_ref(ty
: Ty
<'tcx
>) -> Option
<(DefId
, SubstsRef
<'tcx
>)> {
149 let referent_ty
= match ty
.kind() {
150 ty
::Ref(_
, referent_ty
, _
) => Some(referent_ty
),
151 ty
::RawPtr(ty_and_mut
) => Some(&ty_and_mut
.ty
),
156 if let ty
::FnDef(def_id
, substs_ref
) = *ref_ty
.kind() {
157 Some((def_id
, substs_ref
))
165 fn nth_arg_span(&self, args
: &[Operand
<'tcx
>], n
: usize) -> Span
{
167 Operand
::Copy(place
) | Operand
::Move(place
) => {
168 self.body
.local_decls
[place
.local
].source_info
.span
170 Operand
::Constant(constant
) => constant
.span
,
177 fn_substs
: SubstsRef
<'tcx
>,
178 source_info
: SourceInfo
,
181 let lint_root
= self.body
.source_scopes
[source_info
.scope
]
184 .assert_crate_local()
186 let fn_sig
= self.tcx
.fn_sig(fn_id
);
187 let unsafety
= fn_sig
.unsafety().prefix_str();
188 let abi
= match fn_sig
.abi() {
189 Abi
::Rust
=> String
::from(""),
191 let mut s
= String
::from("extern \"");
192 s
.push_str(other_abi
.name());
197 let ident
= self.tcx
.item_name(fn_id
).to_ident_string();
198 let ty_params
= fn_substs
.types().map(|ty
| format
!("{}", ty
));
199 let const_params
= fn_substs
.consts().map(|c
| format
!("{}", c
));
200 let params
= ty_params
.chain(const_params
).collect
::<Vec
<String
>>().join(", ");
201 let num_args
= fn_sig
.inputs().map_bound(|inputs
| inputs
.len()).skip_binder();
202 let variadic
= if fn_sig
.c_variadic() { ", ..." }
else { "" }
;
203 let ret
= if fn_sig
.output().skip_binder().is_unit() { "" }
else { " -> _" }
;
204 self.tcx
.struct_span_lint_hir(FUNCTION_ITEM_REFERENCES
, lint_root
, span
, |lint
| {
205 lint
.build("taking a reference to a function item does not give a function pointer")
208 &format
!("cast `{}` to obtain a function pointer", ident
),
210 "{} as {}{}fn({}{}){}",
211 if params
.is_empty() { ident }
else { format!("{}
::<{}
>", ident, params) },
214 vec!["_
"; num_args].join(", "),
218 Applicability::Unspecified,