@@ -47,7 +47,7 @@ impl<'sm> CachingSourceMapView<'sm> {
47
47
48
48
// Check if the position is in one of the cached lines
49
49
for cache_entry in self . line_cache . iter_mut ( ) {
50
- if pos >= cache_entry. line_start && pos < cache_entry. line_end {
50
+ if line_contains ( ( cache_entry. line_start , cache_entry. line_end ) , pos ) {
51
51
cache_entry. time_stamp = self . time_stamp ;
52
52
53
53
return Some ( (
@@ -69,13 +69,13 @@ impl<'sm> CachingSourceMapView<'sm> {
69
69
let cache_entry = & mut self . line_cache [ oldest] ;
70
70
71
71
// If the entry doesn't point to the correct file, fix it up
72
- if pos < cache_entry. file . start_pos || pos >= cache_entry . file . end_pos {
72
+ if ! file_contains ( & cache_entry. file , pos) {
73
73
let file_valid;
74
74
if self . source_map . files ( ) . len ( ) > 0 {
75
75
let file_index = self . source_map . lookup_source_file_idx ( pos) ;
76
76
let file = self . source_map . files ( ) [ file_index] . clone ( ) ;
77
77
78
- if pos >= file. start_pos && pos < file . end_pos {
78
+ if file_contains ( & file, pos) {
79
79
cache_entry. file = file;
80
80
cache_entry. file_index = file_index;
81
81
file_valid = true ;
@@ -102,3 +102,29 @@ impl<'sm> CachingSourceMapView<'sm> {
102
102
Some ( ( cache_entry. file . clone ( ) , cache_entry. line_number , pos - cache_entry. line_start ) )
103
103
}
104
104
}
105
+
106
+ #[ inline]
107
+ fn line_contains ( line_bounds : ( BytePos , BytePos ) , pos : BytePos ) -> bool {
108
+ // This condition will be false in one case where we'd rather it wasn't. Spans often start/end
109
+ // one past something, and when that something is the last character of a file (this can happen
110
+ // when a file doesn't end in a newline, for example), we'd still like for the position to be
111
+ // considered within the last line. However, it isn't according to the exclusive upper bound
112
+ // below. We cannot change the upper bound to be inclusive, because for most lines, the upper
113
+ // bound is the same as the lower bound of the next line, so there would be an ambiguity.
114
+ //
115
+ // Supposing we only use this function to check whether or not the line cache entry contains
116
+ // a position, the only ramification of the above is that we will get cache misses for these
117
+ // rare positions. A line lookup for the position via `SourceMap::lookup_line` after a cache
118
+ // miss will produce the last line number, as desired.
119
+ line_bounds. 0 <= pos && pos < line_bounds. 1
120
+ }
121
+
122
+ #[ inline]
123
+ fn file_contains ( file : & SourceFile , pos : BytePos ) -> bool {
124
+ // `SourceMap::lookup_source_file_idx` and `SourceFile::contains` both consider the position
125
+ // one past the end of a file to belong to it. Normally, that's what we want. But for the
126
+ // purposes of converting a byte position to a line and column number, we can't come up with a
127
+ // line and column number if the file is empty, because an empty file doesn't contain any
128
+ // lines. So for our purposes, we don't consider empty files to contain any byte position.
129
+ file. contains ( pos) && !file. is_empty ( )
130
+ }
0 commit comments