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()
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);
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));
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));
"-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));
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));
Ok(())
}
- fn highlight_lines(&mut self,
+ pub fn highlight_lines(&mut self,
msp: &MultiSpan,
lvl: Level)
-> 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();
- });
- }
-}
use std::rc::Rc;
use std::mem;
-mod test;
-
#[derive(Clone)]
pub struct SnippetData {
codemap: Rc<CodeMapper>,
+++ /dev/null
-// 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..]);
-}
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 () {
";
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();
+ });
+ }
}
#![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};
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..]);
+}