@@ -807,6 +807,96 @@ impl CodeMap {
807
807
hi. col. to_usize( ) + 1 ) ) . to_string ( )
808
808
}
809
809
810
+ // Returns true if two spans have the same callee
811
+ // (Assumes the same ExpnFormat implies same callee)
812
+ fn match_callees ( & self , sp_a : & Span , sp_b : & Span ) -> bool {
813
+ let fmt_a = self
814
+ . with_expn_info ( sp_a. expn_id ,
815
+ |ei| ei. map ( |ei| ei. callee . format . clone ( ) ) ) ;
816
+
817
+ let fmt_b = self
818
+ . with_expn_info ( sp_b. expn_id ,
819
+ |ei| ei. map ( |ei| ei. callee . format . clone ( ) ) ) ;
820
+ fmt_a == fmt_b
821
+ }
822
+
823
+ /// Returns a formatted string showing the expansion chain of a span
824
+ ///
825
+ /// Spans are printed in the following format:
826
+ ///
827
+ /// filename:start_line:col: end_line:col
828
+ /// snippet
829
+ /// Callee:
830
+ /// Callee span
831
+ /// Callsite:
832
+ /// Callsite span
833
+ ///
834
+ /// Callees and callsites are printed recursively (if available, otherwise header
835
+ /// and span is omitted), expanding into their own callee/callsite spans.
836
+ /// Each layer of recursion has an increased indent, and snippets are truncated
837
+ /// to at most 50 characters. Finally, recursive calls to the same macro are squashed,
838
+ /// with '...' used to represent any number of recursive calls.
839
+ pub fn span_to_expanded_string ( & self , sp : Span ) -> String {
840
+ self . span_to_expanded_string_internal ( sp, "" )
841
+ }
842
+
843
+ fn span_to_expanded_string_internal ( & self , sp : Span , indent : & str ) -> String {
844
+ let mut indent = indent. to_owned ( ) ;
845
+ let mut output = "" . to_owned ( ) ;
846
+ let span_str = self . span_to_string ( sp) ;
847
+ let mut span_snip = self . span_to_snippet ( sp)
848
+ . unwrap_or ( "Snippet unavailable" . to_owned ( ) ) ;
849
+ if span_snip. len ( ) > 50 {
850
+ span_snip. truncate ( 50 ) ;
851
+ span_snip. push_str ( "..." ) ;
852
+ }
853
+ output. push_str ( & format ! ( "{}{}\n {}`{}`\n " , indent, span_str, indent, span_snip) ) ;
854
+
855
+ if sp. expn_id == NO_EXPANSION || sp. expn_id == COMMAND_LINE_EXPN {
856
+ return output;
857
+ }
858
+
859
+ let mut callee = self . with_expn_info ( sp. expn_id ,
860
+ |ei| ei. and_then ( |ei| ei. callee . span . clone ( ) ) ) ;
861
+ let mut callsite = self . with_expn_info ( sp. expn_id ,
862
+ |ei| ei. map ( |ei| ei. call_site . clone ( ) ) ) ;
863
+
864
+ indent. push_str ( " " ) ;
865
+ let mut is_recursive = false ;
866
+
867
+ while callee. is_some ( ) && self . match_callees ( & sp, & callee. unwrap ( ) ) {
868
+ callee = self . with_expn_info ( callee. unwrap ( ) . expn_id ,
869
+ |ei| ei. and_then ( |ei| ei. callee . span . clone ( ) ) ) ;
870
+ is_recursive = true ;
871
+ }
872
+ if let Some ( span) = callee {
873
+ output. push_str ( & indent) ;
874
+ output. push_str ( "Callee:\n " ) ;
875
+ if is_recursive {
876
+ output. push_str ( & indent) ;
877
+ output. push_str ( "...\n " ) ;
878
+ }
879
+ output. push_str ( & ( self . span_to_expanded_string_internal ( span, & indent) ) ) ;
880
+ }
881
+
882
+ is_recursive = false ;
883
+ while callsite. is_some ( ) && self . match_callees ( & sp, & callsite. unwrap ( ) ) {
884
+ callsite = self . with_expn_info ( callsite. unwrap ( ) . expn_id ,
885
+ |ei| ei. map ( |ei| ei. call_site . clone ( ) ) ) ;
886
+ is_recursive = true ;
887
+ }
888
+ if let Some ( span) = callsite {
889
+ output. push_str ( & indent) ;
890
+ output. push_str ( "Callsite:\n " ) ;
891
+ if is_recursive {
892
+ output. push_str ( & indent) ;
893
+ output. push_str ( "...\n " ) ;
894
+ }
895
+ output. push_str ( & ( self . span_to_expanded_string_internal ( span, & indent) ) ) ;
896
+ }
897
+ output
898
+ }
899
+
810
900
pub fn span_to_filename ( & self , sp : Span ) -> FileName {
811
901
self . lookup_char_pos ( sp. lo ) . file . name . to_string ( )
812
902
}
@@ -1274,4 +1364,118 @@ mod tests {
1274
1364
1275
1365
assert_eq ! ( sstr, "blork.rs:2:1: 2:12" ) ;
1276
1366
}
1367
+
1368
+ #[ test]
1369
+ fn t10 ( ) {
1370
+ // Test span_to_expanded_string works in base case (no expansion)
1371
+ let cm = init_code_map ( ) ;
1372
+ let span = Span { lo : BytePos ( 0 ) , hi : BytePos ( 11 ) , expn_id : NO_EXPANSION } ;
1373
+ let sstr = cm. span_to_expanded_string ( span) ;
1374
+ assert_eq ! ( sstr, "blork.rs:1:1: 1:12\n `first line.`\n " ) ;
1375
+
1376
+ let span = Span { lo : BytePos ( 12 ) , hi : BytePos ( 23 ) , expn_id : NO_EXPANSION } ;
1377
+ let sstr = cm. span_to_expanded_string ( span) ;
1378
+ assert_eq ! ( sstr, "blork.rs:2:1: 2:12\n `second line`\n " ) ;
1379
+ }
1380
+
1381
+ #[ test]
1382
+ fn t11 ( ) {
1383
+ // Test span_to_expanded_string works with expansion
1384
+ use ast:: Name ;
1385
+ let cm = init_code_map ( ) ;
1386
+ let root = Span { lo : BytePos ( 0 ) , hi : BytePos ( 11 ) , expn_id : NO_EXPANSION } ;
1387
+ let format = ExpnFormat :: MacroBang ( Name ( 0u32 ) ) ;
1388
+ let callee = NameAndSpan { format : format,
1389
+ allow_internal_unstable : false ,
1390
+ span : None } ;
1391
+
1392
+ let info = ExpnInfo { call_site : root, callee : callee } ;
1393
+ let id = cm. record_expansion ( info) ;
1394
+ let sp = Span { lo : BytePos ( 12 ) , hi : BytePos ( 23 ) , expn_id : id } ;
1395
+
1396
+ let sstr = cm. span_to_expanded_string ( sp) ;
1397
+ assert_eq ! ( sstr,
1398
+ "blork.rs:2:1: 2:12\n `second line`\n Callsite:\n \
1399
+ blork.rs:1:1: 1:12\n `first line.`\n ") ;
1400
+ }
1401
+
1402
+ fn init_expansion_chain ( cm : & CodeMap ) -> Span {
1403
+ // Creates an expansion chain containing two recursive calls
1404
+ // root -> expA -> expA -> expB -> expB -> end
1405
+ use ast:: Name ;
1406
+
1407
+ let root = Span { lo : BytePos ( 0 ) , hi : BytePos ( 11 ) , expn_id : NO_EXPANSION } ;
1408
+
1409
+ let format_root = ExpnFormat :: MacroBang ( Name ( 0u32 ) ) ;
1410
+ let callee_root = NameAndSpan { format : format_root,
1411
+ allow_internal_unstable : false ,
1412
+ span : Some ( root) } ;
1413
+
1414
+ let info_a1 = ExpnInfo { call_site : root, callee : callee_root } ;
1415
+ let id_a1 = cm. record_expansion ( info_a1) ;
1416
+ let span_a1 = Span { lo : BytePos ( 12 ) , hi : BytePos ( 23 ) , expn_id : id_a1 } ;
1417
+
1418
+ let format_a = ExpnFormat :: MacroBang ( Name ( 1u32 ) ) ;
1419
+ let callee_a = NameAndSpan { format : format_a,
1420
+ allow_internal_unstable : false ,
1421
+ span : Some ( span_a1) } ;
1422
+
1423
+ let info_a2 = ExpnInfo { call_site : span_a1, callee : callee_a. clone ( ) } ;
1424
+ let id_a2 = cm. record_expansion ( info_a2) ;
1425
+ let span_a2 = Span { lo : BytePos ( 12 ) , hi : BytePos ( 23 ) , expn_id : id_a2 } ;
1426
+
1427
+ let info_b1 = ExpnInfo { call_site : span_a2, callee : callee_a } ;
1428
+ let id_b1 = cm. record_expansion ( info_b1) ;
1429
+ let span_b1 = Span { lo : BytePos ( 25 ) , hi : BytePos ( 36 ) , expn_id : id_b1 } ;
1430
+
1431
+ let format_b = ExpnFormat :: MacroBang ( Name ( 2u32 ) ) ;
1432
+ let callee_b = NameAndSpan { format : format_b,
1433
+ allow_internal_unstable : false ,
1434
+ span : None } ;
1435
+
1436
+ let info_b2 = ExpnInfo { call_site : span_b1, callee : callee_b. clone ( ) } ;
1437
+ let id_b2 = cm. record_expansion ( info_b2) ;
1438
+ let span_b2 = Span { lo : BytePos ( 25 ) , hi : BytePos ( 36 ) , expn_id : id_b2 } ;
1439
+
1440
+ let info_end = ExpnInfo { call_site : span_b2, callee : callee_b } ;
1441
+ let id_end = cm. record_expansion ( info_end) ;
1442
+ Span { lo : BytePos ( 37 ) , hi : BytePos ( 48 ) , expn_id : id_end }
1443
+ }
1444
+
1445
+ #[ test]
1446
+ fn t12 ( ) {
1447
+ // Test span_to_expanded_string collapses recursive macros and handles
1448
+ // recursive callsite and callee expansions
1449
+ let cm = init_code_map ( ) ;
1450
+ let end = init_expansion_chain ( & cm) ;
1451
+ let sstr = cm. span_to_expanded_string ( end) ;
1452
+ let res_str =
1453
+ r"blork2.rs:2:1: 2:12
1454
+ `second line`
1455
+ Callsite:
1456
+ ...
1457
+ blork2.rs:1:1: 1:12
1458
+ `first line.`
1459
+ Callee:
1460
+ blork.rs:2:1: 2:12
1461
+ `second line`
1462
+ Callee:
1463
+ blork.rs:1:1: 1:12
1464
+ `first line.`
1465
+ Callsite:
1466
+ blork.rs:1:1: 1:12
1467
+ `first line.`
1468
+ Callsite:
1469
+ ...
1470
+ blork.rs:2:1: 2:12
1471
+ `second line`
1472
+ Callee:
1473
+ blork.rs:1:1: 1:12
1474
+ `first line.`
1475
+ Callsite:
1476
+ blork.rs:1:1: 1:12
1477
+ `first line.`
1478
+ " ;
1479
+ assert_eq ! ( sstr, res_str) ;
1480
+ }
1277
1481
}
0 commit comments