From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_expand/src/tests.rs | 1016 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1016 insertions(+) create mode 100644 compiler/rustc_expand/src/tests.rs (limited to 'compiler/rustc_expand/src/tests.rs') diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs new file mode 100644 index 000000000..e44f06081 --- /dev/null +++ b/compiler/rustc_expand/src/tests.rs @@ -0,0 +1,1016 @@ +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) +} + +pub(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. +pub(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. +pub(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(?). +pub(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>(iter: &mut Peekable) { + 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, +} + +pub(crate) struct Shared { + pub data: Arc>, +} + +impl Write for Shared { + fn write(&mut self, buf: &[u8]) -> io::Result { + 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, 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); + 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 + +"#, + ); +} -- cgit v1.2.3