]> git.lizzy.rs Git - rust.git/commitdiff
Consolidate codemap tests and fix more errors for travis
authorJonathan Turner <jturner@mozilla.com>
Wed, 22 Jun 2016 22:39:43 +0000 (18:39 -0400)
committerJonathan Turner <jturner@mozilla.com>
Thu, 23 Jun 2016 12:07:35 +0000 (08:07 -0400)
src/librustc/session/config.rs
src/librustc_errors/emitter.rs
src/librustc_errors/snippet/mod.rs
src/librustc_errors/snippet/test.rs [deleted file]
src/libsyntax/codemap.rs
src/libsyntax/test.rs

index 8f6cd640a273e5c3676e30a830320cf63c01be3f..a37990061920b6fa299a2de535010bb78a8490e7 100644 (file)
@@ -1421,12 +1421,11 @@ mod tests {
     use middle::cstore::DummyCrateStore;
     use session::config::{build_configuration, build_session_options};
     use session::build_session;
-
+    use errors;
     use std::rc::Rc;
     use getopts::{getopts, OptGroup};
     use syntax::attr;
     use syntax::attr::AttrMetaMethods;
-    use syntax::diagnostics;
 
     fn optgroups() -> Vec<OptGroup> {
         super::rustc_optgroups().into_iter()
@@ -1443,7 +1442,7 @@ fn test_switch_implies_cfg_test() {
               Ok(m) => m,
               Err(f) => panic!("test_switch_implies_cfg_test: {}", f)
             };
-        let registry = diagnostics::registry::Registry::new(&[]);
+        let registry = errors::registry::Registry::new(&[]);
         let sessopts = build_session_options(matches);
         let sess = build_session(sessopts, &dep_graph, None, registry, Rc::new(DummyCrateStore));
         let cfg = build_configuration(&sess);
@@ -1463,7 +1462,7 @@ fn test_switch_implies_cfg_test_unless_cfg_test() {
                 panic!("test_switch_implies_cfg_test_unless_cfg_test: {}", f)
               }
             };
-        let registry = diagnostics::registry::Registry::new(&[]);
+        let registry = errors::registry::Registry::new(&[]);
         let sessopts = build_session_options(matches);
         let sess = build_session(sessopts, &dep_graph, None, registry,
                                  Rc::new(DummyCrateStore));
@@ -1480,7 +1479,7 @@ fn test_can_print_warnings() {
             let matches = getopts(&[
                 "-Awarnings".to_string()
             ], &optgroups()).unwrap();
-            let registry = diagnostics::registry::Registry::new(&[]);
+            let registry = errors::registry::Registry::new(&[]);
             let sessopts = build_session_options(&matches);
             let sess = build_session(sessopts, &dep_graph, None, registry,
                                      Rc::new(DummyCrateStore));
@@ -1492,7 +1491,7 @@ fn test_can_print_warnings() {
                 "-Awarnings".to_string(),
                 "-Dwarnings".to_string()
             ], &optgroups()).unwrap();
-            let registry = diagnostics::registry::Registry::new(&[]);
+            let registry = errors::registry::Registry::new(&[]);
             let sessopts = build_session_options(&matches);
             let sess = build_session(sessopts, &dep_graph, None, registry,
                                      Rc::new(DummyCrateStore));
@@ -1503,7 +1502,7 @@ fn test_can_print_warnings() {
             let matches = getopts(&[
                 "-Adead_code".to_string()
             ], &optgroups()).unwrap();
-            let registry = diagnostics::registry::Registry::new(&[]);
+            let registry = errors::registry::Registry::new(&[]);
             let sessopts = build_session_options(&matches);
             let sess = build_session(sessopts, &dep_graph, None, registry,
                                      Rc::new(DummyCrateStore));
index a31b1e876fad449453de8047c650c84786eb0655..d46a8f3c453d6ba15fb05d066b5fdc35af11e65d 100644 (file)
@@ -358,7 +358,7 @@ fn highlight_suggestion(&mut self, suggestion: &CodeSuggestion) -> io::Result<()
         Ok(())
     }
 
-    fn highlight_lines(&mut self,
+    pub fn highlight_lines(&mut self,
                        msp: &MultiSpan,
                        lvl: Level)
                        -> io::Result<()>
@@ -619,256 +619,3 @@ fn flush(&mut self) -> io::Result<()> {
         }
     }
 }
