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 , Ty , TyCtxt } ;
5
- use rustc_session:: lint:: builtin:: FUNCTION_REFERENCES ;
6
- use rustc_span:: Span ;
4
+ use rustc_middle:: ty:: { self , subst :: GenericArgKind , PredicateAtom , Ty , TyCtxt , TyS } ;
5
+ use rustc_session:: lint:: builtin:: FUNCTION_ITEM_REFERENCES ;
6
+ use rustc_span:: { symbol :: sym , Span } ;
7
7
use rustc_target:: spec:: abi:: Abi ;
8
8
9
- use crate :: transform:: { MirPass , MirSource } ;
9
+ use crate :: transform:: MirPass ;
10
10
11
- pub struct FunctionReferences ;
11
+ pub struct FunctionItemReferences ;
12
12
13
- impl < ' tcx > MirPass < ' tcx > for FunctionReferences {
14
- fn run_pass ( & self , tcx : TyCtxt < ' tcx > , _src : MirSource < ' tcx > , body : & mut Body < ' tcx > ) {
15
- let source_info = SourceInfo :: outermost ( body. span ) ;
16
- let mut checker = FunctionRefChecker {
17
- tcx,
18
- body,
19
- potential_lints : Vec :: new ( ) ,
20
- casts : Vec :: new ( ) ,
21
- calls : Vec :: new ( ) ,
22
- source_info,
23
- } ;
13
+ impl < ' tcx > MirPass < ' tcx > for FunctionItemReferences {
14
+ fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
15
+ let mut checker = FunctionItemRefChecker { tcx, body } ;
24
16
checker. visit_body ( & body) ;
25
17
}
26
18
}
27
19
28
- struct FunctionRefChecker < ' a , ' tcx > {
20
+ struct FunctionItemRefChecker < ' a , ' tcx > {
29
21
tcx : TyCtxt < ' tcx > ,
30
22
body : & ' a Body < ' tcx > ,
31
- potential_lints : Vec < FunctionRefLint > ,
32
- casts : Vec < Span > ,
33
- calls : Vec < Span > ,
34
- source_info : SourceInfo ,
35
23
}
36
24
37
- impl < ' a , ' tcx > Visitor < ' tcx > for FunctionRefChecker < ' a , ' tcx > {
38
- fn visit_basic_block_data ( & mut self , block : BasicBlock , data : & BasicBlockData < ' tcx > ) {
39
- self . super_basic_block_data ( block, data) ;
40
- for cast_span in self . casts . drain ( ..) {
41
- self . potential_lints . retain ( |lint| lint. source_info . span != cast_span) ;
42
- }
43
- for call_span in self . calls . drain ( ..) {
44
- self . potential_lints . retain ( |lint| lint. source_info . span != call_span) ;
45
- }
46
- for lint in self . potential_lints . drain ( ..) {
47
- lint. emit ( self . tcx , self . body ) ;
48
- }
49
- }
50
- fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
51
- self . source_info = statement. source_info ;
52
- self . super_statement ( statement, location) ;
53
- }
25
+ impl < ' a , ' tcx > Visitor < ' tcx > for FunctionItemRefChecker < ' a , ' tcx > {
54
26
fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , location : Location ) {
55
- self . source_info = terminator. source_info ;
56
27
if let TerminatorKind :: Call {
57
28
func,
58
- args : _ ,
29
+ args,
59
30
destination : _,
60
31
cleanup : _,
61
32
from_hir_call : _,
62
33
fn_span : _,
63
34
} = & terminator. kind
64
35
{
65
- let span = match func {
66
- Operand :: Copy ( place) | Operand :: Move ( place) => {
67
- self . body . local_decls [ place. local ] . source_info . span
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) ;
48
+ }
49
+ }
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) ;
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
68
85
}
69
- Operand :: Constant ( constant) => constant. span ,
70
- } ;
71
- self . calls . push ( span) ;
72
- } ;
86
+ }
87
+ }
73
88
self . super_terminator ( terminator, location) ;
74
89
}
75
- fn visit_rvalue ( & mut self , rvalue : & Rvalue < ' tcx > , location : Location ) {
76
- match rvalue {
77
- Rvalue :: Ref ( _, _, place) | Rvalue :: AddressOf ( _, place) => {
78
- let decl = & self . body . local_decls [ place. local ] ;
79
- if let ty:: FnDef ( def_id, _) = decl. ty . kind {
80
- let ident = self
81
- . body
82
- . var_debug_info
83
- . iter ( )
84
- . find ( |info| info. source_info . span == decl. source_info . span )
85
- . map ( |info| info. name . to_ident_string ( ) )
86
- . unwrap_or ( self . tcx . def_path_str ( def_id) ) ;
87
- let lint = FunctionRefLint { ident, def_id, source_info : self . source_info } ;
88
- self . potential_lints . push ( lint) ;
89
- }
90
- }
91
- Rvalue :: Cast ( _, op, _) => {
92
- let op_ty = op. ty ( self . body , self . tcx ) ;
93
- if self . is_fn_ref ( op_ty) {
94
- self . casts . push ( self . source_info . span ) ;
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
92
+ 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) ;
95
103
}
96
104
}
97
- _ => { }
98
105
}
99
- self . super_rvalue ( rvalue , location) ;
106
+ self . super_operand ( operand , location) ;
100
107
}
101
108
}
102
109
103
- impl < ' a , ' tcx > FunctionRefChecker < ' a , ' tcx > {
104
- fn is_fn_ref ( & self , ty : Ty < ' tcx > ) -> bool {
105
- let referent_ty = match ty. kind {
110
+ impl < ' a , ' tcx > FunctionItemRefChecker < ' a , ' tcx > {
111
+ //return the bound parameter type if the trait is `std::fmt::Pointer`
112
+ fn is_pointer_trait ( & self , bound : & PredicateAtom < ' tcx > ) -> Option < Ty < ' tcx > > {
113
+ if let ty:: PredicateAtom :: Trait ( predicate, _) = bound {
114
+ if self . tcx . is_diagnostic_item ( sym:: pointer_trait, predicate. def_id ( ) ) {
115
+ Some ( predicate. trait_ref . self_ty ( ) )
116
+ } else {
117
+ None
118
+ }
119
+ } else {
120
+ None
121
+ }
122
+ }
123
+ fn is_fn_ref ( ty : Ty < ' tcx > ) -> Option < DefId > {
124
+ let referent_ty = match ty. kind ( ) {
106
125
ty:: Ref ( _, referent_ty, _) => Some ( referent_ty) ,
107
- ty:: RawPtr ( ty_and_mut) => Some ( ty_and_mut. ty ) ,
126
+ ty:: RawPtr ( ty_and_mut) => Some ( & ty_and_mut. ty ) ,
108
127
_ => None ,
109
128
} ;
110
129
referent_ty
111
- . map ( |ref_ty| if let ty:: FnDef ( ..) = ref_ty. kind { true } else { false } )
112
- . unwrap_or ( false )
130
+ . map (
131
+ |ref_ty| {
132
+ if let ty:: FnDef ( def_id, _) = * ref_ty. kind ( ) { Some ( def_id) } else { None }
133
+ } ,
134
+ )
135
+ . unwrap_or ( None )
113
136
}
114
- }
115
-
116
- struct FunctionRefLint {
117
- ident : String ,
118
- def_id : DefId ,
119
- source_info : SourceInfo ,
120
- }
121
-
122
- impl < ' tcx > FunctionRefLint {
123
- fn emit ( & self , tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > ) {
124
- let def_id = self . def_id ;
125
- let source_info = self . source_info ;
126
- let lint_root = body. source_scopes [ source_info. scope ]
137
+ fn nth_arg_span ( & self , args : & Vec < Operand < ' tcx > > , n : usize ) -> Span {
138
+ match & args[ n] {
139
+ Operand :: Copy ( place) | Operand :: Move ( place) => {
140
+ self . body . local_decls [ place. local ] . source_info . span
141
+ }
142
+ Operand :: Constant ( constant) => constant. span ,
143
+ }
144
+ }
145
+ fn emit_lint ( & self , ident : String , fn_id : DefId , source_info : SourceInfo , span : Span ) {
146
+ let lint_root = self . body . source_scopes [ source_info. scope ]
127
147
. local_data
128
148
. as_ref ( )
129
149
. assert_crate_local ( )
130
150
. lint_root ;
131
- let fn_sig = tcx. fn_sig ( def_id ) ;
151
+ let fn_sig = self . tcx . fn_sig ( fn_id ) ;
132
152
let unsafety = fn_sig. unsafety ( ) . prefix_str ( ) ;
133
153
let abi = match fn_sig. abi ( ) {
134
154
Abi :: Rust => String :: from ( "" ) ,
@@ -142,17 +162,17 @@ impl<'tcx> FunctionRefLint {
142
162
let num_args = fn_sig. inputs ( ) . map_bound ( |inputs| inputs. len ( ) ) . skip_binder ( ) ;
143
163
let variadic = if fn_sig. c_variadic ( ) { ", ..." } else { "" } ;
144
164
let ret = if fn_sig. output ( ) . skip_binder ( ) . is_unit ( ) { "" } else { " -> _" } ;
145
- tcx. struct_span_lint_hir ( FUNCTION_REFERENCES , lint_root, source_info . span , |lint| {
165
+ self . tcx . struct_span_lint_hir ( FUNCTION_ITEM_REFERENCES , lint_root, span, |lint| {
146
166
lint. build ( & format ! (
147
167
"cast `{}` with `as {}{}fn({}{}){}` to use it as a pointer" ,
148
- self . ident,
168
+ ident,
149
169
unsafety,
150
170
abi,
151
171
vec![ "_" ; num_args] . join( ", " ) ,
152
172
variadic,
153
173
ret,
154
174
) )
155
- . emit ( )
175
+ . emit ( ) ;
156
176
} ) ;
157
177
}
158
178
}
0 commit comments