11use rustc_hir:: def_id:: DefId ;
22use rustc_middle:: mir:: visit:: Visitor ;
33use rustc_middle:: mir:: * ;
4- use rustc_middle:: ty:: { self , subst:: GenericArgKind , PredicateAtom , Ty , TyCtxt , TyS } ;
4+ use rustc_middle:: ty:: {
5+ self ,
6+ subst:: { GenericArgKind , Subst } ,
7+ PredicateAtom , Ty , TyCtxt , TyS ,
8+ } ;
59use rustc_session:: lint:: builtin:: FUNCTION_ITEM_REFERENCES ;
610use rustc_span:: { symbol:: sym, Span } ;
711use rustc_target:: spec:: abi:: Abi ;
@@ -33,48 +37,50 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
3337 fn_span : _,
3438 } = & terminator. kind
3539 {
36- let func_ty = func. ty ( self . body , self . tcx ) ;
37- if let ty:: FnDef ( def_id, substs_ref) = * func_ty. kind ( ) {
38- //check arguments for `std::mem::transmute`
39- if self . tcx . is_diagnostic_item ( sym:: transmute, def_id) {
40- let arg_ty = args[ 0 ] . ty ( self . body , self . tcx ) ;
41- for generic_inner_ty in arg_ty. walk ( ) {
42- if let GenericArgKind :: Type ( inner_ty) = generic_inner_ty. unpack ( ) {
43- if let Some ( fn_id) = FunctionItemRefChecker :: is_fn_ref ( inner_ty) {
44- let ident = self . tcx . item_name ( fn_id) . to_ident_string ( ) ;
45- let source_info = * self . body . source_info ( location) ;
46- let span = self . nth_arg_span ( & args, 0 ) ;
47- self . emit_lint ( ident, fn_id, source_info, span) ;
40+ let source_info = * self . body . source_info ( location) ;
41+ //this handles all function calls outside macros
42+ if !source_info. span . from_expansion ( ) {
43+ let func_ty = func. ty ( self . body , self . tcx ) ;
44+ if let ty:: FnDef ( def_id, substs_ref) = * func_ty. kind ( ) {
45+ //handle `std::mem::transmute`
46+ if self . tcx . is_diagnostic_item ( sym:: transmute, def_id) {
47+ let arg_ty = args[ 0 ] . ty ( self . body , self . tcx ) ;
48+ for generic_inner_ty in arg_ty. walk ( ) {
49+ if let GenericArgKind :: Type ( inner_ty) = generic_inner_ty. unpack ( ) {
50+ if let Some ( fn_id) = FunctionItemRefChecker :: is_fn_ref ( inner_ty) {
51+ let ident = self . tcx . item_name ( fn_id) . to_ident_string ( ) ;
52+ let span = self . nth_arg_span ( & args, 0 ) ;
53+ self . emit_lint ( ident, fn_id, source_info, span) ;
54+ }
4855 }
4956 }
50- }
51- } else {
52- //check arguments for any function with `std::fmt::Pointer` as a bound trait
53- let param_env = self . tcx . param_env ( def_id) ;
54- let bounds = param_env. caller_bounds ( ) ;
55- for bound in bounds {
56- if let Some ( bound_ty) = self . is_pointer_trait ( & bound. skip_binders ( ) ) {
57- let arg_defs = self . tcx . fn_sig ( def_id) . skip_binder ( ) . inputs ( ) ;
58- for ( arg_num, arg_def) in arg_defs. iter ( ) . enumerate ( ) {
59- for generic_inner_ty in arg_def. walk ( ) {
60- if let GenericArgKind :: Type ( inner_ty) =
61- generic_inner_ty. unpack ( )
62- {
63- //if any type reachable from the argument types in the fn sig matches the type bound by `Pointer`
64- if TyS :: same_type ( inner_ty, bound_ty) {
65- //check if this type is a function reference in the function call
66- let norm_ty =
67- self . tcx . subst_and_normalize_erasing_regions (
68- substs_ref, param_env, & inner_ty,
69- ) ;
70- if let Some ( fn_id) =
71- FunctionItemRefChecker :: is_fn_ref ( norm_ty)
72- {
73- let ident =
74- self . tcx . item_name ( fn_id) . to_ident_string ( ) ;
75- let source_info = * self . body . source_info ( location) ;
76- let span = self . nth_arg_span ( & args, arg_num) ;
77- self . emit_lint ( ident, fn_id, source_info, span) ;
57+ } else {
58+ //handle any function call with `std::fmt::Pointer` as a bound trait
59+ //this includes calls to `std::fmt::Pointer::fmt` outside of macros
60+ let param_env = self . tcx . param_env ( def_id) ;
61+ let bounds = param_env. caller_bounds ( ) ;
62+ for bound in bounds {
63+ if let Some ( bound_ty) = self . is_pointer_trait ( & bound. skip_binders ( ) ) {
64+ //get the argument types as they appear in the function signature
65+ let arg_defs = self . tcx . fn_sig ( def_id) . skip_binder ( ) . inputs ( ) ;
66+ for ( arg_num, arg_def) in arg_defs. iter ( ) . enumerate ( ) {
67+ //for all types reachable from the argument type in the fn sig
68+ for generic_inner_ty in arg_def. walk ( ) {
69+ if let GenericArgKind :: Type ( inner_ty) =
70+ generic_inner_ty. unpack ( )
71+ {
72+ //if the inner type matches the type bound by `Pointer`
73+ if TyS :: same_type ( inner_ty, bound_ty) {
74+ //do a substitution using the parameters from the callsite
75+ let subst_ty = inner_ty. subst ( self . tcx , substs_ref) ;
76+ if let Some ( fn_id) =
77+ FunctionItemRefChecker :: is_fn_ref ( subst_ty)
78+ {
79+ let ident =
80+ self . tcx . item_name ( fn_id) . to_ident_string ( ) ;
81+ let span = self . nth_arg_span ( & args, arg_num) ;
82+ self . emit_lint ( ident, fn_id, source_info, span) ;
83+ }
7884 }
7985 }
8086 }
@@ -87,19 +93,25 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
8793 }
8894 self . super_terminator ( terminator, location) ;
8995 }
90- //check for `std::fmt::Pointer::<T>::fmt` where T is a function reference
91- //this is used in formatting macros, but doesn't rely on the specific expansion
96+ //This handles `std::fmt::Pointer::fmt` when it's used in the formatting macros.
97+ //It's handled as an operand instead of a Call terminator so it won't depend on
98+ //whether the formatting macros call `fmt` directly, transmute it first or other
99+ //internal fmt details.
92100 fn visit_operand ( & mut self , operand : & Operand < ' tcx > , location : Location ) {
93- let op_ty = operand. ty ( self . body , self . tcx ) ;
94- if let ty:: FnDef ( def_id, substs_ref) = * op_ty. kind ( ) {
95- if self . tcx . is_diagnostic_item ( sym:: pointer_trait_fmt, def_id) {
96- let param_ty = substs_ref. type_at ( 0 ) ;
97- if let Some ( fn_id) = FunctionItemRefChecker :: is_fn_ref ( param_ty) {
98- let source_info = * self . body . source_info ( location) ;
99- let callsite_ctxt = source_info. span . source_callsite ( ) . ctxt ( ) ;
100- let span = source_info. span . with_ctxt ( callsite_ctxt) ;
101- let ident = self . tcx . item_name ( fn_id) . to_ident_string ( ) ;
102- self . emit_lint ( ident, fn_id, source_info, span) ;
101+ let source_info = * self . body . source_info ( location) ;
102+ if source_info. span . from_expansion ( ) {
103+ let op_ty = operand. ty ( self . body , self . tcx ) ;
104+ if let ty:: FnDef ( def_id, substs_ref) = * op_ty. kind ( ) {
105+ if self . tcx . is_diagnostic_item ( sym:: pointer_trait_fmt, def_id) {
106+ let param_ty = substs_ref. type_at ( 0 ) ;
107+ if let Some ( fn_id) = FunctionItemRefChecker :: is_fn_ref ( param_ty) {
108+ //the operand's ctxt wouldn't display the lint since it's inside a macro
109+ //so we have to use the callsite's ctxt
110+ let callsite_ctxt = source_info. span . source_callsite ( ) . ctxt ( ) ;
111+ let span = source_info. span . with_ctxt ( callsite_ctxt) ;
112+ let ident = self . tcx . item_name ( fn_id) . to_ident_string ( ) ;
113+ self . emit_lint ( ident, fn_id, source_info, span) ;
114+ }
103115 }
104116 }
105117 }
0 commit comments