1
1
use rustc_hir:: def_id:: DefId ;
2
2
use rustc_middle:: mir:: visit:: Visitor ;
3
3
use 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
+ } ;
5
9
use rustc_session:: lint:: builtin:: FUNCTION_ITEM_REFERENCES ;
6
10
use rustc_span:: { symbol:: sym, Span } ;
7
11
use rustc_target:: spec:: abi:: Abi ;
@@ -33,48 +37,50 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
33
37
fn_span : _,
34
38
} = & terminator. kind
35
39
{
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
+ }
48
55
}
49
56
}
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
+ }
78
84
}
79
85
}
80
86
}
@@ -87,19 +93,25 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
87
93
}
88
94
self . super_terminator ( terminator, location) ;
89
95
}
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.
92
100
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
+ }
103
115
}
104
116
}
105
117
}
0 commit comments