]>
Commit | Line | Data |
---|---|---|
3157f602 XL |
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. | |
4 | // | |
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. | |
10 | ||
11 | //! The source positions and related helper functions | |
12 | //! | |
13 | //! # Note | |
14 | //! | |
15 | //! This API is completely unstable and subject to change. | |
16 | ||
3157f602 XL |
17 | #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", |
18 | html_favicon_url = "https://doc.rust-lang.org/favicon.ico", | |
19 | html_root_url = "https://doc.rust-lang.org/nightly/")] | |
32a655c1 | 20 | #![deny(warnings)] |
3157f602 | 21 | |
cc61c64b | 22 | #![feature(const_fn)] |
3157f602 | 23 | #![feature(custom_attribute)] |
041b39d2 | 24 | #![feature(i128_type)] |
cc61c64b | 25 | #![feature(optin_builtin_traits)] |
3157f602 | 26 | #![allow(unused_attributes)] |
9e0c209e | 27 | #![feature(specialization)] |
3157f602 | 28 | |
041b39d2 | 29 | use std::borrow::Cow; |
3157f602 | 30 | use std::cell::{Cell, RefCell}; |
ea8adc8c | 31 | use std::cmp::{self, Ordering}; |
041b39d2 XL |
32 | use std::fmt; |
33 | use std::hash::Hasher; | |
3157f602 | 34 | use std::ops::{Add, Sub}; |
ea8adc8c | 35 | use std::path::PathBuf; |
3157f602 | 36 | use std::rc::Rc; |
3157f602 | 37 | |
041b39d2 XL |
38 | use rustc_data_structures::stable_hasher::StableHasher; |
39 | ||
40 | extern crate rustc_data_structures; | |
3157f602 XL |
41 | |
42 | use serialize::{Encodable, Decodable, Encoder, Decoder}; | |
43 | ||
44 | extern crate serialize; | |
45 | extern crate serialize as rustc_serialize; // used by deriving | |
46 | ||
abe05a73 XL |
47 | extern crate unicode_width; |
48 | ||
cc61c64b | 49 | pub mod hygiene; |
3b2f2976 | 50 | pub use hygiene::{SyntaxContext, ExpnInfo, ExpnFormat, NameAndSpan, CompilerDesugaringKind}; |
cc61c64b | 51 | |
ea8adc8c XL |
52 | mod span_encoding; |
53 | pub use span_encoding::{Span, DUMMY_SP}; | |
54 | ||
cc61c64b XL |
55 | pub mod symbol; |
56 | ||
3157f602 XL |
57 | pub type FileName = String; |
58 | ||
59 | /// Spans represent a region of code, used for error reporting. Positions in spans | |
60 | /// are *absolute* positions from the beginning of the codemap, not positions | |
61 | /// relative to FileMaps. Methods on the CodeMap can be used to relate spans back | |
62 | /// to the original source. | |
63 | /// You must be careful if the span crosses more than one file - you will not be | |
64 | /// able to use many of the functions on spans in codemap and you cannot assume | |
65 | /// that the length of the span = hi - lo; there may be space in the BytePos | |
66 | /// range between files. | |
ea8adc8c XL |
67 | /// |
68 | /// `SpanData` is public because `Span` uses a thread-local interner and can't be | |
69 | /// sent to other threads, but some pieces of performance infra run in a separate thread. | |
70 | /// Using `Span` is generally preferred. | |
476ff2be | 71 | #[derive(Clone, Copy, Hash, PartialEq, Eq, Ord, PartialOrd)] |
ea8adc8c | 72 | pub struct SpanData { |
3157f602 XL |
73 | pub lo: BytePos, |
74 | pub hi: BytePos, | |
75 | /// Information about where the macro came from, if this piece of | |
76 | /// code was created by a macro expansion. | |
cc61c64b | 77 | pub ctxt: SyntaxContext, |
3157f602 XL |
78 | } |
79 | ||
abe05a73 XL |
80 | impl SpanData { |
81 | #[inline] | |
82 | pub fn with_lo(&self, lo: BytePos) -> Span { | |
83 | Span::new(lo, self.hi, self.ctxt) | |
84 | } | |
85 | #[inline] | |
86 | pub fn with_hi(&self, hi: BytePos) -> Span { | |
87 | Span::new(self.lo, hi, self.ctxt) | |
88 | } | |
89 | #[inline] | |
90 | pub fn with_ctxt(&self, ctxt: SyntaxContext) -> Span { | |
91 | Span::new(self.lo, self.hi, ctxt) | |
92 | } | |
93 | } | |
94 | ||
ea8adc8c XL |
95 | // The interner in thread-local, so `Span` shouldn't move between threads. |
96 | impl !Send for Span {} | |
97 | impl !Sync for Span {} | |
98 | ||
99 | impl PartialOrd for Span { | |
100 | fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> { | |
101 | PartialOrd::partial_cmp(&self.data(), &rhs.data()) | |
102 | } | |
103 | } | |
104 | impl Ord for Span { | |
105 | fn cmp(&self, rhs: &Self) -> Ordering { | |
106 | Ord::cmp(&self.data(), &rhs.data()) | |
107 | } | |
108 | } | |
109 | ||
3157f602 XL |
110 | /// A collection of spans. Spans have two orthogonal attributes: |
111 | /// | |
112 | /// - they can be *primary spans*. In this case they are the locus of | |
113 | /// the error, and would be rendered with `^^^`. | |
114 | /// - they can have a *label*. In this case, the label is written next | |
115 | /// to the mark in the snippet when we render. | |
8bb4bdeb | 116 | #[derive(Clone, Debug, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)] |
3157f602 XL |
117 | pub struct MultiSpan { |
118 | primary_spans: Vec<Span>, | |
119 | span_labels: Vec<(Span, String)>, | |
120 | } | |
121 | ||
122 | impl Span { | |
ea8adc8c XL |
123 | #[inline] |
124 | pub fn lo(self) -> BytePos { | |
125 | self.data().lo | |
126 | } | |
127 | #[inline] | |
128 | pub fn with_lo(self, lo: BytePos) -> Span { | |
abe05a73 | 129 | self.data().with_lo(lo) |
ea8adc8c XL |
130 | } |
131 | #[inline] | |
132 | pub fn hi(self) -> BytePos { | |
133 | self.data().hi | |
134 | } | |
135 | #[inline] | |
136 | pub fn with_hi(self, hi: BytePos) -> Span { | |
abe05a73 | 137 | self.data().with_hi(hi) |
ea8adc8c XL |
138 | } |
139 | #[inline] | |
140 | pub fn ctxt(self) -> SyntaxContext { | |
141 | self.data().ctxt | |
142 | } | |
143 | #[inline] | |
144 | pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span { | |
abe05a73 | 145 | self.data().with_ctxt(ctxt) |
ea8adc8c XL |
146 | } |
147 | ||
3157f602 XL |
148 | /// Returns a new span representing just the end-point of this span |
149 | pub fn end_point(self) -> Span { | |
abe05a73 XL |
150 | let span = self.data(); |
151 | let lo = cmp::max(span.hi.0 - 1, span.lo.0); | |
152 | span.with_lo(BytePos(lo)) | |
cc61c64b XL |
153 | } |
154 | ||
155 | /// Returns a new span representing the next character after the end-point of this span | |
156 | pub fn next_point(self) -> Span { | |
abe05a73 XL |
157 | let span = self.data(); |
158 | let lo = cmp::max(span.hi.0, span.lo.0 + 1); | |
159 | Span::new(BytePos(lo), BytePos(lo), span.ctxt) | |
3157f602 XL |
160 | } |
161 | ||
162 | /// Returns `self` if `self` is not the dummy span, and `other` otherwise. | |
163 | pub fn substitute_dummy(self, other: Span) -> Span { | |
164 | if self.source_equal(&DUMMY_SP) { other } else { self } | |
165 | } | |
166 | ||
3b2f2976 | 167 | /// Return true if `self` fully encloses `other`. |
3157f602 | 168 | pub fn contains(self, other: Span) -> bool { |
abe05a73 XL |
169 | let span = self.data(); |
170 | let other = other.data(); | |
171 | span.lo <= other.lo && other.hi <= span.hi | |
3157f602 XL |
172 | } |
173 | ||
174 | /// Return true if the spans are equal with regards to the source text. | |
175 | /// | |
176 | /// Use this instead of `==` when either span could be generated code, | |
177 | /// and you only care that they point to the same bytes of source text. | |
178 | pub fn source_equal(&self, other: &Span) -> bool { | |
abe05a73 XL |
179 | let span = self.data(); |
180 | let other = other.data(); | |
181 | span.lo == other.lo && span.hi == other.hi | |
3157f602 XL |
182 | } |
183 | ||
3157f602 XL |
184 | /// Returns `Some(span)`, where the start is trimmed by the end of `other` |
185 | pub fn trim_start(self, other: Span) -> Option<Span> { | |
abe05a73 XL |
186 | let span = self.data(); |
187 | let other = other.data(); | |
188 | if span.hi > other.hi { | |
189 | Some(span.with_lo(cmp::max(span.lo, other.hi))) | |
3157f602 XL |
190 | } else { |
191 | None | |
192 | } | |
193 | } | |
cc61c64b XL |
194 | |
195 | /// Return the source span - this is either the supplied span, or the span for | |
196 | /// the macro callsite that expanded to it. | |
197 | pub fn source_callsite(self) -> Span { | |
ea8adc8c | 198 | self.ctxt().outer().expn_info().map(|info| info.call_site.source_callsite()).unwrap_or(self) |
cc61c64b XL |
199 | } |
200 | ||
201 | /// Return the source callee. | |
202 | /// | |
203 | /// Returns None if the supplied span has no expansion trace, | |
204 | /// else returns the NameAndSpan for the macro definition | |
205 | /// corresponding to the source callsite. | |
206 | pub fn source_callee(self) -> Option<NameAndSpan> { | |
207 | fn source_callee(info: ExpnInfo) -> NameAndSpan { | |
ea8adc8c | 208 | match info.call_site.ctxt().outer().expn_info() { |
cc61c64b XL |
209 | Some(info) => source_callee(info), |
210 | None => info.callee, | |
211 | } | |
212 | } | |
ea8adc8c | 213 | self.ctxt().outer().expn_info().map(source_callee) |
cc61c64b XL |
214 | } |
215 | ||
216 | /// Check if a span is "internal" to a macro in which #[unstable] | |
217 | /// items can be used (that is, a macro marked with | |
218 | /// `#[allow_internal_unstable]`). | |
219 | pub fn allows_unstable(&self) -> bool { | |
ea8adc8c | 220 | match self.ctxt().outer().expn_info() { |
cc61c64b XL |
221 | Some(info) => info.callee.allow_internal_unstable, |
222 | None => false, | |
223 | } | |
224 | } | |
225 | ||
3b2f2976 XL |
226 | /// Check if this span arises from a compiler desugaring of kind `kind`. |
227 | pub fn is_compiler_desugaring(&self, kind: CompilerDesugaringKind) -> bool { | |
ea8adc8c | 228 | match self.ctxt().outer().expn_info() { |
3b2f2976 XL |
229 | Some(info) => match info.callee.format { |
230 | ExpnFormat::CompilerDesugaring(k) => k == kind, | |
231 | _ => false, | |
232 | }, | |
233 | None => false, | |
234 | } | |
235 | } | |
236 | ||
ea8adc8c XL |
237 | /// Return the compiler desugaring that created this span, or None |
238 | /// if this span is not from a desugaring. | |
239 | pub fn compiler_desugaring_kind(&self) -> Option<CompilerDesugaringKind> { | |
240 | match self.ctxt().outer().expn_info() { | |
241 | Some(info) => match info.callee.format { | |
242 | ExpnFormat::CompilerDesugaring(k) => Some(k), | |
243 | _ => None | |
244 | }, | |
245 | None => None | |
246 | } | |
247 | } | |
248 | ||
3b2f2976 XL |
249 | /// Check if a span is "internal" to a macro in which `unsafe` |
250 | /// can be used without triggering the `unsafe_code` lint | |
251 | // (that is, a macro marked with `#[allow_internal_unsafe]`). | |
252 | pub fn allows_unsafe(&self) -> bool { | |
ea8adc8c | 253 | match self.ctxt().outer().expn_info() { |
3b2f2976 XL |
254 | Some(info) => info.callee.allow_internal_unsafe, |
255 | None => false, | |
256 | } | |
257 | } | |
258 | ||
cc61c64b XL |
259 | pub fn macro_backtrace(mut self) -> Vec<MacroBacktrace> { |
260 | let mut prev_span = DUMMY_SP; | |
261 | let mut result = vec![]; | |
262 | loop { | |
ea8adc8c | 263 | let info = match self.ctxt().outer().expn_info() { |
cc61c64b XL |
264 | Some(info) => info, |
265 | None => break, | |
266 | }; | |
267 | ||
268 | let (pre, post) = match info.callee.format { | |
269 | ExpnFormat::MacroAttribute(..) => ("#[", "]"), | |
270 | ExpnFormat::MacroBang(..) => ("", "!"), | |
271 | ExpnFormat::CompilerDesugaring(..) => ("desugaring of `", "`"), | |
272 | }; | |
273 | let macro_decl_name = format!("{}{}{}", pre, info.callee.name(), post); | |
274 | let def_site_span = info.callee.span; | |
275 | ||
276 | // Don't print recursive invocations | |
277 | if !info.call_site.source_equal(&prev_span) { | |
278 | result.push(MacroBacktrace { | |
279 | call_site: info.call_site, | |
3b2f2976 XL |
280 | macro_decl_name, |
281 | def_site_span, | |
cc61c64b XL |
282 | }); |
283 | } | |
284 | ||
285 | prev_span = self; | |
286 | self = info.call_site; | |
287 | } | |
288 | result | |
289 | } | |
290 | ||
3b2f2976 | 291 | /// Return a `Span` that would enclose both `self` and `end`. |
cc61c64b | 292 | pub fn to(self, end: Span) -> Span { |
abe05a73 XL |
293 | let span = self.data(); |
294 | let end = end.data(); | |
ea8adc8c | 295 | Span::new( |
abe05a73 XL |
296 | cmp::min(span.lo, end.lo), |
297 | cmp::max(span.hi, end.hi), | |
3b2f2976 | 298 | // FIXME(jseyfried): self.ctxt should always equal end.ctxt here (c.f. issue #23480) |
abe05a73 | 299 | if span.ctxt == SyntaxContext::empty() { end.ctxt } else { span.ctxt }, |
ea8adc8c | 300 | ) |
cc61c64b XL |
301 | } |
302 | ||
3b2f2976 | 303 | /// Return a `Span` between the end of `self` to the beginning of `end`. |
cc61c64b | 304 | pub fn between(self, end: Span) -> Span { |
abe05a73 XL |
305 | let span = self.data(); |
306 | let end = end.data(); | |
ea8adc8c | 307 | Span::new( |
abe05a73 XL |
308 | span.hi, |
309 | end.lo, | |
310 | if end.ctxt == SyntaxContext::empty() { end.ctxt } else { span.ctxt }, | |
ea8adc8c | 311 | ) |
cc61c64b XL |
312 | } |
313 | ||
3b2f2976 | 314 | /// Return a `Span` between the beginning of `self` to the beginning of `end`. |
cc61c64b | 315 | pub fn until(self, end: Span) -> Span { |
abe05a73 XL |
316 | let span = self.data(); |
317 | let end = end.data(); | |
ea8adc8c | 318 | Span::new( |
abe05a73 XL |
319 | span.lo, |
320 | end.lo, | |
321 | if end.ctxt == SyntaxContext::empty() { end.ctxt } else { span.ctxt }, | |
ea8adc8c | 322 | ) |
cc61c64b | 323 | } |
3157f602 XL |
324 | } |
325 | ||
326 | #[derive(Clone, Debug)] | |
327 | pub struct SpanLabel { | |
328 | /// The span we are going to include in the final snippet. | |
329 | pub span: Span, | |
330 | ||
331 | /// Is this a primary span? This is the "locus" of the message, | |
332 | /// and is indicated with a `^^^^` underline, versus `----`. | |
333 | pub is_primary: bool, | |
334 | ||
335 | /// What label should we attach to this span (if any)? | |
336 | pub label: Option<String>, | |
337 | } | |
338 | ||
3b2f2976 XL |
339 | impl Default for Span { |
340 | fn default() -> Self { | |
341 | DUMMY_SP | |
342 | } | |
343 | } | |
344 | ||
9e0c209e SL |
345 | impl serialize::UseSpecializedEncodable for Span { |
346 | fn default_encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { | |
abe05a73 | 347 | let span = self.data(); |
3157f602 XL |
348 | s.emit_struct("Span", 2, |s| { |
349 | s.emit_struct_field("lo", 0, |s| { | |
abe05a73 | 350 | span.lo.encode(s) |
3157f602 XL |
351 | })?; |
352 | ||
353 | s.emit_struct_field("hi", 1, |s| { | |
abe05a73 | 354 | span.hi.encode(s) |
3157f602 XL |
355 | }) |
356 | }) | |
357 | } | |
358 | } | |
359 | ||
9e0c209e SL |
360 | impl serialize::UseSpecializedDecodable for Span { |
361 | fn default_decode<D: Decoder>(d: &mut D) -> Result<Span, D::Error> { | |
3157f602 | 362 | d.read_struct("Span", 2, |d| { |
9e0c209e SL |
363 | let lo = d.read_struct_field("lo", 0, Decodable::decode)?; |
364 | let hi = d.read_struct_field("hi", 1, Decodable::decode)?; | |
ea8adc8c | 365 | Ok(Span::new(lo, hi, NO_EXPANSION)) |
3157f602 XL |
366 | }) |
367 | } | |
368 | } | |
369 | ||
370 | fn default_span_debug(span: Span, f: &mut fmt::Formatter) -> fmt::Result { | |
abe05a73 XL |
371 | f.debug_struct("Span") |
372 | .field("lo", &span.lo()) | |
373 | .field("hi", &span.hi()) | |
374 | .field("ctxt", &span.ctxt()) | |
375 | .finish() | |
3157f602 XL |
376 | } |
377 | ||
378 | impl fmt::Debug for Span { | |
379 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
380 | SPAN_DEBUG.with(|span_debug| span_debug.get()(*self, f)) | |
381 | } | |
382 | } | |
383 | ||
ea8adc8c XL |
384 | impl fmt::Debug for SpanData { |
385 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
386 | SPAN_DEBUG.with(|span_debug| span_debug.get()(Span::new(self.lo, self.hi, self.ctxt), f)) | |
387 | } | |
388 | } | |
3157f602 XL |
389 | |
390 | impl MultiSpan { | |
391 | pub fn new() -> MultiSpan { | |
392 | MultiSpan { | |
393 | primary_spans: vec![], | |
394 | span_labels: vec![] | |
395 | } | |
396 | } | |
397 | ||
398 | pub fn from_span(primary_span: Span) -> MultiSpan { | |
399 | MultiSpan { | |
400 | primary_spans: vec![primary_span], | |
401 | span_labels: vec![] | |
402 | } | |
403 | } | |
404 | ||
405 | pub fn from_spans(vec: Vec<Span>) -> MultiSpan { | |
406 | MultiSpan { | |
407 | primary_spans: vec, | |
408 | span_labels: vec![] | |
409 | } | |
410 | } | |
411 | ||
412 | pub fn push_span_label(&mut self, span: Span, label: String) { | |
413 | self.span_labels.push((span, label)); | |
414 | } | |
415 | ||
416 | /// Selects the first primary span (if any) | |
417 | pub fn primary_span(&self) -> Option<Span> { | |
418 | self.primary_spans.first().cloned() | |
419 | } | |
420 | ||
421 | /// Returns all primary spans. | |
422 | pub fn primary_spans(&self) -> &[Span] { | |
423 | &self.primary_spans | |
424 | } | |
425 | ||
3b2f2976 | 426 | /// Replaces all occurrences of one Span with another. Used to move Spans in areas that don't |
9e0c209e SL |
427 | /// display well (like std macros). Returns true if replacements occurred. |
428 | pub fn replace(&mut self, before: Span, after: Span) -> bool { | |
429 | let mut replacements_occurred = false; | |
430 | for primary_span in &mut self.primary_spans { | |
431 | if *primary_span == before { | |
432 | *primary_span = after; | |
433 | replacements_occurred = true; | |
434 | } | |
435 | } | |
436 | for span_label in &mut self.span_labels { | |
437 | if span_label.0 == before { | |
438 | span_label.0 = after; | |
439 | replacements_occurred = true; | |
440 | } | |
441 | } | |
442 | replacements_occurred | |
443 | } | |
444 | ||
3157f602 XL |
445 | /// Returns the strings to highlight. We always ensure that there |
446 | /// is an entry for each of the primary spans -- for each primary | |
447 | /// span P, if there is at least one label with span P, we return | |
448 | /// those labels (marked as primary). But otherwise we return | |
449 | /// `SpanLabel` instances with empty labels. | |
450 | pub fn span_labels(&self) -> Vec<SpanLabel> { | |
451 | let is_primary = |span| self.primary_spans.contains(&span); | |
452 | let mut span_labels = vec![]; | |
453 | ||
454 | for &(span, ref label) in &self.span_labels { | |
455 | span_labels.push(SpanLabel { | |
3b2f2976 | 456 | span, |
3157f602 XL |
457 | is_primary: is_primary(span), |
458 | label: Some(label.clone()) | |
459 | }); | |
460 | } | |
461 | ||
462 | for &span in &self.primary_spans { | |
463 | if !span_labels.iter().any(|sl| sl.span == span) { | |
464 | span_labels.push(SpanLabel { | |
3b2f2976 | 465 | span, |
3157f602 XL |
466 | is_primary: true, |
467 | label: None | |
468 | }); | |
469 | } | |
470 | } | |
471 | ||
472 | span_labels | |
473 | } | |
474 | } | |
475 | ||
476 | impl From<Span> for MultiSpan { | |
477 | fn from(span: Span) -> MultiSpan { | |
478 | MultiSpan::from_span(span) | |
479 | } | |
480 | } | |
481 | ||
ea8adc8c XL |
482 | impl From<Vec<Span>> for MultiSpan { |
483 | fn from(spans: Vec<Span>) -> MultiSpan { | |
484 | MultiSpan::from_spans(spans) | |
485 | } | |
486 | } | |
487 | ||
cc61c64b | 488 | pub const NO_EXPANSION: SyntaxContext = SyntaxContext::empty(); |
3157f602 XL |
489 | |
490 | /// Identifies an offset of a multi-byte character in a FileMap | |
491 | #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq)] | |
492 | pub struct MultiByteChar { | |
493 | /// The absolute offset of the character in the CodeMap | |
494 | pub pos: BytePos, | |
495 | /// The number of bytes, >=2 | |
496 | pub bytes: usize, | |
497 | } | |
498 | ||
abe05a73 XL |
499 | /// Identifies an offset of a non-narrow character in a FileMap |
500 | #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq)] | |
501 | pub enum NonNarrowChar { | |
502 | /// Represents a zero-width character | |
503 | ZeroWidth(BytePos), | |
504 | /// Represents a wide (fullwidth) character | |
505 | Wide(BytePos), | |
506 | } | |
507 | ||
508 | impl NonNarrowChar { | |
509 | fn new(pos: BytePos, width: usize) -> Self { | |
510 | match width { | |
511 | 0 => NonNarrowChar::ZeroWidth(pos), | |
512 | 2 => NonNarrowChar::Wide(pos), | |
513 | _ => panic!("width {} given for non-narrow character", width), | |
514 | } | |
515 | } | |
516 | ||
517 | /// Returns the absolute offset of the character in the CodeMap | |
518 | pub fn pos(&self) -> BytePos { | |
519 | match *self { | |
520 | NonNarrowChar::ZeroWidth(p) | | |
521 | NonNarrowChar::Wide(p) => p, | |
522 | } | |
523 | } | |
524 | ||
525 | /// Returns the width of the character, 0 (zero-width) or 2 (wide) | |
526 | pub fn width(&self) -> usize { | |
527 | match *self { | |
528 | NonNarrowChar::ZeroWidth(_) => 0, | |
529 | NonNarrowChar::Wide(_) => 2, | |
530 | } | |
531 | } | |
532 | } | |
533 | ||
534 | impl Add<BytePos> for NonNarrowChar { | |
535 | type Output = Self; | |
536 | ||
537 | fn add(self, rhs: BytePos) -> Self { | |
538 | match self { | |
539 | NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos + rhs), | |
540 | NonNarrowChar::Wide(pos) => NonNarrowChar::Wide(pos + rhs), | |
541 | } | |
542 | } | |
543 | } | |
544 | ||
545 | impl Sub<BytePos> for NonNarrowChar { | |
546 | type Output = Self; | |
547 | ||
548 | fn sub(self, rhs: BytePos) -> Self { | |
549 | match self { | |
550 | NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos - rhs), | |
551 | NonNarrowChar::Wide(pos) => NonNarrowChar::Wide(pos - rhs), | |
552 | } | |
553 | } | |
554 | } | |
555 | ||
041b39d2 XL |
556 | /// The state of the lazy external source loading mechanism of a FileMap. |
557 | #[derive(PartialEq, Eq, Clone)] | |
558 | pub enum ExternalSource { | |
559 | /// The external source has been loaded already. | |
560 | Present(String), | |
561 | /// No attempt has been made to load the external source. | |
562 | AbsentOk, | |
563 | /// A failed attempt has been made to load the external source. | |
564 | AbsentErr, | |
565 | /// No external source has to be loaded, since the FileMap represents a local crate. | |
566 | Unneeded, | |
567 | } | |
568 | ||
569 | impl ExternalSource { | |
570 | pub fn is_absent(&self) -> bool { | |
571 | match *self { | |
572 | ExternalSource::Present(_) => false, | |
573 | _ => true, | |
574 | } | |
575 | } | |
576 | ||
577 | pub fn get_source(&self) -> Option<&str> { | |
578 | match *self { | |
579 | ExternalSource::Present(ref src) => Some(src), | |
580 | _ => None, | |
581 | } | |
582 | } | |
583 | } | |
584 | ||
3157f602 | 585 | /// A single source in the CodeMap. |
7cac9316 | 586 | #[derive(Clone)] |
3157f602 XL |
587 | pub struct FileMap { |
588 | /// The name of the file that the source came from, source that doesn't | |
589 | /// originate from files has names between angle brackets by convention, | |
590 | /// e.g. `<anon>` | |
591 | pub name: FileName, | |
7cac9316 XL |
592 | /// True if the `name` field above has been modified by -Zremap-path-prefix |
593 | pub name_was_remapped: bool, | |
ea8adc8c XL |
594 | /// The unmapped path of the file that the source came from. |
595 | /// Set to `None` if the FileMap was imported from an external crate. | |
596 | pub unmapped_path: Option<PathBuf>, | |
7cac9316 XL |
597 | /// Indicates which crate this FileMap was imported from. |
598 | pub crate_of_origin: u32, | |
3157f602 XL |
599 | /// The complete source code |
600 | pub src: Option<Rc<String>>, | |
041b39d2 XL |
601 | /// The source code's hash |
602 | pub src_hash: u128, | |
603 | /// The external source code (used for external crates, which will have a `None` | |
604 | /// value as `self.src`. | |
605 | pub external_src: RefCell<ExternalSource>, | |
3157f602 XL |
606 | /// The start position of this source in the CodeMap |
607 | pub start_pos: BytePos, | |
608 | /// The end position of this source in the CodeMap | |
609 | pub end_pos: BytePos, | |
610 | /// Locations of lines beginnings in the source code | |
611 | pub lines: RefCell<Vec<BytePos>>, | |
612 | /// Locations of multi-byte characters in the source code | |
613 | pub multibyte_chars: RefCell<Vec<MultiByteChar>>, | |
abe05a73 XL |
614 | /// Width of characters that are not narrow in the source code |
615 | pub non_narrow_chars: RefCell<Vec<NonNarrowChar>>, | |
3157f602 XL |
616 | } |
617 | ||
618 | impl Encodable for FileMap { | |
619 | fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { | |
abe05a73 | 620 | s.emit_struct("FileMap", 8, |s| { |
3157f602 | 621 | s.emit_struct_field("name", 0, |s| self.name.encode(s))?; |
7cac9316 | 622 | s.emit_struct_field("name_was_remapped", 1, |s| self.name_was_remapped.encode(s))?; |
041b39d2 | 623 | s.emit_struct_field("src_hash", 6, |s| self.src_hash.encode(s))?; |
3157f602 XL |
624 | s.emit_struct_field("start_pos", 2, |s| self.start_pos.encode(s))?; |
625 | s.emit_struct_field("end_pos", 3, |s| self.end_pos.encode(s))?; | |
626 | s.emit_struct_field("lines", 4, |s| { | |
627 | let lines = self.lines.borrow(); | |
628 | // store the length | |
629 | s.emit_u32(lines.len() as u32)?; | |
630 | ||
631 | if !lines.is_empty() { | |
632 | // In order to preserve some space, we exploit the fact that | |
633 | // the lines list is sorted and individual lines are | |
634 | // probably not that long. Because of that we can store lines | |
635 | // as a difference list, using as little space as possible | |
636 | // for the differences. | |
637 | let max_line_length = if lines.len() == 1 { | |
638 | 0 | |
639 | } else { | |
640 | lines.windows(2) | |
641 | .map(|w| w[1] - w[0]) | |
642 | .map(|bp| bp.to_usize()) | |
643 | .max() | |
644 | .unwrap() | |
645 | }; | |
646 | ||
647 | let bytes_per_diff: u8 = match max_line_length { | |
648 | 0 ... 0xFF => 1, | |
649 | 0x100 ... 0xFFFF => 2, | |
650 | _ => 4 | |
651 | }; | |
652 | ||
653 | // Encode the number of bytes used per diff. | |
654 | bytes_per_diff.encode(s)?; | |
655 | ||
656 | // Encode the first element. | |
657 | lines[0].encode(s)?; | |
658 | ||
659 | let diff_iter = (&lines[..]).windows(2) | |
660 | .map(|w| (w[1] - w[0])); | |
661 | ||
662 | match bytes_per_diff { | |
663 | 1 => for diff in diff_iter { (diff.0 as u8).encode(s)? }, | |
664 | 2 => for diff in diff_iter { (diff.0 as u16).encode(s)? }, | |
665 | 4 => for diff in diff_iter { diff.0.encode(s)? }, | |
666 | _ => unreachable!() | |
667 | } | |
668 | } | |
669 | ||
670 | Ok(()) | |
671 | })?; | |
672 | s.emit_struct_field("multibyte_chars", 5, |s| { | |
673 | (*self.multibyte_chars.borrow()).encode(s) | |
abe05a73 XL |
674 | })?; |
675 | s.emit_struct_field("non_narrow_chars", 7, |s| { | |
676 | (*self.non_narrow_chars.borrow()).encode(s) | |
3157f602 XL |
677 | }) |
678 | }) | |
679 | } | |
680 | } | |
681 | ||
682 | impl Decodable for FileMap { | |
683 | fn decode<D: Decoder>(d: &mut D) -> Result<FileMap, D::Error> { | |
684 | ||
abe05a73 | 685 | d.read_struct("FileMap", 8, |d| { |
3157f602 | 686 | let name: String = d.read_struct_field("name", 0, |d| Decodable::decode(d))?; |
7cac9316 XL |
687 | let name_was_remapped: bool = |
688 | d.read_struct_field("name_was_remapped", 1, |d| Decodable::decode(d))?; | |
041b39d2 XL |
689 | let src_hash: u128 = |
690 | d.read_struct_field("src_hash", 6, |d| Decodable::decode(d))?; | |
691 | let start_pos: BytePos = | |
692 | d.read_struct_field("start_pos", 2, |d| Decodable::decode(d))?; | |
3157f602 XL |
693 | let end_pos: BytePos = d.read_struct_field("end_pos", 3, |d| Decodable::decode(d))?; |
694 | let lines: Vec<BytePos> = d.read_struct_field("lines", 4, |d| { | |
695 | let num_lines: u32 = Decodable::decode(d)?; | |
696 | let mut lines = Vec::with_capacity(num_lines as usize); | |
697 | ||
698 | if num_lines > 0 { | |
699 | // Read the number of bytes used per diff. | |
700 | let bytes_per_diff: u8 = Decodable::decode(d)?; | |
701 | ||
702 | // Read the first element. | |
703 | let mut line_start: BytePos = Decodable::decode(d)?; | |
704 | lines.push(line_start); | |
705 | ||
706 | for _ in 1..num_lines { | |
707 | let diff = match bytes_per_diff { | |
708 | 1 => d.read_u8()? as u32, | |
709 | 2 => d.read_u16()? as u32, | |
710 | 4 => d.read_u32()?, | |
711 | _ => unreachable!() | |
712 | }; | |
713 | ||
714 | line_start = line_start + BytePos(diff); | |
715 | ||
716 | lines.push(line_start); | |
717 | } | |
718 | } | |
719 | ||
720 | Ok(lines) | |
721 | })?; | |
722 | let multibyte_chars: Vec<MultiByteChar> = | |
723 | d.read_struct_field("multibyte_chars", 5, |d| Decodable::decode(d))?; | |
abe05a73 XL |
724 | let non_narrow_chars: Vec<NonNarrowChar> = |
725 | d.read_struct_field("non_narrow_chars", 7, |d| Decodable::decode(d))?; | |
3157f602 | 726 | Ok(FileMap { |
3b2f2976 XL |
727 | name, |
728 | name_was_remapped, | |
ea8adc8c | 729 | unmapped_path: None, |
7cac9316 XL |
730 | // `crate_of_origin` has to be set by the importer. |
731 | // This value matches up with rustc::hir::def_id::INVALID_CRATE. | |
732 | // That constant is not available here unfortunately :( | |
733 | crate_of_origin: ::std::u32::MAX - 1, | |
3b2f2976 XL |
734 | start_pos, |
735 | end_pos, | |
3157f602 | 736 | src: None, |
3b2f2976 | 737 | src_hash, |
041b39d2 | 738 | external_src: RefCell::new(ExternalSource::AbsentOk), |
3157f602 | 739 | lines: RefCell::new(lines), |
abe05a73 XL |
740 | multibyte_chars: RefCell::new(multibyte_chars), |
741 | non_narrow_chars: RefCell::new(non_narrow_chars) | |
3157f602 XL |
742 | }) |
743 | }) | |
744 | } | |
745 | } | |
746 | ||
747 | impl fmt::Debug for FileMap { | |
748 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | |
749 | write!(fmt, "FileMap({})", self.name) | |
750 | } | |
751 | } | |
752 | ||
753 | impl FileMap { | |
041b39d2 XL |
754 | pub fn new(name: FileName, |
755 | name_was_remapped: bool, | |
ea8adc8c | 756 | unmapped_path: PathBuf, |
041b39d2 XL |
757 | mut src: String, |
758 | start_pos: BytePos) -> FileMap { | |
759 | remove_bom(&mut src); | |
760 | ||
761 | let mut hasher: StableHasher<u128> = StableHasher::new(); | |
762 | hasher.write(src.as_bytes()); | |
763 | let src_hash = hasher.finish(); | |
764 | ||
765 | let end_pos = start_pos.to_usize() + src.len(); | |
766 | ||
767 | FileMap { | |
3b2f2976 XL |
768 | name, |
769 | name_was_remapped, | |
ea8adc8c | 770 | unmapped_path: Some(unmapped_path), |
041b39d2 XL |
771 | crate_of_origin: 0, |
772 | src: Some(Rc::new(src)), | |
3b2f2976 | 773 | src_hash, |
041b39d2 | 774 | external_src: RefCell::new(ExternalSource::Unneeded), |
3b2f2976 | 775 | start_pos, |
041b39d2 XL |
776 | end_pos: Pos::from_usize(end_pos), |
777 | lines: RefCell::new(Vec::new()), | |
778 | multibyte_chars: RefCell::new(Vec::new()), | |
abe05a73 | 779 | non_narrow_chars: RefCell::new(Vec::new()), |
041b39d2 XL |
780 | } |
781 | } | |
782 | ||
3157f602 XL |
783 | /// EFFECT: register a start-of-line offset in the |
784 | /// table of line-beginnings. | |
785 | /// UNCHECKED INVARIANT: these offsets must be added in the right | |
786 | /// order and must be in the right places; there is shared knowledge | |
787 | /// about what ends a line between this file and parse.rs | |
788 | /// WARNING: pos param here is the offset relative to start of CodeMap, | |
789 | /// and CodeMap will append a newline when adding a filemap without a newline at the end, | |
790 | /// so the safe way to call this is with value calculated as | |
791 | /// filemap.start_pos + newline_offset_relative_to_the_start_of_filemap. | |
792 | pub fn next_line(&self, pos: BytePos) { | |
793 | // the new charpos must be > the last one (or it's the first one). | |
794 | let mut lines = self.lines.borrow_mut(); | |
795 | let line_len = lines.len(); | |
796 | assert!(line_len == 0 || ((*lines)[line_len - 1] < pos)); | |
797 | lines.push(pos); | |
798 | } | |
799 | ||
041b39d2 XL |
800 | /// Add externally loaded source. |
801 | /// If the hash of the input doesn't match or no input is supplied via None, | |
802 | /// it is interpreted as an error and the corresponding enum variant is set. | |
803 | /// The return value signifies whether some kind of source is present. | |
804 | pub fn add_external_src<F>(&self, get_src: F) -> bool | |
805 | where F: FnOnce() -> Option<String> | |
806 | { | |
807 | if *self.external_src.borrow() == ExternalSource::AbsentOk { | |
808 | let src = get_src(); | |
809 | let mut external_src = self.external_src.borrow_mut(); | |
810 | if let Some(src) = src { | |
811 | let mut hasher: StableHasher<u128> = StableHasher::new(); | |
812 | hasher.write(src.as_bytes()); | |
813 | ||
814 | if hasher.finish() == self.src_hash { | |
815 | *external_src = ExternalSource::Present(src); | |
816 | return true; | |
817 | } | |
818 | } else { | |
819 | *external_src = ExternalSource::AbsentErr; | |
820 | } | |
821 | ||
822 | false | |
823 | } else { | |
824 | self.src.is_some() || self.external_src.borrow().get_source().is_some() | |
825 | } | |
826 | } | |
827 | ||
828 | /// Get a line from the list of pre-computed line-beginnings. | |
829 | /// The line number here is 0-based. | |
830 | pub fn get_line(&self, line_number: usize) -> Option<Cow<str>> { | |
831 | fn get_until_newline(src: &str, begin: usize) -> &str { | |
832 | // We can't use `lines.get(line_number+1)` because we might | |
833 | // be parsing when we call this function and thus the current | |
834 | // line is the last one we have line info for. | |
835 | let slice = &src[begin..]; | |
836 | match slice.find('\n') { | |
837 | Some(e) => &slice[..e], | |
838 | None => slice | |
3157f602 | 839 | } |
041b39d2 XL |
840 | } |
841 | ||
842 | let lines = self.lines.borrow(); | |
843 | let line = if let Some(line) = lines.get(line_number) { | |
844 | line | |
845 | } else { | |
846 | return None; | |
847 | }; | |
848 | let begin: BytePos = *line - self.start_pos; | |
849 | let begin = begin.to_usize(); | |
850 | ||
851 | if let Some(ref src) = self.src { | |
852 | Some(Cow::from(get_until_newline(src, begin))) | |
853 | } else if let Some(src) = self.external_src.borrow().get_source() { | |
854 | Some(Cow::Owned(String::from(get_until_newline(src, begin)))) | |
855 | } else { | |
856 | None | |
3157f602 XL |
857 | } |
858 | } | |
859 | ||
860 | pub fn record_multibyte_char(&self, pos: BytePos, bytes: usize) { | |
861 | assert!(bytes >=2 && bytes <= 4); | |
862 | let mbc = MultiByteChar { | |
3b2f2976 XL |
863 | pos, |
864 | bytes, | |
3157f602 XL |
865 | }; |
866 | self.multibyte_chars.borrow_mut().push(mbc); | |
867 | } | |
868 | ||
abe05a73 XL |
869 | pub fn record_width(&self, pos: BytePos, ch: char) { |
870 | let width = match ch { | |
871 | '\t' | '\n' => | |
872 | // Tabs will consume one column. | |
873 | // Make newlines take one column so that displayed spans can point them. | |
874 | 1, | |
875 | ch => | |
876 | // Assume control characters are zero width. | |
877 | // FIXME: How can we decide between `width` and `width_cjk`? | |
878 | unicode_width::UnicodeWidthChar::width(ch).unwrap_or(0), | |
879 | }; | |
880 | // Only record non-narrow characters. | |
881 | if width != 1 { | |
882 | self.non_narrow_chars.borrow_mut().push(NonNarrowChar::new(pos, width)); | |
883 | } | |
884 | } | |
885 | ||
3157f602 XL |
886 | pub fn is_real_file(&self) -> bool { |
887 | !(self.name.starts_with("<") && | |
888 | self.name.ends_with(">")) | |
889 | } | |
890 | ||
891 | pub fn is_imported(&self) -> bool { | |
892 | self.src.is_none() | |
893 | } | |
894 | ||
9e0c209e SL |
895 | pub fn byte_length(&self) -> u32 { |
896 | self.end_pos.0 - self.start_pos.0 | |
897 | } | |
3157f602 XL |
898 | pub fn count_lines(&self) -> usize { |
899 | self.lines.borrow().len() | |
900 | } | |
9e0c209e SL |
901 | |
902 | /// Find the line containing the given position. The return value is the | |
903 | /// index into the `lines` array of this FileMap, not the 1-based line | |
904 | /// number. If the filemap is empty or the position is located before the | |
905 | /// first line, None is returned. | |
906 | pub fn lookup_line(&self, pos: BytePos) -> Option<usize> { | |
907 | let lines = self.lines.borrow(); | |
908 | if lines.len() == 0 { | |
909 | return None; | |
910 | } | |
911 | ||
912 | let line_index = lookup_line(&lines[..], pos); | |
913 | assert!(line_index < lines.len() as isize); | |
914 | if line_index >= 0 { | |
915 | Some(line_index as usize) | |
916 | } else { | |
917 | None | |
918 | } | |
919 | } | |
920 | ||
921 | pub fn line_bounds(&self, line_index: usize) -> (BytePos, BytePos) { | |
922 | if self.start_pos == self.end_pos { | |
923 | return (self.start_pos, self.end_pos); | |
924 | } | |
925 | ||
926 | let lines = self.lines.borrow(); | |
927 | assert!(line_index < lines.len()); | |
928 | if line_index == (lines.len() - 1) { | |
929 | (lines[line_index], self.end_pos) | |
930 | } else { | |
931 | (lines[line_index], lines[line_index + 1]) | |
932 | } | |
933 | } | |
3157f602 XL |
934 | } |
935 | ||
041b39d2 XL |
936 | /// Remove utf-8 BOM if any. |
937 | fn remove_bom(src: &mut String) { | |
938 | if src.starts_with("\u{feff}") { | |
939 | src.drain(..3); | |
940 | } | |
941 | } | |
942 | ||
3157f602 XL |
943 | // _____________________________________________________________________________ |
944 | // Pos, BytePos, CharPos | |
945 | // | |
946 | ||
947 | pub trait Pos { | |
948 | fn from_usize(n: usize) -> Self; | |
949 | fn to_usize(&self) -> usize; | |
950 | } | |
951 | ||
952 | /// A byte offset. Keep this small (currently 32-bits), as AST contains | |
953 | /// a lot of them. | |
954 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] | |
955 | pub struct BytePos(pub u32); | |
956 | ||
957 | /// A character offset. Because of multibyte utf8 characters, a byte offset | |
958 | /// is not equivalent to a character offset. The CodeMap will convert BytePos | |
959 | /// values to CharPos values as necessary. | |
960 | #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] | |
961 | pub struct CharPos(pub usize); | |
962 | ||
963 | // FIXME: Lots of boilerplate in these impls, but so far my attempts to fix | |
964 | // have been unsuccessful | |
965 | ||
966 | impl Pos for BytePos { | |
967 | fn from_usize(n: usize) -> BytePos { BytePos(n as u32) } | |
968 | fn to_usize(&self) -> usize { let BytePos(n) = *self; n as usize } | |
969 | } | |
970 | ||
971 | impl Add for BytePos { | |
972 | type Output = BytePos; | |
973 | ||
974 | fn add(self, rhs: BytePos) -> BytePos { | |
975 | BytePos((self.to_usize() + rhs.to_usize()) as u32) | |
976 | } | |
977 | } | |
978 | ||
979 | impl Sub for BytePos { | |
980 | type Output = BytePos; | |
981 | ||
982 | fn sub(self, rhs: BytePos) -> BytePos { | |
983 | BytePos((self.to_usize() - rhs.to_usize()) as u32) | |
984 | } | |
985 | } | |
986 | ||
987 | impl Encodable for BytePos { | |
988 | fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { | |
989 | s.emit_u32(self.0) | |
990 | } | |
991 | } | |
992 | ||
993 | impl Decodable for BytePos { | |
994 | fn decode<D: Decoder>(d: &mut D) -> Result<BytePos, D::Error> { | |
995 | Ok(BytePos(d.read_u32()?)) | |
996 | } | |
997 | } | |
998 | ||
999 | impl Pos for CharPos { | |
1000 | fn from_usize(n: usize) -> CharPos { CharPos(n) } | |
1001 | fn to_usize(&self) -> usize { let CharPos(n) = *self; n } | |
1002 | } | |
1003 | ||
1004 | impl Add for CharPos { | |
1005 | type Output = CharPos; | |
1006 | ||
1007 | fn add(self, rhs: CharPos) -> CharPos { | |
1008 | CharPos(self.to_usize() + rhs.to_usize()) | |
1009 | } | |
1010 | } | |
1011 | ||
1012 | impl Sub for CharPos { | |
1013 | type Output = CharPos; | |
1014 | ||
1015 | fn sub(self, rhs: CharPos) -> CharPos { | |
1016 | CharPos(self.to_usize() - rhs.to_usize()) | |
1017 | } | |
1018 | } | |
1019 | ||
1020 | // _____________________________________________________________________________ | |
1021 | // Loc, LocWithOpt, FileMapAndLine, FileMapAndBytePos | |
1022 | // | |
1023 | ||
1024 | /// A source code location used for error reporting | |
5bcae85e | 1025 | #[derive(Debug, Clone)] |
3157f602 XL |
1026 | pub struct Loc { |
1027 | /// Information about the original source | |
1028 | pub file: Rc<FileMap>, | |
1029 | /// The (1-based) line number | |
1030 | pub line: usize, | |
1031 | /// The (0-based) column offset | |
abe05a73 XL |
1032 | pub col: CharPos, |
1033 | /// The (0-based) column offset when displayed | |
1034 | pub col_display: usize, | |
3157f602 XL |
1035 | } |
1036 | ||
1037 | /// A source code location used as the result of lookup_char_pos_adj | |
1038 | // Actually, *none* of the clients use the filename *or* file field; | |
1039 | // perhaps they should just be removed. | |
1040 | #[derive(Debug)] | |
1041 | pub struct LocWithOpt { | |
1042 | pub filename: FileName, | |
1043 | pub line: usize, | |
1044 | pub col: CharPos, | |
1045 | pub file: Option<Rc<FileMap>>, | |
1046 | } | |
1047 | ||
1048 | // used to be structural records. Better names, anyone? | |
1049 | #[derive(Debug)] | |
1050 | pub struct FileMapAndLine { pub fm: Rc<FileMap>, pub line: usize } | |
1051 | #[derive(Debug)] | |
1052 | pub struct FileMapAndBytePos { pub fm: Rc<FileMap>, pub pos: BytePos } | |
1053 | ||
1054 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | |
1055 | pub struct LineInfo { | |
1056 | /// Index of line, starting from 0. | |
1057 | pub line_index: usize, | |
1058 | ||
1059 | /// Column in line where span begins, starting from 0. | |
1060 | pub start_col: CharPos, | |
1061 | ||
1062 | /// Column in line where span ends, starting from 0, exclusive. | |
1063 | pub end_col: CharPos, | |
1064 | } | |
1065 | ||
1066 | pub struct FileLines { | |
1067 | pub file: Rc<FileMap>, | |
1068 | pub lines: Vec<LineInfo> | |
1069 | } | |
1070 | ||
1071 | thread_local!(pub static SPAN_DEBUG: Cell<fn(Span, &mut fmt::Formatter) -> fmt::Result> = | |
1072 | Cell::new(default_span_debug)); | |
1073 | ||
3b2f2976 | 1074 | #[derive(Debug)] |
3157f602 XL |
1075 | pub struct MacroBacktrace { |
1076 | /// span where macro was applied to generate this code | |
1077 | pub call_site: Span, | |
1078 | ||
1079 | /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]") | |
1080 | pub macro_decl_name: String, | |
1081 | ||
1082 | /// span where macro was defined (if known) | |
1083 | pub def_site_span: Option<Span>, | |
1084 | } | |
1085 | ||
1086 | // _____________________________________________________________________________ | |
1087 | // SpanLinesError, SpanSnippetError, DistinctSources, MalformedCodemapPositions | |
1088 | // | |
1089 | ||
1090 | pub type FileLinesResult = Result<FileLines, SpanLinesError>; | |
1091 | ||
1092 | #[derive(Clone, PartialEq, Eq, Debug)] | |
1093 | pub enum SpanLinesError { | |
1094 | IllFormedSpan(Span), | |
1095 | DistinctSources(DistinctSources), | |
1096 | } | |
1097 | ||
1098 | #[derive(Clone, PartialEq, Eq, Debug)] | |
1099 | pub enum SpanSnippetError { | |
1100 | IllFormedSpan(Span), | |
1101 | DistinctSources(DistinctSources), | |
1102 | MalformedForCodemap(MalformedCodemapPositions), | |
1103 | SourceNotAvailable { filename: String } | |
1104 | } | |
1105 | ||
1106 | #[derive(Clone, PartialEq, Eq, Debug)] | |
1107 | pub struct DistinctSources { | |
1108 | pub begin: (String, BytePos), | |
1109 | pub end: (String, BytePos) | |
1110 | } | |
1111 | ||
1112 | #[derive(Clone, PartialEq, Eq, Debug)] | |
1113 | pub struct MalformedCodemapPositions { | |
1114 | pub name: String, | |
1115 | pub source_len: usize, | |
1116 | pub begin_pos: BytePos, | |
1117 | pub end_pos: BytePos | |
1118 | } | |
1119 | ||
9e0c209e SL |
1120 | // Given a slice of line start positions and a position, returns the index of |
1121 | // the line the position is on. Returns -1 if the position is located before | |
1122 | // the first line. | |
1123 | fn lookup_line(lines: &[BytePos], pos: BytePos) -> isize { | |
1124 | match lines.binary_search(&pos) { | |
1125 | Ok(line) => line as isize, | |
1126 | Err(line) => line as isize - 1 | |
1127 | } | |
1128 | } | |
1129 | ||
1130 | #[cfg(test)] | |
1131 | mod tests { | |
1132 | use super::{lookup_line, BytePos}; | |
1133 | ||
1134 | #[test] | |
1135 | fn test_lookup_line() { | |
1136 | ||
1137 | let lines = &[BytePos(3), BytePos(17), BytePos(28)]; | |
1138 | ||
1139 | assert_eq!(lookup_line(lines, BytePos(0)), -1); | |
1140 | assert_eq!(lookup_line(lines, BytePos(3)), 0); | |
1141 | assert_eq!(lookup_line(lines, BytePos(4)), 0); | |
1142 | ||
1143 | assert_eq!(lookup_line(lines, BytePos(16)), 0); | |
1144 | assert_eq!(lookup_line(lines, BytePos(17)), 1); | |
1145 | assert_eq!(lookup_line(lines, BytePos(18)), 1); | |
1146 | ||
1147 | assert_eq!(lookup_line(lines, BytePos(28)), 2); | |
1148 | assert_eq!(lookup_line(lines, BytePos(29)), 2); | |
1149 | } | |
1150 | } |