Skip to content

Commit c450d4a

Browse files
authored
Rollup merge of rust-lang#78659 - ayrtonm:fn-ref-lint-fix, r=oli-obk
Corrected suggestion for generic parameters in `function_item_references` lint This commit handles functions with generic type parameters like you pointed out as well as const generics. Also this is probably a minor thing, but the type alias you used in the example doesn't show up so the suggestion right now would be `size_of::<[u8; 16]> as fn() ->`. This is because the lint checker works with MIR instead of HIR. I don't think we can get the alias at that point, but let me know if I'm wrong and there's a way to fix this. Also I put you as the reviewer, but I'm not sure if you want to review it or if it makes more sense to ask one of the original reviewers of this lint. closes rust-lang#78571
2 parents 6a7753e + ace02c4 commit c450d4a

File tree

3 files changed

+111
-49
lines changed

3 files changed

+111
-49
lines changed

compiler/rustc_mir/src/transform/function_item_references.rs

+36-18
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,11 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
5151
let arg_ty = args[0].ty(self.body, self.tcx);
5252
for generic_inner_ty in arg_ty.walk() {
5353
if let GenericArgKind::Type(inner_ty) = generic_inner_ty.unpack() {
54-
if let Some(fn_id) = FunctionItemRefChecker::is_fn_ref(inner_ty) {
55-
let ident = self.tcx.item_name(fn_id).to_ident_string();
54+
if let Some((fn_id, fn_substs)) =
55+
FunctionItemRefChecker::is_fn_ref(inner_ty)
56+
{
5657
let span = self.nth_arg_span(&args, 0);
57-
self.emit_lint(ident, fn_id, source_info, span);
58+
self.emit_lint(fn_id, fn_substs, source_info, span);
5859
}
5960
}
6061
}
@@ -66,6 +67,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
6667
}
6768
self.super_terminator(terminator, location);
6869
}
70+
6971
/// Emits a lint for function references formatted with `fmt::Pointer::fmt` by macros. These
7072
/// cases are handled as operands instead of call terminators to avoid any dependence on
7173
/// unstable, internal formatting details like whether `fmt` is called directly or not.
@@ -76,13 +78,12 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
7678
if let ty::FnDef(def_id, substs_ref) = *op_ty.kind() {
7779
if self.tcx.is_diagnostic_item(sym::pointer_trait_fmt, def_id) {
7880
let param_ty = substs_ref.type_at(0);
79-
if let Some(fn_id) = FunctionItemRefChecker::is_fn_ref(param_ty) {
81+
if let Some((fn_id, fn_substs)) = FunctionItemRefChecker::is_fn_ref(param_ty) {
8082
// The operand's ctxt wouldn't display the lint since it's inside a macro so
8183
// we have to use the callsite's ctxt.
8284
let callsite_ctxt = source_info.span.source_callsite().ctxt();
8385
let span = source_info.span.with_ctxt(callsite_ctxt);
84-
let ident = self.tcx.item_name(fn_id).to_ident_string();
85-
self.emit_lint(ident, fn_id, source_info, span);
86+
self.emit_lint(fn_id, fn_substs, source_info, span);
8687
}
8788
}
8889
}
@@ -115,10 +116,11 @@ impl<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> {
115116
if TyS::same_type(inner_ty, bound_ty) {
116117
// Do a substitution using the parameters from the callsite
117118
let subst_ty = inner_ty.subst(self.tcx, substs_ref);
118-
if let Some(fn_id) = FunctionItemRefChecker::is_fn_ref(subst_ty) {
119-
let ident = self.tcx.item_name(fn_id).to_ident_string();
119+
if let Some((fn_id, fn_substs)) =
120+
FunctionItemRefChecker::is_fn_ref(subst_ty)
121+
{
120122
let span = self.nth_arg_span(args, arg_num);
121-
self.emit_lint(ident, fn_id, source_info, span);
123+
self.emit_lint(fn_id, fn_substs, source_info, span);
122124
}
123125
}
124126
}
@@ -127,6 +129,7 @@ impl<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> {
127129
}
128130
}
129131
}
132+
130133
/// If the given predicate is the trait `fmt::Pointer`, returns the bound parameter type.
131134
fn is_pointer_trait(&self, bound: &PredicateAtom<'tcx>) -> Option<Ty<'tcx>> {
132135
if let ty::PredicateAtom::Trait(predicate, _) = bound {
@@ -139,22 +142,26 @@ impl<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> {
139142
None
140143
}
141144
}
145+
142146
/// If a type is a reference or raw pointer to the anonymous type of a function definition,
143-
/// returns that function's `DefId`.
144-
fn is_fn_ref(ty: Ty<'tcx>) -> Option<DefId> {
147+
/// returns that function's `DefId` and `SubstsRef`.
148+
fn is_fn_ref(ty: Ty<'tcx>) -> Option<(DefId, SubstsRef<'tcx>)> {
145149
let referent_ty = match ty.kind() {
146150
ty::Ref(_, referent_ty, _) => Some(referent_ty),
147151
ty::RawPtr(ty_and_mut) => Some(&ty_and_mut.ty),
148152
_ => None,
149153
};
150154
referent_ty
151-
.map(
152-
|ref_ty| {
153-
if let ty::FnDef(def_id, _) = *ref_ty.kind() { Some(def_id) } else { None }
154-
},
155-
)
155+
.map(|ref_ty| {
156+
if let ty::FnDef(def_id, substs_ref) = *ref_ty.kind() {
157+
Some((def_id, substs_ref))
158+
} else {
159+
None
160+
}
161+
})
156162
.unwrap_or(None)
157163
}
164+
158165
fn nth_arg_span(&self, args: &Vec<Operand<'tcx>>, n: usize) -> Span {
159166
match &args[n] {
160167
Operand::Copy(place) | Operand::Move(place) => {
@@ -163,7 +170,14 @@ impl<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> {
163170
Operand::Constant(constant) => constant.span,
164171
}
165172
}
166-
fn emit_lint(&self, ident: String, fn_id: DefId, source_info: SourceInfo, span: Span) {
173+
174+
fn emit_lint(
175+
&self,
176+
fn_id: DefId,
177+
fn_substs: SubstsRef<'tcx>,
178+
source_info: SourceInfo,
179+
span: Span,
180+
) {
167181
let lint_root = self.body.source_scopes[source_info.scope]
168182
.local_data
169183
.as_ref()
@@ -180,6 +194,10 @@ impl<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> {
180194
s
181195
}
182196
};
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(", ");
183201
let num_args = fn_sig.inputs().map_bound(|inputs| inputs.len()).skip_binder();
184202
let variadic = if fn_sig.c_variadic() { ", ..." } else { "" };
185203
let ret = if fn_sig.output().skip_binder().is_unit() { "" } else { " -> _" };
@@ -190,7 +208,7 @@ impl<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> {
190208
&format!("cast `{}` to obtain a function pointer", ident),
191209
format!(
192210
"{} as {}{}fn({}{}){}",
193-
ident,
211+
if params.is_empty() { ident } else { format!("{}::<{}>", ident, params) },
194212
unsafety,
195213
abi,
196214
vec!["_"; num_args].join(", "),

src/test/ui/lint/function-item-references.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// check-pass
2-
#![feature(c_variadic)]
2+
#![feature(c_variadic, min_const_generics)]
33
#![warn(function_item_references)]
44
use std::fmt::Pointer;
55
use std::fmt::Formatter;
@@ -12,6 +12,10 @@ unsafe fn unsafe_fn() { }
1212
extern "C" fn c_fn() { }
1313
unsafe extern "C" fn unsafe_c_fn() { }
1414
unsafe extern fn variadic(_x: u32, _args: ...) { }
15+
fn take_generic_ref<'a, T>(_x: &'a T) { }
16+
fn take_generic_array<T, const N: usize>(_x: [T; N]) { }
17+
fn multiple_generic<T, U>(_x: T, _y: U) { }
18+
fn multiple_generic_arrays<T, U, const N: usize, const M: usize>(_x: [T; N], _y: [U; M]) { }
1519

1620
//function references passed to these functions should never lint
1721
fn call_fn(f: &dyn Fn(u32) -> u32, x: u32) { f(x); }
@@ -109,6 +113,14 @@ fn main() {
109113
//~^ WARNING taking a reference to a function item does not give a function pointer
110114
println!("{:p}", &variadic);
111115
//~^ WARNING taking a reference to a function item does not give a function pointer
116+
println!("{:p}", &take_generic_ref::<u32>);
117+
//~^ WARNING taking a reference to a function item does not give a function pointer
118+
println!("{:p}", &take_generic_array::<u32, 4>);
119+
//~^ WARNING taking a reference to a function item does not give a function pointer
120+
println!("{:p}", &multiple_generic::<u32, f32>);
121+
//~^ WARNING taking a reference to a function item does not give a function pointer
122+
println!("{:p}", &multiple_generic_arrays::<u32, f32, 4, 8>);
123+
//~^ WARNING taking a reference to a function item does not give a function pointer
112124
println!("{:p}", &std::env::var::<String>);
113125
//~^ WARNING taking a reference to a function item does not give a function pointer
114126

@@ -132,6 +144,8 @@ fn main() {
132144
std::mem::transmute::<_, (usize, usize)>((&foo, &bar));
133145
//~^ WARNING taking a reference to a function item does not give a function pointer
134146
//~^^ WARNING taking a reference to a function item does not give a function pointer
147+
std::mem::transmute::<_, usize>(&take_generic_ref::<u32>);
148+
//~^ WARNING taking a reference to a function item does not give a function pointer
135149

136150
//the correct way to transmute function pointers
137151
std::mem::transmute::<_, usize>(foo as fn() -> u32);

0 commit comments

Comments
 (0)