@@ -3,7 +3,7 @@ use rustc_middle::mir::visit::Visitor;
3
3
use rustc_middle:: mir:: * ;
4
4
use rustc_middle:: ty:: {
5
5
self ,
6
- subst:: { GenericArgKind , Subst } ,
6
+ subst:: { GenericArgKind , Subst , SubstsRef } ,
7
7
PredicateAtom , Ty , TyCtxt , TyS ,
8
8
} ;
9
9
use rustc_session:: lint:: builtin:: FUNCTION_ITEM_REFERENCES ;
@@ -27,6 +27,9 @@ struct FunctionItemRefChecker<'a, 'tcx> {
27
27
}
28
28
29
29
impl < ' a , ' tcx > Visitor < ' tcx > for FunctionItemRefChecker < ' a , ' tcx > {
30
+ /// Emits a lint for function reference arguments bound by `fmt::Pointer` or passed to
31
+ /// `transmute`. This only handles arguments in calls outside macro expansions to avoid double
32
+ /// counting function references formatted as pointers by macros.
30
33
fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , location : Location ) {
31
34
if let TerminatorKind :: Call {
32
35
func,
@@ -38,11 +41,11 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
38
41
} = & terminator. kind
39
42
{
40
43
let source_info = * self . body . source_info ( location) ;
41
- //this handles all function calls outside macros
44
+ // Only handle function calls outside macros
42
45
if !source_info. span . from_expansion ( ) {
43
46
let func_ty = func. ty ( self . body , self . tcx ) ;
44
47
if let ty:: FnDef ( def_id, substs_ref) = * func_ty. kind ( ) {
45
- //handle `std::mem:: transmute`
48
+ // Handle calls to ` transmute`
46
49
if self . tcx . is_diagnostic_item ( sym:: transmute, def_id) {
47
50
let arg_ty = args[ 0 ] . ty ( self . body , self . tcx ) ;
48
51
for generic_inner_ty in arg_ty. walk ( ) {
@@ -55,48 +58,16 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
55
58
}
56
59
}
57
60
} 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
- }
84
- }
85
- }
86
- }
87
- }
88
- }
89
- }
61
+ self . check_bound_args ( def_id, substs_ref, & args, source_info) ;
90
62
}
91
63
}
92
64
}
93
65
}
94
66
self . super_terminator ( terminator, location) ;
95
67
}
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.
68
+ /// Emits a lint for function references formatted with `fmt::Pointer::fmt` by macros. These
69
+ /// cases are handled as operands instead of call terminators to avoid any dependence on
70
+ /// unstable, internal formatting details like whether `fmt` is called directly or not.
100
71
fn visit_operand ( & mut self , operand : & Operand < ' tcx > , location : Location ) {
101
72
let source_info = * self . body . source_info ( location) ;
102
73
if source_info. span . from_expansion ( ) {
@@ -105,8 +76,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
105
76
if self . tcx . is_diagnostic_item ( sym:: pointer_trait_fmt, def_id) {
106
77
let param_ty = substs_ref. type_at ( 0 ) ;
107
78
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
79
+ // The operand's ctxt wouldn't display the lint since it's inside a macro so
80
+ // we have to use the callsite's ctxt.
110
81
let callsite_ctxt = source_info. span . source_callsite ( ) . ctxt ( ) ;
111
82
let span = source_info. span . with_ctxt ( callsite_ctxt) ;
112
83
let ident = self . tcx . item_name ( fn_id) . to_ident_string ( ) ;
@@ -120,7 +91,42 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
120
91
}
121
92
122
93
impl < ' a , ' tcx > FunctionItemRefChecker < ' a , ' tcx > {
123
- //return the bound parameter type if the trait is `std::fmt::Pointer`
94
+ /// Emits a lint for function reference arguments bound by `fmt::Pointer` in calls to the
95
+ /// function defined by `def_id` with the substitutions `substs_ref`.
96
+ fn check_bound_args (
97
+ & self ,
98
+ def_id : DefId ,
99
+ substs_ref : SubstsRef < ' tcx > ,
100
+ args : & Vec < Operand < ' tcx > > ,
101
+ source_info : SourceInfo ,
102
+ ) {
103
+ let param_env = self . tcx . param_env ( def_id) ;
104
+ let bounds = param_env. caller_bounds ( ) ;
105
+ for bound in bounds {
106
+ if let Some ( bound_ty) = self . is_pointer_trait ( & bound. skip_binders ( ) ) {
107
+ // Get the argument types as they appear in the function signature.
108
+ let arg_defs = self . tcx . fn_sig ( def_id) . skip_binder ( ) . inputs ( ) ;
109
+ for ( arg_num, arg_def) in arg_defs. iter ( ) . enumerate ( ) {
110
+ // For all types reachable from the argument type in the fn sig
111
+ for generic_inner_ty in arg_def. walk ( ) {
112
+ if let GenericArgKind :: Type ( inner_ty) = generic_inner_ty. unpack ( ) {
113
+ // If the inner type matches the type bound by `Pointer`
114
+ if TyS :: same_type ( inner_ty, bound_ty) {
115
+ // Do a substitution using the parameters from the callsite
116
+ let subst_ty = inner_ty. subst ( self . tcx , substs_ref) ;
117
+ if let Some ( fn_id) = FunctionItemRefChecker :: is_fn_ref ( subst_ty) {
118
+ let ident = self . tcx . item_name ( fn_id) . to_ident_string ( ) ;
119
+ let span = self . nth_arg_span ( args, arg_num) ;
120
+ self . emit_lint ( ident, fn_id, source_info, span) ;
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }
126
+ }
127
+ }
128
+ }
129
+ /// If the given predicate is the trait `fmt::Pointer`, returns the bound parameter type.
124
130
fn is_pointer_trait ( & self , bound : & PredicateAtom < ' tcx > ) -> Option < Ty < ' tcx > > {
125
131
if let ty:: PredicateAtom :: Trait ( predicate, _) = bound {
126
132
if self . tcx . is_diagnostic_item ( sym:: pointer_trait, predicate. def_id ( ) ) {
@@ -132,6 +138,8 @@ impl<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> {
132
138
None
133
139
}
134
140
}
141
+ /// If a type is a reference or raw pointer to the anonymous type of a function definition,
142
+ /// returns that function's `DefId`.
135
143
fn is_fn_ref ( ty : Ty < ' tcx > ) -> Option < DefId > {
136
144
let referent_ty = match ty. kind ( ) {
137
145
ty:: Ref ( _, referent_ty, _) => Some ( referent_ty) ,
0 commit comments