1
1
use crate :: source_map:: SourceMap ;
2
2
use crate :: { BytePos , SourceFile } ;
3
3
use rustc_data_structures:: sync:: Lrc ;
4
+ use std:: ops:: Range ;
4
5
5
6
#[ derive( Clone ) ]
6
7
struct CacheEntry {
7
8
time_stamp : usize ,
8
9
line_number : usize ,
9
- line_start : BytePos ,
10
- line_end : BytePos ,
10
+ // The line's byte position range in the `SourceMap`. This range will fail to contain a valid
11
+ // position in certain edge cases. Spans often start/end one past something, and when that
12
+ // something is the last character of a file (this can happen when a file doesn't end in a
13
+ // newline, for example), we'd still like for the position to be considered within the last
14
+ // line. However, it isn't according to the exclusive upper bound of this range. We cannot
15
+ // change the upper bound to be inclusive, because for most lines, the upper bound is the same
16
+ // as the lower bound of the next line, so there would be an ambiguity.
17
+ //
18
+ // Since the containment aspect of this range is only used to see whether or not the cache
19
+ // entry contains a position, the only ramification of the above is that we will get cache
20
+ // misses for these rare positions. A line lookup for the position via `SourceMap::lookup_line`
21
+ // after a cache miss will produce the last line number, as desired.
22
+ line : Range < BytePos > ,
11
23
file : Lrc < SourceFile > ,
12
24
file_index : usize ,
13
25
}
@@ -26,8 +38,7 @@ impl<'sm> CachingSourceMapView<'sm> {
26
38
let entry = CacheEntry {
27
39
time_stamp : 0 ,
28
40
line_number : 0 ,
29
- line_start : BytePos ( 0 ) ,
30
- line_end : BytePos ( 0 ) ,
41
+ line : BytePos ( 0 ) ..BytePos ( 0 ) ,
31
42
file : first_file,
32
43
file_index : 0 ,
33
44
} ;
@@ -47,13 +58,13 @@ impl<'sm> CachingSourceMapView<'sm> {
47
58
48
59
// Check if the position is in one of the cached lines
49
60
for cache_entry in self . line_cache . iter_mut ( ) {
50
- if pos >= cache_entry. line_start && pos < cache_entry . line_end {
61
+ if cache_entry. line . contains ( & pos) {
51
62
cache_entry. time_stamp = self . time_stamp ;
52
63
53
64
return Some ( (
54
65
cache_entry. file . clone ( ) ,
55
66
cache_entry. line_number ,
56
- pos - cache_entry. line_start ,
67
+ pos - cache_entry. line . start ,
57
68
) ) ;
58
69
}
59
70
}
@@ -69,13 +80,13 @@ impl<'sm> CachingSourceMapView<'sm> {
69
80
let cache_entry = & mut self . line_cache [ oldest] ;
70
81
71
82
// 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 {
83
+ if ! file_contains ( & cache_entry. file , pos) {
73
84
let file_valid;
74
85
if self . source_map . files ( ) . len ( ) > 0 {
75
86
let file_index = self . source_map . lookup_source_file_idx ( pos) ;
76
87
let file = self . source_map . files ( ) [ file_index] . clone ( ) ;
77
88
78
- if pos >= file. start_pos && pos < file . end_pos {
89
+ if file_contains ( & file, pos) {
79
90
cache_entry. file = file;
80
91
cache_entry. file_index = file_index;
81
92
file_valid = true ;
@@ -95,10 +106,19 @@ impl<'sm> CachingSourceMapView<'sm> {
95
106
let line_bounds = cache_entry. file . line_bounds ( line_index) ;
96
107
97
108
cache_entry. line_number = line_index + 1 ;
98
- cache_entry. line_start = line_bounds. 0 ;
99
- cache_entry. line_end = line_bounds. 1 ;
109
+ cache_entry. line = line_bounds;
100
110
cache_entry. time_stamp = self . time_stamp ;
101
111
102
- Some ( ( cache_entry. file . clone ( ) , cache_entry. line_number , pos - cache_entry. line_start ) )
112
+ Some ( ( cache_entry. file . clone ( ) , cache_entry. line_number , pos - cache_entry. line . start ) )
103
113
}
104
114
}
115
+
116
+ #[ inline]
117
+ fn file_contains ( file : & SourceFile , pos : BytePos ) -> bool {
118
+ // `SourceMap::lookup_source_file_idx` and `SourceFile::contains` both consider the position
119
+ // one past the end of a file to belong to it. Normally, that's what we want. But for the
120
+ // purposes of converting a byte position to a line and column number, we can't come up with a
121
+ // line and column number if the file is empty, because an empty file doesn't contain any
122
+ // lines. So for our purposes, we don't consider empty files to contain any byte position.
123
+ file. contains ( pos) && !file. is_empty ( )
124
+ }
0 commit comments