mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-31 04:57:19 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1013 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			1013 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, MultiSpan, Span};
 | |
| 
 | |
| use rustc_data_structures::sync::Lrc;
 | |
| use rustc_errors::emitter::EmitterWriter;
 | |
| use rustc_errors::{Handler, 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 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()),
 | |
|             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
 | |
| 
 | |
| "#,
 | |
|     );
 | |
| }
 | 
