@@ -115,6 +115,10 @@ impl Sub for CharPos {
115
115
/// are *absolute* positions from the beginning of the codemap, not positions
116
116
/// relative to FileMaps. Methods on the CodeMap can be used to relate spans back
117
117
/// to the original source.
118
+ /// You must be careful if the span crosses more than one file - you will not be
119
+ /// able to use many of the functions on spans in codemap and you cannot assume
120
+ /// that the length of the span = hi - lo; there may be space in the BytePos
121
+ /// range between files.
118
122
#[ derive( Clone , Copy , Hash ) ]
119
123
pub struct Span {
120
124
pub lo : BytePos ,
@@ -339,7 +343,7 @@ pub struct MultiByteChar {
339
343
pub bytes : usize ,
340
344
}
341
345
342
- /// A single source in the CodeMap
346
+ /// A single source in the CodeMap.
343
347
pub struct FileMap {
344
348
/// The name of the file that the source came from, source that doesn't
345
349
/// originate from files has names between angle brackets by convention,
@@ -508,6 +512,9 @@ impl FileMap {
508
512
lines. get ( line_number) . map ( |& line| {
509
513
let begin: BytePos = line - self . start_pos ;
510
514
let begin = begin. to_usize ( ) ;
515
+ // We can't use `lines.get(line_number+1)` because we might
516
+ // be parsing when we call this function and thus the current
517
+ // line is the last one we have line info for.
511
518
let slice = & src[ begin..] ;
512
519
match slice. find ( '\n' ) {
513
520
Some ( e) => & slice[ ..e] ,
@@ -598,27 +605,27 @@ impl CodeMap {
598
605
Ok ( self . new_filemap ( path. to_str ( ) . unwrap ( ) . to_string ( ) , src) )
599
606
}
600
607
608
+ fn next_start_pos ( & self ) -> usize {
609
+ let files = self . files . borrow ( ) ;
610
+ match files. last ( ) {
611
+ None => 0 ,
612
+ // Add one so there is some space between files. This lets us distinguish
613
+ // positions in the codemap, even in the presence of zero-length files.
614
+ Some ( last) => last. end_pos . to_usize ( ) + 1 ,
615
+ }
616
+ }
617
+
618
+ /// Creates a new filemap without setting its line information. If you don't
619
+ /// intend to set the line information yourself, you should use new_filemap_and_lines.
601
620
pub fn new_filemap ( & self , filename : FileName , mut src : String ) -> Rc < FileMap > {
621
+ let start_pos = self . next_start_pos ( ) ;
602
622
let mut files = self . files . borrow_mut ( ) ;
603
- let start_pos = match files. last ( ) {
604
- None => 0 ,
605
- Some ( last) => last. end_pos . to_usize ( ) ,
606
- } ;
607
623
608
624
// Remove utf-8 BOM if any.
609
625
if src. starts_with ( "\u{feff} " ) {
610
626
src. drain ( ..3 ) ;
611
627
}
612
628
613
- // Append '\n' in case it's not already there.
614
- // This is a workaround to prevent CodeMap.lookup_filemap_idx from
615
- // accidentally overflowing into the next filemap in case the last byte
616
- // of span is also the last byte of filemap, which leads to incorrect
617
- // results from CodeMap.span_to_*.
618
- if !src. is_empty ( ) && !src. ends_with ( "\n " ) {
619
- src. push ( '\n' ) ;
620
- }
621
-
622
629
let end_pos = start_pos + src. len ( ) ;
623
630
624
631
let filemap = Rc :: new ( FileMap {
@@ -635,6 +642,21 @@ impl CodeMap {
635
642
filemap
636
643
}
637
644
645
+ /// Creates a new filemap and sets its line information.
646
+ pub fn new_filemap_and_lines ( & self , filename : & str , src : & str ) -> Rc < FileMap > {
647
+ let fm = self . new_filemap ( filename. to_string ( ) , src. to_owned ( ) ) ;
648
+ let mut byte_pos: u32 = 0 ;
649
+ for line in src. lines ( ) {
650
+ // register the start of this line
651
+ fm. next_line ( BytePos ( byte_pos) ) ;
652
+
653
+ // update byte_pos to include this line and the \n at the end
654
+ byte_pos += line. len ( ) as u32 + 1 ;
655
+ }
656
+ fm
657
+ }
658
+
659
+
638
660
/// Allocates a new FileMap representing a source file from an external
639
661
/// crate. The source code of such an "imported filemap" is not available,
640
662
/// but we still know enough to generate accurate debuginfo location
@@ -645,11 +667,8 @@ impl CodeMap {
645
667
mut file_local_lines : Vec < BytePos > ,
646
668
mut file_local_multibyte_chars : Vec < MultiByteChar > )
647
669
-> Rc < FileMap > {
670
+ let start_pos = self . next_start_pos ( ) ;
648
671
let mut files = self . files . borrow_mut ( ) ;
649
- let start_pos = match files. last ( ) {
650
- None => 0 ,
651
- Some ( last) => last. end_pos . to_usize ( ) ,
652
- } ;
653
672
654
673
let end_pos = Pos :: from_usize ( start_pos + source_len) ;
655
674
let start_pos = Pos :: from_usize ( start_pos) ;
@@ -686,39 +705,61 @@ impl CodeMap {
686
705
687
706
/// Lookup source information about a BytePos
688
707
pub fn lookup_char_pos ( & self , pos : BytePos ) -> Loc {
689
- let FileMapAndLine { fm : f, line : a} = self . lookup_line ( pos) ;
690
- let line = a + 1 ; // Line numbers start at 1
691
708
let chpos = self . bytepos_to_file_charpos ( pos) ;
692
- let linebpos = ( * f. lines . borrow ( ) ) [ a] ;
693
- let linechpos = self . bytepos_to_file_charpos ( linebpos) ;
694
- debug ! ( "byte pos {:?} is on the line at byte pos {:?}" ,
695
- pos, linebpos) ;
696
- debug ! ( "char pos {:?} is on the line at char pos {:?}" ,
697
- chpos, linechpos) ;
698
- debug ! ( "byte is on line: {}" , line) ;
699
- assert ! ( chpos >= linechpos) ;
700
- Loc {
701
- file : f,
702
- line : line,
703
- col : chpos - linechpos
709
+ match self . lookup_line ( pos) {
710
+ Ok ( FileMapAndLine { fm : f, line : a } ) => {
711
+ let line = a + 1 ; // Line numbers start at 1
712
+ let linebpos = ( * f. lines . borrow ( ) ) [ a] ;
713
+ let linechpos = self . bytepos_to_file_charpos ( linebpos) ;
714
+ debug ! ( "byte pos {:?} is on the line at byte pos {:?}" ,
715
+ pos, linebpos) ;
716
+ debug ! ( "char pos {:?} is on the line at char pos {:?}" ,
717
+ chpos, linechpos) ;
718
+ debug ! ( "byte is on line: {}" , line) ;
719
+ assert ! ( chpos >= linechpos) ;
720
+ Loc {
721
+ file : f,
722
+ line : line,
723
+ col : chpos - linechpos,
724
+ }
725
+ }
726
+ Err ( f) => {
727
+ Loc {
728
+ file : f,
729
+ line : 0 ,
730
+ col : chpos,
731
+ }
732
+ }
704
733
}
705
734
}
706
735
707
- fn lookup_line ( & self , pos : BytePos ) -> FileMapAndLine {
736
+ // If the relevant filemap is empty, we don't return a line number.
737
+ fn lookup_line ( & self , pos : BytePos ) -> Result < FileMapAndLine , Rc < FileMap > > {
708
738
let idx = self . lookup_filemap_idx ( pos) ;
709
739
710
740
let files = self . files . borrow ( ) ;
711
741
let f = ( * files) [ idx] . clone ( ) ;
742
+
743
+ let len = f. lines . borrow ( ) . len ( ) ;
744
+ if len == 0 {
745
+ return Err ( f) ;
746
+ }
747
+
712
748
let mut a = 0 ;
713
749
{
714
750
let lines = f. lines . borrow ( ) ;
715
751
let mut b = lines. len ( ) ;
716
752
while b - a > 1 {
717
753
let m = ( a + b) / 2 ;
718
- if ( * lines) [ m] > pos { b = m; } else { a = m; }
754
+ if ( * lines) [ m] > pos {
755
+ b = m;
756
+ } else {
757
+ a = m;
758
+ }
719
759
}
760
+ assert ! ( a <= lines. len( ) ) ;
720
761
}
721
- FileMapAndLine { fm : f, line : a}
762
+ Ok ( FileMapAndLine { fm : f, line : a } )
722
763
}
723
764
724
765
pub fn lookup_char_pos_adj ( & self , pos : BytePos ) -> LocWithOpt {
@@ -853,7 +894,7 @@ impl CodeMap {
853
894
FileMapAndBytePos { fm : fm, pos : offset}
854
895
}
855
896
856
- /// Converts an absolute BytePos to a CharPos relative to the filemap and above .
897
+ /// Converts an absolute BytePos to a CharPos relative to the filemap.
857
898
pub fn bytepos_to_file_charpos ( & self , bpos : BytePos ) -> CharPos {
858
899
let idx = self . lookup_filemap_idx ( bpos) ;
859
900
let files = self . files . borrow ( ) ;
@@ -880,12 +921,15 @@ impl CodeMap {
880
921
CharPos ( bpos. to_usize ( ) - map. start_pos . to_usize ( ) - total_extra_bytes)
881
922
}
882
923
924
+ // Return the index of the filemap (in self.files) which contains pos.
883
925
fn lookup_filemap_idx ( & self , pos : BytePos ) -> usize {
884
926
let files = self . files . borrow ( ) ;
885
927
let files = & * files;
886
- let len = files. len ( ) ;
928
+ let count = files. len ( ) ;
929
+
930
+ // Binary search for the filemap.
887
931
let mut a = 0 ;
888
- let mut b = len ;
932
+ let mut b = count ;
889
933
while b - a > 1 {
890
934
let m = ( a + b) / 2 ;
891
935
if files[ m] . start_pos > pos {
@@ -894,26 +938,8 @@ impl CodeMap {
894
938
a = m;
895
939
}
896
940
}
897
- // There can be filemaps with length 0. These have the same start_pos as
898
- // the previous filemap, but are not the filemaps we want (because they
899
- // are length 0, they cannot contain what we are looking for). So,
900
- // rewind until we find a useful filemap.
901
- loop {
902
- let lines = files[ a] . lines . borrow ( ) ;
903
- let lines = lines;
904
- if !lines. is_empty ( ) {
905
- break ;
906
- }
907
- if a == 0 {
908
- panic ! ( "position {} does not resolve to a source location" ,
909
- pos. to_usize( ) ) ;
910
- }
911
- a -= 1 ;
912
- }
913
- if a >= len {
914
- panic ! ( "position {} does not resolve to a source location" ,
915
- pos. to_usize( ) )
916
- }
941
+
942
+ assert ! ( a < count, "position {} does not resolve to a source location" , pos. to_usize( ) ) ;
917
943
918
944
return a;
919
945
}
@@ -1027,10 +1053,13 @@ mod tests {
1027
1053
let fm = cm. new_filemap ( "blork.rs" . to_string ( ) ,
1028
1054
"first line.\n second line" . to_string ( ) ) ;
1029
1055
fm. next_line ( BytePos ( 0 ) ) ;
1056
+ // Test we can get lines with partial line info.
1030
1057
assert_eq ! ( fm. get_line( 0 ) , Some ( "first line." ) ) ;
1031
- // TESTING BROKEN BEHAVIOR:
1058
+ // TESTING BROKEN BEHAVIOR: line break declared before actual line break.
1032
1059
fm. next_line ( BytePos ( 10 ) ) ;
1033
1060
assert_eq ! ( fm. get_line( 1 ) , Some ( "." ) ) ;
1061
+ fm. next_line ( BytePos ( 12 ) ) ;
1062
+ assert_eq ! ( fm. get_line( 2 ) , Some ( "second line" ) ) ;
1034
1063
}
1035
1064
1036
1065
#[ test]
@@ -1056,9 +1085,9 @@ mod tests {
1056
1085
1057
1086
fm1. next_line ( BytePos ( 0 ) ) ;
1058
1087
fm1. next_line ( BytePos ( 12 ) ) ;
1059
- fm2. next_line ( BytePos ( 24 ) ) ;
1060
- fm3. next_line ( BytePos ( 24 ) ) ;
1061
- fm3. next_line ( BytePos ( 34 ) ) ;
1088
+ fm2. next_line ( fm2 . start_pos ) ;
1089
+ fm3. next_line ( fm3 . start_pos ) ;
1090
+ fm3. next_line ( fm3 . start_pos + BytePos ( 12 ) ) ;
1062
1091
1063
1092
cm
1064
1093
}
@@ -1068,11 +1097,15 @@ mod tests {
1068
1097
// Test lookup_byte_offset
1069
1098
let cm = init_code_map ( ) ;
1070
1099
1071
- let fmabp1 = cm. lookup_byte_offset ( BytePos ( 22 ) ) ;
1100
+ let fmabp1 = cm. lookup_byte_offset ( BytePos ( 23 ) ) ;
1072
1101
assert_eq ! ( fmabp1. fm. name, "blork.rs" ) ;
1073
- assert_eq ! ( fmabp1. pos, BytePos ( 22 ) ) ;
1102
+ assert_eq ! ( fmabp1. pos, BytePos ( 23 ) ) ;
1103
+
1104
+ let fmabp1 = cm. lookup_byte_offset ( BytePos ( 24 ) ) ;
1105
+ assert_eq ! ( fmabp1. fm. name, "empty.rs" ) ;
1106
+ assert_eq ! ( fmabp1. pos, BytePos ( 0 ) ) ;
1074
1107
1075
- let fmabp2 = cm. lookup_byte_offset ( BytePos ( 24 ) ) ;
1108
+ let fmabp2 = cm. lookup_byte_offset ( BytePos ( 25 ) ) ;
1076
1109
assert_eq ! ( fmabp2. fm. name, "blork2.rs" ) ;
1077
1110
assert_eq ! ( fmabp2. pos, BytePos ( 0 ) ) ;
1078
1111
}
@@ -1085,7 +1118,7 @@ mod tests {
1085
1118
let cp1 = cm. bytepos_to_file_charpos ( BytePos ( 22 ) ) ;
1086
1119
assert_eq ! ( cp1, CharPos ( 22 ) ) ;
1087
1120
1088
- let cp2 = cm. bytepos_to_file_charpos ( BytePos ( 24 ) ) ;
1121
+ let cp2 = cm. bytepos_to_file_charpos ( BytePos ( 25 ) ) ;
1089
1122
assert_eq ! ( cp2, CharPos ( 0 ) ) ;
1090
1123
}
1091
1124
@@ -1099,7 +1132,7 @@ mod tests {
1099
1132
assert_eq ! ( loc1. line, 2 ) ;
1100
1133
assert_eq ! ( loc1. col, CharPos ( 10 ) ) ;
1101
1134
1102
- let loc2 = cm. lookup_char_pos ( BytePos ( 24 ) ) ;
1135
+ let loc2 = cm. lookup_char_pos ( BytePos ( 25 ) ) ;
1103
1136
assert_eq ! ( loc2. file. name, "blork2.rs" ) ;
1104
1137
assert_eq ! ( loc2. line, 1 ) ;
1105
1138
assert_eq ! ( loc2. col, CharPos ( 0 ) ) ;
@@ -1115,18 +1148,18 @@ mod tests {
1115
1148
"first line€€.\n € second line" . to_string ( ) ) ;
1116
1149
1117
1150
fm1. next_line ( BytePos ( 0 ) ) ;
1118
- fm1. next_line ( BytePos ( 22 ) ) ;
1119
- fm2. next_line ( BytePos ( 40 ) ) ;
1120
- fm2. next_line ( BytePos ( 58 ) ) ;
1151
+ fm1. next_line ( BytePos ( 28 ) ) ;
1152
+ fm2. next_line ( fm2 . start_pos ) ;
1153
+ fm2. next_line ( fm2 . start_pos + BytePos ( 20 ) ) ;
1121
1154
1122
1155
fm1. record_multibyte_char ( BytePos ( 3 ) , 3 ) ;
1123
1156
fm1. record_multibyte_char ( BytePos ( 9 ) , 3 ) ;
1124
1157
fm1. record_multibyte_char ( BytePos ( 12 ) , 3 ) ;
1125
1158
fm1. record_multibyte_char ( BytePos ( 15 ) , 3 ) ;
1126
1159
fm1. record_multibyte_char ( BytePos ( 18 ) , 3 ) ;
1127
- fm2. record_multibyte_char ( BytePos ( 50 ) , 3 ) ;
1128
- fm2. record_multibyte_char ( BytePos ( 53 ) , 3 ) ;
1129
- fm2. record_multibyte_char ( BytePos ( 58 ) , 3 ) ;
1160
+ fm2. record_multibyte_char ( fm2 . start_pos + BytePos ( 10 ) , 3 ) ;
1161
+ fm2. record_multibyte_char ( fm2 . start_pos + BytePos ( 13 ) , 3 ) ;
1162
+ fm2. record_multibyte_char ( fm2 . start_pos + BytePos ( 18 ) , 3 ) ;
1130
1163
1131
1164
cm
1132
1165
}
@@ -1172,27 +1205,14 @@ mod tests {
1172
1205
Span { lo : BytePos ( left_index) , hi : BytePos ( right_index + 1 ) , expn_id : NO_EXPANSION }
1173
1206
}
1174
1207
1175
- fn new_filemap_and_lines ( cm : & CodeMap , filename : & str , input : & str ) -> Rc < FileMap > {
1176
- let fm = cm. new_filemap ( filename. to_string ( ) , input. to_string ( ) ) ;
1177
- let mut byte_pos: u32 = 0 ;
1178
- for line in input. lines ( ) {
1179
- // register the start of this line
1180
- fm. next_line ( BytePos ( byte_pos) ) ;
1181
-
1182
- // update byte_pos to include this line and the \n at the end
1183
- byte_pos += line. len ( ) as u32 + 1 ;
1184
- }
1185
- fm
1186
- }
1187
-
1188
1208
/// Test span_to_snippet and span_to_lines for a span coverting 3
1189
1209
/// lines in the middle of a file.
1190
1210
#[ test]
1191
1211
fn span_to_snippet_and_lines_spanning_multiple_lines ( ) {
1192
1212
let cm = CodeMap :: new ( ) ;
1193
1213
let inputtext = "aaaaa\n bbbbBB\n CCC\n DDDDDddddd\n eee\n " ;
1194
1214
let selection = " \n ^~\n ~~~\n ~~~~~ \n \n " ;
1195
- new_filemap_and_lines ( & cm , "blork.rs" , inputtext) ;
1215
+ cm . new_filemap_and_lines ( "blork.rs" , inputtext) ;
1196
1216
let span = span_from_selection ( inputtext, selection) ;
1197
1217
1198
1218
// check that we are extracting the text we thought we were extracting
0 commit comments