1 use crate::{ast, panictry};
2 use crate::parse::{ParseSess, PResult, source_file_to_stream};
3 use crate::parse::new_parser_from_source_str;
4 use crate::parse::parser::Parser;
5 use crate::source_map::{SourceMap, FilePathMapping};
6 use crate::tokenstream::TokenStream;
7 use crate::with_default_globals;
9 use errors::emitter::EmitterWriter;
11 use rustc_data_structures::sync::Lrc;
12 use syntax_pos::{BytePos, Span, MultiSpan};
15 use std::io::prelude::*;
16 use std::iter::Peekable;
17 use std::path::{Path, PathBuf};
19 use std::sync::{Arc, Mutex};
21 /// Map string to parser (via tts).
22 fn string_to_parser(ps: &ParseSess, source_str: String) -> Parser<'_> {
23 new_parser_from_source_str(ps, PathBuf::from("bogofile").into(), source_str)
26 crate fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> T where
27 F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>,
29 let mut p = string_to_parser(&ps, s);
30 let x = panictry!(f(&mut p));
31 p.sess.span_diagnostic.abort_if_errors();
35 /// Maps a string to tts, using a made-up filename.
36 crate fn string_to_stream(source_str: String) -> TokenStream {
37 let ps = ParseSess::new(FilePathMapping::empty());
38 source_file_to_stream(
40 ps.source_map().new_source_file(PathBuf::from("bogofile").into(),
45 /// Parses a string, returns a crate.
46 crate fn string_to_crate(source_str : String) -> ast::Crate {
47 let ps = ParseSess::new(FilePathMapping::empty());
48 with_error_checking_parse(source_str, &ps, |p| {
53 /// Does the given string match the pattern? whitespace in the first string
54 /// may be deleted or replaced with other whitespace to match the pattern.
55 /// This function is relatively Unicode-ignorant; fortunately, the careful design
56 /// of UTF-8 mitigates this ignorance. It doesn't do NKF-normalization(?).
57 crate fn matches_codepattern(a : &str, b : &str) -> bool {
58 let mut a_iter = a.chars().peekable();
59 let mut b_iter = b.chars().peekable();
62 let (a, b) = match (a_iter.peek(), b_iter.peek()) {
63 (None, None) => return true,
64 (None, _) => return false,
66 if rustc_lexer::is_whitespace(a) {
67 break // Trailing whitespace check is out of loop for borrowck.
72 (Some(&a), Some(&b)) => (a, b)
75 if rustc_lexer::is_whitespace(a) && rustc_lexer::is_whitespace(b) {
76 // Skip whitespace for `a` and `b`.
77 scan_for_non_ws_or_end(&mut a_iter);
78 scan_for_non_ws_or_end(&mut b_iter);
79 } else if rustc_lexer::is_whitespace(a) {
80 // Skip whitespace for `a`.
81 scan_for_non_ws_or_end(&mut a_iter);
90 // Check if a has *only* trailing whitespace.
91 a_iter.all(rustc_lexer::is_whitespace)
94 /// Advances the given peekable `Iterator` until it reaches a non-whitespace character.
95 fn scan_for_non_ws_or_end<I: Iterator<Item = char>>(iter: &mut Peekable<I>) {
96 while iter.peek().copied().map(|c| rustc_lexer::is_whitespace(c)) == Some(true) {
101 /// Identifies a position in the text by the n'th occurrence of a string.
103 string: &'static str,
113 struct Shared<T: Write> {
117 impl<T: Write> Write for Shared<T> {
118 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
119 self.data.lock().unwrap().write(buf)
122 fn flush(&mut self) -> io::Result<()> {
123 self.data.lock().unwrap().flush()
127 fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &str) {
128 with_default_globals(|| {
129 let output = Arc::new(Mutex::new(Vec::new()));
131 let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
132 source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned());
134 let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end);
135 let mut msp = MultiSpan::from_span(primary_span);
136 for span_label in span_labels {
137 let span = make_span(&file_text, &span_label.start, &span_label.end);
138 msp.push_span_label(span, span_label.label.to_string());
139 println!("span: {:?} label: {:?}", span, span_label.label);
140 println!("text: {:?}", source_map.span_to_snippet(span));
143 let emitter = EmitterWriter::new(
144 Box::new(Shared { data: output.clone() }),
145 Some(source_map.clone()),
152 let handler = Handler::with_emitter(true, None, Box::new(emitter));
153 handler.span_err(msp, "foo");
155 assert!(expected_output.chars().next() == Some('\n'),
156 "expected output should begin with newline");
157 let expected_output = &expected_output[1..];
159 let bytes = output.lock().unwrap();
160 let actual_output = str::from_utf8(&bytes).unwrap();
161 println!("expected output:\n------\n{}------", expected_output);
162 println!("actual output:\n------\n{}------", actual_output);
164 assert!(expected_output == actual_output)
168 fn make_span(file_text: &str, start: &Position, end: &Position) -> Span {
169 let start = make_pos(file_text, start);
170 let end = make_pos(file_text, end) + end.string.len(); // just after matching thing ends
171 assert!(start <= end);
172 Span::with_root_ctxt(BytePos(start as u32), BytePos(end as u32))
175 fn make_pos(file_text: &str, pos: &Position) -> usize {
176 let mut remainder = file_text;
178 for _ in 0..pos.count {
179 if let Some(n) = remainder.find(&pos.string) {
181 remainder = &remainder[n + 1..];
183 panic!("failed to find {} instances of {:?} in {:?}",
276 label: "`X` is a good letter",
287 label: "`Y` is a good letter too",
300 | ||____^__- `Y` is a good letter too
302 | `X` is a good letter
325 label: "`X` is a good letter",
336 label: "`Y` is a good letter too",
348 | ||____-__^ `X` is a good letter
350 | `Y` is a good letter too
356 fn different_overlap() {
375 label: "`X` is a good letter",
386 label: "`Y` is a good letter too",
398 | ||____^ `X` is a good letter
400 | |_____- `Y` is a good letter too
406 fn triple_overlap() {
424 label: "`X` is a good letter",
435 label: "`Y` is a good letter too",
460 | |||____^__-__- `Z` label
462 | |____| `Y` is a good letter too
463 | `X` is a good letter
469 fn triple_exact_overlap() {
487 label: "`X` is a good letter",
498 label: "`Y` is a good letter too",
521 | | `X` is a good letter
522 | |____`Y` is a good letter too
548 label: "`X` is a good letter",
559 label: "`Y` is a good letter too",
582 | | `X` is a good letter
584 | |____-______- `Y` is a good letter too
594 fn non_overlaping() {
613 label: "`X` is a good letter",
624 label: "`Y` is a good letter too",
633 | |____^ `X` is a good letter
637 | |__________- `Y` is a good letter too
643 fn overlaping_start_and_end() {
662 label: "`X` is a good letter",
673 label: "`Y` is a good letter too",
685 | | `X` is a good letter
688 | |___________- `Y` is a good letter too
694 fn multiple_labels_primary_without_message() {
721 label: "`a` is a good letter",
740 | ----^^^^-^^-- `a` is a good letter
746 fn multiple_labels_secondary_without_message() {
762 label: "`a` is a good letter",
781 | ^^^^-------^^ `a` is a good letter
787 fn multiple_labels_primary_without_message_2() {
803 label: "`b` is a good letter",
835 | `b` is a good letter
841 fn multiple_labels_secondary_without_message_2() {
868 label: "`b` is a good letter",
878 | `b` is a good letter
884 fn multiple_labels_secondary_without_message_3() {
900 label: "`a` is a good letter",
921 | `a` is a good letter
927 fn multiple_labels_without_message() {
968 fn multiple_labels_without_message_2() {
1020 fn multiple_labels_with_message() {
1036 label: "`a` is a good letter",
1047 label: "`b` is a good letter",
1057 | | `b` is a good letter
1058 | `a` is a good letter
1064 fn single_label_with_message() {
1080 label: "`a` is a good letter",
1088 | ^^^^^^^^^^^^^ `a` is a good letter
1094 fn single_label_without_message() {
1153 label: "`X` is a good letter",
1164 label: "`Y` is a good letter too",
1176 | | `X` is a good letter
1183 | |___________- `Y` is a good letter too
1189 fn long_snippet_multiple_spans() {
1218 label: "`Y` is a good letter",
1229 label: "`Z` is a good letter too",
1247 | ||__________- `Z` is a good letter too
1251 | |_______^ `Y` is a good letter