Fixed compiler error in lint checker triggered by associated types
When a function argument bound by `Pointer` is an associated type, we only perform substitutions using the parameters from the callsite but don't attempt to normalize since it may not succeed. A simplified version of the scenario that triggered this error was added as a test case. Also fixed `Pointer::fmt` which was being double-counted when called outside of macros and added a test case for this.
This commit is contained in:
parent
432ebd57ef
commit
d6fa7e15d6
@ -1,7 +1,11 @@
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{self, subst::GenericArgKind, PredicateAtom, Ty, TyCtxt, TyS};
|
||||
use rustc_middle::ty::{
|
||||
self,
|
||||
subst::{GenericArgKind, Subst},
|
||||
PredicateAtom, Ty, TyCtxt, TyS,
|
||||
};
|
||||
use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES;
|
||||
use rustc_span::{symbol::sym, Span};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
@ -33,48 +37,50 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
|
||||
fn_span: _,
|
||||
} = &terminator.kind
|
||||
{
|
||||
let func_ty = func.ty(self.body, self.tcx);
|
||||
if let ty::FnDef(def_id, substs_ref) = *func_ty.kind() {
|
||||
//check arguments for `std::mem::transmute`
|
||||
if self.tcx.is_diagnostic_item(sym::transmute, def_id) {
|
||||
let arg_ty = args[0].ty(self.body, self.tcx);
|
||||
for generic_inner_ty in arg_ty.walk() {
|
||||
if let GenericArgKind::Type(inner_ty) = generic_inner_ty.unpack() {
|
||||
if let Some(fn_id) = FunctionItemRefChecker::is_fn_ref(inner_ty) {
|
||||
let ident = self.tcx.item_name(fn_id).to_ident_string();
|
||||
let source_info = *self.body.source_info(location);
|
||||
let span = self.nth_arg_span(&args, 0);
|
||||
self.emit_lint(ident, fn_id, source_info, span);
|
||||
let source_info = *self.body.source_info(location);
|
||||
//this handles all function calls outside macros
|
||||
if !source_info.span.from_expansion() {
|
||||
let func_ty = func.ty(self.body, self.tcx);
|
||||
if let ty::FnDef(def_id, substs_ref) = *func_ty.kind() {
|
||||
//handle `std::mem::transmute`
|
||||
if self.tcx.is_diagnostic_item(sym::transmute, def_id) {
|
||||
let arg_ty = args[0].ty(self.body, self.tcx);
|
||||
for generic_inner_ty in arg_ty.walk() {
|
||||
if let GenericArgKind::Type(inner_ty) = generic_inner_ty.unpack() {
|
||||
if let Some(fn_id) = FunctionItemRefChecker::is_fn_ref(inner_ty) {
|
||||
let ident = self.tcx.item_name(fn_id).to_ident_string();
|
||||
let span = self.nth_arg_span(&args, 0);
|
||||
self.emit_lint(ident, fn_id, source_info, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//check arguments for any function with `std::fmt::Pointer` as a bound trait
|
||||
let param_env = self.tcx.param_env(def_id);
|
||||
let bounds = param_env.caller_bounds();
|
||||
for bound in bounds {
|
||||
if let Some(bound_ty) = self.is_pointer_trait(&bound.skip_binders()) {
|
||||
let arg_defs = self.tcx.fn_sig(def_id).skip_binder().inputs();
|
||||
for (arg_num, arg_def) in arg_defs.iter().enumerate() {
|
||||
for generic_inner_ty in arg_def.walk() {
|
||||
if let GenericArgKind::Type(inner_ty) =
|
||||
generic_inner_ty.unpack()
|
||||
{
|
||||
//if any type reachable from the argument types in the fn sig matches the type bound by `Pointer`
|
||||
if TyS::same_type(inner_ty, bound_ty) {
|
||||
//check if this type is a function reference in the function call
|
||||
let norm_ty =
|
||||
self.tcx.subst_and_normalize_erasing_regions(
|
||||
substs_ref, param_env, &inner_ty,
|
||||
);
|
||||
if let Some(fn_id) =
|
||||
FunctionItemRefChecker::is_fn_ref(norm_ty)
|
||||
{
|
||||
let ident =
|
||||
self.tcx.item_name(fn_id).to_ident_string();
|
||||
let source_info = *self.body.source_info(location);
|
||||
let span = self.nth_arg_span(&args, arg_num);
|
||||
self.emit_lint(ident, fn_id, source_info, span);
|
||||
} else {
|
||||
//handle any function call with `std::fmt::Pointer` as a bound trait
|
||||
//this includes calls to `std::fmt::Pointer::fmt` outside of macros
|
||||
let param_env = self.tcx.param_env(def_id);
|
||||
let bounds = param_env.caller_bounds();
|
||||
for bound in bounds {
|
||||
if let Some(bound_ty) = self.is_pointer_trait(&bound.skip_binders()) {
|
||||
//get the argument types as they appear in the function signature
|
||||
let arg_defs = self.tcx.fn_sig(def_id).skip_binder().inputs();
|
||||
for (arg_num, arg_def) in arg_defs.iter().enumerate() {
|
||||
//for all types reachable from the argument type in the fn sig
|
||||
for generic_inner_ty in arg_def.walk() {
|
||||
if let GenericArgKind::Type(inner_ty) =
|
||||
generic_inner_ty.unpack()
|
||||
{
|
||||
//if the inner type matches the type bound by `Pointer`
|
||||
if TyS::same_type(inner_ty, bound_ty) {
|
||||
//do a substitution using the parameters from the callsite
|
||||
let subst_ty = inner_ty.subst(self.tcx, substs_ref);
|
||||
if let Some(fn_id) =
|
||||
FunctionItemRefChecker::is_fn_ref(subst_ty)
|
||||
{
|
||||
let ident =
|
||||
self.tcx.item_name(fn_id).to_ident_string();
|
||||
let span = self.nth_arg_span(&args, arg_num);
|
||||
self.emit_lint(ident, fn_id, source_info, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -87,19 +93,25 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
|
||||
}
|
||||
self.super_terminator(terminator, location);
|
||||
}
|
||||
//check for `std::fmt::Pointer::<T>::fmt` where T is a function reference
|
||||
//this is used in formatting macros, but doesn't rely on the specific expansion
|
||||
//This handles `std::fmt::Pointer::fmt` when it's used in the formatting macros.
|
||||
//It's handled as an operand instead of a Call terminator so it won't depend on
|
||||
//whether the formatting macros call `fmt` directly, transmute it first or other
|
||||
//internal fmt details.
|
||||
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
|
||||
let op_ty = operand.ty(self.body, self.tcx);
|
||||
if let ty::FnDef(def_id, substs_ref) = *op_ty.kind() {
|
||||
if self.tcx.is_diagnostic_item(sym::pointer_trait_fmt, def_id) {
|
||||
let param_ty = substs_ref.type_at(0);
|
||||
if let Some(fn_id) = FunctionItemRefChecker::is_fn_ref(param_ty) {
|
||||
let source_info = *self.body.source_info(location);
|
||||
let callsite_ctxt = source_info.span.source_callsite().ctxt();
|
||||
let span = source_info.span.with_ctxt(callsite_ctxt);
|
||||
let ident = self.tcx.item_name(fn_id).to_ident_string();
|
||||
self.emit_lint(ident, fn_id, source_info, span);
|
||||
let source_info = *self.body.source_info(location);
|
||||
if source_info.span.from_expansion() {
|
||||
let op_ty = operand.ty(self.body, self.tcx);
|
||||
if let ty::FnDef(def_id, substs_ref) = *op_ty.kind() {
|
||||
if self.tcx.is_diagnostic_item(sym::pointer_trait_fmt, def_id) {
|
||||
let param_ty = substs_ref.type_at(0);
|
||||
if let Some(fn_id) = FunctionItemRefChecker::is_fn_ref(param_ty) {
|
||||
//the operand's ctxt wouldn't display the lint since it's inside a macro
|
||||
//so we have to use the callsite's ctxt
|
||||
let callsite_ctxt = source_info.span.source_callsite().ctxt();
|
||||
let span = source_info.span.with_ctxt(callsite_ctxt);
|
||||
let ident = self.tcx.item_name(fn_id).to_ident_string();
|
||||
self.emit_lint(ident, fn_id, source_info, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#![feature(c_variadic)]
|
||||
#![warn(function_item_references)]
|
||||
use std::fmt::Pointer;
|
||||
use std::fmt::Formatter;
|
||||
|
||||
fn nop() { }
|
||||
fn foo() -> u32 { 42 }
|
||||
@ -20,6 +21,25 @@ fn parameterized_call_fn<F: Fn(u32) -> u32>(f: &F, x: u32) { f(x); }
|
||||
fn print_ptr<F: Pointer>(f: F) { println!("{:p}", f); }
|
||||
fn bound_by_ptr_trait<F: Pointer>(_f: F) { }
|
||||
fn bound_by_ptr_trait_tuple<F: Pointer, G: Pointer>(_t: (F, G)) { }
|
||||
fn implicit_ptr_trait<F>(f: &F) { println!("{:p}", f); }
|
||||
|
||||
//case found in tinyvec that triggered a compiler error in an earlier version of the lint checker
|
||||
trait HasItem {
|
||||
type Item;
|
||||
fn assoc_item(&self) -> Self::Item;
|
||||
}
|
||||
fn _format_assoc_item<T: HasItem>(data: T, f: &mut Formatter) -> std::fmt::Result
|
||||
where T::Item: Pointer {
|
||||
//when the arg type bound by `Pointer` is an associated type, we shouldn't attempt to normalize
|
||||
Pointer::fmt(&data.assoc_item(), f)
|
||||
}
|
||||
|
||||
//simple test to make sure that calls to `Pointer::fmt` aren't double counted
|
||||
fn _call_pointer_fmt(f: &mut Formatter) -> std::fmt::Result {
|
||||
let zst_ref = &foo;
|
||||
Pointer::fmt(&zst_ref, f)
|
||||
//~^ WARNING cast `foo` with `as fn() -> _` to obtain a function pointer
|
||||
}
|
||||
|
||||
fn main() {
|
||||
//`let` bindings with function references shouldn't lint
|
||||
@ -126,6 +146,7 @@ fn main() {
|
||||
bound_by_ptr_trait_tuple((&foo, &bar));
|
||||
//~^ WARNING cast `foo` with `as fn() -> _` to obtain a function pointer
|
||||
//~^^ WARNING cast `bar` with `as fn(_) -> _` to obtain a function pointer
|
||||
implicit_ptr_trait(&bar); // ignore
|
||||
|
||||
//correct ways to pass function pointers as arguments bound by std::fmt::Pointer
|
||||
print_ptr(bar as fn(u32) -> u32);
|
||||
|
@ -1,8 +1,8 @@
|
||||
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:57:22
|
||||
--> $DIR/function-references.rs:40:18
|
||||
|
|
||||
LL | println!("{:p}", &foo);
|
||||
| ^^^^
|
||||
LL | Pointer::fmt(&zst_ref, f)
|
||||
| ^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/function-references.rs:3:9
|
||||
@ -11,160 +11,166 @@ LL | #![warn(function_item_references)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:59:20
|
||||
--> $DIR/function-references.rs:77:22
|
||||
|
|
||||
LL | println!("{:p}", &foo);
|
||||
| ^^^^
|
||||
|
||||
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:79:20
|
||||
|
|
||||
LL | print!("{:p}", &foo);
|
||||
| ^^^^
|
||||
|
||||
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:61:21
|
||||
--> $DIR/function-references.rs:81:21
|
||||
|
|
||||
LL | format!("{:p}", &foo);
|
||||
| ^^^^
|
||||
|
||||
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:64:22
|
||||
--> $DIR/function-references.rs:84:22
|
||||
|
|
||||
LL | println!("{:p}", &foo as *const _);
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:66:22
|
||||
--> $DIR/function-references.rs:86:22
|
||||
|
|
||||
LL | println!("{:p}", zst_ref);
|
||||
| ^^^^^^^
|
||||
|
||||
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:68:22
|
||||
--> $DIR/function-references.rs:88:22
|
||||
|
|
||||
LL | println!("{:p}", cast_zst_ptr);
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:70:22
|
||||
--> $DIR/function-references.rs:90:22
|
||||
|
|
||||
LL | println!("{:p}", coerced_zst_ptr);
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:73:22
|
||||
--> $DIR/function-references.rs:93:22
|
||||
|
|
||||
LL | println!("{:p}", &fn_item);
|
||||
| ^^^^^^^^
|
||||
|
||||
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:75:22
|
||||
--> $DIR/function-references.rs:95:22
|
||||
|
|
||||
LL | println!("{:p}", indirect_ref);
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
warning: cast `nop` with `as fn()` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:78:22
|
||||
--> $DIR/function-references.rs:98:22
|
||||
|
|
||||
LL | println!("{:p}", &nop);
|
||||
| ^^^^
|
||||
|
||||
warning: cast `bar` with `as fn(_) -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:80:22
|
||||
--> $DIR/function-references.rs:100:22
|
||||
|
|
||||
LL | println!("{:p}", &bar);
|
||||
| ^^^^
|
||||
|
||||
warning: cast `baz` with `as fn(_, _) -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:82:22
|
||||
--> $DIR/function-references.rs:102:22
|
||||
|
|
||||
LL | println!("{:p}", &baz);
|
||||
| ^^^^
|
||||
|
||||
warning: cast `unsafe_fn` with `as unsafe fn()` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:84:22
|
||||
--> $DIR/function-references.rs:104:22
|
||||
|
|
||||
LL | println!("{:p}", &unsafe_fn);
|
||||
| ^^^^^^^^^^
|
||||
|
||||
warning: cast `c_fn` with `as extern "C" fn()` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:86:22
|
||||
--> $DIR/function-references.rs:106:22
|
||||
|
|
||||
LL | println!("{:p}", &c_fn);
|
||||
| ^^^^^
|
||||
|
||||
warning: cast `unsafe_c_fn` with `as unsafe extern "C" fn()` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:88:22
|
||||
--> $DIR/function-references.rs:108:22
|
||||
|
|
||||
LL | println!("{:p}", &unsafe_c_fn);
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
warning: cast `variadic` with `as unsafe extern "C" fn(_, ...)` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:90:22
|
||||
--> $DIR/function-references.rs:110:22
|
||||
|
|
||||
LL | println!("{:p}", &variadic);
|
||||
| ^^^^^^^^^
|
||||
|
||||
warning: cast `var` with `as fn(_) -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:92:22
|
||||
--> $DIR/function-references.rs:112:22
|
||||
|
|
||||
LL | println!("{:p}", &std::env::var::<String>);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: cast `nop` with `as fn()` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:95:32
|
||||
--> $DIR/function-references.rs:115:32
|
||||
|
|
||||
LL | println!("{:p} {:p} {:p}", &nop, &foo, &bar);
|
||||
| ^^^^
|
||||
|
||||
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:95:38
|
||||
--> $DIR/function-references.rs:115:38
|
||||
|
|
||||
LL | println!("{:p} {:p} {:p}", &nop, &foo, &bar);
|
||||
| ^^^^
|
||||
|
||||
warning: cast `bar` with `as fn(_) -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:95:44
|
||||
--> $DIR/function-references.rs:115:44
|
||||
|
|
||||
LL | println!("{:p} {:p} {:p}", &nop, &foo, &bar);
|
||||
| ^^^^
|
||||
|
||||
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:110:41
|
||||
--> $DIR/function-references.rs:130:41
|
||||
|
|
||||
LL | std::mem::transmute::<_, usize>(&foo);
|
||||
| ^^^^
|
||||
|
||||
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:112:50
|
||||
--> $DIR/function-references.rs:132:50
|
||||
|
|
||||
LL | std::mem::transmute::<_, (usize, usize)>((&foo, &bar));
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
warning: cast `bar` with `as fn(_) -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:112:50
|
||||
--> $DIR/function-references.rs:132:50
|
||||
|
|
||||
LL | std::mem::transmute::<_, (usize, usize)>((&foo, &bar));
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
warning: cast `bar` with `as fn(_) -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:122:15
|
||||
--> $DIR/function-references.rs:142:15
|
||||
|
|
||||
LL | print_ptr(&bar);
|
||||
| ^^^^
|
||||
|
||||
warning: cast `bar` with `as fn(_) -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:124:24
|
||||
--> $DIR/function-references.rs:144:24
|
||||
|
|
||||
LL | bound_by_ptr_trait(&bar);
|
||||
| ^^^^
|
||||
|
||||
warning: cast `bar` with `as fn(_) -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:126:30
|
||||
--> $DIR/function-references.rs:146:30
|
||||
|
|
||||
LL | bound_by_ptr_trait_tuple((&foo, &bar));
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
|
||||
--> $DIR/function-references.rs:126:30
|
||||
--> $DIR/function-references.rs:146:30
|
||||
|
|
||||
LL | bound_by_ptr_trait_tuple((&foo, &bar));
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
warning: 27 warnings emitted
|
||||
warning: 28 warnings emitted
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user