-
-
-#[cfg(test)]
-mod test {
-    use errors::{Level, CodeSuggestion};
-    use super::EmitterWriter;
-    use codemap::CodeMap;
-    use syntax_pos::{mk_sp, Span, MultiSpan, BytePos, NO_EXPANSION};
-    use std::sync::{Arc, Mutex};
-    use std::io::{self, Write};
-    use std::str::from_utf8;
-    use std::rc::Rc;
-
-    struct Sink(Arc<Mutex<Vec<u8>>>);
-    impl Write for Sink {
-        fn write(&mut self, data: &[u8]) -> io::Result<usize> {
-            Write::write(&mut *self.0.lock().unwrap(), data)
-        }
-        fn flush(&mut self) -> io::Result<()> { Ok(()) }
-    }
-
-    /// Given a string like " ^~~~~~~~~~~~ ", produces a span
-    /// coverting that range. The idea is that the string has the same
-    /// length as the input, and we uncover the byte positions.  Note
-    /// that this can span lines and so on.
-    fn span_from_selection(input: &str, selection: &str) -> Span {
-        assert_eq!(input.len(), selection.len());
-        let left_index = selection.find('~').unwrap() as u32;
-        let right_index = selection.rfind('~').map(|x|x as u32).unwrap_or(left_index);
-        Span { lo: BytePos(left_index), hi: BytePos(right_index + 1), expn_id: NO_EXPANSION }
-    }
-
-    // Diagnostic doesn't align properly in span where line number increases by one digit
-    #[test]
-    fn test_hilight_suggestion_issue_11715() {
-        let data = Arc::new(Mutex::new(Vec::new()));
-        let cm = Rc::new(CodeMap::new());
-        let mut ew = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone());
-        let content = "abcdefg
-        koksi
-        line3
-        line4
-        cinq
-        line6
-        line7
-        line8
-        line9
-        line10
-        e-lä-vän
-        tolv
-        dreizehn
-        ";
-        let file = cm.new_filemap_and_lines("dummy.txt", None, content);
-        let start = file.lines.borrow()[10];
-        let end = file.lines.borrow()[11];
-        let sp = mk_sp(start, end);
-        let lvl = Level::Error;
-        println!("highlight_lines");
-        ew.highlight_lines(&sp.into(), lvl).unwrap();
-        println!("done");
-        let vec = data.lock().unwrap().clone();
-        let vec: &[u8] = &vec;
-        let str = from_utf8(vec).unwrap();
-        println!("r#\"\n{}\"#", str);
-        assert_eq!(str, &r#"
-  --> dummy.txt:11:1
-   |>
-11 |>         e-lä-vän
-   |> ^
-"#[1..]);
-    }
-
-    #[test]
-    fn test_single_span_splice() {
-        // Test that a `MultiSpan` containing a single span splices a substition correctly
-        let cm = CodeMap::new();
-        let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
-        let selection = "     \n    ~~\n~~~\n~~~~~     \n   \n";
-        cm.new_filemap_and_lines("blork.rs", None, inputtext);
-        let sp = span_from_selection(inputtext, selection);
-        let msp: MultiSpan = sp.into();
-
-        // check that we are extracting the text we thought we were extracting
-        assert_eq!(&cm.span_to_snippet(sp).unwrap(), "BB\nCCC\nDDDDD");
-
-        let substitute = "ZZZZZZ".to_owned();
-        let expected = "bbbbZZZZZZddddd";
-        let suggest = CodeSuggestion {
-            msp: msp,
-            substitutes: vec![substitute],
-        };
-        assert_eq!(suggest.splice_lines(&cm), expected);
-    }
-
-    #[test]
-    fn test_multi_span_splice() {
-        // Test that a `MultiSpan` containing multiple spans splices a substition correctly
-        let cm = CodeMap::new();
-        let inputtext  = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
-        let selection1 = "     \n      \n   \n          \n ~ \n"; // intentionally out of order
-        let selection2 = "     \n    ~~\n~~~\n~~~~~     \n   \n";
-        cm.new_filemap_and_lines("blork.rs", None, inputtext);
-        let sp1 = span_from_selection(inputtext, selection1);
-        let sp2 = span_from_selection(inputtext, selection2);
-        let msp: MultiSpan = MultiSpan::from_spans(vec![sp1, sp2]);
-
-        let expected = "bbbbZZZZZZddddd\neXYZe";
-        let suggest = CodeSuggestion {
-            msp: msp,
-            substitutes: vec!["ZZZZZZ".to_owned(),
-                              "XYZ".to_owned()]
-        };
-
-        assert_eq!(suggest.splice_lines(&cm), expected);
-    }
-
-    #[test]
-    fn test_multispan_highlight() {
-        let data = Arc::new(Mutex::new(Vec::new()));
-        let cm = Rc::new(CodeMap::new());
-        let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone());
-
-        let inp =       "_____aaaaaa____bbbbbb__cccccdd_";
-        let sp1 =       "     ~~~~~~                    ";
-        let sp2 =       "               ~~~~~~          ";
-        let sp3 =       "                       ~~~~~   ";
-        let sp4 =       "                          ~~~~ ";
-        let sp34 =      "                       ~~~~~~~ ";
-
-        let expect_start = &r#"
- --> dummy.txt:1:6
-  |>
-1 |> _____aaaaaa____bbbbbb__cccccdd_
-  |>      ^^^^^^    ^^^^^^  ^^^^^^^
-"#[1..];
-
-        let span = |sp, expected| {
-            let sp = span_from_selection(inp, sp);
-            assert_eq!(&cm.span_to_snippet(sp).unwrap(), expected);
-            sp
-        };
-        cm.new_filemap_and_lines("dummy.txt", None, inp);
-        let sp1 = span(sp1, "aaaaaa");
-        let sp2 = span(sp2, "bbbbbb");
-        let sp3 = span(sp3, "ccccc");
-        let sp4 = span(sp4, "ccdd");
-        let sp34 = span(sp34, "cccccdd");
-
-        let spans = vec![sp1, sp2, sp3, sp4];
-
-        let test = |expected, highlight: &mut FnMut()| {
-            data.lock().unwrap().clear();
-            highlight();
-            let vec = data.lock().unwrap().clone();
-            let actual = from_utf8(&vec[..]).unwrap();
-            println!("actual=\n{}", actual);
-            assert_eq!(actual, expected);
-        };
-
-        let msp = MultiSpan::from_spans(vec![sp1, sp2, sp34]);
-        test(expect_start, &mut || {
-            diag.highlight_lines(&msp, Level::Error).unwrap();
-        });
-        test(expect_start, &mut || {
-            let msp = MultiSpan::from_spans(spans.clone());
-            diag.highlight_lines(&msp, Level::Error).unwrap();
-        });
-    }
-
-    #[test]
-    fn test_huge_multispan_highlight() {
-        let data = Arc::new(Mutex::new(Vec::new()));
-        let cm = Rc::new(CodeMap::new());
-        let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone());
-
-        let inp = "aaaaa\n\
-                   aaaaa\n\
-                   aaaaa\n\
-                   bbbbb\n\
-                   ccccc\n\
-                   xxxxx\n\
-                   yyyyy\n\
-                   _____\n\
-                   ddd__eee_\n\
-                   elided\n\
-                   __f_gg";
-        let file = cm.new_filemap_and_lines("dummy.txt", None, inp);
-
-        let span = |lo, hi, (off_lo, off_hi)| {
-            let lines = file.lines.borrow();
-            let (mut lo, mut hi): (BytePos, BytePos) = (lines[lo], lines[hi]);
-            lo.0 += off_lo;
-            hi.0 += off_hi;
-            mk_sp(lo, hi)
-        };
-        let sp0 = span(4, 6, (0, 5));
-        let sp1 = span(0, 6, (0, 5));
-        let sp2 = span(8, 8, (0, 3));
-        let sp3 = span(8, 8, (5, 8));
-        let sp4 = span(10, 10, (2, 3));
-        let sp5 = span(10, 10, (4, 6));
-
-        let expect0 = &r#"
-   --> dummy.txt:5:1
-    |>
-5   |> ccccc
-    |> ^
-...
-9   |> ddd__eee_
-    |> ^^^  ^^^
-10  |> elided
-11  |> __f_gg
-    |>   ^ ^^
-"#[1..];
-
-        let expect = &r#"
-   --> dummy.txt:1:1
-    |>
-1   |> aaaaa
-    |> ^
-...
-9   |> ddd__eee_
-    |> ^^^  ^^^
-10  |> elided
-11  |> __f_gg
-    |>   ^ ^^
-"#[1..];
-
-        macro_rules! test {
-            ($expected: expr, $highlight: expr) => ({
-                data.lock().unwrap().clear();
-                $highlight();
-                let vec = data.lock().unwrap().clone();
-                let actual = from_utf8(&vec[..]).unwrap();
-                println!("actual:");
-                println!("{}", actual);
-                println!("expected:");
-                println!("{}", $expected);
-                assert_eq!(&actual[..], &$expected[..]);
-            });
-        }
-
-        let msp0 = MultiSpan::from_spans(vec![sp0, sp2, sp3, sp4, sp5]);
-        let msp = MultiSpan::from_spans(vec![sp1, sp2, sp3, sp4, sp5]);
-
-        test!(expect0, || {
-            diag.highlight_lines(&msp0, Level::Error).unwrap();
-        });
-        test!(expect, || {
-            diag.highlight_lines(&msp, Level::Error).unwrap();
-        });
-    }
-}
index 59a9d5b05f9061c537d6fc6735adc6df4780611d..c472994599ccb40ee0dc659ec5fa5a0a2f12be55 100644 (file)
@@ -17,8 +17,6 @@
 use std::rc::Rc;
 use std::mem;
 
