2 use rustc_ast
::tokenstream
::TokenStream
;
3 use rustc_ast
::with_default_globals
;
4 use rustc_parse
::{new_parser_from_source_str, parser::Parser, source_file_to_stream}
;
5 use rustc_session
::parse
::ParseSess
;
6 use rustc_span
::source_map
::{FilePathMapping, SourceMap}
;
7 use rustc_span
::{BytePos, MultiSpan, Span}
;
9 use rustc_data_structures
::sync
::Lrc
;
10 use rustc_errors
::emitter
::EmitterWriter
;
11 use rustc_errors
::{Handler, PResult}
;
14 use std
::io
::prelude
::*;
15 use std
::iter
::Peekable
;
16 use std
::path
::{Path, PathBuf}
;
18 use std
::sync
::{Arc, Mutex}
;
20 /// Map string to parser (via tts).
21 fn string_to_parser(ps
: &ParseSess
, source_str
: String
) -> Parser
<'_
> {
22 new_parser_from_source_str(ps
, PathBuf
::from("bogofile").into(), source_str
)
25 crate fn with_error_checking_parse
<'a
, T
, F
>(s
: String
, ps
: &'a ParseSess
, f
: F
) -> T
27 F
: FnOnce(&mut Parser
<'a
>) -> PResult
<'a
, T
>,
29 let mut p
= string_to_parser(&ps
, s
);
30 let x
= f(&mut p
).unwrap();
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(), source_str
),
46 /// Parses a string, returns a crate.
47 crate fn string_to_crate(source_str
: String
) -> ast
::Crate
{
48 let ps
= ParseSess
::new(FilePathMapping
::empty());
49 with_error_checking_parse(source_str
, &ps
, |p
| p
.parse_crate_mod())
52 /// Does the given string match the pattern? whitespace in the first string
53 /// may be deleted or replaced with other whitespace to match the pattern.
54 /// This function is relatively Unicode-ignorant; fortunately, the careful design
55 /// of UTF-8 mitigates this ignorance. It doesn't do NKF-normalization(?).
56 crate fn matches_codepattern(a
: &str, b
: &str) -> bool
{
57 let mut a_iter
= a
.chars().peekable();
58 let mut b_iter
= b
.chars().peekable();
61 let (a
, b
) = match (a_iter
.peek(), b_iter
.peek()) {
62 (None
, None
) => return true,
63 (None
, _
) => return false,
65 if rustc_lexer
::is_whitespace(a
) {
66 break; // Trailing whitespace check is out of loop for borrowck.
71 (Some(&a
), Some(&b
)) => (a
, b
),
74 if rustc_lexer
::is_whitespace(a
) && rustc_lexer
::is_whitespace(b
) {
75 // Skip whitespace for `a` and `b`.
76 scan_for_non_ws_or_end(&mut a_iter
);
77 scan_for_non_ws_or_end(&mut b_iter
);
78 } else if rustc_lexer
::is_whitespace(a
) {
79 // Skip whitespace for `a`.
80 scan_for_non_ws_or_end(&mut a_iter
);
89 // Check if a has *only* trailing whitespace.
90 a_iter
.all(rustc_lexer
::is_whitespace
)
93 /// Advances the given peekable `Iterator` until it reaches a non-whitespace character.
94 fn scan_for_non_ws_or_end
<I
: Iterator
<Item
= char>>(iter
: &mut Peekable
<I
>) {
95 while iter
.peek().copied().map(|c
| rustc_lexer
::is_whitespace(c
)) == Some(true) {
100 /// Identifies a position in the text by the n'th occurrence of a string.
102 string
: &'
static str,
112 crate struct Shared
<T
: Write
> {
113 pub data
: Arc
<Mutex
<T
>>,
116 impl<T
: Write
> Write
for Shared
<T
> {
117 fn write(&mut self, buf
: &[u8]) -> io
::Result
<usize> {
118 self.data
.lock().unwrap().write(buf
)
121 fn flush(&mut self) -> io
::Result
<()> {
122 self.data
.lock().unwrap().flush()
126 fn test_harness(file_text
: &str, span_labels
: Vec
<SpanLabel
>, expected_output
: &str) {
127 with_default_globals(|| {
128 let output
= Arc
::new(Mutex
::new(Vec
::new()));
130 let source_map
= Lrc
::new(SourceMap
::new(FilePathMapping
::empty()));
131 source_map
.new_source_file(Path
::new("test.rs").to_owned().into(), file_text
.to_owned());
133 let primary_span
= make_span(&file_text
, &span_labels
[0].start
, &span_labels
[0].end
);
134 let mut msp
= MultiSpan
::from_span(primary_span
);
135 for span_label
in span_labels
{
136 let span
= make_span(&file_text
, &span_label
.start
, &span_label
.end
);
137 msp
.push_span_label(span
, span_label
.label
.to_string());
138 println
!("span: {:?} label: {:?}", span
, span_label
.label
);
139 println
!("text: {:?}", source_map
.span_to_snippet(span
));
142 let emitter
= EmitterWriter
::new(
143 Box
::new(Shared { data: output.clone() }
),
144 Some(source_map
.clone()),
151 let handler
= Handler
::with_emitter(true, None
, Box
::new(emitter
));
152 handler
.span_err(msp
, "foo");
155 expected_output
.chars().next() == Some('
\n'
),
156 "expected output should begin with newline"
158 let expected_output
= &expected_output
[1..];
160 let bytes
= output
.lock().unwrap();
161 let actual_output
= str::from_utf8(&bytes
).unwrap();
162 println
!("expected output:\n------\n{}------", expected_output
);
163 println
!("actual output:\n------\n{}------", actual_output
);
165 assert
!(expected_output
== actual_output
)
169 fn make_span(file_text
: &str, start
: &Position
, end
: &Position
) -> Span
{
170 let start
= make_pos(file_text
, start
);
171 let end
= make_pos(file_text
, end
) + end
.string
.len(); // just after matching thing ends
172 assert
!(start
<= end
);
173 Span
::with_root_ctxt(BytePos(start
as u32), BytePos(end
as u32))
176 fn make_pos(file_text
: &str, pos
: &Position
) -> usize {
177 let mut remainder
= file_text
;
179 for _
in 0..pos
.count
{
180 if let Some(n
) = remainder
.find(&pos
.string
) {
182 remainder
= &remainder
[n
+ 1..];
184 panic
!("failed to find {} instances of {:?} in {:?}", pos
.count
, pos
.string
, file_text
);
198 start
: Position { string: "{", count: 1 }
,
199 end
: Position { string: "}
", count: 1 },
225 start: Position { string: "{", count: 1 },
226 end: Position { string: "}", count
: 1 },
255 start
: Position { string: "X0", count: 1 }
,
256 end
: Position { string: "X2", count: 1 }
,
257 label
: "`X` is a good letter",
260 start
: Position { string: "Y0", count: 1 }
,
261 end
: Position { string: "Y2", count: 1 }
,
262 label
: "`Y` is a good letter too",
275 | ||____^__- `Y` is a good letter too
277 | `X` is a good letter
294 start
: Position { string: "X0", count: 1 }
,
295 end
: Position { string: "X1", count: 1 }
,
296 label
: "`X` is a good letter",
299 start
: Position { string: "Y0", count: 1 }
,
300 end
: Position { string: "Y1", count: 1 }
,
301 label
: "`Y` is a good letter too",
313 | ||____-__^ `X` is a good letter
315 | `Y` is a good letter too
322 fn different_overlap() {
334 start
: Position { string: "Y0", count: 1 }
,
335 end
: Position { string: "X2", count: 1 }
,
336 label
: "`X` is a good letter",
339 start
: Position { string: "Z1", count: 1 }
,
340 end
: Position { string: "X3", count: 1 }
,
341 label
: "`Y` is a good letter too",
353 | ||____^ `X` is a good letter
355 | |_____- `Y` is a good letter too
362 fn triple_overlap() {
373 start
: Position { string: "X0", count: 1 }
,
374 end
: Position { string: "X2", count: 1 }
,
375 label
: "`X` is a good letter",
378 start
: Position { string: "Y0", count: 1 }
,
379 end
: Position { string: "Y2", count: 1 }
,
380 label
: "`Y` is a good letter too",
383 start
: Position { string: "Z0", count: 1 }
,
384 end
: Position { string: "Z2", count: 1 }
,
399 | |||____^__-__- `Z` label
401 | |____| `Y` is a good letter too
402 | `X` is a good letter
409 fn triple_exact_overlap() {
420 start
: Position { string: "X0", count: 1 }
,
421 end
: Position { string: "X2", count: 1 }
,
422 label
: "`X` is a good letter",
425 start
: Position { string: "X0", count: 1 }
,
426 end
: Position { string: "X2", count: 1 }
,
427 label
: "`Y` is a good letter too",
430 start
: Position { string: "X0", count: 1 }
,
431 end
: Position { string: "X2", count: 1 }
,
444 | | `X` is a good letter
445 | |____`Y` is a good letter too
465 start
: Position { string: "Y0", count: 1 }
,
466 end
: Position { string: "X1", count: 1 }
,
467 label
: "`X` is a good letter",
470 start
: Position { string: "Y1", count: 1 }
,
471 end
: Position { string: "Z2", count: 1 }
,
472 label
: "`Y` is a good letter too",
475 start
: Position { string: "X2", count: 1 }
,
476 end
: Position { string: "Y3", count: 1 }
,
489 | | `X` is a good letter
491 | |____-______- `Y` is a good letter too
502 fn non_overlaping() {
514 start
: Position { string: "X0", count: 1 }
,
515 end
: Position { string: "X1", count: 1 }
,
516 label
: "`X` is a good letter",
519 start
: Position { string: "Y2", count: 1 }
,
520 end
: Position { string: "Z3", count: 1 }
,
521 label
: "`Y` is a good letter too",
530 | |____^ `X` is a good letter
534 | |__________- `Y` is a good letter too
541 fn overlaping_start_and_end() {
553 start
: Position { string: "Y0", count: 1 }
,
554 end
: Position { string: "X1", count: 1 }
,
555 label
: "`X` is a good letter",
558 start
: Position { string: "Z1", count: 1 }
,
559 end
: Position { string: "Z3", count: 1 }
,
560 label
: "`Y` is a good letter too",
572 | | `X` is a good letter
575 | |___________- `Y` is a good letter too
582 fn multiple_labels_primary_without_message() {
591 start
: Position { string: "b", count: 1 }
,
592 end
: Position { string: "}
", count: 1 },
596 start: Position { string: "a", count: 1 },
597 end: Position { string: "d", count: 1 },
598 label: "`a` is a good letter
",
601 start: Position { string: "c", count: 1 },
602 end: Position { string: "c", count: 1 },
611 | ----^^^^
-^^
-- `a` is a good letter
618 fn multiple_labels_secondary_without_message() {
627 start: Position { string: "a", count: 1 },
628 end: Position { string: "d", count: 1 },
629 label: "`a` is a good letter
",
632 start: Position { string: "b", count: 1 },
633 end: Position { string: "}", count
: 1 },
642 | ^^^^-------^^ `a` is a good letter
649 fn multiple_labels_primary_without_message_2() {
658 start
: Position { string: "b", count: 1 }
,
659 end
: Position { string: "}
", count: 1 },
660 label: "`b` is a good letter
",
663 start: Position { string: "a", count: 1 },
664 end: Position { string: "d", count: 1 },
668 start: Position { string: "c", count: 1 },
669 end: Position { string: "c", count: 1 },
680 | `b` is a good letter
687 fn multiple_labels_secondary_without_message_2() {
696 start: Position { string: "a", count: 1 },
697 end: Position { string: "d", count: 1 },
701 start: Position { string: "b", count: 1 },
702 end: Position { string: "}", count
: 1 },
703 label
: "`b` is a good letter",
713 | `b` is a good letter
720 fn multiple_labels_secondary_without_message_3() {
729 start
: Position { string: "a", count: 1 }
,
730 end
: Position { string: "b", count: 1 }
,
731 label
: "`a` is a good letter",
734 start
: Position { string: "c", count: 1 }
,
735 end
: Position { string: "d", count: 1 }
,
746 | `a` is a good letter
753 fn multiple_labels_without_message() {
762 start
: Position { string: "a", count: 1 }
,
763 end
: Position { string: "d", count: 1 }
,
767 start
: Position { string: "b", count: 1 }
,
768 end
: Position { string: "}
", count: 1 },
784 fn multiple_labels_without_message_2() {
793 start: Position { string: "b", count: 1 },
794 end: Position { string: "}", count
: 1 },
798 start
: Position { string: "a", count: 1 }
,
799 end
: Position { string: "d", count: 1 }
,
803 start
: Position { string: "c", count: 1 }
,
804 end
: Position { string: "c", count: 1 }
,
820 fn multiple_labels_with_message() {
829 start
: Position { string: "a", count: 1 }
,
830 end
: Position { string: "d", count: 1 }
,
831 label
: "`a` is a good letter",
834 start
: Position { string: "b", count: 1 }
,
835 end
: Position { string: "}
", count: 1 },
836 label: "`b` is a good letter
",
846 | | `b` is a good letter
847 | `a` is a good letter
854 fn single_label_with_message() {
862 start: Position { string: "a", count: 1 },
863 end: Position { string: "d", count: 1 },
864 label: "`a` is a good letter
",
871 | ^^^^^^^^^^^^^ `a` is a good letter
878 fn single_label_without_message() {
886 start: Position { string: "a", count: 1 },
887 end: Position { string: "d", count: 1 },
924 start: Position { string: "Y0", count: 1 },
925 end: Position { string: "X1", count: 1 },
926 label: "`X` is a good letter
",
929 start: Position { string: "Z1", count: 1 },
930 end: Position { string: "Z3", count: 1 },
931 label: "`Y` is a good letter too
",
943 | | `X` is a good letter
950 | |___________
- `Y` is a good letter too
957 fn long_snippet_multiple_spans() {
979 start: Position { string: "Y0", count: 1 },
980 end: Position { string: "Y3", count: 1 },
981 label: "`Y` is a good letter
",
984 start: Position { string: "Z1", count: 1 },
985 end: Position { string: "Z2", count: 1 },
986 label: "`Z` is a good letter too
",
1004 | ||__________
- `Z` is a good letter too
1008 | |_______^ `Y` is a good letter