11use rustc_hir:: def_id:: DefId ;
22use rustc_middle:: mir:: visit:: Visitor ;
33use 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 } ;
77use rustc_target:: spec:: abi:: Abi ;
88
9- use crate :: transform:: { MirPass , MirSource } ;
9+ use crate :: transform:: MirPass ;
1010
11- pub struct FunctionReferences ;
11+ pub struct FunctionItemReferences ;
1212
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 } ;
2416 checker. visit_body ( & body) ;
2517 }
2618}
2719
28- struct FunctionRefChecker < ' a , ' tcx > {
20+ struct FunctionItemRefChecker < ' a , ' tcx > {
2921 tcx : TyCtxt < ' tcx > ,
3022 body : & ' a Body < ' tcx > ,
31- potential_lints : Vec < FunctionRefLint > ,
32- casts : Vec < Span > ,
33- calls : Vec < Span > ,
34- source_info : SourceInfo ,
3523}
3624
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 > {
5426 fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , location : Location ) {
55- self . source_info = terminator. source_info ;
5627 if let TerminatorKind :: Call {
5728 func,
58- args : _ ,
29+ args,
5930 destination : _,
6031 cleanup : _,
6132 from_hir_call : _,
6233 fn_span : _,
6334 } = & terminator. kind
6435 {
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+ }
6885 }
69- Operand :: Constant ( constant) => constant. span ,
70- } ;
71- self . calls . push ( span) ;
72- } ;
86+ }
87+ }
7388 self . super_terminator ( terminator, location) ;
7489 }
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) ;
95103 }
96104 }
97- _ => { }
98105 }
99- self . super_rvalue ( rvalue , location) ;
106+ self . super_operand ( operand , location) ;
100107 }
101108}
102109
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 ( ) {
106125 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 ) ,
108127 _ => None ,
109128 } ;
110129 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 )
113136 }
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 ]
127147 . local_data
128148 . as_ref ( )
129149 . assert_crate_local ( )
130150 . lint_root ;
131- let fn_sig = tcx. fn_sig ( def_id ) ;
151+ let fn_sig = self . tcx . fn_sig ( fn_id ) ;
132152 let unsafety = fn_sig. unsafety ( ) . prefix_str ( ) ;
133153 let abi = match fn_sig. abi ( ) {
134154 Abi :: Rust => String :: from ( "" ) ,
@@ -142,17 +162,17 @@ impl<'tcx> FunctionRefLint {
142162 let num_args = fn_sig. inputs ( ) . map_bound ( |inputs| inputs. len ( ) ) . skip_binder ( ) ;
143163 let variadic = if fn_sig. c_variadic ( ) { ", ..." } else { "" } ;
144164 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| {
146166 lint. build ( & format ! (
147167 "cast `{}` with `as {}{}fn({}{}){}` to use it as a pointer" ,
148- self . ident,
168+ ident,
149169 unsafety,
150170 abi,
151171 vec![ "_" ; num_args] . join( ", " ) ,
152172 variadic,
153173 ret,
154174 ) )
155- . emit ( )
175+ . emit ( ) ;
156176 } ) ;
157177 }
158178}
0 commit comments