-mod test;
-
 #[derive(Clone)]
 pub struct SnippetData {
     codemap: Rc<CodeMapper>,
diff --git a/src/librustc_errors/snippet/test.rs b/src/librustc_errors/snippet/test.rs
deleted file mode 100644 (file)
index f5faccb..0000000
+++ /dev/null
@@ -1,598 +0,0 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Code for testing annotated snippets.
-
-#![cfg(test)]
-
-use codemap::{CodeMap};
-use syntax_pos::{NO_EXPANSION, Span, FileMap, BytePos};
-use std::rc::Rc;
-use super::{RenderedLine, SnippetData};
-
-/// Returns the span corresponding to the `n`th occurrence of
-/// `substring` in `source_text`.
-trait CodeMapExtension {
-    fn span_substr(&self,
-                   file: &Rc<FileMap>,
-                   source_text: &str,
-                   substring: &str,
-                   n: usize)
-                   -> Span;
-}
-
-impl CodeMapExtension for CodeMap {
-    fn span_substr(&self,
-                   file: &Rc<FileMap>,
-                   source_text: &str,
-                   substring: &str,
-                   n: usize)
-                   -> Span
-    {
-        println!("span_substr(file={:?}/{:?}, substring={:?}, n={})",
-                 file.name, file.start_pos, substring, n);
-        let mut i = 0;
-        let mut hi = 0;
-        loop {
-            let offset = source_text[hi..].find(substring).unwrap_or_else(|| {
-                panic!("source_text `{}` does not have {} occurrences of `{}`, only {}",
-                       source_text, n, substring, i);
-            });
-            let lo = hi + offset;
-            hi = lo + substring.len();
-            if i == n {
-                let span = Span {
-                    lo: BytePos(lo as u32 + file.start_pos.0),
-                    hi: BytePos(hi as u32 + file.start_pos.0),
-                    expn_id: NO_EXPANSION,
-                };
-                assert_eq!(&self.span_to_snippet(span).unwrap()[..],
-                           substring);
-                return span;
-            }
-            i += 1;
-        }
-    }
-}
-
-fn splice(start: Span, end: Span) -> Span {
-    Span {
-        lo: start.lo,
-        hi: end.hi,
-        expn_id: NO_EXPANSION,
-    }
-}
-
-fn make_string(lines: &[RenderedLine]) -> String {
-    lines.iter()
-         .flat_map(|rl| {
-             rl.text.iter()
-                    .map(|s| &s.text[..])
-                    .chain(Some("\n"))
-         })
-         .collect()
-}
-
-#[test]
-fn tab() {
-    let file_text = "
-fn foo() {
-\tbar;
-}
-";
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
-    let span_bar = cm.span_substr(&foo, file_text, "bar", 0);
-
-    let mut snippet = SnippetData::new(cm, Some(span_bar));
-    snippet.push(span_bar, true, None);
-
-    let lines = snippet.render_lines();
-    let text = make_string(&lines);
-    assert_eq!(&text[..], &"
- --> foo.rs:3:2
-  |>
-3 |> \tbar;
-  |> \t^^^
-"[1..]);
-}
-
-#[test]
-fn one_line() {
-    let file_text = r#"
-fn foo() {
-    vec.push(vec.pop().unwrap());
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
-    let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0);
-    let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1);
-    let span_semi = cm.span_substr(&foo, file_text, ";", 0);
-
-    let mut snippet = SnippetData::new(cm, None);
-    snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here")));
-    snippet.push(span_vec1, false, Some(format!("error occurs here")));
-    snippet.push(span_semi, false, Some(format!("previous borrow ends here")));
-
-    let lines = snippet.render_lines();
-    println!("{:#?}", lines);
-
-    let text: String = make_string(&lines);
-
-    println!("text=\n{}", text);
-    assert_eq!(&text[..], &r#"
- ::: foo.rs
-  |>
-3 |>     vec.push(vec.pop().unwrap());
-  |>     ---      ---                - previous borrow ends here
-  |>     |        |
-  |>     |        error occurs here
-  |>     previous borrow of `vec` occurs here
-"#[1..]);
-}
-
-#[test]
-fn two_files() {
-    let file_text_foo = r#"
-fn foo() {
-    vec.push(vec.pop().unwrap());
-}
-"#;
-
-    let file_text_bar = r#"
-fn bar() {
-    // these blank links here
-    // serve to ensure that the line numbers
-    // from bar.rs
-    // require more digits
-
-
-
-
-
-
-
-
-
-
-    vec.push();
-
-    // this line will get elided
-
-    vec.pop().unwrap());
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo_map = cm.new_filemap_and_lines("foo.rs", None, file_text_foo);
-    let span_foo_vec0 = cm.span_substr(&foo_map, file_text_foo, "vec", 0);
-    let span_foo_vec1 = cm.span_substr(&foo_map, file_text_foo, "vec", 1);
-    let span_foo_semi = cm.span_substr(&foo_map, file_text_foo, ";", 0);
-
-    let bar_map = cm.new_filemap_and_lines("bar.rs", None, file_text_bar);
-    let span_bar_vec0 = cm.span_substr(&bar_map, file_text_bar, "vec", 0);
-    let span_bar_vec1 = cm.span_substr(&bar_map, file_text_bar, "vec", 1);
-    let span_bar_semi = cm.span_substr(&bar_map, file_text_bar, ";", 0);
-
-    let mut snippet = SnippetData::new(cm, Some(span_foo_vec1));
-    snippet.push(span_foo_vec0, false, Some(format!("a")));
-    snippet.push(span_foo_vec1, true, Some(format!("b")));
-    snippet.push(span_foo_semi, false, Some(format!("c")));
-    snippet.push(span_bar_vec0, false, Some(format!("d")));
-    snippet.push(span_bar_vec1, false, Some(format!("e")));
-    snippet.push(span_bar_semi, false, Some(format!("f")));
-
-    let lines = snippet.render_lines();
-    println!("{:#?}", lines);
-
-    let text: String = make_string(&lines);
-
-    println!("text=\n{}", text);
-
-    // Note that the `|>` remain aligned across both files:
-    assert_eq!(&text[..], &r#"
-   --> foo.rs:3:14
-    |>
-3   |>     vec.push(vec.pop().unwrap());
-    |>     ---      ^^^                - c
-    |>     |        |
-    |>     |        b
-    |>     a
-   ::: bar.rs
-    |>
-17  |>     vec.push();
-    |>     ---       - f
-    |>     |
-    |>     d
-...
-21  |>     vec.pop().unwrap());
-    |>     --- e
-"#[1..]);
-}
-
-#[test]
-fn multi_line() {
-    let file_text = r#"
-fn foo() {
-    let name = find_id(&data, 22).unwrap();
-
-    // Add one more item we forgot to the vector. Silly us.
-    data.push(Data { name: format!("Hera"), id: 66 });
-
-    // Print everything out.
-    println!("Name: {:?}", name);
-    println!("Data: {:?}", data);
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
-    let span_data0 = cm.span_substr(&foo, file_text, "data", 0);
-    let span_data1 = cm.span_substr(&foo, file_text, "data", 1);
-    let span_rbrace = cm.span_substr(&foo, file_text, "}", 3);
-
-    let mut snippet = SnippetData::new(cm, None);
-    snippet.push(span_data0, false, Some(format!("immutable borrow begins here")));
-    snippet.push(span_data1, false, Some(format!("mutable borrow occurs here")));
-    snippet.push(span_rbrace, false, Some(format!("immutable borrow ends here")));
-
-    let lines = snippet.render_lines();
-    println!("{:#?}", lines);
-
-    let text: String = make_string(&lines);
-
-    println!("text=\n{}", text);
-    assert_eq!(&text[..], &r#"
-   ::: foo.rs
-    |>
-3   |>     let name = find_id(&data, 22).unwrap();
-    |>                         ---- immutable borrow begins here
-...
-6   |>     data.push(Data { name: format!("Hera"), id: 66 });
-    |>     ---- mutable borrow occurs here
-...
-11  |> }
-    |> - immutable borrow ends here
-"#[1..]);
-}
-
-#[test]
-fn overlapping() {
-    let file_text = r#"
-fn foo() {
-    vec.push(vec.pop().unwrap());
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
-    let span0 = cm.span_substr(&foo, file_text, "vec.push", 0);
-    let span1 = cm.span_substr(&foo, file_text, "vec", 0);
-    let span2 = cm.span_substr(&foo, file_text, "ec.push", 0);
-    let span3 = cm.span_substr(&foo, file_text, "unwrap", 0);
-
-    let mut snippet = SnippetData::new(cm, None);
-    snippet.push(span0, false, Some(format!("A")));
-    snippet.push(span1, false, Some(format!("B")));
-    snippet.push(span2, false, Some(format!("C")));
-    snippet.push(span3, false, Some(format!("D")));
-
-    let lines = snippet.render_lines();
-    println!("{:#?}", lines);
-    let text: String = make_string(&lines);
-
-    println!("text=r#\"\n{}\".trim_left()", text);
-    assert_eq!(&text[..], &r#"
- ::: foo.rs
-  |>
-3 |>     vec.push(vec.pop().unwrap());
-  |>     --------           ------ D
-  |>     ||
-  |>     |C
-  |>     A
-  |>     B
-"#[1..]);
-}
-
-#[test]
-fn one_line_out_of_order() {
-    let file_text = r#"
-fn foo() {
-    vec.push(vec.pop().unwrap());
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
-    let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0);
-    let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1);
-    let span_semi = cm.span_substr(&foo, file_text, ";", 0);
-
-    // intentionally don't push the snippets left to right
-    let mut snippet = SnippetData::new(cm, None);
-    snippet.push(span_vec1, false, Some(format!("error occurs here")));
-    snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here")));
-    snippet.push(span_semi, false, Some(format!("previous borrow ends here")));
-
-    let lines = snippet.render_lines();
-    println!("{:#?}", lines);
-    let text: String = make_string(&lines);
-
-    println!("text=r#\"\n{}\".trim_left()", text);
-    assert_eq!(&text[..], &r#"
- ::: foo.rs
-  |>
-3 |>     vec.push(vec.pop().unwrap());
-  |>     ---      ---                - previous borrow ends here
-  |>     |        |
-  |>     |        error occurs here
-  |>     previous borrow of `vec` occurs here
-"#[1..]);
-}
-
-#[test]
-fn elide_unnecessary_lines() {
-    let file_text = r#"
-fn foo() {
-    let mut vec = vec![0, 1, 2];
-    let mut vec2 = vec;
-    vec2.push(3);
-    vec2.push(4);
-    vec2.push(5);
-    vec2.push(6);
-    vec.push(7);
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
-    let span_vec0 = cm.span_substr(&foo, file_text, "vec", 3);
-    let span_vec1 = cm.span_substr(&foo, file_text, "vec", 8);
-
-    let mut snippet = SnippetData::new(cm, None);
-    snippet.push(span_vec0, false, Some(format!("`vec` moved here because it \
-        has type `collections::vec::Vec<i32>`")));
-    snippet.push(span_vec1, false, Some(format!("use of moved value: `vec`")));
-
-    let lines = snippet.render_lines();
-    println!("{:#?}", lines);
-    let text: String = make_string(&lines);
-    println!("text=r#\"\n{}\".trim_left()", text);
-    assert_eq!(&text[..], &r#"
-   ::: foo.rs
-    |>
-4   |>     let mut vec2 = vec;
-    |>                    --- `vec` moved here because it has type `collections::vec::Vec<i32>`
-...
-9   |>     vec.push(7);
-    |>     --- use of moved value: `vec`
-"#[1..]);
-}
-
-#[test]
-fn spans_without_labels() {
-    let file_text = r#"
-fn foo() {
-    let mut vec = vec![0, 1, 2];
-    let mut vec2 = vec;
-    vec2.push(3);
-    vec2.push(4);
-    vec2.push(5);
-    vec2.push(6);
-    vec.push(7);
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
-
-    let mut snippet = SnippetData::new(cm.clone(), None);
-    for i in 0..4 {
-        let span_veci = cm.span_substr(&foo, file_text, "vec", i);
-        snippet.push(span_veci, false, None);
-    }
-
-    let lines = snippet.render_lines();
-    let text: String = make_string(&lines);
-    println!("text=&r#\"\n{}\n\"#[1..]", text);
-    assert_eq!(text, &r#"
- ::: foo.rs
-  |>
-3 |>     let mut vec = vec![0, 1, 2];
-  |>             ---   ---
-4 |>     let mut vec2 = vec;
-  |>             ---    ---
-"#[1..]);
-}
-
-#[test]
-fn span_long_selection() {
-    let file_text = r#"
-impl SomeTrait for () {
-    fn foo(x: u32) {
-        // impl 1
-        // impl 2
-        // impl 3
-    }
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
-
-    let mut snippet = SnippetData::new(cm.clone(), None);
-    let fn_span = cm.span_substr(&foo, file_text, "fn", 0);
-    let rbrace_span = cm.span_substr(&foo, file_text, "}", 0);
-    snippet.push(splice(fn_span, rbrace_span), false, None);
-    let lines = snippet.render_lines();
-    let text: String = make_string(&lines);
-    println!("r#\"\n{}\"", text);
-    assert_eq!(text, &r#"
- ::: foo.rs
-  |>
-3 |>     fn foo(x: u32) {
-  |>     -
-"#[1..]);
-}
-
-#[test]
-fn span_overlap_label() {
-    // Test that we don't put `x_span` to the right of its highlight,
-    // since there is another highlight that overlaps it.
-
-    let file_text = r#"
-    fn foo(x: u32) {
-    }
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
-
-    let mut snippet = SnippetData::new(cm.clone(), None);
-    let fn_span = cm.span_substr(&foo, file_text, "fn foo(x: u32)", 0);
-    let x_span = cm.span_substr(&foo, file_text, "x", 0);
-    snippet.push(fn_span, false, Some(format!("fn_span")));
-    snippet.push(x_span, false, Some(format!("x_span")));
-    let lines = snippet.render_lines();
-    let text: String = make_string(&lines);
-    println!("r#\"\n{}\"", text);
-    assert_eq!(text, &r#"
- ::: foo.rs
-  |>
-2 |>     fn foo(x: u32) {
-  |>     --------------
-  |>     |      |
-  |>     |      x_span
-  |>     fn_span
-"#[1..]);
-}
-
-#[test]
-fn span_overlap_label2() {
-    // Test that we don't put `x_span` to the right of its highlight,
-    // since there is another highlight that overlaps it. In this
-    // case, the overlap is only at the beginning, but it's still
-    // better to show the beginning more clearly.
-
-    let file_text = r#"
-    fn foo(x: u32) {
-    }
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
-
-    let mut snippet = SnippetData::new(cm.clone(), None);
-    let fn_span = cm.span_substr(&foo, file_text, "fn foo(x", 0);
-    let x_span = cm.span_substr(&foo, file_text, "x: u32)", 0);
-    snippet.push(fn_span, false, Some(format!("fn_span")));
-    snippet.push(x_span, false, Some(format!("x_span")));
-    let lines = snippet.render_lines();
-    let text: String = make_string(&lines);
-    println!("r#\"\n{}\"", text);
-    assert_eq!(text, &r#"
- ::: foo.rs
-  |>
-2 |>     fn foo(x: u32) {
-  |>     --------------
-  |>     |      |
-  |>     |      x_span
-  |>     fn_span
-"#[1..]);
-}
-
-#[test]
-fn span_overlap_label3() {
-    // Test that we don't put `x_span` to the right of its highlight,
-    // since there is another highlight that overlaps it. In this
-    // case, the overlap is only at the beginning, but it's still
-    // better to show the beginning more clearly.
-
-    let file_text = r#"
-    fn foo() {
-       let closure = || {
-           inner
-       };
-    }
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
-
-    let mut snippet = SnippetData::new(cm.clone(), None);
-
-    let closure_span = {
-        let closure_start_span = cm.span_substr(&foo, file_text, "||", 0);
-        let closure_end_span = cm.span_substr(&foo, file_text, "}", 0);
-        splice(closure_start_span, closure_end_span)
-    };
-
-    let inner_span = cm.span_substr(&foo, file_text, "inner", 0);
-
-    snippet.push(closure_span, false, Some(format!("foo")));
-    snippet.push(inner_span, false, Some(format!("bar")));
-
-    let lines = snippet.render_lines();
-    let text: String = make_string(&lines);
-    println!("r#\"\n{}\"", text);
-    assert_eq!(text, &r#"
- ::: foo.rs
-  |>
-3 |>        let closure = || {
-  |>                      - foo
-4 |>            inner
-  |>            ----- bar
-"#[1..]);
-}
-
-#[test]
-fn span_empty() {
-    // In one of the unit tests, we found that the parser sometimes
-    // gives empty spans, and in particular it supplied an EOF span
-    // like this one, which points at the very end. We want to
-    // fallback gracefully in this case.
-
-    let file_text = r#"
-fn main() {
-    struct Foo;
-
-    impl !Sync for Foo {}
-
-    unsafe impl Send for &'static Foo {
-    // error: cross-crate traits with a default impl, like `core::marker::Send`,
-    //        can only be implemented for a struct/enum type, not
-    //        `&'static Foo`
-}"#;
-
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
-
-    let mut rbrace_span = cm.span_substr(&foo, file_text, "}", 1);
-    rbrace_span.lo = rbrace_span.hi;
-
-    let mut snippet = SnippetData::new(cm.clone(), Some(rbrace_span));
-    snippet.push(rbrace_span, false, None);
-    let lines = snippet.render_lines();
-    let text: String = make_string(&lines);
-    println!("r#\"\n{}\"", text);
-    assert_eq!(text, &r#"
-  --> foo.rs:11:2
-   |>
-11 |> }
-   |>  -
-"#[1..]);
-}
index 2188dc36ec84a8266dad90b7ce6ceaa70900a502..cfa13f7f95ad2f8f486c6732717eace425155ea8 100644 (file)
@@ -828,6 +828,12 @@ fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace> {
 mod tests {
     use super::*;
     use syntax_pos::*;
+    use errors::{Level, CodeSuggestion};
+    use errors::emitter::EmitterWriter;
+    use std::sync::{Arc, Mutex};
+    use std::io::{self, Write};
+    use std::str::from_utf8;
+    use std::rc::Rc;
 
     #[test]
     fn t1 () {
@@ -1150,4 +1156,233 @@ fn t12() {
 ";
         assert_eq!(sstr, res_str);
     }
+
+    struct Sink(Arc<Mutex<Vec<u8>>>);
+    impl Write for Sink {
+        fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+            Write::write(&mut *self.0.lock().unwrap(), data)
+        }
+        fn flush(&mut self) -> io::Result<()> { Ok(()) }
+    }
+
+    // Diagnostic doesn't align properly in span where line number increases by one digit
+    #[test]
+    fn test_hilight_suggestion_issue_11715() {
+        let data = Arc::new(Mutex::new(Vec::new()));
+        let cm = Rc::new(CodeMap::new());
+        let mut ew = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone());
+        let content = "abcdefg
+        koksi
+        line3
+        line4
+        cinq
+        line6
+        line7
+        line8
+        line9
+        line10
+        e-lä-vän
+        tolv
+        dreizehn
+        ";
+        let file = cm.new_filemap_and_lines("dummy.txt", None, content);
+        let start = file.lines.borrow()[10];
+        let end = file.lines.borrow()[11];
+        let sp = mk_sp(start, end);
+        let lvl = Level::Error;
+        println!("highlight_lines");
+        ew.highlight_lines(&sp.into(), lvl).unwrap();
+        println!("done");
+        let vec = data.lock().unwrap().clone();
+        let vec: &[u8] = &vec;
+        let str = from_utf8(vec).unwrap();
+        println!("r#\"\n{}\"#", str);
+        assert_eq!(str, &r#"
+  --> dummy.txt:11:1
+   |>
+11 |>         e-lä-vän
+   |> ^
+"#[1..]);
+    }
+
+    #[test]
+    fn test_single_span_splice() {
+        // Test that a `MultiSpan` containing a single span splices a substition correctly
+        let cm = CodeMap::new();
+        let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
+        let selection = "     \n    ~~\n~~~\n~~~~~     \n   \n";
+        cm.new_filemap_and_lines("blork.rs", None, inputtext);
+        let sp = span_from_selection(inputtext, selection);
+        let msp: MultiSpan = sp.into();
+
+        // check that we are extracting the text we thought we were extracting
+        assert_eq!(&cm.span_to_snippet(sp).unwrap(), "BB\nCCC\nDDDDD");
+
+        let substitute = "ZZZZZZ".to_owned();
+        let expected = "bbbbZZZZZZddddd";
+        let suggest = CodeSuggestion {
+            msp: msp,
+            substitutes: vec![substitute],
+        };
+        assert_eq!(suggest.splice_lines(&cm), expected);
+    }
+
+    #[test]
+    fn test_multi_span_splice() {
+        // Test that a `MultiSpan` containing multiple spans splices a substition correctly
+        let cm = CodeMap::new();
+        let inputtext  = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
+        let selection1 = "     \n      \n   \n          \n ~ \n"; // intentionally out of order
+        let selection2 = "     \n    ~~\n~~~\n~~~~~     \n   \n";
+        cm.new_filemap_and_lines("blork.rs", None, inputtext);
+        let sp1 = span_from_selection(inputtext, selection1);
+        let sp2 = span_from_selection(inputtext, selection2);
+        let msp: MultiSpan = MultiSpan::from_spans(vec![sp1, sp2]);
+
+        let expected = "bbbbZZZZZZddddd\neXYZe";
+        let suggest = CodeSuggestion {
+            msp: msp,
+            substitutes: vec!["ZZZZZZ".to_owned(),
+                              "XYZ".to_owned()]
+        };
+
+        assert_eq!(suggest.splice_lines(&cm), expected);
+    }
+
+    #[test]
+    fn test_multispan_highlight() {
+        let data = Arc::new(Mutex::new(Vec::new()));
+        let cm = Rc::new(CodeMap::new());
+        let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone());
+
+        let inp =       "_____aaaaaa____bbbbbb__cccccdd_";
+        let sp1 =       "     ~~~~~~                    ";
+        let sp2 =       "               ~~~~~~          ";
+        let sp3 =       "                       ~~~~~   ";
+        let sp4 =       "                          ~~~~ ";
+        let sp34 =      "                       ~~~~~~~ ";
+
+        let expect_start = &r#"
+ --> dummy.txt:1:6
+  |>
+1 |> _____aaaaaa____bbbbbb__cccccdd_
+  |>      ^^^^^^    ^^^^^^  ^^^^^^^
+"#[1..];
+
+        let span = |sp, expected| {
+            let sp = span_from_selection(inp, sp);
+            assert_eq!(&cm.span_to_snippet(sp).unwrap(), expected);
+            sp
+        };
+        cm.new_filemap_and_lines("dummy.txt", None, inp);
+        let sp1 = span(sp1, "aaaaaa");
+        let sp2 = span(sp2, "bbbbbb");
+        let sp3 = span(sp3, "ccccc");
+        let sp4 = span(sp4, "ccdd");
+        let sp34 = span(sp34, "cccccdd");
+
+        let spans = vec![sp1, sp2, sp3, sp4];
+
+        let test = |expected, highlight: &mut FnMut()| {
+            data.lock().unwrap().clear();
+            highlight();
+            let vec = data.lock().unwrap().clone();
+            let actual = from_utf8(&vec[..]).unwrap();
+            println!("actual=\n{}", actual);
+            assert_eq!(actual, expected);
+        };
+
+        let msp = MultiSpan::from_spans(vec![sp1, sp2, sp34]);
+        test(expect_start, &mut || {
+            diag.highlight_lines(&msp, Level::Error).unwrap();
+        });
+        test(expect_start, &mut || {
+            let msp = MultiSpan::from_spans(spans.clone());
+            diag.highlight_lines(&msp, Level::Error).unwrap();
+        });
+    }
+
+    #[test]
+    fn test_huge_multispan_highlight() {
+        let data = Arc::new(Mutex::new(Vec::new()));
+        let cm = Rc::new(CodeMap::new());
+        let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone());
+
+        let inp = "aaaaa\n\
+                   aaaaa\n\
+                   aaaaa\n\
+                   bbbbb\n\
+                   ccccc\n\
+                   xxxxx\n\
+                   yyyyy\n\
+                   _____\n\
+                   ddd__eee_\n\
+                   elided\n\
+                   __f_gg";
+        let file = cm.new_filemap_and_lines("dummy.txt", None, inp);
+
+        let span = |lo, hi, (off_lo, off_hi)| {
+            let lines = file.lines.borrow();
+            let (mut lo, mut hi): (BytePos, BytePos) = (lines[lo], lines[hi]);
+            lo.0 += off_lo;
+            hi.0 += off_hi;
+            mk_sp(lo, hi)
+        };
+        let sp0 = span(4, 6, (0, 5));
+        let sp1 = span(0, 6, (0, 5));
+        let sp2 = span(8, 8, (0, 3));
+        let sp3 = span(8, 8, (5, 8));
+        let sp4 = span(10, 10, (2, 3));
+        let sp5 = span(10, 10, (4, 6));
+
+        let expect0 = &r#"
+   --> dummy.txt:5:1
+    |>
+5   |> ccccc
+    |> ^
+...
+9   |> ddd__eee_
+    |> ^^^  ^^^
+10  |> elided
+11  |> __f_gg
+    |>   ^ ^^
+"#[1..];
+
+        let expect = &r#"
+   --> dummy.txt:1:1
+    |>
+1   |> aaaaa
+    |> ^
+...
+9   |> ddd__eee_
+    |> ^^^  ^^^
+10  |> elided
+11  |> __f_gg
+    |>   ^ ^^
+"#[1..];
+
+        macro_rules! test {
+            ($expected: expr, $highlight: expr) => ({
+                data.lock().unwrap().clear();
+                $highlight();
+                let vec = data.lock().unwrap().clone();
+                let actual = from_utf8(&vec[..]).unwrap();
+                println!("actual:");
+                println!("{}", actual);
+                println!("expected:");
+                println!("{}", $expected);
+                assert_eq!(&actual[..], &$expected[..]);
+            });
+        }
+
+        let msp0 = MultiSpan::from_spans(vec![sp0, sp2, sp3, sp4, sp5]);
+        let msp = MultiSpan::from_spans(vec![sp1, sp2, sp3, sp4, sp5]);
+
+        test!(expect0, || {
+            diag.highlight_lines(&msp0, Level::Error).unwrap();
+        });
+        test!(expect, || {
+            diag.highlight_lines(&msp, Level::Error).unwrap();
+        });
+    }
 }
index b80c01853dc3f8963ec5a4db75c0b18886334d30..2572ac35e9861946673be55377283ed8ae389094 100644 (file)
@@ -12,6 +12,7 @@
 
 #![allow(dead_code)]
 #![allow(unused_imports)]
+
 use self::HasTestSignature::*;
 
 use std::iter;
 use std::vec;
 use attr::AttrMetaMethods;
 use attr;
-use syntax_pos::{self, DUMMY_SP, Span};
-use codemap::{self, ExpnInfo, NameAndSpan, MacroAttribute};
+use syntax_pos::{self, DUMMY_SP, NO_EXPANSION, Span, FileMap, BytePos};
+use std::rc::Rc;
+
+use codemap::{self, CodeMap, ExpnInfo, NameAndSpan, MacroAttribute};
 use errors;
+use errors::snippet::{RenderedLine, SnippetData};
 use config;
 use entry::{self, EntryPointType};
 use ext::base::{ExtCtxt, DummyMacroLoader};
@@ -688,3 +692,583 @@ fn mk_test_desc_and_fn_rec(cx: &TestCtxt, test: &Test) -> P<ast::Expr> {
                     vec![field("desc", desc_expr),
                          field("testfn", testfn_expr)])
 }
+
+/// Returns the span corresponding to the `n`th occurrence of
+/// `substring` in `source_text`.
+trait CodeMapExtension {
+    fn span_substr(&self,
+                   file: &Rc<FileMap>,
+                   source_text: &str,
+                   substring: &str,
+                   n: usize)
+                   -> Span;
+}
+
+impl CodeMapExtension for CodeMap {
+    fn span_substr(&self,
+                   file: &Rc<FileMap>,
+                   source_text: &str,
+                   substring: &str,
+                   n: usize)
+                   -> Span
+    {
+        println!("span_substr(file={:?}/{:?}, substring={:?}, n={})",
+                 file.name, file.start_pos, substring, n);
+        let mut i = 0;
+        let mut hi = 0;
+        loop {
+            let offset = source_text[hi..].find(substring).unwrap_or_else(|| {
+                panic!("source_text `{}` does not have {} occurrences of `{}`, only {}",
+                       source_text, n, substring, i);
+            });
+            let lo = hi + offset;
+            hi = lo + substring.len();
+            if i == n {
+                let span = Span {
+                    lo: BytePos(lo as u32 + file.start_pos.0),
+                    hi: BytePos(hi as u32 + file.start_pos.0),
+                    expn_id: NO_EXPANSION,
+                };
+                assert_eq!(&self.span_to_snippet(span).unwrap()[..],
+                           substring);
+                return span;
+            }
+            i += 1;
+        }
+    }
+}
+
+fn splice(start: Span, end: Span) -> Span {
+    Span {
+        lo: start.lo,
+        hi: end.hi,
+        expn_id: NO_EXPANSION,
+    }
+}
+
+fn make_string(lines: &[RenderedLine]) -> String {
+    lines.iter()
+         .flat_map(|rl| {
+             rl.text.iter()
+                    .map(|s| &s.text[..])
+                    .chain(Some("\n"))
+         })
+         .collect()
+}
+
+#[test]
+fn tab() {
+    let file_text = "
+fn foo() {
+\tbar;
+}
+";
+
+    let cm = Rc::new(CodeMap::new());
+    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+    let span_bar = cm.span_substr(&foo, file_text, "bar", 0);
+
+    let mut snippet = SnippetData::new(cm, Some(span_bar));
+    snippet.push(span_bar, true, None);
+
+    let lines = snippet.render_lines();
+    let text = make_string(&lines);
+    assert_eq!(&text[..], &"
+ --> foo.rs:3:2
+  |>
+3 |> \tbar;
+  |> \t^^^
+"[1..]);
+}
+
+#[test]
+fn one_line() {
+    let file_text = r#"
+fn foo() {
+    vec.push(vec.pop().unwrap());
+}
+"#;
+
+    let cm = Rc::new(CodeMap::new());
+    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+    let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0);
+    let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1);
+    let span_semi = cm.span_substr(&foo, file_text, ";", 0);
+
+    let mut snippet = SnippetData::new(cm, None);
+    snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here")));
+    snippet.push(span_vec1, false, Some(format!("error occurs here")));
+    snippet.push(span_semi, false, Some(format!("previous borrow ends here")));
+
+    let lines = snippet.render_lines();
+    println!("{:#?}", lines);
+
+    let text: String = make_string(&lines);
+
+    println!("text=\n{}", text);
+    assert_eq!(&text[..], &r#"
+ ::: foo.rs
+  |>
+3 |>     vec.push(vec.pop().unwrap());
+  |>     ---      ---                - previous borrow ends here
+  |>     |        |
+  |>     |        error occurs here
+  |>     previous borrow of `vec` occurs here
+"#[1..]);
+}
+
+#[test]
+fn two_files() {
+    let file_text_foo = r#"
+fn foo() {
+    vec.push(vec.pop().unwrap());
+}
+"#;
+
+    let file_text_bar = r#"
+fn bar() {
+    // these blank links here
+    // serve to ensure that the line numbers
+    // from bar.rs
+    // require more digits
+
+
+
+
+
+
+
+
+
+
+    vec.push();
+
+    // this line will get elided
+
+    vec.pop().unwrap());
+}
+"#;
+
+    let cm = Rc::new(CodeMap::new());
+    let foo_map = cm.new_filemap_and_lines("foo.rs", None, file_text_foo);
+    let span_foo_vec0 = cm.span_substr(&foo_map, file_text_foo, "vec", 0);
+    let span_foo_vec1 = cm.span_substr(&foo_map, file_text_foo, "vec", 1);
+    let span_foo_semi = cm.span_substr(&foo_map, file_text_foo, ";", 0);
+
+    let bar_map = cm.new_filemap_and_lines("bar.rs", None, file_text_bar);
+    let span_bar_vec0 = cm.span_substr(&bar_map, file_text_bar, "vec", 0);
+    let span_bar_vec1 = cm.span_substr(&bar_map, file_text_bar, "vec", 1);
+    let span_bar_semi = cm.span_substr(&bar_map, file_text_bar, ";", 0);
+
+    let mut snippet = SnippetData::new(cm, Some(span_foo_vec1));
+    snippet.push(span_foo_vec0, false, Some(format!("a")));
+    snippet.push(span_foo_vec1, true, Some(format!("b")));
+    snippet.push(span_foo_semi, false, Some(format!("c")));
+    snippet.push(span_bar_vec0, false, Some(format!("d")));
+    snippet.push(span_bar_vec1, false, Some(format!("e")));
+    snippet.push(span_bar_semi, false, Some(format!("f")));
+
+    let lines = snippet.render_lines();
+    println!("{:#?}", lines);
+
+    let text: String = make_string(&lines);
+
+    println!("text=\n{}", text);
+
+    // Note that the `|>` remain aligned across both files:
+    assert_eq!(&text[..], &r#"
+   --> foo.rs:3:14
+    |>
+3   |>     vec.push(vec.pop().unwrap());
+    |>     ---      ^^^                - c
+    |>     |        |
+    |>     |        b
+    |>     a
+   ::: bar.rs
+    |>
+17  |>     vec.push();
+    |>     ---       - f
+    |>     |
+    |>     d
+...
+21  |>     vec.pop().unwrap());
+    |>     --- e
+"#[1..]);
+}
+
+#[test]
+fn multi_line() {
+    let file_text = r#"
+fn foo() {
+    let name = find_id(&data, 22).unwrap();
+
+    // Add one more item we forgot to the vector. Silly us.
+    data.push(Data { name: format!("Hera"), id: 66 });
+
+    // Print everything out.
+    println!("Name: {:?}", name);
+    println!("Data: {:?}", data);
+}
+"#;
+
+    let cm = Rc::new(CodeMap::new());
+    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+    let span_data0 = cm.span_substr(&foo, file_text, "data", 0);
+    let span_data1 = cm.span_substr(&foo, file_text, "data", 1);
+    let span_rbrace = cm.span_substr(&foo, file_text, "}", 3);
+
+    let mut snippet = SnippetData::new(cm, None);
+    snippet.push(span_data0, false, Some(format!("immutable borrow begins here")));
+    snippet.push(span_data1, false, Some(format!("mutable borrow occurs here")));
+    snippet.push(span_rbrace, false, Some(format!("immutable borrow ends here")));
+
+    let lines = snippet.render_lines();
+    println!("{:#?}", lines);
+
+    let text: String = make_string(&lines);
+
+    println!("text=\n{}", text);
+    assert_eq!(&text[..], &r#"
+   ::: foo.rs
+    |>
+3   |>     let name = find_id(&data, 22).unwrap();
+    |>                         ---- immutable borrow begins here
+...
+6   |>     data.push(Data { name: format!("Hera"), id: 66 });
+    |>     ---- mutable borrow occurs here
+...
+11  |> }
+    |> - immutable borrow ends here
+"#[1..]);
+}
+
+#[test]
+fn overlapping() {
+    let file_text = r#"
+fn foo() {
+    vec.push(vec.pop().unwrap());
+}
+"#;
+
+    let cm = Rc::new(CodeMap::new());
+    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+    let span0 = cm.span_substr(&foo, file_text, "vec.push", 0);
+    let span1 = cm.span_substr(&foo, file_text, "vec", 0);
+    let span2 = cm.span_substr(&foo, file_text, "ec.push", 0);
+    let span3 = cm.span_substr(&foo, file_text, "unwrap", 0);
+
+    let mut snippet = SnippetData::new(cm, None);
+    snippet.push(span0, false, Some(format!("A")));
+    snippet.push(span1, false, Some(format!("B")));
+    snippet.push(span2, false, Some(format!("C")));
+    snippet.push(span3, false, Some(format!("D")));
+
+    let lines = snippet.render_lines();
+    println!("{:#?}", lines);
+    let text: String = make_string(&lines);
+
+    println!("text=r#\"\n{}\".trim_left()", text);
+    assert_eq!(&text[..], &r#"
+ ::: foo.rs
+  |>
+3 |>     vec.push(vec.pop().unwrap());
+  |>     --------           ------ D
+  |>     ||
+  |>     |C
+  |>     A
+  |>     B
+"#[1..]);
+}
+
+#[test]
+fn one_line_out_of_order() {
+    let file_text = r#"
+fn foo() {
+    vec.push(vec.pop().unwrap());
+}
+"#;
+
+    let cm = Rc::new(CodeMap::new());
+    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+    let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0);
+    let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1);
+    let span_semi = cm.span_substr(&foo, file_text, ";", 0);
+
+    // intentionally don't push the snippets left to right
+    let mut snippet = SnippetData::new(cm, None);
+    snippet.push(span_vec1, false, Some(format!("error occurs here")));
+    snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here")));
+    snippet.push(span_semi, false, Some(format!("previous borrow ends here")));
+
+    let lines = snippet.render_lines();
+    println!("{:#?}", lines);
+    let text: String = make_string(&lines);
+
+    println!("text=r#\"\n{}\".trim_left()", text);
+    assert_eq!(&text[..], &r#"
+ ::: foo.rs
+  |>
+3 |>     vec.push(vec.pop().unwrap());
+  |>     ---      ---                - previous borrow ends here
+  |>     |        |
+  |>     |        error occurs here
+  |>     previous borrow of `vec` occurs here
+"#[1..]);
+}
+
+#[test]
+fn elide_unnecessary_lines() {
+    let file_text = r#"
+fn foo() {
+    let mut vec = vec![0, 1, 2];
+    let mut vec2 = vec;
+    vec2.push(3);
+    vec2.push(4);
+    vec2.push(5);
+    vec2.push(6);
+    vec.push(7);
+}
+"#;
+
+    let cm = Rc::new(CodeMap::new());
+    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+    let span_vec0 = cm.span_substr(&foo, file_text, "vec", 3);
+    let span_vec1 = cm.span_substr(&foo, file_text, "vec", 8);
+
+    let mut snippet = SnippetData::new(cm, None);
+    snippet.push(span_vec0, false, Some(format!("`vec` moved here because it \
+        has type `collections::vec::Vec<i32>`")));
+    snippet.push(span_vec1, false, Some(format!("use of moved value: `vec`")));
+
+    let lines = snippet.render_lines();
+    println!("{:#?}", lines);
+    let text: String = make_string(&lines);
+    println!("text=r#\"\n{}\".trim_left()", text);
+    assert_eq!(&text[..], &r#"
+   ::: foo.rs
+    |>
+4   |>     let mut vec2 = vec;
+    |>                    --- `vec` moved here because it has type `collections::vec::Vec<i32>`
+...
+9   |>     vec.push(7);
+    |>     --- use of moved value: `vec`
+"#[1..]);
+}
+
+#[test]
+fn spans_without_labels() {
+    let file_text = r#"
+fn foo() {
+    let mut vec = vec![0, 1, 2];
+    let mut vec2 = vec;
+    vec2.push(3);
+    vec2.push(4);
+    vec2.push(5);
+    vec2.push(6);
+    vec.push(7);
+}
+"#;
+
+    let cm = Rc::new(CodeMap::new());
+    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+
+    let mut snippet = SnippetData::new(cm.clone(), None);
+    for i in 0..4 {
+        let span_veci = cm.span_substr(&foo, file_text, "vec", i);
+        snippet.push(span_veci, false, None);
+    }
+
+    let lines = snippet.render_lines();
+    let text: String = make_string(&lines);
+    println!("text=&r#\"\n{}\n\"#[1..]", text);
+    assert_eq!(text, &r#"
+ ::: foo.rs
+  |>
+3 |>     let mut vec = vec![0, 1, 2];
+  |>             ---   ---
+4 |>     let mut vec2 = vec;
+  |>             ---    ---
+"#[1..]);
+}
+
+#[test]
+fn span_long_selection() {
+    let file_text = r#"
+impl SomeTrait for () {
+    fn foo(x: u32) {
+        // impl 1
+        // impl 2
+        // impl 3
+    }
+}
+"#;
+
+    let cm = Rc::new(CodeMap::new());
+    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+
+    let mut snippet = SnippetData::new(cm.clone(), None);
+    let fn_span = cm.span_substr(&foo, file_text, "fn", 0);
+    let rbrace_span = cm.span_substr(&foo, file_text, "}", 0);
+    snippet.push(splice(fn_span, rbrace_span), false, None);
+    let lines = snippet.render_lines();
+    let text: String = make_string(&lines);
+    println!("r#\"\n{}\"", text);
+    assert_eq!(text, &r#"
+ ::: foo.rs
+  |>
+3 |>     fn foo(x: u32) {
+  |>     -
+"#[1..]);
+}
+
+#[test]
+fn span_overlap_label() {
+    // Test that we don't put `x_span` to the right of its highlight,
+    // since there is another highlight that overlaps it.
+
+    let file_text = r#"
+    fn foo(x: u32) {
+    }
+}
+"#;
+
+    let cm = Rc::new(CodeMap::new());
+    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+
+    let mut snippet = SnippetData::new(cm.clone(), None);
+    let fn_span = cm.span_substr(&foo, file_text, "fn foo(x: u32)", 0);
+    let x_span = cm.span_substr(&foo, file_text, "x", 0);
+    snippet.push(fn_span, false, Some(format!("fn_span")));
+    snippet.push(x_span, false, Some(format!("x_span")));
+    let lines = snippet.render_lines();
+    let text: String = make_string(&lines);
+    println!("r#\"\n{}\"", text);
+    assert_eq!(text, &r#"
+ ::: foo.rs
+  |>
+2 |>     fn foo(x: u32) {
+  |>     --------------
+  |>     |      |
+  |>     |      x_span
+  |>     fn_span
+"#[1..]);
+}
+
+#[test]
+fn span_overlap_label2() {
+    // Test that we don't put `x_span` to the right of its highlight,
+    // since there is another highlight that overlaps it. In this
+    // case, the overlap is only at the beginning, but it's still
+    // better to show the beginning more clearly.
+
+    let file_text = r#"
+    fn foo(x: u32) {
+    }
+}
+"#;
+
+    let cm = Rc::new(CodeMap::new());
+    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+
+    let mut snippet = SnippetData::new(cm.clone(), None);
+    let fn_span = cm.span_substr(&foo, file_text, "fn foo(x", 0);
+    let x_span = cm.span_substr(&foo, file_text, "x: u32)", 0);
+    snippet.push(fn_span, false, Some(format!("fn_span")));
+    snippet.push(x_span, false, Some(format!("x_span")));
+    let lines = snippet.render_lines();
+    let text: String = make_string(&lines);
+    println!("r#\"\n{}\"", text);
+    assert_eq!(text, &r#"
+ ::: foo.rs
+  |>
+2 |>     fn foo(x: u32) {
+  |>     --------------
+  |>     |      |
+  |>     |      x_span
+  |>     fn_span
+"#[1..]);
+}
+
+#[test]
+fn span_overlap_label3() {
+    // Test that we don't put `x_span` to the right of its highlight,
+    // since there is another highlight that overlaps it. In this
+    // case, the overlap is only at the beginning, but it's still
+    // better to show the beginning more clearly.
+
+    let file_text = r#"
+    fn foo() {
+       let closure = || {
+           inner
+       };
+    }
+}
+"#;
+
+    let cm = Rc::new(CodeMap::new());
+    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+
+    let mut snippet = SnippetData::new(cm.clone(), None);
+
+    let closure_span = {
+        let closure_start_span = cm.span_substr(&foo, file_text, "||", 0);
+        let closure_end_span = cm.span_substr(&foo, file_text, "}", 0);
+        splice(closure_start_span, closure_end_span)
+    };
+
+    let inner_span = cm.span_substr(&foo, file_text, "inner", 0);
+
+    snippet.push(closure_span, false, Some(format!("foo")));
+    snippet.push(inner_span, false, Some(format!("bar")));
+
+    let lines = snippet.render_lines();
+    let text: String = make_string(&lines);
+    println!("r#\"\n{}\"", text);
+    assert_eq!(text, &r#"
+ ::: foo.rs
+  |>
+3 |>        let closure = || {
+  |>                      - foo
+4 |>            inner
+  |>            ----- bar
+"#[1..]);
+}
+
+#[test]
+fn span_empty() {
+    // In one of the unit tests, we found that the parser sometimes
+    // gives empty spans, and in particular it supplied an EOF span
+    // like this one, which points at the very end. We want to
+    // fallback gracefully in this case.
+
+    let file_text = r#"
+fn main() {
+    struct Foo;
+
+    impl !Sync for Foo {}
+
+    unsafe impl Send for &'static Foo {
+    // error: cross-crate traits with a default impl, like `core::marker::Send`,
+    //        can only be implemented for a struct/enum type, not
+    //        `&'static Foo`
+}"#;
+
+
+    let cm = Rc::new(CodeMap::new());
+    let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+
+    let mut rbrace_span = cm.span_substr(&foo, file_text, "}", 1);
+    rbrace_span.lo = rbrace_span.hi;
+
+    let mut snippet = SnippetData::new(cm.clone(), Some(rbrace_span));
+    snippet.push(rbrace_span, false, None);
+    let lines = snippet.render_lines();
+    let text: String = make_string(&lines);
+    println!("r#\"\n{}\"", text);
+    assert_eq!(text, &r#"
+  --> foo.rs:11:2
+   |>
+11 |> }
+   |>  -
+"#[1..]);
+}