11#![ feature( core_intrinsics) ]
2+ #![ feature( stmt_expr_attributes) ]
3+ #![ feature( closure_track_caller) ]
4+ #![ feature( generator_trait) ]
5+ #![ feature( generators) ]
26
7+ use std:: ops:: { Generator , GeneratorState } ;
38use std:: panic:: Location ;
9+ use std:: pin:: Pin ;
10+
11+ type Loc = & ' static Location < ' static > ;
412
513#[ track_caller]
6- fn tracked ( ) -> & ' static Location < ' static > {
14+ fn tracked ( ) -> Loc {
715 Location :: caller ( ) // most importantly, we never get line 7
816}
917
10- fn nested_intrinsic ( ) -> & ' static Location < ' static > {
18+ fn nested_intrinsic ( ) -> Loc {
1119 Location :: caller ( )
1220}
1321
14- fn nested_tracked ( ) -> & ' static Location < ' static > {
22+ fn nested_tracked ( ) -> Loc {
1523 tracked ( )
1624}
1725
@@ -21,6 +29,44 @@ macro_rules! caller_location_from_macro {
2129 } ;
2230}
2331
32+ fn test_basic ( ) {
33+ let location = Location :: caller ( ) ;
34+ let expected_line = line ! ( ) - 1 ;
35+ assert_eq ! ( location. file( ) , file!( ) ) ;
36+ assert_eq ! ( location. line( ) , expected_line) ;
37+ assert_eq ! ( location. column( ) , 20 ) ;
38+
39+ let tracked = tracked ( ) ;
40+ let expected_line = line ! ( ) - 1 ;
41+ assert_eq ! ( tracked. file( ) , file!( ) ) ;
42+ assert_eq ! ( tracked. line( ) , expected_line) ;
43+ assert_eq ! ( tracked. column( ) , 19 ) ;
44+
45+ let nested = nested_intrinsic ( ) ;
46+ assert_eq ! ( nested. file( ) , file!( ) ) ;
47+ assert_eq ! ( nested. line( ) , 19 ) ;
48+ assert_eq ! ( nested. column( ) , 5 ) ;
49+
50+ let contained = nested_tracked ( ) ;
51+ assert_eq ! ( contained. file( ) , file!( ) ) ;
52+ assert_eq ! ( contained. line( ) , 23 ) ;
53+ assert_eq ! ( contained. column( ) , 5 ) ;
54+
55+ // `Location::caller()` in a macro should behave similarly to `file!` and `line!`,
56+ // i.e. point to where the macro was invoked, instead of the macro itself.
57+ let inmacro = caller_location_from_macro ! ( ) ;
58+ let expected_line = line ! ( ) - 1 ;
59+ assert_eq ! ( inmacro. file( ) , file!( ) ) ;
60+ assert_eq ! ( inmacro. line( ) , expected_line) ;
61+ assert_eq ! ( inmacro. column( ) , 19 ) ;
62+
63+ let intrinsic = core:: intrinsics:: caller_location ( ) ;
64+ let expected_line = line ! ( ) - 1 ;
65+ assert_eq ! ( intrinsic. file( ) , file!( ) ) ;
66+ assert_eq ! ( intrinsic. line( ) , expected_line) ;
67+ assert_eq ! ( intrinsic. column( ) , 21 ) ;
68+ }
69+
2470fn test_fn_ptr ( ) {
2571 fn pass_to_ptr_call < T > ( f : fn ( T ) , x : T ) {
2672 f ( x) ;
@@ -87,44 +133,144 @@ fn test_trait_obj2() {
87133 assert_eq ! ( loc. line( ) , expected_line) ;
88134}
89135
90- fn main ( ) {
91- let location = Location :: caller ( ) ;
92- let expected_line = line ! ( ) - 1 ;
93- assert_eq ! ( location. file( ) , file!( ) ) ;
94- assert_eq ! ( location. line( ) , expected_line) ;
95- assert_eq ! ( location. column( ) , 20 ) ;
136+ fn test_closure ( ) {
137+ #[ track_caller]
138+ fn mono_invoke_fn < F : Fn ( & ' static str , bool ) -> ( & ' static str , bool , Loc ) > (
139+ val : & F ,
140+ ) -> ( & ' static str , bool , Loc ) {
141+ val ( "from_mono" , false )
142+ }
96143
97- let tracked = tracked ( ) ;
98- let expected_line = line ! ( ) - 1 ;
99- assert_eq ! ( tracked. file( ) , file!( ) ) ;
100- assert_eq ! ( tracked. line( ) , expected_line) ;
101- assert_eq ! ( tracked. column( ) , 19 ) ;
144+ #[ track_caller]
145+ fn mono_invoke_fn_once < F : FnOnce ( & ' static str , bool ) -> ( & ' static str , bool , Loc ) > (
146+ val : F ,
147+ ) -> ( & ' static str , bool , Loc ) {
148+ val ( "from_mono" , false )
149+ }
102150
103- let nested = nested_intrinsic ( ) ;
104- assert_eq ! ( nested. file( ) , file!( ) ) ;
105- assert_eq ! ( nested. line( ) , 11 ) ;
106- assert_eq ! ( nested. column( ) , 5 ) ;
151+ #[ track_caller]
152+ fn dyn_invoke_fn_mut (
153+ val : & mut dyn FnMut ( & ' static str , bool ) -> ( & ' static str , bool , Loc ) ,
154+ ) -> ( & ' static str , bool , Loc ) {
155+ val ( "from_dyn" , false )
156+ }
107157
108- let contained = nested_tracked ( ) ;
109- assert_eq ! ( contained. file( ) , file!( ) ) ;
110- assert_eq ! ( contained. line( ) , 15 ) ;
111- assert_eq ! ( contained. column( ) , 5 ) ;
158+ #[ track_caller]
159+ fn dyn_invoke_fn_once (
160+ val : Box < dyn FnOnce ( & ' static str , bool ) -> ( & ' static str , bool , Loc ) > ,
161+ ) -> ( & ' static str , bool , Loc ) {
162+ val ( "from_dyn" , false )
163+ }
112164
113- // `Location::caller()` in a macro should behave similarly to `file!` and `line!`,
114- // i.e. point to where the macro was invoked, instead of the macro itself.
115- let inmacro = caller_location_from_macro ! ( ) ;
116- let expected_line = line ! ( ) - 1 ;
117- assert_eq ! ( inmacro. file( ) , file!( ) ) ;
118- assert_eq ! ( inmacro. line( ) , expected_line) ;
119- assert_eq ! ( inmacro. column( ) , 19 ) ;
165+ let mut track_closure = #[ track_caller]
166+ |first: & ' static str , second: bool | ( first, second, Location :: caller( ) ) ;
167+ let ( first_arg, first_bool, first_loc) = track_closure ( "first_arg" , true ) ;
168+ let first_line = line ! ( ) - 1 ;
169+ assert_eq ! ( first_arg, "first_arg" ) ;
170+ assert_eq ! ( first_bool, true ) ;
171+ assert_eq ! ( first_loc. file( ) , file!( ) ) ;
172+ assert_eq ! ( first_loc. line( ) , first_line) ;
173+ assert_eq ! ( first_loc. column( ) , 46 ) ;
120174
121- let intrinsic = core:: intrinsics:: caller_location ( ) ;
122- let expected_line = line ! ( ) - 1 ;
123- assert_eq ! ( intrinsic. file( ) , file!( ) ) ;
124- assert_eq ! ( intrinsic. line( ) , expected_line) ;
125- assert_eq ! ( intrinsic. column( ) , 21 ) ;
175+ let ( dyn_arg, dyn_bool, dyn_loc) = dyn_invoke_fn_mut ( & mut track_closure) ;
176+ assert_eq ! ( dyn_arg, "from_dyn" ) ;
177+ assert_eq ! ( dyn_bool, false ) ;
178+ // `FnMut::call_mut` does not have `#[track_caller]`,
179+ // so this will not match
180+ assert_ne ! ( dyn_loc. file( ) , file!( ) ) ;
181+
182+ let ( dyn_arg, dyn_bool, dyn_loc) = dyn_invoke_fn_once ( Box :: new ( track_closure) ) ;
183+ assert_eq ! ( dyn_arg, "from_dyn" ) ;
184+ assert_eq ! ( dyn_bool, false ) ;
185+ // `FnOnce::call_once` does not have `#[track_caller]`
186+ // so this will not match
187+ assert_ne ! ( dyn_loc. file( ) , file!( ) ) ;
188+
189+ let ( mono_arg, mono_bool, mono_loc) = mono_invoke_fn ( & track_closure) ;
190+ let mono_line = line ! ( ) - 1 ;
191+ assert_eq ! ( mono_arg, "from_mono" ) ;
192+ assert_eq ! ( mono_bool, false ) ;
193+ assert_eq ! ( mono_loc. file( ) , file!( ) ) ;
194+ assert_eq ! ( mono_loc. line( ) , mono_line) ;
195+ assert_eq ! ( mono_loc. column( ) , 43 ) ;
196+
197+ let ( mono_arg, mono_bool, mono_loc) = mono_invoke_fn_once ( track_closure) ;
198+ let mono_line = line ! ( ) - 1 ;
199+ assert_eq ! ( mono_arg, "from_mono" ) ;
200+ assert_eq ! ( mono_bool, false ) ;
201+ assert_eq ! ( mono_loc. file( ) , file!( ) ) ;
202+ assert_eq ! ( mono_loc. line( ) , mono_line) ;
203+ assert_eq ! ( mono_loc. column( ) , 43 ) ;
204+
205+ let non_tracked_caller = || Location :: caller ( ) ;
206+ let non_tracked_line = line ! ( ) - 1 ; // This is the line of the closure, not its caller
207+ let non_tracked_loc = non_tracked_caller ( ) ;
208+ assert_eq ! ( non_tracked_loc. file( ) , file!( ) ) ;
209+ assert_eq ! ( non_tracked_loc. line( ) , non_tracked_line) ;
210+ assert_eq ! ( non_tracked_loc. column( ) , 33 ) ;
211+ }
212+
213+ fn test_generator ( ) {
214+ #[ track_caller]
215+ fn mono_generator < F : Generator < String , Yield = ( & ' static str , String , Loc ) , Return = ( ) > > (
216+ val : Pin < & mut F > ,
217+ ) -> ( & ' static str , String , Loc ) {
218+ match val. resume ( "Mono" . to_string ( ) ) {
219+ GeneratorState :: Yielded ( val) => val,
220+ _ => unreachable ! ( ) ,
221+ }
222+ }
126223
224+ #[ track_caller]
225+ fn dyn_generator (
226+ val : Pin < & mut dyn Generator < String , Yield = ( & ' static str , String , Loc ) , Return = ( ) > > ,
227+ ) -> ( & ' static str , String , Loc ) {
228+ match val. resume ( "Dyn" . to_string ( ) ) {
229+ GeneratorState :: Yielded ( val) => val,
230+ _ => unreachable ! ( ) ,
231+ }
232+ }
233+
234+ #[ rustfmt:: skip]
235+ let generator = #[ track_caller] |arg: String | {
236+ yield ( "first" , arg. clone ( ) , Location :: caller ( ) ) ;
237+ yield ( "second" , arg. clone ( ) , Location :: caller ( ) ) ;
238+ } ;
239+
240+ let mut pinned = Box :: pin ( generator) ;
241+ let ( dyn_ret, dyn_arg, dyn_loc) = dyn_generator ( pinned. as_mut ( ) ) ;
242+ assert_eq ! ( dyn_ret, "first" ) ;
243+ assert_eq ! ( dyn_arg, "Dyn" . to_string( ) ) ;
244+ // The `Generator` trait does not have `#[track_caller]` on `resume`, so
245+ // this will not match.
246+ assert_ne ! ( dyn_loc. file( ) , file!( ) ) ;
247+
248+ let ( mono_ret, mono_arg, mono_loc) = mono_generator ( pinned. as_mut ( ) ) ;
249+ let mono_line = line ! ( ) - 1 ;
250+ assert_eq ! ( mono_ret, "second" ) ;
251+ // The generator ignores the argument to the second `resume` call
252+ assert_eq ! ( mono_arg, "Dyn" . to_string( ) ) ;
253+ assert_eq ! ( mono_loc. file( ) , file!( ) ) ;
254+ assert_eq ! ( mono_loc. line( ) , mono_line) ;
255+ assert_eq ! ( mono_loc. column( ) , 42 ) ;
256+
257+ #[ rustfmt:: skip]
258+ let non_tracked_generator = || { yield Location :: caller ( ) ; } ;
259+ let non_tracked_line = line ! ( ) - 1 ; // This is the line of the generator, not its caller
260+ let non_tracked_loc = match Box :: pin ( non_tracked_generator) . as_mut ( ) . resume ( ( ) ) {
261+ GeneratorState :: Yielded ( val) => val,
262+ _ => unreachable ! ( ) ,
263+ } ;
264+ assert_eq ! ( non_tracked_loc. file( ) , file!( ) ) ;
265+ assert_eq ! ( non_tracked_loc. line( ) , non_tracked_line) ;
266+ assert_eq ! ( non_tracked_loc. column( ) , 44 ) ;
267+ }
268+
269+ fn main ( ) {
270+ test_basic ( ) ;
127271 test_fn_ptr ( ) ;
128272 test_trait_obj ( ) ;
129273 test_trait_obj2 ( ) ;
274+ test_closure ( ) ;
275+ test_generator ( ) ;
130276}
0 commit comments