
Loading the fallback bundle in compilation sessions that won't go on to emit any errors unnecessarily degrades compile time performance, so lazily create the Fluent bundle when it is first required. Signed-off-by: David Wood <david.wood@huawei.com>
1016 lines
22 KiB
Rust
1016 lines
22 KiB
Rust
use rustc_ast as ast;
|
|
use rustc_ast::tokenstream::TokenStream;
|
|
use rustc_parse::{new_parser_from_source_str, parser::Parser, source_file_to_stream};
|
|
use rustc_session::parse::ParseSess;
|
|
use rustc_span::create_default_session_if_not_set_then;
|
|
use rustc_span::source_map::{FilePathMapping, SourceMap};
|
|
use rustc_span::{BytePos, Span};
|
|
|
|
use rustc_data_structures::sync::Lrc;
|
|
use rustc_errors::emitter::EmitterWriter;
|
|
use rustc_errors::{Handler, MultiSpan, PResult};
|
|
|
|
use std::io;
|
|
use std::io::prelude::*;
|
|
use std::iter::Peekable;
|
|
use std::path::{Path, PathBuf};
|
|
use std::str;
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
/// Map string to parser (via tts).
|
|
fn string_to_parser(ps: &ParseSess, source_str: String) -> Parser<'_> {
|
|
new_parser_from_source_str(ps, PathBuf::from("bogofile").into(), source_str)
|
|
}
|
|
|
|
crate fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> T
|
|
where
|
|
F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>,
|
|
{
|
|
let mut p = string_to_parser(&ps, s);
|
|
let x = f(&mut p).unwrap();
|
|
p.sess.span_diagnostic.abort_if_errors();
|
|
x
|
|
}
|
|
|
|
/// Maps a string to tts, using a made-up filename.
|
|
crate fn string_to_stream(source_str: String) -> TokenStream {
|
|
let ps = ParseSess::new(FilePathMapping::empty());
|
|
source_file_to_stream(
|
|
&ps,
|
|
ps.source_map().new_source_file(PathBuf::from("bogofile").into(), source_str),
|
|
None,
|
|
)
|
|
.0
|
|
}
|
|
|
|
/// Parses a string, returns a crate.
|
|
crate fn string_to_crate(source_str: String) -> ast::Crate {
|
|
let ps = ParseSess::new(FilePathMapping::empty());
|
|
with_error_checking_parse(source_str, &ps, |p| p.parse_crate_mod())
|
|
}
|
|
|
|
/// Does the given string match the pattern? whitespace in the first string
|
|
/// may be deleted or replaced with other whitespace to match the pattern.
|
|
/// This function is relatively Unicode-ignorant; fortunately, the careful design
|
|
/// of UTF-8 mitigates this ignorance. It doesn't do NKF-normalization(?).
|
|
crate fn matches_codepattern(a: &str, b: &str) -> bool {
|
|
let mut a_iter = a.chars().peekable();
|
|
let mut b_iter = b.chars().peekable();
|
|
|
|
loop {
|
|
let (a, b) = match (a_iter.peek(), b_iter.peek()) {
|
|
(None, None) => return true,
|
|
(None, _) => return false,
|
|
(Some(&a), None) => {
|
|
if rustc_lexer::is_whitespace(a) {
|
|
break; // Trailing whitespace check is out of loop for borrowck.
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
(Some(&a), Some(&b)) => (a, b),
|
|
};
|
|
|
|
if rustc_lexer::is_whitespace(a) && rustc_lexer::is_whitespace(b) {
|
|
// Skip whitespace for `a` and `b`.
|
|
scan_for_non_ws_or_end(&mut a_iter);
|
|
scan_for_non_ws_or_end(&mut b_iter);
|
|
} else if rustc_lexer::is_whitespace(a) {
|
|
// Skip whitespace for `a`.
|
|
scan_for_non_ws_or_end(&mut a_iter);
|
|
} else if a == b {
|
|
a_iter.next();
|
|
b_iter.next();
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check if a has *only* trailing whitespace.
|
|
a_iter.all(rustc_lexer::is_whitespace)
|
|
}
|
|
|
|
/// Advances the given peekable `Iterator` until it reaches a non-whitespace character.
|
|
fn scan_for_non_ws_or_end<I: Iterator<Item = char>>(iter: &mut Peekable<I>) {
|
|
while iter.peek().copied().map(rustc_lexer::is_whitespace) == Some(true) {
|
|
iter.next();
|
|
}
|
|
}
|
|
|
|
/// Identifies a position in the text by the n'th occurrence of a string.
|
|
struct Position {
|
|
string: &'static str,
|
|
count: usize,
|
|
}
|
|
|
|
struct SpanLabel {
|
|
start: Position,
|
|
end: Position,
|
|
label: &'static str,
|
|
}
|
|
|
|
crate struct Shared<T: Write> {
|
|
pub data: Arc<Mutex<T>>,
|
|
}
|
|
|
|
impl<T: Write> Write for Shared<T> {
|
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
self.data.lock().unwrap().write(buf)
|
|
}
|
|
|
|
fn flush(&mut self) -> io::Result<()> {
|
|
self.data.lock().unwrap().flush()
|
|
}
|
|
}
|
|
|
|
fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &str) {
|
|
create_default_session_if_not_set_then(|_| {
|
|
let output = Arc::new(Mutex::new(Vec::new()));
|
|
|
|
let fallback_bundle =
|
|
rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
|
|
let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
|
source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned());
|
|
|
|
let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end);
|
|
let mut msp = MultiSpan::from_span(primary_span);
|
|
for span_label in span_labels {
|
|
let span = make_span(&file_text, &span_label.start, &span_label.end);
|
|
msp.push_span_label(span, span_label.label.to_string());
|
|
println!("span: {:?} label: {:?}", span, span_label.label);
|
|
println!("text: {:?}", source_map.span_to_snippet(span));
|
|
}
|
|
|
|
let emitter = EmitterWriter::new(
|
|
Box::new(Shared { data: output.clone() }),
|
|
Some(source_map.clone()),
|
|
None,
|
|
fallback_bundle,
|
|
false,
|
|
false,
|
|
false,
|
|
None,
|
|
false,
|
|
);
|
|
let handler = Handler::with_emitter(true, None, Box::new(emitter));
|
|
handler.span_err(msp, "foo");
|
|
|
|
assert!(
|
|
expected_output.chars().next() == Some('\n'),
|
|
"expected output should begin with newline"
|
|
);
|
|
let expected_output = &expected_output[1..];
|
|
|
|
let bytes = output.lock().unwrap();
|
|
let actual_output = str::from_utf8(&bytes).unwrap();
|
|
println!("expected output:\n------\n{}------", expected_output);
|
|
println!("actual output:\n------\n{}------", actual_output);
|
|
|
|
assert!(expected_output == actual_output)
|
|
})
|
|
}
|
|
|
|
fn make_span(file_text: &str, start: &Position, end: &Position) -> Span {
|
|
let start = make_pos(file_text, start);
|
|
let end = make_pos(file_text, end) + end.string.len(); // just after matching thing ends
|
|
assert!(start <= end);
|
|
Span::with_root_ctxt(BytePos(start as u32), BytePos(end as u32))
|
|
}
|
|
|
|
fn make_pos(file_text: &str, pos: &Position) -> usize {
|
|
let mut remainder = file_text;
|
|
let mut offset = 0;
|
|
for _ in 0..pos.count {
|
|
if let Some(n) = remainder.find(&pos.string) {
|
|
offset += n;
|
|
remainder = &remainder[n + 1..];
|
|
} else {
|
|
panic!("failed to find {} instances of {:?} in {:?}", pos.count, pos.string, file_text);
|
|
}
|
|
}
|
|
offset
|
|
}
|
|
|
|
#[test]
|
|
fn ends_on_col0() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
}
|
|
"#,
|
|
vec![SpanLabel {
|
|
start: Position { string: "{", count: 1 },
|
|
end: Position { string: "}", count: 1 },
|
|
label: "test",
|
|
}],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:2:10
|
|
|
|
|
2 | fn foo() {
|
|
| __________^
|
|
3 | | }
|
|
| |_^ test
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ends_on_col2() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
|
|
|
|
}
|
|
"#,
|
|
vec![SpanLabel {
|
|
start: Position { string: "{", count: 1 },
|
|
end: Position { string: "}", count: 1 },
|
|
label: "test",
|
|
}],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:2:10
|
|
|
|
|
2 | fn foo() {
|
|
| __________^
|
|
3 | |
|
|
4 | |
|
|
5 | | }
|
|
| |___^ test
|
|
|
|
"#,
|
|
);
|
|
}
|
|
#[test]
|
|
fn non_nested() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
X0 Y0
|
|
X1 Y1
|
|
X2 Y2
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "X0", count: 1 },
|
|
end: Position { string: "X2", count: 1 },
|
|
label: "`X` is a good letter",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "Y0", count: 1 },
|
|
end: Position { string: "Y2", count: 1 },
|
|
label: "`Y` is a good letter too",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:3
|
|
|
|
|
3 | X0 Y0
|
|
| ____^__-
|
|
| | ___|
|
|
| ||
|
|
4 | || X1 Y1
|
|
5 | || X2 Y2
|
|
| ||____^__- `Y` is a good letter too
|
|
| |____|
|
|
| `X` is a good letter
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn nested() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
X0 Y0
|
|
Y1 X1
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "X0", count: 1 },
|
|
end: Position { string: "X1", count: 1 },
|
|
label: "`X` is a good letter",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "Y0", count: 1 },
|
|
end: Position { string: "Y1", count: 1 },
|
|
label: "`Y` is a good letter too",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:3
|
|
|
|
|
3 | X0 Y0
|
|
| ____^__-
|
|
| | ___|
|
|
| ||
|
|
4 | || Y1 X1
|
|
| ||____-__^ `X` is a good letter
|
|
| |_____|
|
|
| `Y` is a good letter too
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn different_overlap() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
X0 Y0 Z0
|
|
X1 Y1 Z1
|
|
X2 Y2 Z2
|
|
X3 Y3 Z3
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "Y0", count: 1 },
|
|
end: Position { string: "X2", count: 1 },
|
|
label: "`X` is a good letter",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "Z1", count: 1 },
|
|
end: Position { string: "X3", count: 1 },
|
|
label: "`Y` is a good letter too",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:6
|
|
|
|
|
3 | X0 Y0 Z0
|
|
| ______^
|
|
4 | | X1 Y1 Z1
|
|
| |_________-
|
|
5 | || X2 Y2 Z2
|
|
| ||____^ `X` is a good letter
|
|
6 | | X3 Y3 Z3
|
|
| |_____- `Y` is a good letter too
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn triple_overlap() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
X0 Y0 Z0
|
|
X1 Y1 Z1
|
|
X2 Y2 Z2
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "X0", count: 1 },
|
|
end: Position { string: "X2", count: 1 },
|
|
label: "`X` is a good letter",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "Y0", count: 1 },
|
|
end: Position { string: "Y2", count: 1 },
|
|
label: "`Y` is a good letter too",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "Z0", count: 1 },
|
|
end: Position { string: "Z2", count: 1 },
|
|
label: "`Z` label",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:3
|
|
|
|
|
3 | X0 Y0 Z0
|
|
| _____^__-__-
|
|
| | ____|__|
|
|
| || ___|
|
|
| |||
|
|
4 | ||| X1 Y1 Z1
|
|
5 | ||| X2 Y2 Z2
|
|
| |||____^__-__- `Z` label
|
|
| ||____|__|
|
|
| |____| `Y` is a good letter too
|
|
| `X` is a good letter
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn triple_exact_overlap() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
X0 Y0 Z0
|
|
X1 Y1 Z1
|
|
X2 Y2 Z2
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "X0", count: 1 },
|
|
end: Position { string: "X2", count: 1 },
|
|
label: "`X` is a good letter",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "X0", count: 1 },
|
|
end: Position { string: "X2", count: 1 },
|
|
label: "`Y` is a good letter too",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "X0", count: 1 },
|
|
end: Position { string: "X2", count: 1 },
|
|
label: "`Z` label",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:3
|
|
|
|
|
3 | / X0 Y0 Z0
|
|
4 | | X1 Y1 Z1
|
|
5 | | X2 Y2 Z2
|
|
| | ^
|
|
| | |
|
|
| | `X` is a good letter
|
|
| |____`Y` is a good letter too
|
|
| `Z` label
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn minimum_depth() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
X0 Y0 Z0
|
|
X1 Y1 Z1
|
|
X2 Y2 Z2
|
|
X3 Y3 Z3
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "Y0", count: 1 },
|
|
end: Position { string: "X1", count: 1 },
|
|
label: "`X` is a good letter",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "Y1", count: 1 },
|
|
end: Position { string: "Z2", count: 1 },
|
|
label: "`Y` is a good letter too",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "X2", count: 1 },
|
|
end: Position { string: "Y3", count: 1 },
|
|
label: "`Z`",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:6
|
|
|
|
|
3 | X0 Y0 Z0
|
|
| ______^
|
|
4 | | X1 Y1 Z1
|
|
| |____^_-
|
|
| ||____|
|
|
| | `X` is a good letter
|
|
5 | | X2 Y2 Z2
|
|
| |____-______- `Y` is a good letter too
|
|
| ____|
|
|
| |
|
|
6 | | X3 Y3 Z3
|
|
| |________- `Z`
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn non_overlaping() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
X0 Y0 Z0
|
|
X1 Y1 Z1
|
|
X2 Y2 Z2
|
|
X3 Y3 Z3
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "X0", count: 1 },
|
|
end: Position { string: "X1", count: 1 },
|
|
label: "`X` is a good letter",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "Y2", count: 1 },
|
|
end: Position { string: "Z3", count: 1 },
|
|
label: "`Y` is a good letter too",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:3
|
|
|
|
|
3 | / X0 Y0 Z0
|
|
4 | | X1 Y1 Z1
|
|
| |____^ `X` is a good letter
|
|
5 | X2 Y2 Z2
|
|
| ______-
|
|
6 | | X3 Y3 Z3
|
|
| |__________- `Y` is a good letter too
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn overlaping_start_and_end() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
X0 Y0 Z0
|
|
X1 Y1 Z1
|
|
X2 Y2 Z2
|
|
X3 Y3 Z3
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "Y0", count: 1 },
|
|
end: Position { string: "X1", count: 1 },
|
|
label: "`X` is a good letter",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "Z1", count: 1 },
|
|
end: Position { string: "Z3", count: 1 },
|
|
label: "`Y` is a good letter too",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:6
|
|
|
|
|
3 | X0 Y0 Z0
|
|
| ______^
|
|
4 | | X1 Y1 Z1
|
|
| |____^____-
|
|
| ||____|
|
|
| | `X` is a good letter
|
|
5 | | X2 Y2 Z2
|
|
6 | | X3 Y3 Z3
|
|
| |___________- `Y` is a good letter too
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn multiple_labels_primary_without_message() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
a { b { c } d }
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "b", count: 1 },
|
|
end: Position { string: "}", count: 1 },
|
|
label: "",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "a", count: 1 },
|
|
end: Position { string: "d", count: 1 },
|
|
label: "`a` is a good letter",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "c", count: 1 },
|
|
end: Position { string: "c", count: 1 },
|
|
label: "",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:7
|
|
|
|
|
3 | a { b { c } d }
|
|
| ----^^^^-^^-- `a` is a good letter
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn multiple_labels_secondary_without_message() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
a { b { c } d }
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "a", count: 1 },
|
|
end: Position { string: "d", count: 1 },
|
|
label: "`a` is a good letter",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "b", count: 1 },
|
|
end: Position { string: "}", count: 1 },
|
|
label: "",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:3
|
|
|
|
|
3 | a { b { c } d }
|
|
| ^^^^-------^^ `a` is a good letter
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn multiple_labels_primary_without_message_2() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
a { b { c } d }
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "b", count: 1 },
|
|
end: Position { string: "}", count: 1 },
|
|
label: "`b` is a good letter",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "a", count: 1 },
|
|
end: Position { string: "d", count: 1 },
|
|
label: "",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "c", count: 1 },
|
|
end: Position { string: "c", count: 1 },
|
|
label: "",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:7
|
|
|
|
|
3 | a { b { c } d }
|
|
| ----^^^^-^^--
|
|
| |
|
|
| `b` is a good letter
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn multiple_labels_secondary_without_message_2() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
a { b { c } d }
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "a", count: 1 },
|
|
end: Position { string: "d", count: 1 },
|
|
label: "",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "b", count: 1 },
|
|
end: Position { string: "}", count: 1 },
|
|
label: "`b` is a good letter",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:3
|
|
|
|
|
3 | a { b { c } d }
|
|
| ^^^^-------^^
|
|
| |
|
|
| `b` is a good letter
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn multiple_labels_secondary_without_message_3() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
a bc d
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "a", count: 1 },
|
|
end: Position { string: "b", count: 1 },
|
|
label: "`a` is a good letter",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "c", count: 1 },
|
|
end: Position { string: "d", count: 1 },
|
|
label: "",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:3
|
|
|
|
|
3 | a bc d
|
|
| ^^^^----
|
|
| |
|
|
| `a` is a good letter
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn multiple_labels_without_message() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
a { b { c } d }
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "a", count: 1 },
|
|
end: Position { string: "d", count: 1 },
|
|
label: "",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "b", count: 1 },
|
|
end: Position { string: "}", count: 1 },
|
|
label: "",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:3
|
|
|
|
|
3 | a { b { c } d }
|
|
| ^^^^-------^^
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn multiple_labels_without_message_2() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
a { b { c } d }
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "b", count: 1 },
|
|
end: Position { string: "}", count: 1 },
|
|
label: "",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "a", count: 1 },
|
|
end: Position { string: "d", count: 1 },
|
|
label: "",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "c", count: 1 },
|
|
end: Position { string: "c", count: 1 },
|
|
label: "",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:7
|
|
|
|
|
3 | a { b { c } d }
|
|
| ----^^^^-^^--
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn multiple_labels_with_message() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
a { b { c } d }
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "a", count: 1 },
|
|
end: Position { string: "d", count: 1 },
|
|
label: "`a` is a good letter",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "b", count: 1 },
|
|
end: Position { string: "}", count: 1 },
|
|
label: "`b` is a good letter",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:3
|
|
|
|
|
3 | a { b { c } d }
|
|
| ^^^^-------^^
|
|
| | |
|
|
| | `b` is a good letter
|
|
| `a` is a good letter
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn single_label_with_message() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
a { b { c } d }
|
|
}
|
|
"#,
|
|
vec![SpanLabel {
|
|
start: Position { string: "a", count: 1 },
|
|
end: Position { string: "d", count: 1 },
|
|
label: "`a` is a good letter",
|
|
}],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:3
|
|
|
|
|
3 | a { b { c } d }
|
|
| ^^^^^^^^^^^^^ `a` is a good letter
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn single_label_without_message() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
a { b { c } d }
|
|
}
|
|
"#,
|
|
vec![SpanLabel {
|
|
start: Position { string: "a", count: 1 },
|
|
end: Position { string: "d", count: 1 },
|
|
label: "",
|
|
}],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:3
|
|
|
|
|
3 | a { b { c } d }
|
|
| ^^^^^^^^^^^^^
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn long_snippet() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
X0 Y0 Z0
|
|
X1 Y1 Z1
|
|
1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
6
|
|
7
|
|
8
|
|
9
|
|
10
|
|
X2 Y2 Z2
|
|
X3 Y3 Z3
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "Y0", count: 1 },
|
|
end: Position { string: "X1", count: 1 },
|
|
label: "`X` is a good letter",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "Z1", count: 1 },
|
|
end: Position { string: "Z3", count: 1 },
|
|
label: "`Y` is a good letter too",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:6
|
|
|
|
|
3 | X0 Y0 Z0
|
|
| ______^
|
|
4 | | X1 Y1 Z1
|
|
| |____^____-
|
|
| ||____|
|
|
| | `X` is a good letter
|
|
5 | | 1
|
|
6 | | 2
|
|
7 | | 3
|
|
... |
|
|
15 | | X2 Y2 Z2
|
|
16 | | X3 Y3 Z3
|
|
| |___________- `Y` is a good letter too
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn long_snippet_multiple_spans() {
|
|
test_harness(
|
|
r#"
|
|
fn foo() {
|
|
X0 Y0 Z0
|
|
1
|
|
2
|
|
3
|
|
X1 Y1 Z1
|
|
4
|
|
5
|
|
6
|
|
X2 Y2 Z2
|
|
7
|
|
8
|
|
9
|
|
10
|
|
X3 Y3 Z3
|
|
}
|
|
"#,
|
|
vec![
|
|
SpanLabel {
|
|
start: Position { string: "Y0", count: 1 },
|
|
end: Position { string: "Y3", count: 1 },
|
|
label: "`Y` is a good letter",
|
|
},
|
|
SpanLabel {
|
|
start: Position { string: "Z1", count: 1 },
|
|
end: Position { string: "Z2", count: 1 },
|
|
label: "`Z` is a good letter too",
|
|
},
|
|
],
|
|
r#"
|
|
error: foo
|
|
--> test.rs:3:6
|
|
|
|
|
3 | X0 Y0 Z0
|
|
| ______^
|
|
4 | | 1
|
|
5 | | 2
|
|
6 | | 3
|
|
7 | | X1 Y1 Z1
|
|
| |_________-
|
|
8 | || 4
|
|
9 | || 5
|
|
10 | || 6
|
|
11 | || X2 Y2 Z2
|
|
| ||__________- `Z` is a good letter too
|
|
... |
|
|
15 | | 10
|
|
16 | | X3 Y3 Z3
|
|
| |_______^ `Y` is a good letter
|
|
|
|
"#,
|
|
);
|
|
}
|