about summary refs log tree commit diff
path: root/src/libsyntax/test.rs
diff options
context:
space:
mode:
authorJonathan Turner <jturner@mozilla.com>2016-06-22 18:39:43 -0400
committerJonathan Turner <jturner@mozilla.com>2016-06-23 08:07:35 -0400
commitf2fe204dcce8f6509e51b3c518fc02d7e0e25e92 (patch)
tree497ce51943e4dd162a664852b412212da4485c81 /src/libsyntax/test.rs
parent51deb4fedbdab4b9b5a7610985c0be54e89cae73 (diff)
downloadrust-f2fe204dcce8f6509e51b3c518fc02d7e0e25e92.tar.gz
rust-f2fe204dcce8f6509e51b3c518fc02d7e0e25e92.zip
Consolidate codemap tests and fix more errors for travis
Diffstat (limited to 'src/libsyntax/test.rs')
-rw-r--r--src/libsyntax/test.rs588
1 files changed, 586 insertions, 2 deletions
diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs
index b80c01853dc..2572ac35e98 100644
--- a/src/libsyntax/test.rs
+++ b/src/libsyntax/test.rs
@@ -12,6 +12,7 @@
 
 #![allow(dead_code)]
 #![allow(unused_imports)]
+
 use self::HasTestSignature::*;
 
 use std::iter;
@@ -20,9 +21,12 @@ use std::mem;
 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..]);
+}