1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! The source positions and related helper functions
15 //! This API is completely unstable and subject to change.
17 #![crate_name = "syntax_pos"]
18 #![crate_type = "dylib"]
19 #![crate_type = "rlib"]
20 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
21 html_favicon_url
= "https://doc.rust-lang.org/favicon.ico",
22 html_root_url
= "https://doc.rust-lang.org/nightly/")]
26 #![feature(custom_attribute)]
27 #![feature(optin_builtin_traits)]
28 #![allow(unused_attributes)]
29 #![feature(specialization)]
31 #![cfg_attr(stage0, unstable(feature = "rustc_private", issue = "27812"))]
32 #![cfg_attr(stage0, feature(rustc_private))]
33 #![cfg_attr(stage0, feature(staged_api))]
35 use std
::cell
::{Cell, RefCell}
;
36 use std
::ops
::{Add, Sub}
;
42 use serialize
::{Encodable, Decodable, Encoder, Decoder}
;
44 extern crate serialize
;
45 extern crate serialize
as rustc_serialize
; // used by deriving
48 pub use hygiene
::{SyntaxContext, ExpnInfo, ExpnFormat, NameAndSpan}
;
52 pub type FileName
= String
;
54 /// Spans represent a region of code, used for error reporting. Positions in spans
55 /// are *absolute* positions from the beginning of the codemap, not positions
56 /// relative to FileMaps. Methods on the CodeMap can be used to relate spans back
57 /// to the original source.
58 /// You must be careful if the span crosses more than one file - you will not be
59 /// able to use many of the functions on spans in codemap and you cannot assume
60 /// that the length of the span = hi - lo; there may be space in the BytePos
61 /// range between files.
62 #[derive(Clone, Copy, Hash, PartialEq, Eq, Ord, PartialOrd)]
66 /// Information about where the macro came from, if this piece of
67 /// code was created by a macro expansion.
68 pub ctxt
: SyntaxContext
,
71 /// A collection of spans. Spans have two orthogonal attributes:
73 /// - they can be *primary spans*. In this case they are the locus of
74 /// the error, and would be rendered with `^^^`.
75 /// - they can have a *label*. In this case, the label is written next
76 /// to the mark in the snippet when we render.
77 #[derive(Clone, Debug, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)]
78 pub struct MultiSpan
{
79 primary_spans
: Vec
<Span
>,
80 span_labels
: Vec
<(Span
, String
)>,
84 /// Returns a new span representing just the end-point of this span
85 pub fn end_point(self) -> Span
{
86 let lo
= cmp
::max(self.hi
.0 - 1, self.lo
.0);
87 Span { lo: BytePos(lo), ..self }
90 /// Returns a new span representing the next character after the end-point of this span
91 pub fn next_point(self) -> Span
{
92 let lo
= cmp
::max(self.hi
.0, self.lo
.0 + 1);
93 Span { lo: BytePos(lo), hi: BytePos(lo), ..self }
96 /// Returns `self` if `self` is not the dummy span, and `other` otherwise.
97 pub fn substitute_dummy(self, other
: Span
) -> Span
{
98 if self.source_equal(&DUMMY_SP
) { other }
else { self }
101 pub fn contains(self, other
: Span
) -> bool
{
102 self.lo
<= other
.lo
&& other
.hi
<= self.hi
105 /// Return true if the spans are equal with regards to the source text.
107 /// Use this instead of `==` when either span could be generated code,
108 /// and you only care that they point to the same bytes of source text.
109 pub fn source_equal(&self, other
: &Span
) -> bool
{
110 self.lo
== other
.lo
&& self.hi
== other
.hi
113 /// Returns `Some(span)`, where the start is trimmed by the end of `other`
114 pub fn trim_start(self, other
: Span
) -> Option
<Span
> {
115 if self.hi
> other
.hi
{
116 Some(Span { lo: cmp::max(self.lo, other.hi), .. self }
)
122 /// Return the source span - this is either the supplied span, or the span for
123 /// the macro callsite that expanded to it.
124 pub fn source_callsite(self) -> Span
{
125 self.ctxt
.outer().expn_info().map(|info
| info
.call_site
.source_callsite()).unwrap_or(self)
128 /// Return the source callee.
130 /// Returns None if the supplied span has no expansion trace,
131 /// else returns the NameAndSpan for the macro definition
132 /// corresponding to the source callsite.
133 pub fn source_callee(self) -> Option
<NameAndSpan
> {
134 fn source_callee(info
: ExpnInfo
) -> NameAndSpan
{
135 match info
.call_site
.ctxt
.outer().expn_info() {
136 Some(info
) => source_callee(info
),
140 self.ctxt
.outer().expn_info().map(source_callee
)
143 /// Check if a span is "internal" to a macro in which #[unstable]
144 /// items can be used (that is, a macro marked with
145 /// `#[allow_internal_unstable]`).
146 pub fn allows_unstable(&self) -> bool
{
147 match self.ctxt
.outer().expn_info() {
148 Some(info
) => info
.callee
.allow_internal_unstable
,
153 pub fn macro_backtrace(mut self) -> Vec
<MacroBacktrace
> {
154 let mut prev_span
= DUMMY_SP
;
155 let mut result
= vec
![];
157 let info
= match self.ctxt
.outer().expn_info() {
162 let (pre
, post
) = match info
.callee
.format
{
163 ExpnFormat
::MacroAttribute(..) => ("#[", "]"),
164 ExpnFormat
::MacroBang(..) => ("", "!"),
165 ExpnFormat
::CompilerDesugaring(..) => ("desugaring of `", "`"),
167 let macro_decl_name
= format
!("{}{}{}", pre
, info
.callee
.name(), post
);
168 let def_site_span
= info
.callee
.span
;
170 // Don't print recursive invocations
171 if !info
.call_site
.source_equal(&prev_span
) {
172 result
.push(MacroBacktrace
{
173 call_site
: info
.call_site
,
174 macro_decl_name
: macro_decl_name
,
175 def_site_span
: def_site_span
,
180 self = info
.call_site
;
185 pub fn to(self, end
: Span
) -> Span
{
186 // FIXME(jseyfried): self.ctxt should always equal end.ctxt here (c.f. issue #23480)
187 if end
.ctxt
== SyntaxContext
::empty() {
188 Span { lo: self.lo, ..end }
190 Span { hi: end.hi, ..self }
194 pub fn between(self, end
: Span
) -> Span
{
198 ctxt
: if end
.ctxt
== SyntaxContext
::empty() {
206 pub fn until(self, end
: Span
) -> Span
{
210 ctxt
: if end
.ctxt
== SyntaxContext
::empty() {
219 #[derive(Clone, Debug)]
220 pub struct SpanLabel
{
221 /// The span we are going to include in the final snippet.
224 /// Is this a primary span? This is the "locus" of the message,
225 /// and is indicated with a `^^^^` underline, versus `----`.
226 pub is_primary
: bool
,
228 /// What label should we attach to this span (if any)?
229 pub label
: Option
<String
>,
232 impl serialize
::UseSpecializedEncodable
for Span
{
233 fn default_encode
<S
: Encoder
>(&self, s
: &mut S
) -> Result
<(), S
::Error
> {
234 s
.emit_struct("Span", 2, |s
| {
235 s
.emit_struct_field("lo", 0, |s
| {
239 s
.emit_struct_field("hi", 1, |s
| {
246 impl serialize
::UseSpecializedDecodable
for Span
{
247 fn default_decode
<D
: Decoder
>(d
: &mut D
) -> Result
<Span
, D
::Error
> {
248 d
.read_struct("Span", 2, |d
| {
249 let lo
= d
.read_struct_field("lo", 0, Decodable
::decode
)?
;
250 let hi
= d
.read_struct_field("hi", 1, Decodable
::decode
)?
;
251 Ok(Span { lo: lo, hi: hi, ctxt: NO_EXPANSION }
)
256 fn default_span_debug(span
: Span
, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
257 write
!(f
, "Span {{ lo: {:?}, hi: {:?}, ctxt: {:?} }}",
258 span
.lo
, span
.hi
, span
.ctxt
)
261 impl fmt
::Debug
for Span
{
262 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
263 SPAN_DEBUG
.with(|span_debug
| span_debug
.get()(*self, f
))
267 pub const DUMMY_SP
: Span
= Span { lo: BytePos(0), hi: BytePos(0), ctxt: NO_EXPANSION }
;
270 pub fn new() -> MultiSpan
{
272 primary_spans
: vec
![],
277 pub fn from_span(primary_span
: Span
) -> MultiSpan
{
279 primary_spans
: vec
![primary_span
],
284 pub fn from_spans(vec
: Vec
<Span
>) -> MultiSpan
{
291 pub fn push_span_label(&mut self, span
: Span
, label
: String
) {
292 self.span_labels
.push((span
, label
));
295 /// Selects the first primary span (if any)
296 pub fn primary_span(&self) -> Option
<Span
> {
297 self.primary_spans
.first().cloned()
300 /// Returns all primary spans.
301 pub fn primary_spans(&self) -> &[Span
] {
305 /// Replaces all occurances of one Span with another. Used to move Spans in areas that don't
306 /// display well (like std macros). Returns true if replacements occurred.
307 pub fn replace(&mut self, before
: Span
, after
: Span
) -> bool
{
308 let mut replacements_occurred
= false;
309 for primary_span
in &mut self.primary_spans
{
310 if *primary_span
== before
{
311 *primary_span
= after
;
312 replacements_occurred
= true;
315 for span_label
in &mut self.span_labels
{
316 if span_label
.0 == before
{
317 span_label
.0 = after
;
318 replacements_occurred
= true;
321 replacements_occurred
324 /// Returns the strings to highlight. We always ensure that there
325 /// is an entry for each of the primary spans -- for each primary
326 /// span P, if there is at least one label with span P, we return
327 /// those labels (marked as primary). But otherwise we return
328 /// `SpanLabel` instances with empty labels.
329 pub fn span_labels(&self) -> Vec
<SpanLabel
> {
330 let is_primary
= |span
| self.primary_spans
.contains(&span
);
331 let mut span_labels
= vec
![];
333 for &(span
, ref label
) in &self.span_labels
{
334 span_labels
.push(SpanLabel
{
336 is_primary
: is_primary(span
),
337 label
: Some(label
.clone())
341 for &span
in &self.primary_spans
{
342 if !span_labels
.iter().any(|sl
| sl
.span
== span
) {
343 span_labels
.push(SpanLabel
{
355 impl From
<Span
> for MultiSpan
{
356 fn from(span
: Span
) -> MultiSpan
{
357 MultiSpan
::from_span(span
)
361 pub const NO_EXPANSION
: SyntaxContext
= SyntaxContext
::empty();
363 /// Identifies an offset of a multi-byte character in a FileMap
364 #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq)]
365 pub struct MultiByteChar
{
366 /// The absolute offset of the character in the CodeMap
368 /// The number of bytes, >=2
372 /// A single source in the CodeMap.
375 /// The name of the file that the source came from, source that doesn't
376 /// originate from files has names between angle brackets by convention,
379 /// True if the `name` field above has been modified by -Zremap-path-prefix
380 pub name_was_remapped
: bool
,
381 /// Indicates which crate this FileMap was imported from.
382 pub crate_of_origin
: u32,
383 /// The complete source code
384 pub src
: Option
<Rc
<String
>>,
385 /// The start position of this source in the CodeMap
386 pub start_pos
: BytePos
,
387 /// The end position of this source in the CodeMap
388 pub end_pos
: BytePos
,
389 /// Locations of lines beginnings in the source code
390 pub lines
: RefCell
<Vec
<BytePos
>>,
391 /// Locations of multi-byte characters in the source code
392 pub multibyte_chars
: RefCell
<Vec
<MultiByteChar
>>,
395 impl Encodable
for FileMap
{
396 fn encode
<S
: Encoder
>(&self, s
: &mut S
) -> Result
<(), S
::Error
> {
397 s
.emit_struct("FileMap", 6, |s
| {
398 s
.emit_struct_field("name", 0, |s
| self.name
.encode(s
))?
;
399 s
.emit_struct_field("name_was_remapped", 1, |s
| self.name_was_remapped
.encode(s
))?
;
400 s
.emit_struct_field("start_pos", 2, |s
| self.start_pos
.encode(s
))?
;
401 s
.emit_struct_field("end_pos", 3, |s
| self.end_pos
.encode(s
))?
;
402 s
.emit_struct_field("lines", 4, |s
| {
403 let lines
= self.lines
.borrow();
405 s
.emit_u32(lines
.len() as u32)?
;
407 if !lines
.is_empty() {
408 // In order to preserve some space, we exploit the fact that
409 // the lines list is sorted and individual lines are
410 // probably not that long. Because of that we can store lines
411 // as a difference list, using as little space as possible
412 // for the differences.
413 let max_line_length
= if lines
.len() == 1 {
417 .map(|w
| w
[1] - w
[0])
418 .map(|bp
| bp
.to_usize())
423 let bytes_per_diff
: u8 = match max_line_length
{
425 0x100 ... 0xFFFF => 2,
429 // Encode the number of bytes used per diff.
430 bytes_per_diff
.encode(s
)?
;
432 // Encode the first element.
435 let diff_iter
= (&lines
[..]).windows(2)
436 .map(|w
| (w
[1] - w
[0]));
438 match bytes_per_diff
{
439 1 => for diff
in diff_iter { (diff.0 as u8).encode(s)? }
,
440 2 => for diff
in diff_iter { (diff.0 as u16).encode(s)? }
,
441 4 => for diff
in diff_iter { diff.0.encode(s)? }
,
448 s
.emit_struct_field("multibyte_chars", 5, |s
| {
449 (*self.multibyte_chars
.borrow()).encode(s
)
455 impl Decodable
for FileMap
{
456 fn decode
<D
: Decoder
>(d
: &mut D
) -> Result
<FileMap
, D
::Error
> {
458 d
.read_struct("FileMap", 6, |d
| {
459 let name
: String
= d
.read_struct_field("name", 0, |d
| Decodable
::decode(d
))?
;
460 let name_was_remapped
: bool
=
461 d
.read_struct_field("name_was_remapped", 1, |d
| Decodable
::decode(d
))?
;
462 let start_pos
: BytePos
= d
.read_struct_field("start_pos", 2, |d
| Decodable
::decode(d
))?
;
463 let end_pos
: BytePos
= d
.read_struct_field("end_pos", 3, |d
| Decodable
::decode(d
))?
;
464 let lines
: Vec
<BytePos
> = d
.read_struct_field("lines", 4, |d
| {
465 let num_lines
: u32 = Decodable
::decode(d
)?
;
466 let mut lines
= Vec
::with_capacity(num_lines
as usize);
469 // Read the number of bytes used per diff.
470 let bytes_per_diff
: u8 = Decodable
::decode(d
)?
;
472 // Read the first element.
473 let mut line_start
: BytePos
= Decodable
::decode(d
)?
;
474 lines
.push(line_start
);
476 for _
in 1..num_lines
{
477 let diff
= match bytes_per_diff
{
478 1 => d
.read_u8()?
as u32,
479 2 => d
.read_u16()?
as u32,
484 line_start
= line_start
+ BytePos(diff
);
486 lines
.push(line_start
);
492 let multibyte_chars
: Vec
<MultiByteChar
> =
493 d
.read_struct_field("multibyte_chars", 5, |d
| Decodable
::decode(d
))?
;
496 name_was_remapped
: name_was_remapped
,
497 // `crate_of_origin` has to be set by the importer.
498 // This value matches up with rustc::hir::def_id::INVALID_CRATE.
499 // That constant is not available here unfortunately :(
500 crate_of_origin
: ::std
::u32::MAX
- 1,
501 start_pos
: start_pos
,
504 lines
: RefCell
::new(lines
),
505 multibyte_chars
: RefCell
::new(multibyte_chars
)
511 impl fmt
::Debug
for FileMap
{
512 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
513 write
!(fmt
, "FileMap({})", self.name
)
518 /// EFFECT: register a start-of-line offset in the
519 /// table of line-beginnings.
520 /// UNCHECKED INVARIANT: these offsets must be added in the right
521 /// order and must be in the right places; there is shared knowledge
522 /// about what ends a line between this file and parse.rs
523 /// WARNING: pos param here is the offset relative to start of CodeMap,
524 /// and CodeMap will append a newline when adding a filemap without a newline at the end,
525 /// so the safe way to call this is with value calculated as
526 /// filemap.start_pos + newline_offset_relative_to_the_start_of_filemap.
527 pub fn next_line(&self, pos
: BytePos
) {
528 // the new charpos must be > the last one (or it's the first one).
529 let mut lines
= self.lines
.borrow_mut();
530 let line_len
= lines
.len();
531 assert
!(line_len
== 0 || ((*lines
)[line_len
- 1] < pos
));
535 /// get a line from the list of pre-computed line-beginnings.
536 /// line-number here is 0-based.
537 pub fn get_line(&self, line_number
: usize) -> Option
<&str> {
540 let lines
= self.lines
.borrow();
541 lines
.get(line_number
).map(|&line
| {
542 let begin
: BytePos
= line
- self.start_pos
;
543 let begin
= begin
.to_usize();
544 // We can't use `lines.get(line_number+1)` because we might
545 // be parsing when we call this function and thus the current
546 // line is the last one we have line info for.
547 let slice
= &src
[begin
..];
548 match slice
.find('
\n'
) {
549 Some(e
) => &slice
[..e
],
558 pub fn record_multibyte_char(&self, pos
: BytePos
, bytes
: usize) {
559 assert
!(bytes
>=2 && bytes
<= 4);
560 let mbc
= MultiByteChar
{
564 self.multibyte_chars
.borrow_mut().push(mbc
);
567 pub fn is_real_file(&self) -> bool
{
568 !(self.name
.starts_with("<") &&
569 self.name
.ends_with(">"))
572 pub fn is_imported(&self) -> bool
{
576 pub fn byte_length(&self) -> u32 {
577 self.end_pos
.0 - self.start_pos
.0
579 pub fn count_lines(&self) -> usize {
580 self.lines
.borrow().len()
583 /// Find the line containing the given position. The return value is the
584 /// index into the `lines` array of this FileMap, not the 1-based line
585 /// number. If the filemap is empty or the position is located before the
586 /// first line, None is returned.
587 pub fn lookup_line(&self, pos
: BytePos
) -> Option
<usize> {
588 let lines
= self.lines
.borrow();
589 if lines
.len() == 0 {
593 let line_index
= lookup_line(&lines
[..], pos
);
594 assert
!(line_index
< lines
.len() as isize);
596 Some(line_index
as usize)
602 pub fn line_bounds(&self, line_index
: usize) -> (BytePos
, BytePos
) {
603 if self.start_pos
== self.end_pos
{
604 return (self.start_pos
, self.end_pos
);
607 let lines
= self.lines
.borrow();
608 assert
!(line_index
< lines
.len());
609 if line_index
== (lines
.len() - 1) {
610 (lines
[line_index
], self.end_pos
)
612 (lines
[line_index
], lines
[line_index
+ 1])
617 // _____________________________________________________________________________
618 // Pos, BytePos, CharPos
622 fn from_usize(n
: usize) -> Self;
623 fn to_usize(&self) -> usize;
626 /// A byte offset. Keep this small (currently 32-bits), as AST contains
628 #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
629 pub struct BytePos(pub u32);
631 /// A character offset. Because of multibyte utf8 characters, a byte offset
632 /// is not equivalent to a character offset. The CodeMap will convert BytePos
633 /// values to CharPos values as necessary.
634 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
635 pub struct CharPos(pub usize);
637 // FIXME: Lots of boilerplate in these impls, but so far my attempts to fix
638 // have been unsuccessful
640 impl Pos
for BytePos
{
641 fn from_usize(n
: usize) -> BytePos { BytePos(n as u32) }
642 fn to_usize(&self) -> usize { let BytePos(n) = *self; n as usize }
645 impl Add
for BytePos
{
646 type Output
= BytePos
;
648 fn add(self, rhs
: BytePos
) -> BytePos
{
649 BytePos((self.to_usize() + rhs
.to_usize()) as u32)
653 impl Sub
for BytePos
{
654 type Output
= BytePos
;
656 fn sub(self, rhs
: BytePos
) -> BytePos
{
657 BytePos((self.to_usize() - rhs
.to_usize()) as u32)
661 impl Encodable
for BytePos
{
662 fn encode
<S
: Encoder
>(&self, s
: &mut S
) -> Result
<(), S
::Error
> {
667 impl Decodable
for BytePos
{
668 fn decode
<D
: Decoder
>(d
: &mut D
) -> Result
<BytePos
, D
::Error
> {
669 Ok(BytePos(d
.read_u32()?
))
673 impl Pos
for CharPos
{
674 fn from_usize(n
: usize) -> CharPos { CharPos(n) }
675 fn to_usize(&self) -> usize { let CharPos(n) = *self; n }
678 impl Add
for CharPos
{
679 type Output
= CharPos
;
681 fn add(self, rhs
: CharPos
) -> CharPos
{
682 CharPos(self.to_usize() + rhs
.to_usize())
686 impl Sub
for CharPos
{
687 type Output
= CharPos
;
689 fn sub(self, rhs
: CharPos
) -> CharPos
{
690 CharPos(self.to_usize() - rhs
.to_usize())
694 // _____________________________________________________________________________
695 // Loc, LocWithOpt, FileMapAndLine, FileMapAndBytePos
698 /// A source code location used for error reporting
699 #[derive(Debug, Clone)]
701 /// Information about the original source
702 pub file
: Rc
<FileMap
>,
703 /// The (1-based) line number
705 /// The (0-based) column offset
709 /// A source code location used as the result of lookup_char_pos_adj
710 // Actually, *none* of the clients use the filename *or* file field;
711 // perhaps they should just be removed.
713 pub struct LocWithOpt
{
714 pub filename
: FileName
,
717 pub file
: Option
<Rc
<FileMap
>>,
720 // used to be structural records. Better names, anyone?
722 pub struct FileMapAndLine { pub fm: Rc<FileMap>, pub line: usize }
724 pub struct FileMapAndBytePos { pub fm: Rc<FileMap>, pub pos: BytePos }
726 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
727 pub struct LineInfo
{
728 /// Index of line, starting from 0.
729 pub line_index
: usize,
731 /// Column in line where span begins, starting from 0.
732 pub start_col
: CharPos
,
734 /// Column in line where span ends, starting from 0, exclusive.
735 pub end_col
: CharPos
,
738 pub struct FileLines
{
739 pub file
: Rc
<FileMap
>,
740 pub lines
: Vec
<LineInfo
>
743 thread_local
!(pub static SPAN_DEBUG
: Cell
<fn(Span
, &mut fmt
::Formatter
) -> fmt
::Result
> =
744 Cell
::new(default_span_debug
));
746 pub struct MacroBacktrace
{
747 /// span where macro was applied to generate this code
750 /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
751 pub macro_decl_name
: String
,
753 /// span where macro was defined (if known)
754 pub def_site_span
: Option
<Span
>,
757 // _____________________________________________________________________________
758 // SpanLinesError, SpanSnippetError, DistinctSources, MalformedCodemapPositions
761 pub type FileLinesResult
= Result
<FileLines
, SpanLinesError
>;
763 #[derive(Clone, PartialEq, Eq, Debug)]
764 pub enum SpanLinesError
{
766 DistinctSources(DistinctSources
),
769 #[derive(Clone, PartialEq, Eq, Debug)]
770 pub enum SpanSnippetError
{
772 DistinctSources(DistinctSources
),
773 MalformedForCodemap(MalformedCodemapPositions
),
774 SourceNotAvailable { filename: String }
777 #[derive(Clone, PartialEq, Eq, Debug)]
778 pub struct DistinctSources
{
779 pub begin
: (String
, BytePos
),
780 pub end
: (String
, BytePos
)
783 #[derive(Clone, PartialEq, Eq, Debug)]
784 pub struct MalformedCodemapPositions
{
786 pub source_len
: usize,
787 pub begin_pos
: BytePos
,
791 // Given a slice of line start positions and a position, returns the index of
792 // the line the position is on. Returns -1 if the position is located before
794 fn lookup_line(lines
: &[BytePos
], pos
: BytePos
) -> isize {
795 match lines
.binary_search(&pos
) {
796 Ok(line
) => line
as isize,
797 Err(line
) => line
as isize - 1
803 use super::{lookup_line, BytePos}
;
806 fn test_lookup_line() {
808 let lines
= &[BytePos(3), BytePos(17), BytePos(28)];
810 assert_eq
!(lookup_line(lines
, BytePos(0)), -1);
811 assert_eq
!(lookup_line(lines
, BytePos(3)), 0);
812 assert_eq
!(lookup_line(lines
, BytePos(4)), 0);
814 assert_eq
!(lookup_line(lines
, BytePos(16)), 0);
815 assert_eq
!(lookup_line(lines
, BytePos(17)), 1);
816 assert_eq
!(lookup_line(lines
, BytePos(18)), 1);
818 assert_eq
!(lookup_line(lines
, BytePos(28)), 2);
819 assert_eq
!(lookup_line(lines
, BytePos(29)), 2);