]> git.proxmox.com Git - rustc.git/blame - src/libsyntax/codemap.rs
New upstream version 1.13.0+dfsg1
[rustc.git] / src / libsyntax / codemap.rs
CommitLineData
223e47cc
LB
1// Copyright 2012 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
1a4d82fc
JJ
11//! The CodeMap tracks all the source code used within a single crate, mapping
12//! from integer byte positions to the original source code location. Each bit
13//! of source parsed during crate parsing (typically files, in-memory strings,
14//! or various bits of macro expansion) cover a continuous range of bytes in the
15//! CodeMap and are represented by FileMaps. Byte positions are stored in
16//! `spans` and used pervasively in the compiler. They are absolute positions
17//! within the CodeMap, which upon request can be converted to line and column
18//! information, source code snippets, etc.
223e47cc 19
d9579d0f 20pub use self::ExpnFormat::*;
223e47cc 21
3157f602
XL
22use std::cell::RefCell;
23use std::path::{Path,PathBuf};
1a4d82fc 24use std::rc::Rc;
223e47cc 25
3157f602
XL
26use std::env;
27use std::fs;
62682a34 28use std::io::{self, Read};
3157f602
XL
29pub use syntax_pos::*;
30use errors::CodeMapper;
223e47cc 31
e9174d1e 32use ast::Name;
c34b1796 33
1a4d82fc
JJ
34/// Return the span itself if it doesn't come from a macro expansion,
35/// otherwise return the call site span up to the `enclosing_sp` by
36/// following the `expn_info` chain.
37pub fn original_sp(cm: &CodeMap, sp: Span, enclosing_sp: Span) -> Span {
38 let call_site1 = cm.with_expn_info(sp.expn_id, |ei| ei.map(|ei| ei.call_site));
39 let call_site2 = cm.with_expn_info(enclosing_sp.expn_id, |ei| ei.map(|ei| ei.call_site));
40 match (call_site1, call_site2) {
41 (None, _) => sp,
42 (Some(call_site1), Some(call_site2)) if call_site1 == call_site2 => sp,
43 (Some(call_site1), _) => original_sp(cm, call_site1, enclosing_sp),
44 }
45}
223e47cc 46
3157f602
XL
47/// The source of expansion.
48#[derive(Clone, Hash, Debug, PartialEq, Eq)]
49pub enum ExpnFormat {
50 /// e.g. #[derive(...)] <item>
51 MacroAttribute(Name),
52 /// e.g. `format!()`
53 MacroBang(Name),
7453a54e
SL
54}
55
3157f602
XL
56#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
57pub struct Spanned<T> {
58 pub node: T,
59 pub span: Span,
7453a54e
SL
60}
61
3157f602
XL
62pub fn spanned<T>(lo: BytePos, hi: BytePos, t: T) -> Spanned<T> {
63 respan(mk_sp(lo, hi), t)
223e47cc
LB
64}
65
3157f602
XL
66pub fn respan<T>(sp: Span, t: T) -> Spanned<T> {
67 Spanned {node: t, span: sp}
223e47cc
LB
68}
69
3157f602
XL
70pub fn dummy_spanned<T>(t: T) -> Spanned<T> {
71 respan(DUMMY_SP, t)
1a4d82fc
JJ
72}
73
5bcae85e
SL
74/// Build a span that covers the two provided spans.
75pub fn combine_spans(sp1: Span, sp2: Span) -> Span {
76 if sp1 == DUMMY_SP && sp2 == DUMMY_SP {
77 DUMMY_SP
78 } else if sp1 == DUMMY_SP {
79 sp2
80 } else if sp2 == DUMMY_SP {
81 sp1
82 } else {
83 Span {
84 lo: if sp1.lo < sp2.lo { sp1.lo } else { sp2.lo },
85 hi: if sp1.hi > sp2.hi { sp1.hi } else { sp2.hi },
86 expn_id: if sp1.expn_id == sp2.expn_id { sp1.expn_id } else { NO_EXPANSION },
87 }
88 }
89}
90
85aaf69f 91#[derive(Clone, Hash, Debug)]
1a4d82fc 92pub struct NameAndSpan {
1a4d82fc 93 /// The format with which the macro was invoked.
d9579d0f 94 pub format: ExpnFormat,
c34b1796
AL
95 /// Whether the macro is allowed to use #[unstable]/feature-gated
96 /// features internally without forcing the whole crate to opt-in
97 /// to them.
98 pub allow_internal_unstable: bool,
1a4d82fc
JJ
99 /// The span of the macro definition itself. The macro may not
100 /// have a sensible definition span (e.g. something defined
101 /// completely inside libsyntax) in which case this is None.
102 pub span: Option<Span>
223e47cc
LB
103}
104
e9174d1e
SL
105impl NameAndSpan {
106 pub fn name(&self) -> Name {
107 match self.format {
108 ExpnFormat::MacroAttribute(s) => s,
109 ExpnFormat::MacroBang(s) => s,
e9174d1e
SL
110 }
111 }
112}
113
d9579d0f 114/// Extra information for tracking spans of macro and syntax sugar expansion
85aaf69f 115#[derive(Hash, Debug)]
1a4d82fc 116pub struct ExpnInfo {
d9579d0f
AL
117 /// The location of the actual macro invocation or syntax sugar , e.g.
118 /// `let x = foo!();` or `if let Some(y) = x {}`
1a4d82fc
JJ
119 ///
120 /// This may recursively refer to other macro invocations, e.g. if
121 /// `foo!()` invoked `bar!()` internally, and there was an
122 /// expression inside `bar!`; the call_site of the expression in
123 /// the expansion would point to the `bar!` invocation; that
124 /// call_site span would have its own ExpnInfo, with the call_site
125 /// pointing to the `foo!` invocation.
126 pub call_site: Span,
d9579d0f 127 /// Information about the expansion.
1a4d82fc 128 pub callee: NameAndSpan
223e47cc
LB
129}
130
c34b1796
AL
131// _____________________________________________________________________________
132// FileMap, MultiByteChar, FileName, FileLines
133//
134
62682a34
SL
135/// An abstraction over the fs operations used by the Parser.
136pub trait FileLoader {
137 /// Query the existence of a file.
138 fn file_exists(&self, path: &Path) -> bool;
139
3157f602
XL
140 /// Return an absolute path to a file, if possible.
141 fn abs_path(&self, path: &Path) -> Option<PathBuf>;
142
62682a34
SL
143 /// Read the contents of an UTF-8 file into memory.
144 fn read_file(&self, path: &Path) -> io::Result<String>;
145}
146
147/// A FileLoader that uses std::fs to load real files.
148pub struct RealFileLoader;
149
150impl FileLoader for RealFileLoader {
151 fn file_exists(&self, path: &Path) -> bool {
152 fs::metadata(path).is_ok()
153 }
154
3157f602
XL
155 fn abs_path(&self, path: &Path) -> Option<PathBuf> {
156 if path.is_absolute() {
157 Some(path.to_path_buf())
158 } else {
159 env::current_dir()
160 .ok()
161 .map(|cwd| cwd.join(path))
162 }
163 }
164
62682a34
SL
165 fn read_file(&self, path: &Path) -> io::Result<String> {
166 let mut src = String::new();
54a0048b 167 fs::File::open(path)?.read_to_string(&mut src)?;
62682a34
SL
168 Ok(src)
169 }
170}
c34b1796
AL
171
172// _____________________________________________________________________________
173// CodeMap
174//
175
223e47cc 176pub struct CodeMap {
1a4d82fc 177 pub files: RefCell<Vec<Rc<FileMap>>>,
62682a34
SL
178 expansions: RefCell<Vec<ExpnInfo>>,
179 file_loader: Box<FileLoader>
223e47cc
LB
180}
181
970d7e83 182impl CodeMap {
223e47cc
LB
183 pub fn new() -> CodeMap {
184 CodeMap {
1a4d82fc
JJ
185 files: RefCell::new(Vec::new()),
186 expansions: RefCell::new(Vec::new()),
62682a34
SL
187 file_loader: Box::new(RealFileLoader)
188 }
189 }
190
191 pub fn with_file_loader(file_loader: Box<FileLoader>) -> CodeMap {
192 CodeMap {
193 files: RefCell::new(Vec::new()),
194 expansions: RefCell::new(Vec::new()),
195 file_loader: file_loader
223e47cc
LB
196 }
197 }
198
62682a34
SL
199 pub fn file_exists(&self, path: &Path) -> bool {
200 self.file_loader.file_exists(path)
201 }
202
203 pub fn load_file(&self, path: &Path) -> io::Result<Rc<FileMap>> {
54a0048b 204 let src = self.file_loader.read_file(path)?;
3157f602
XL
205 let abs_path = self.file_loader.abs_path(path).map(|p| p.to_str().unwrap().to_string());
206 Ok(self.new_filemap(path.to_str().unwrap().to_string(), abs_path, src))
62682a34
SL
207 }
208
c1a9b12d
SL
209 fn next_start_pos(&self) -> usize {
210 let files = self.files.borrow();
211 match files.last() {
212 None => 0,
213 // Add one so there is some space between files. This lets us distinguish
214 // positions in the codemap, even in the presence of zero-length files.
215 Some(last) => last.end_pos.to_usize() + 1,
216 }
217 }
218
219 /// Creates a new filemap without setting its line information. If you don't
220 /// intend to set the line information yourself, you should use new_filemap_and_lines.
3157f602
XL
221 pub fn new_filemap(&self, filename: FileName, abs_path: Option<FileName>,
222 mut src: String) -> Rc<FileMap> {
c1a9b12d 223 let start_pos = self.next_start_pos();
1a4d82fc 224 let mut files = self.files.borrow_mut();
223e47cc 225
1a4d82fc 226 // Remove utf-8 BOM if any.
d9579d0f
AL
227 if src.starts_with("\u{feff}") {
228 src.drain(..3);
229 }
223e47cc 230
c34b1796
AL
231 let end_pos = start_pos + src.len();
232
1a4d82fc
JJ
233 let filemap = Rc::new(FileMap {
234 name: filename,
3157f602 235 abs_path: abs_path,
c34b1796 236 src: Some(Rc::new(src)),
85aaf69f 237 start_pos: Pos::from_usize(start_pos),
c34b1796 238 end_pos: Pos::from_usize(end_pos),
1a4d82fc
JJ
239 lines: RefCell::new(Vec::new()),
240 multibyte_chars: RefCell::new(Vec::new()),
241 });
223e47cc 242
1a4d82fc
JJ
243 files.push(filemap.clone());
244
245 filemap
223e47cc
LB
246 }
247
c1a9b12d 248 /// Creates a new filemap and sets its line information.
3157f602
XL
249 pub fn new_filemap_and_lines(&self, filename: &str, abs_path: Option<&str>,
250 src: &str) -> Rc<FileMap> {
251 let fm = self.new_filemap(filename.to_string(),
252 abs_path.map(|s| s.to_owned()),
253 src.to_owned());
a7813a04 254 let mut byte_pos: u32 = fm.start_pos.0;
c1a9b12d
SL
255 for line in src.lines() {
256 // register the start of this line
257 fm.next_line(BytePos(byte_pos));
258
259 // update byte_pos to include this line and the \n at the end
260 byte_pos += line.len() as u32 + 1;
261 }
262 fm
263 }
264
265
c34b1796
AL
266 /// Allocates a new FileMap representing a source file from an external
267 /// crate. The source code of such an "imported filemap" is not available,
268 /// but we still know enough to generate accurate debuginfo location
269 /// information for things inlined from other crates.
270 pub fn new_imported_filemap(&self,
271 filename: FileName,
3157f602 272 abs_path: Option<FileName>,
c34b1796 273 source_len: usize,
d9579d0f
AL
274 mut file_local_lines: Vec<BytePos>,
275 mut file_local_multibyte_chars: Vec<MultiByteChar>)
c34b1796 276 -> Rc<FileMap> {
c1a9b12d 277 let start_pos = self.next_start_pos();
c34b1796 278 let mut files = self.files.borrow_mut();
c34b1796
AL
279
280 let end_pos = Pos::from_usize(start_pos + source_len);
281 let start_pos = Pos::from_usize(start_pos);
282
d9579d0f
AL
283 for pos in &mut file_local_lines {
284 *pos = *pos + start_pos;
285 }
286
287 for mbc in &mut file_local_multibyte_chars {
288 mbc.pos = mbc.pos + start_pos;
289 }
c34b1796
AL
290
291 let filemap = Rc::new(FileMap {
292 name: filename,
3157f602 293 abs_path: abs_path,
c34b1796
AL
294 src: None,
295 start_pos: start_pos,
296 end_pos: end_pos,
d9579d0f
AL
297 lines: RefCell::new(file_local_lines),
298 multibyte_chars: RefCell::new(file_local_multibyte_chars),
c34b1796
AL
299 });
300
301 files.push(filemap.clone());
302
303 filemap
304 }
305
1a4d82fc 306 pub fn mk_substr_filename(&self, sp: Span) -> String {
223e47cc 307 let pos = self.lookup_char_pos(sp.lo);
1a4d82fc
JJ
308 (format!("<{}:{}:{}>",
309 pos.file.name,
310 pos.line,
85aaf69f 311 pos.col.to_usize() + 1)).to_string()
223e47cc
LB
312 }
313
314 /// Lookup source information about a BytePos
970d7e83 315 pub fn lookup_char_pos(&self, pos: BytePos) -> Loc {
d9579d0f 316 let chpos = self.bytepos_to_file_charpos(pos);
c1a9b12d
SL
317 match self.lookup_line(pos) {
318 Ok(FileMapAndLine { fm: f, line: a }) => {
319 let line = a + 1; // Line numbers start at 1
320 let linebpos = (*f.lines.borrow())[a];
321 let linechpos = self.bytepos_to_file_charpos(linebpos);
322 debug!("byte pos {:?} is on the line at byte pos {:?}",
323 pos, linebpos);
324 debug!("char pos {:?} is on the line at char pos {:?}",
325 chpos, linechpos);
326 debug!("byte is on line: {}", line);
327 assert!(chpos >= linechpos);
328 Loc {
329 file: f,
330 line: line,
331 col: chpos - linechpos,
332 }
333 }
334 Err(f) => {
335 Loc {
336 file: f,
337 line: 0,
338 col: chpos,
339 }
340 }
d9579d0f
AL
341 }
342 }
343
c1a9b12d
SL
344 // If the relevant filemap is empty, we don't return a line number.
345 fn lookup_line(&self, pos: BytePos) -> Result<FileMapAndLine, Rc<FileMap>> {
d9579d0f
AL
346 let idx = self.lookup_filemap_idx(pos);
347
348 let files = self.files.borrow();
349 let f = (*files)[idx].clone();
c1a9b12d 350
9e0c209e
SL
351 match f.lookup_line(pos) {
352 Some(line) => Ok(FileMapAndLine { fm: f, line: line }),
353 None => Err(f)
c1a9b12d 354 }
223e47cc
LB
355 }
356
970d7e83 357 pub fn lookup_char_pos_adj(&self, pos: BytePos) -> LocWithOpt {
223e47cc 358 let loc = self.lookup_char_pos(pos);
1a4d82fc
JJ
359 LocWithOpt {
360 filename: loc.file.name.to_string(),
361 line: loc.line,
362 col: loc.col,
363 file: Some(loc.file)
223e47cc
LB
364 }
365 }
366
9e0c209e
SL
367 /// Returns `Some(span)`, a union of the lhs and rhs span. The lhs must precede the rhs. If
368 /// there are gaps between lhs and rhs, the resulting union will cross these gaps.
369 /// For this to work, the spans have to be:
370 /// * the expn_id of both spans much match
371 /// * the lhs span needs to end on the same line the rhs span begins
372 /// * the lhs span must start at or before the rhs span
373 pub fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
374 use std::cmp;
375
376 // make sure we're at the same expansion id
377 if sp_lhs.expn_id != sp_rhs.expn_id {
378 return None;
379 }
380
381 let lhs_end = match self.lookup_line(sp_lhs.hi) {
382 Ok(x) => x,
383 Err(_) => return None
384 };
385 let rhs_begin = match self.lookup_line(sp_rhs.lo) {
386 Ok(x) => x,
387 Err(_) => return None
388 };
389
390 // if we must cross lines to merge, don't merge
391 if lhs_end.line != rhs_begin.line {
392 return None;
393 }
394
395 // ensure these follow the expected order and we don't overlap
396 if (sp_lhs.lo <= sp_rhs.lo) && (sp_lhs.hi <= sp_rhs.lo) {
397 Some(Span {
398 lo: cmp::min(sp_lhs.lo, sp_rhs.lo),
399 hi: cmp::max(sp_lhs.hi, sp_rhs.hi),
400 expn_id: sp_lhs.expn_id,
401 })
402 } else {
403 None
404 }
405 }
406
1a4d82fc 407 pub fn span_to_string(&self, sp: Span) -> String {
a7813a04
XL
408 if sp == COMMAND_LINE_SP {
409 return "<command line option>".to_string();
410 }
411
7453a54e 412 if self.files.borrow().is_empty() && sp.source_equal(&DUMMY_SP) {
1a4d82fc 413 return "no-location".to_string();
223e47cc
LB
414 }
415
416 let lo = self.lookup_char_pos_adj(sp.lo);
417 let hi = self.lookup_char_pos_adj(sp.hi);
1a4d82fc
JJ
418 return (format!("{}:{}:{}: {}:{}",
419 lo.filename,
420 lo.line,
85aaf69f 421 lo.col.to_usize() + 1,
1a4d82fc 422 hi.line,
85aaf69f 423 hi.col.to_usize() + 1)).to_string()
223e47cc
LB
424 }
425
92a42be0
SL
426 // Returns true if two spans have the same callee
427 // (Assumes the same ExpnFormat implies same callee)
428 fn match_callees(&self, sp_a: &Span, sp_b: &Span) -> bool {
429 let fmt_a = self
430 .with_expn_info(sp_a.expn_id,
431 |ei| ei.map(|ei| ei.callee.format.clone()));
432
433 let fmt_b = self
434 .with_expn_info(sp_b.expn_id,
435 |ei| ei.map(|ei| ei.callee.format.clone()));
436 fmt_a == fmt_b
437 }
438
439 /// Returns a formatted string showing the expansion chain of a span
440 ///
441 /// Spans are printed in the following format:
442 ///
443 /// filename:start_line:col: end_line:col
444 /// snippet
445 /// Callee:
446 /// Callee span
447 /// Callsite:
448 /// Callsite span
449 ///
450 /// Callees and callsites are printed recursively (if available, otherwise header
451 /// and span is omitted), expanding into their own callee/callsite spans.
452 /// Each layer of recursion has an increased indent, and snippets are truncated
453 /// to at most 50 characters. Finally, recursive calls to the same macro are squashed,
454 /// with '...' used to represent any number of recursive calls.
455 pub fn span_to_expanded_string(&self, sp: Span) -> String {
456 self.span_to_expanded_string_internal(sp, "")
457 }
458
459 fn span_to_expanded_string_internal(&self, sp:Span, indent: &str) -> String {
460 let mut indent = indent.to_owned();
461 let mut output = "".to_owned();
462 let span_str = self.span_to_string(sp);
463 let mut span_snip = self.span_to_snippet(sp)
464 .unwrap_or("Snippet unavailable".to_owned());
7453a54e
SL
465
466 // Truncate by code points - in worst case this will be more than 50 characters,
467 // but ensures at least 50 characters and respects byte boundaries.
468 let char_vec: Vec<(usize, char)> = span_snip.char_indices().collect();
469 if char_vec.len() > 50 {
470 span_snip.truncate(char_vec[49].0);
92a42be0
SL
471 span_snip.push_str("...");
472 }
7453a54e 473
92a42be0
SL
474 output.push_str(&format!("{}{}\n{}`{}`\n", indent, span_str, indent, span_snip));
475
476 if sp.expn_id == NO_EXPANSION || sp.expn_id == COMMAND_LINE_EXPN {
477 return output;
478 }
479
480 let mut callee = self.with_expn_info(sp.expn_id,
481 |ei| ei.and_then(|ei| ei.callee.span.clone()));
482 let mut callsite = self.with_expn_info(sp.expn_id,
483 |ei| ei.map(|ei| ei.call_site.clone()));
484
485 indent.push_str(" ");
486 let mut is_recursive = false;
487
488 while callee.is_some() && self.match_callees(&sp, &callee.unwrap()) {
489 callee = self.with_expn_info(callee.unwrap().expn_id,
490 |ei| ei.and_then(|ei| ei.callee.span.clone()));
491 is_recursive = true;
492 }
493 if let Some(span) = callee {
494 output.push_str(&indent);
495 output.push_str("Callee:\n");
496 if is_recursive {
497 output.push_str(&indent);
498 output.push_str("...\n");
499 }
500 output.push_str(&(self.span_to_expanded_string_internal(span, &indent)));
501 }
502
503 is_recursive = false;
504 while callsite.is_some() && self.match_callees(&sp, &callsite.unwrap()) {
505 callsite = self.with_expn_info(callsite.unwrap().expn_id,
506 |ei| ei.map(|ei| ei.call_site.clone()));
507 is_recursive = true;
508 }
509 if let Some(span) = callsite {
510 output.push_str(&indent);
511 output.push_str("Callsite:\n");
512 if is_recursive {
513 output.push_str(&indent);
514 output.push_str("...\n");
515 }
516 output.push_str(&(self.span_to_expanded_string_internal(span, &indent)));
517 }
518 output
519 }
520
7453a54e
SL
521 /// Return the source span - this is either the supplied span, or the span for
522 /// the macro callsite that expanded to it.
523 pub fn source_callsite(&self, sp: Span) -> Span {
524 let mut span = sp;
525 // Special case - if a macro is parsed as an argument to another macro, the source
526 // callsite is the first callsite, which is also source-equivalent to the span.
527 let mut first = true;
528 while span.expn_id != NO_EXPANSION && span.expn_id != COMMAND_LINE_EXPN {
529 if let Some(callsite) = self.with_expn_info(span.expn_id,
530 |ei| ei.map(|ei| ei.call_site.clone())) {
531 if first && span.source_equal(&callsite) {
532 if self.lookup_char_pos(span.lo).file.is_real_file() {
533 return Span { expn_id: NO_EXPANSION, .. span };
534 }
535 }
536 first = false;
537 span = callsite;
538 }
539 else {
540 break;
541 }
542 }
543 span
544 }
545
546 /// Return the source callee.
547 ///
548 /// Returns None if the supplied span has no expansion trace,
549 /// else returns the NameAndSpan for the macro definition
550 /// corresponding to the source callsite.
551 pub fn source_callee(&self, sp: Span) -> Option<NameAndSpan> {
552 let mut span = sp;
553 // Special case - if a macro is parsed as an argument to another macro, the source
554 // callsite is source-equivalent to the span, and the source callee is the first callee.
555 let mut first = true;
556 while let Some(callsite) = self.with_expn_info(span.expn_id,
557 |ei| ei.map(|ei| ei.call_site.clone())) {
558 if first && span.source_equal(&callsite) {
559 if self.lookup_char_pos(span.lo).file.is_real_file() {
560 return self.with_expn_info(span.expn_id,
561 |ei| ei.map(|ei| ei.callee.clone()));
562 }
563 }
564 first = false;
565 if let Some(_) = self.with_expn_info(callsite.expn_id,
566 |ei| ei.map(|ei| ei.call_site.clone())) {
567 span = callsite;
568 }
569 else {
570 return self.with_expn_info(span.expn_id,
571 |ei| ei.map(|ei| ei.callee.clone()));
572 }
573 }
574 None
575 }
576
1a4d82fc
JJ
577 pub fn span_to_filename(&self, sp: Span) -> FileName {
578 self.lookup_char_pos(sp.lo).file.name.to_string()
223e47cc
LB
579 }
580
d9579d0f 581 pub fn span_to_lines(&self, sp: Span) -> FileLinesResult {
a7813a04
XL
582 debug!("span_to_lines(sp={:?})", sp);
583
d9579d0f
AL
584 if sp.lo > sp.hi {
585 return Err(SpanLinesError::IllFormedSpan(sp));
586 }
587
223e47cc 588 let lo = self.lookup_char_pos(sp.lo);
a7813a04 589 debug!("span_to_lines: lo={:?}", lo);
223e47cc 590 let hi = self.lookup_char_pos(sp.hi);
a7813a04 591 debug!("span_to_lines: hi={:?}", hi);
d9579d0f
AL
592
593 if lo.file.start_pos != hi.file.start_pos {
594 return Err(SpanLinesError::DistinctSources(DistinctSources {
595 begin: (lo.file.name.clone(), lo.file.start_pos),
596 end: (hi.file.name.clone(), hi.file.start_pos),
597 }));
598 }
599 assert!(hi.line >= lo.line);
600
9346a6ac
AL
601 let mut lines = Vec::with_capacity(hi.line - lo.line + 1);
602
603 // The span starts partway through the first line,
604 // but after that it starts from offset 0.
605 let mut start_col = lo.col;
606
607 // For every line but the last, it extends from `start_col`
608 // and to the end of the line. Be careful because the line
609 // numbers in Loc are 1-based, so we subtract 1 to get 0-based
610 // lines.
611 for line_index in lo.line-1 .. hi.line-1 {
a7813a04
XL
612 let line_len = lo.file.get_line(line_index)
613 .map(|s| s.chars().count())
614 .unwrap_or(0);
9346a6ac
AL
615 lines.push(LineInfo { line_index: line_index,
616 start_col: start_col,
617 end_col: CharPos::from_usize(line_len) });
618 start_col = CharPos::from_usize(0);
619 }
620
621 // For the last line, it extends from `start_col` to `hi.col`:
622 lines.push(LineInfo { line_index: hi.line - 1,
623 start_col: start_col,
624 end_col: hi.col });
625
d9579d0f 626 Ok(FileLines {file: lo.file, lines: lines})
223e47cc
LB
627 }
628
85aaf69f
SL
629 pub fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError> {
630 if sp.lo > sp.hi {
631 return Err(SpanSnippetError::IllFormedSpan(sp));
632 }
633
c34b1796
AL
634 let local_begin = self.lookup_byte_offset(sp.lo);
635 let local_end = self.lookup_byte_offset(sp.hi);
1a4d82fc 636
c34b1796 637 if local_begin.fm.start_pos != local_end.fm.start_pos {
85aaf69f 638 return Err(SpanSnippetError::DistinctSources(DistinctSources {
c34b1796
AL
639 begin: (local_begin.fm.name.clone(),
640 local_begin.fm.start_pos),
641 end: (local_end.fm.name.clone(),
642 local_end.fm.start_pos)
85aaf69f 643 }));
1a4d82fc 644 } else {
c34b1796
AL
645 match local_begin.fm.src {
646 Some(ref src) => {
647 let start_index = local_begin.pos.to_usize();
648 let end_index = local_end.pos.to_usize();
649 let source_len = (local_begin.fm.end_pos -
650 local_begin.fm.start_pos).to_usize();
651
652 if start_index > end_index || end_index > source_len {
653 return Err(SpanSnippetError::MalformedForCodemap(
654 MalformedCodemapPositions {
655 name: local_begin.fm.name.clone(),
656 source_len: source_len,
657 begin_pos: local_begin.pos,
658 end_pos: local_end.pos,
659 }));
660 }
85aaf69f 661
c34b1796
AL
662 return Ok((&src[start_index..end_index]).to_string())
663 }
664 None => {
665 return Err(SpanSnippetError::SourceNotAvailable {
666 filename: local_begin.fm.name.clone()
667 });
668 }
669 }
1a4d82fc 670 }
223e47cc
LB
671 }
672
3157f602 673 pub fn get_filemap(&self, filename: &str) -> Option<Rc<FileMap>> {
62682a34 674 for fm in self.files.borrow().iter() {
1a4d82fc 675 if filename == fm.name {
3157f602 676 return Some(fm.clone());
1a4d82fc
JJ
677 }
678 }
3157f602 679 None
1a4d82fc
JJ
680 }
681
c34b1796 682 /// For a global BytePos compute the local offset within the containing FileMap
1a4d82fc
JJ
683 pub fn lookup_byte_offset(&self, bpos: BytePos) -> FileMapAndBytePos {
684 let idx = self.lookup_filemap_idx(bpos);
685 let fm = (*self.files.borrow())[idx].clone();
686 let offset = bpos - fm.start_pos;
687 FileMapAndBytePos {fm: fm, pos: offset}
688 }
689
c1a9b12d 690 /// Converts an absolute BytePos to a CharPos relative to the filemap.
1a4d82fc
JJ
691 pub fn bytepos_to_file_charpos(&self, bpos: BytePos) -> CharPos {
692 let idx = self.lookup_filemap_idx(bpos);
693 let files = self.files.borrow();
694 let map = &(*files)[idx];
695
696 // The number of extra bytes due to multibyte chars in the FileMap
697 let mut total_extra_bytes = 0;
698
62682a34 699 for mbc in map.multibyte_chars.borrow().iter() {
1a4d82fc
JJ
700 debug!("{}-byte char at {:?}", mbc.bytes, mbc.pos);
701 if mbc.pos < bpos {
702 // every character is at least one byte, so we only
703 // count the actual extra bytes.
704 total_extra_bytes += mbc.bytes - 1;
705 // We should never see a byte position in the middle of a
706 // character
85aaf69f 707 assert!(bpos.to_usize() >= mbc.pos.to_usize() + mbc.bytes);
1a4d82fc
JJ
708 } else {
709 break;
710 }
711 }
712
85aaf69f
SL
713 assert!(map.start_pos.to_usize() + total_extra_bytes <= bpos.to_usize());
714 CharPos(bpos.to_usize() - map.start_pos.to_usize() - total_extra_bytes)
223e47cc 715 }
223e47cc 716
c1a9b12d 717 // Return the index of the filemap (in self.files) which contains pos.
9e0c209e 718 pub fn lookup_filemap_idx(&self, pos: BytePos) -> usize {
1a4d82fc
JJ
719 let files = self.files.borrow();
720 let files = &*files;
c1a9b12d
SL
721 let count = files.len();
722
723 // Binary search for the filemap.
85aaf69f 724 let mut a = 0;
c1a9b12d 725 let mut b = count;
85aaf69f
SL
726 while b - a > 1 {
727 let m = (a + b) / 2;
1a4d82fc 728 if files[m].start_pos > pos {
223e47cc
LB
729 b = m;
730 } else {
731 a = m;
732 }
733 }
c1a9b12d
SL
734
735 assert!(a < count, "position {} does not resolve to a source location", pos.to_usize());
223e47cc
LB
736
737 return a;
738 }
739
1a4d82fc
JJ
740 pub fn record_expansion(&self, expn_info: ExpnInfo) -> ExpnId {
741 let mut expansions = self.expansions.borrow_mut();
742 expansions.push(expn_info);
9346a6ac
AL
743 let len = expansions.len();
744 if len > u32::max_value() as usize {
745 panic!("too many ExpnInfo's!");
746 }
747 ExpnId(len as u32 - 1)
223e47cc
LB
748 }
749
1a4d82fc
JJ
750 pub fn with_expn_info<T, F>(&self, id: ExpnId, f: F) -> T where
751 F: FnOnce(Option<&ExpnInfo>) -> T,
752 {
753 match id {
9346a6ac 754 NO_EXPANSION | COMMAND_LINE_EXPN => f(None),
85aaf69f 755 ExpnId(i) => f(Some(&(*self.expansions.borrow())[i as usize]))
1a4d82fc 756 }
223e47cc
LB
757 }
758
c34b1796
AL
759 /// Check if a span is "internal" to a macro in which #[unstable]
760 /// items can be used (that is, a macro marked with
761 /// `#[allow_internal_unstable]`).
762 pub fn span_allows_unstable(&self, span: Span) -> bool {
763 debug!("span_allows_unstable(span = {:?})", span);
764 let mut allows_unstable = false;
765 let mut expn_id = span.expn_id;
766 loop {
767 let quit = self.with_expn_info(expn_id, |expninfo| {
768 debug!("span_allows_unstable: expninfo = {:?}", expninfo);
769 expninfo.map_or(/* hit the top level */ true, |info| {
770
771 let span_comes_from_this_expansion =
7453a54e 772 info.callee.span.map_or(span.source_equal(&info.call_site), |mac_span| {
b039eaaf 773 mac_span.contains(span)
c34b1796
AL
774 });
775
c1a9b12d
SL
776 debug!("span_allows_unstable: span: {:?} call_site: {:?} callee: {:?}",
777 (span.lo, span.hi),
778 (info.call_site.lo, info.call_site.hi),
779 info.callee.span.map(|x| (x.lo, x.hi)));
c34b1796
AL
780 debug!("span_allows_unstable: from this expansion? {}, allows unstable? {}",
781 span_comes_from_this_expansion,
782 info.callee.allow_internal_unstable);
783 if span_comes_from_this_expansion {
784 allows_unstable = info.callee.allow_internal_unstable;
785 // we've found the right place, stop looking
786 true
1a4d82fc 787 } else {
c34b1796
AL
788 // not the right place, keep looking
789 expn_id = info.call_site.expn_id;
790 false
1a4d82fc 791 }
c34b1796
AL
792 })
793 });
794 if quit {
795 break
223e47cc 796 }
c34b1796
AL
797 }
798 debug!("span_allows_unstable? {}", allows_unstable);
799 allows_unstable
223e47cc 800 }
92a42be0
SL
801
802 pub fn count_lines(&self) -> usize {
803 self.files.borrow().iter().fold(0, |a, f| a + f.count_lines())
804 }
a7813a04
XL
805
806 pub fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace> {
807 let mut last_span = DUMMY_SP;
808 let mut span = span;
809 let mut result = vec![];
810 loop {
811 let span_name_span = self.with_expn_info(span.expn_id, |expn_info| {
812 expn_info.map(|ei| {
813 let (pre, post) = match ei.callee.format {
814 MacroAttribute(..) => ("#[", "]"),
815 MacroBang(..) => ("", "!"),
816 };
817 let macro_decl_name = format!("{}{}{}",
818 pre,
819 ei.callee.name(),
820 post);
821 let def_site_span = ei.callee.span;
822 (ei.call_site, macro_decl_name, def_site_span)
823 })
824 });
825
826 match span_name_span {
827 None => break,
828 Some((call_site, macro_decl_name, def_site_span)) => {
829 // Don't print recursive invocations
830 if !call_site.source_equal(&last_span) {
831 result.push(MacroBacktrace {
832 call_site: call_site,
833 macro_decl_name: macro_decl_name,
834 def_site_span: def_site_span,
835 });
836 }
837 last_span = span;
838 span = call_site;
839 }
840 }
841 }
842 result
843 }
844}
845
3157f602
XL
846impl CodeMapper for CodeMap {
847 fn lookup_char_pos(&self, pos: BytePos) -> Loc {
848 self.lookup_char_pos(pos)
849 }
850 fn span_to_lines(&self, sp: Span) -> FileLinesResult {
851 self.span_to_lines(sp)
852 }
853 fn span_to_string(&self, sp: Span) -> String {
854 self.span_to_string(sp)
855 }
856 fn span_to_filename(&self, sp: Span) -> FileName {
857 self.span_to_filename(sp)
858 }
859 fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace> {
860 self.macro_backtrace(span)
861 }
9e0c209e
SL
862 fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
863 self.merge_spans(sp_lhs, sp_rhs)
864 }
85aaf69f
SL
865}
866
c34b1796
AL
867// _____________________________________________________________________________
868// Tests
869//
870
223e47cc 871#[cfg(test)]
d9579d0f 872mod tests {
223e47cc 873 use super::*;
3157f602 874 use std::rc::Rc;
223e47cc
LB
875
876 #[test]
877 fn t1 () {
878 let cm = CodeMap::new();
1a4d82fc 879 let fm = cm.new_filemap("blork.rs".to_string(),
3157f602 880 None,
1a4d82fc 881 "first line.\nsecond line".to_string());
223e47cc 882 fm.next_line(BytePos(0));
c1a9b12d 883 // Test we can get lines with partial line info.
9346a6ac 884 assert_eq!(fm.get_line(0), Some("first line."));
c1a9b12d 885 // TESTING BROKEN BEHAVIOR: line break declared before actual line break.
223e47cc 886 fm.next_line(BytePos(10));
9346a6ac 887 assert_eq!(fm.get_line(1), Some("."));
c1a9b12d
SL
888 fm.next_line(BytePos(12));
889 assert_eq!(fm.get_line(2), Some("second line"));
223e47cc
LB
890 }
891
892 #[test]
c34b1796 893 #[should_panic]
223e47cc
LB
894 fn t2 () {
895 let cm = CodeMap::new();
1a4d82fc 896 let fm = cm.new_filemap("blork.rs".to_string(),
3157f602 897 None,
1a4d82fc 898 "first line.\nsecond line".to_string());
223e47cc
LB
899 // TESTING *REALLY* BROKEN BEHAVIOR:
900 fm.next_line(BytePos(0));
901 fm.next_line(BytePos(10));
902 fm.next_line(BytePos(2));
903 }
1a4d82fc
JJ
904
905 fn init_code_map() -> CodeMap {
906 let cm = CodeMap::new();
907 let fm1 = cm.new_filemap("blork.rs".to_string(),
3157f602 908 None,
1a4d82fc
JJ
909 "first line.\nsecond line".to_string());
910 let fm2 = cm.new_filemap("empty.rs".to_string(),
3157f602 911 None,
1a4d82fc
JJ
912 "".to_string());
913 let fm3 = cm.new_filemap("blork2.rs".to_string(),
3157f602 914 None,
1a4d82fc
JJ
915 "first line.\nsecond line".to_string());
916
917 fm1.next_line(BytePos(0));
918 fm1.next_line(BytePos(12));
c1a9b12d
SL
919 fm2.next_line(fm2.start_pos);
920 fm3.next_line(fm3.start_pos);
921 fm3.next_line(fm3.start_pos + BytePos(12));
1a4d82fc
JJ
922
923 cm
924 }
925
926 #[test]
927 fn t3() {
928 // Test lookup_byte_offset
929 let cm = init_code_map();
930
c1a9b12d 931 let fmabp1 = cm.lookup_byte_offset(BytePos(23));
1a4d82fc 932 assert_eq!(fmabp1.fm.name, "blork.rs");
c1a9b12d
SL
933 assert_eq!(fmabp1.pos, BytePos(23));
934
935 let fmabp1 = cm.lookup_byte_offset(BytePos(24));
936 assert_eq!(fmabp1.fm.name, "empty.rs");
937 assert_eq!(fmabp1.pos, BytePos(0));
1a4d82fc 938
c1a9b12d 939 let fmabp2 = cm.lookup_byte_offset(BytePos(25));
1a4d82fc
JJ
940 assert_eq!(fmabp2.fm.name, "blork2.rs");
941 assert_eq!(fmabp2.pos, BytePos(0));
942 }
943
944 #[test]
945 fn t4() {
946 // Test bytepos_to_file_charpos
947 let cm = init_code_map();
948
949 let cp1 = cm.bytepos_to_file_charpos(BytePos(22));
950 assert_eq!(cp1, CharPos(22));
951
c1a9b12d 952 let cp2 = cm.bytepos_to_file_charpos(BytePos(25));
1a4d82fc
JJ
953 assert_eq!(cp2, CharPos(0));
954 }
955
956 #[test]
957 fn t5() {
958 // Test zero-length filemaps.
959 let cm = init_code_map();
960
961 let loc1 = cm.lookup_char_pos(BytePos(22));
962 assert_eq!(loc1.file.name, "blork.rs");
963 assert_eq!(loc1.line, 2);
964 assert_eq!(loc1.col, CharPos(10));
965
c1a9b12d 966 let loc2 = cm.lookup_char_pos(BytePos(25));
1a4d82fc
JJ
967 assert_eq!(loc2.file.name, "blork2.rs");
968 assert_eq!(loc2.line, 1);
969 assert_eq!(loc2.col, CharPos(0));
970 }
971
972 fn init_code_map_mbc() -> CodeMap {
973 let cm = CodeMap::new();
974 // € is a three byte utf8 char.
975 let fm1 =
976 cm.new_filemap("blork.rs".to_string(),
3157f602 977 None,
1a4d82fc
JJ
978 "fir€st €€€€ line.\nsecond line".to_string());
979 let fm2 = cm.new_filemap("blork2.rs".to_string(),
3157f602 980 None,
1a4d82fc
JJ
981 "first line€€.\n€ second line".to_string());
982
983 fm1.next_line(BytePos(0));
c1a9b12d
SL
984 fm1.next_line(BytePos(28));
985 fm2.next_line(fm2.start_pos);
986 fm2.next_line(fm2.start_pos + BytePos(20));
1a4d82fc
JJ
987
988 fm1.record_multibyte_char(BytePos(3), 3);
989 fm1.record_multibyte_char(BytePos(9), 3);
990 fm1.record_multibyte_char(BytePos(12), 3);
991 fm1.record_multibyte_char(BytePos(15), 3);
992 fm1.record_multibyte_char(BytePos(18), 3);
c1a9b12d
SL
993 fm2.record_multibyte_char(fm2.start_pos + BytePos(10), 3);
994 fm2.record_multibyte_char(fm2.start_pos + BytePos(13), 3);
995 fm2.record_multibyte_char(fm2.start_pos + BytePos(18), 3);
1a4d82fc
JJ
996
997 cm
998 }
999
1000 #[test]
1001 fn t6() {
1002 // Test bytepos_to_file_charpos in the presence of multi-byte chars
1003 let cm = init_code_map_mbc();
1004
1005 let cp1 = cm.bytepos_to_file_charpos(BytePos(3));
1006 assert_eq!(cp1, CharPos(3));
1007
1008 let cp2 = cm.bytepos_to_file_charpos(BytePos(6));
1009 assert_eq!(cp2, CharPos(4));
1010
1011 let cp3 = cm.bytepos_to_file_charpos(BytePos(56));
1012 assert_eq!(cp3, CharPos(12));
1013
1014 let cp4 = cm.bytepos_to_file_charpos(BytePos(61));
1015 assert_eq!(cp4, CharPos(15));
1016 }
1017
1018 #[test]
1019 fn t7() {
1020 // Test span_to_lines for a span ending at the end of filemap
1021 let cm = init_code_map();
1022 let span = Span {lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION};
d9579d0f 1023 let file_lines = cm.span_to_lines(span).unwrap();
1a4d82fc
JJ
1024
1025 assert_eq!(file_lines.file.name, "blork.rs");
1026 assert_eq!(file_lines.lines.len(), 1);
9346a6ac
AL
1027 assert_eq!(file_lines.lines[0].line_index, 1);
1028 }
1029
a7813a04 1030 /// Given a string like " ~~~~~~~~~~~~ ", produces a span
9346a6ac
AL
1031 /// coverting that range. The idea is that the string has the same
1032 /// length as the input, and we uncover the byte positions. Note
1033 /// that this can span lines and so on.
1034 fn span_from_selection(input: &str, selection: &str) -> Span {
1035 assert_eq!(input.len(), selection.len());
a7813a04 1036 let left_index = selection.find('~').unwrap() as u32;
7453a54e 1037 let right_index = selection.rfind('~').map(|x|x as u32).unwrap_or(left_index);
9346a6ac
AL
1038 Span { lo: BytePos(left_index), hi: BytePos(right_index + 1), expn_id: NO_EXPANSION }
1039 }
1040
9346a6ac
AL
1041 /// Test span_to_snippet and span_to_lines for a span coverting 3
1042 /// lines in the middle of a file.
1043 #[test]
1044 fn span_to_snippet_and_lines_spanning_multiple_lines() {
1045 let cm = CodeMap::new();
1046 let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
a7813a04 1047 let selection = " \n ~~\n~~~\n~~~~~ \n \n";
3157f602 1048 cm.new_filemap_and_lines("blork.rs", None, inputtext);
9346a6ac
AL
1049 let span = span_from_selection(inputtext, selection);
1050
1051 // check that we are extracting the text we thought we were extracting
1052 assert_eq!(&cm.span_to_snippet(span).unwrap(), "BB\nCCC\nDDDDD");
1053
1054 // check that span_to_lines gives us the complete result with the lines/cols we expected
d9579d0f 1055 let lines = cm.span_to_lines(span).unwrap();
9346a6ac
AL
1056 let expected = vec![
1057 LineInfo { line_index: 1, start_col: CharPos(4), end_col: CharPos(6) },
1058 LineInfo { line_index: 2, start_col: CharPos(0), end_col: CharPos(3) },
1059 LineInfo { line_index: 3, start_col: CharPos(0), end_col: CharPos(5) }
1060 ];
1061 assert_eq!(lines.lines, expected);
1a4d82fc
JJ
1062 }
1063
1064 #[test]
1065 fn t8() {
1066 // Test span_to_snippet for a span ending at the end of filemap
1067 let cm = init_code_map();
1068 let span = Span {lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION};
1069 let snippet = cm.span_to_snippet(span);
1070
85aaf69f 1071 assert_eq!(snippet, Ok("second line".to_string()));
1a4d82fc
JJ
1072 }
1073
1074 #[test]
1075 fn t9() {
1076 // Test span_to_str for a span ending at the end of filemap
1077 let cm = init_code_map();
1078 let span = Span {lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION};
1079 let sstr = cm.span_to_string(span);
1080
1081 assert_eq!(sstr, "blork.rs:2:1: 2:12");
1082 }
92a42be0
SL
1083
1084 #[test]
1085 fn t10() {
1086 // Test span_to_expanded_string works in base case (no expansion)
1087 let cm = init_code_map();
1088 let span = Span { lo: BytePos(0), hi: BytePos(11), expn_id: NO_EXPANSION };
1089 let sstr = cm.span_to_expanded_string(span);
1090 assert_eq!(sstr, "blork.rs:1:1: 1:12\n`first line.`\n");
1091
1092 let span = Span { lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION };
1093 let sstr = cm.span_to_expanded_string(span);
1094 assert_eq!(sstr, "blork.rs:2:1: 2:12\n`second line`\n");
1095 }
1096
1097 #[test]
1098 fn t11() {
1099 // Test span_to_expanded_string works with expansion
1100 use ast::Name;
1101 let cm = init_code_map();
1102 let root = Span { lo: BytePos(0), hi: BytePos(11), expn_id: NO_EXPANSION };
1103 let format = ExpnFormat::MacroBang(Name(0u32));
1104 let callee = NameAndSpan { format: format,
1105 allow_internal_unstable: false,
1106 span: None };
1107
1108 let info = ExpnInfo { call_site: root, callee: callee };
1109 let id = cm.record_expansion(info);
1110 let sp = Span { lo: BytePos(12), hi: BytePos(23), expn_id: id };
1111
1112 let sstr = cm.span_to_expanded_string(sp);
1113 assert_eq!(sstr,
1114 "blork.rs:2:1: 2:12\n`second line`\n Callsite:\n \
1115 blork.rs:1:1: 1:12\n `first line.`\n");
1116 }
1117
9e0c209e
SL
1118 /// Test merging two spans on the same line
1119 #[test]
1120 fn span_merging() {
1121 let cm = CodeMap::new();
1122 let inputtext = "bbbb BB bb CCC\n";
1123 let selection1 = " ~~ \n";
1124 let selection2 = " ~~~\n";
1125 cm.new_filemap_and_lines("blork.rs", None, inputtext);
1126 let span1 = span_from_selection(inputtext, selection1);
1127 let span2 = span_from_selection(inputtext, selection2);
1128
1129 if let Some(sp) = cm.merge_spans(span1, span2) {
1130 let sstr = cm.span_to_expanded_string(sp);
1131 assert_eq!(sstr, "blork.rs:1:6: 1:15\n`BB bb CCC`\n");
1132 }
1133 else {
1134 assert!(false);
1135 }
1136 }
1137
1138 /// Test failing to merge two spans on different lines
1139 #[test]
1140 fn span_merging_fail() {
1141 let cm = CodeMap::new();
1142 let inputtext = "bbbb BB\ncc CCC\n";
1143 let selection1 = " ~~\n \n";
1144 let selection2 = " \n ~~~\n";
1145 cm.new_filemap_and_lines("blork.rs", None, inputtext);
1146 let span1 = span_from_selection(inputtext, selection1);
1147 let span2 = span_from_selection(inputtext, selection2);
1148
1149 assert!(cm.merge_spans(span1, span2).is_none());
1150 }
1151
3157f602
XL
1152 /// Returns the span corresponding to the `n`th occurrence of
1153 /// `substring` in `source_text`.
1154 trait CodeMapExtension {
1155 fn span_substr(&self,
1156 file: &Rc<FileMap>,
1157 source_text: &str,
1158 substring: &str,
1159 n: usize)
1160 -> Span;
1161 }
1162
1163 impl CodeMapExtension for CodeMap {
1164 fn span_substr(&self,
1165 file: &Rc<FileMap>,
1166 source_text: &str,
1167 substring: &str,
1168 n: usize)
1169 -> Span
1170 {
1171 println!("span_substr(file={:?}/{:?}, substring={:?}, n={})",
1172 file.name, file.start_pos, substring, n);
1173 let mut i = 0;
1174 let mut hi = 0;
1175 loop {
1176 let offset = source_text[hi..].find(substring).unwrap_or_else(|| {
1177 panic!("source_text `{}` does not have {} occurrences of `{}`, only {}",
1178 source_text, n, substring, i);
1179 });
1180 let lo = hi + offset;
1181 hi = lo + substring.len();
1182 if i == n {
1183 let span = Span {
1184 lo: BytePos(lo as u32 + file.start_pos.0),
1185 hi: BytePos(hi as u32 + file.start_pos.0),
1186 expn_id: NO_EXPANSION,
1187 };
1188 assert_eq!(&self.span_to_snippet(span).unwrap()[..],
1189 substring);
1190 return span;
1191 }
1192 i += 1;
1193 }
1194 }
1195 }
1196
92a42be0
SL
1197 fn init_expansion_chain(cm: &CodeMap) -> Span {
1198 // Creates an expansion chain containing two recursive calls
1199 // root -> expA -> expA -> expB -> expB -> end
1200 use ast::Name;
1201
1202 let root = Span { lo: BytePos(0), hi: BytePos(11), expn_id: NO_EXPANSION };
1203
1204 let format_root = ExpnFormat::MacroBang(Name(0u32));
1205 let callee_root = NameAndSpan { format: format_root,
1206 allow_internal_unstable: false,
1207 span: Some(root) };
1208
1209 let info_a1 = ExpnInfo { call_site: root, callee: callee_root };
1210 let id_a1 = cm.record_expansion(info_a1);
1211 let span_a1 = Span { lo: BytePos(12), hi: BytePos(23), expn_id: id_a1 };
1212
1213 let format_a = ExpnFormat::MacroBang(Name(1u32));
1214 let callee_a = NameAndSpan { format: format_a,
1215 allow_internal_unstable: false,
1216 span: Some(span_a1) };
1217
1218 let info_a2 = ExpnInfo { call_site: span_a1, callee: callee_a.clone() };
1219 let id_a2 = cm.record_expansion(info_a2);
1220 let span_a2 = Span { lo: BytePos(12), hi: BytePos(23), expn_id: id_a2 };
1221
1222 let info_b1 = ExpnInfo { call_site: span_a2, callee: callee_a };
1223 let id_b1 = cm.record_expansion(info_b1);
1224 let span_b1 = Span { lo: BytePos(25), hi: BytePos(36), expn_id: id_b1 };
1225
1226 let format_b = ExpnFormat::MacroBang(Name(2u32));
1227 let callee_b = NameAndSpan { format: format_b,
1228 allow_internal_unstable: false,
1229 span: None };
1230
1231 let info_b2 = ExpnInfo { call_site: span_b1, callee: callee_b.clone() };
1232 let id_b2 = cm.record_expansion(info_b2);
1233 let span_b2 = Span { lo: BytePos(25), hi: BytePos(36), expn_id: id_b2 };
1234
1235 let info_end = ExpnInfo { call_site: span_b2, callee: callee_b };
1236 let id_end = cm.record_expansion(info_end);
1237 Span { lo: BytePos(37), hi: BytePos(48), expn_id: id_end }
1238 }
1239
1240 #[test]
1241 fn t12() {
1242 // Test span_to_expanded_string collapses recursive macros and handles
1243 // recursive callsite and callee expansions
1244 let cm = init_code_map();
1245 let end = init_expansion_chain(&cm);
1246 let sstr = cm.span_to_expanded_string(end);
1247 let res_str =
1248r"blork2.rs:2:1: 2:12
1249`second line`
1250 Callsite:
1251 ...
1252 blork2.rs:1:1: 1:12
1253 `first line.`
1254 Callee:
1255 blork.rs:2:1: 2:12
1256 `second line`
1257 Callee:
1258 blork.rs:1:1: 1:12
1259 `first line.`
1260 Callsite:
1261 blork.rs:1:1: 1:12
1262 `first line.`
1263 Callsite:
1264 ...
1265 blork.rs:2:1: 2:12
1266 `second line`
1267 Callee:
1268 blork.rs:1:1: 1:12
1269 `first line.`
1270 Callsite:
1271 blork.rs:1:1: 1:12
1272 `first line.`
1273";
1274 assert_eq!(sstr, res_str);
1275 }
223e47cc 1276}