]>
Commit | Line | Data |
---|---|---|
3dfed10e | 1 | use rustc_ast as ast; |
74b04a01 | 2 | use rustc_ast::tokenstream::TokenStream; |
dfeec247 | 3 | use rustc_parse::{new_parser_from_source_str, parser::Parser, source_file_to_stream}; |
74b04a01 | 4 | use rustc_session::parse::ParseSess; |
ed00b5ec | 5 | use rustc_span::create_default_session_globals_then; |
dfeec247 | 6 | use rustc_span::source_map::{FilePathMapping, SourceMap}; |
04454e1e | 7 | use rustc_span::{BytePos, Span}; |
dfeec247 XL |
8 | |
9 | use rustc_data_structures::sync::Lrc; | |
10 | use rustc_errors::emitter::EmitterWriter; | |
4b012472 | 11 | use rustc_errors::{DiagCtxt, MultiSpan, PResult}; |
add651ee | 12 | use termcolor::WriteColor; |
dfeec247 XL |
13 | |
14 | use std::io; | |
15 | use std::io::prelude::*; | |
16 | use std::iter::Peekable; | |
17 | use std::path::{Path, PathBuf}; | |
18 | use std::str; | |
19 | use std::sync::{Arc, Mutex}; | |
20 | ||
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) | |
24 | } | |
25 | ||
4b012472 | 26 | fn create_test_handler() -> (DiagCtxt, Lrc<SourceMap>, Arc<Mutex<Vec<u8>>>) { |
add651ee FG |
27 | let output = Arc::new(Mutex::new(Vec::new())); |
28 | let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); | |
29 | let fallback_bundle = rustc_errors::fallback_fluent_bundle( | |
30 | vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], | |
31 | false, | |
32 | ); | |
33 | let emitter = EmitterWriter::new(Box::new(Shared { data: output.clone() }), fallback_bundle) | |
34 | .sm(Some(source_map.clone())) | |
35 | .diagnostic_width(Some(140)); | |
4b012472 FG |
36 | let dcx = DiagCtxt::with_emitter(Box::new(emitter)); |
37 | (dcx, source_map, output) | |
add651ee FG |
38 | } |
39 | ||
40 | /// Returns the result of parsing the given string via the given callback. | |
41 | /// | |
42 | /// If there are any errors, this will panic. | |
923072b8 | 43 | pub(crate) fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> T |
dfeec247 XL |
44 | where |
45 | F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>, | |
46 | { | |
47 | let mut p = string_to_parser(&ps, s); | |
48 | let x = f(&mut p).unwrap(); | |
4b012472 | 49 | p.sess.dcx.abort_if_errors(); |
dfeec247 XL |
50 | x |
51 | } | |
52 | ||
add651ee FG |
53 | /// Verifies that parsing the given string using the given callback will |
54 | /// generate an error that contains the given text. | |
55 | pub(crate) fn with_expected_parse_error<T, F>(source_str: &str, expected_output: &str, f: F) | |
56 | where | |
57 | F: for<'a> FnOnce(&mut Parser<'a>) -> PResult<'a, T>, | |
58 | { | |
59 | let (handler, source_map, output) = create_test_handler(); | |
4b012472 | 60 | let ps = ParseSess::with_dcx(handler, source_map); |
add651ee FG |
61 | let mut p = string_to_parser(&ps, source_str.to_string()); |
62 | let result = f(&mut p); | |
63 | assert!(result.is_ok()); | |
64 | ||
65 | let bytes = output.lock().unwrap(); | |
66 | let actual_output = str::from_utf8(&bytes).unwrap(); | |
67 | println!("expected output:\n------\n{}------", expected_output); | |
68 | println!("actual output:\n------\n{}------", actual_output); | |
69 | ||
70 | assert!(actual_output.contains(expected_output)) | |
71 | } | |
72 | ||
dfeec247 | 73 | /// Maps a string to tts, using a made-up filename. |
923072b8 | 74 | pub(crate) fn string_to_stream(source_str: String) -> TokenStream { |
9ffffee4 FG |
75 | let ps = ParseSess::new( |
76 | vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], | |
77 | FilePathMapping::empty(), | |
78 | ); | |
dfeec247 XL |
79 | source_file_to_stream( |
80 | &ps, | |
81 | ps.source_map().new_source_file(PathBuf::from("bogofile").into(), source_str), | |
82 | None, | |
83 | ) | |
dfeec247 XL |
84 | } |
85 | ||
86 | /// Parses a string, returns a crate. | |
923072b8 | 87 | pub(crate) fn string_to_crate(source_str: String) -> ast::Crate { |
9ffffee4 FG |
88 | let ps = ParseSess::new( |
89 | vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], | |
90 | FilePathMapping::empty(), | |
91 | ); | |
dfeec247 XL |
92 | with_error_checking_parse(source_str, &ps, |p| p.parse_crate_mod()) |
93 | } | |
94 | ||
95 | /// Does the given string match the pattern? whitespace in the first string | |
96 | /// may be deleted or replaced with other whitespace to match the pattern. | |
97 | /// This function is relatively Unicode-ignorant; fortunately, the careful design | |
98 | /// of UTF-8 mitigates this ignorance. It doesn't do NKF-normalization(?). | |
923072b8 | 99 | pub(crate) fn matches_codepattern(a: &str, b: &str) -> bool { |
dfeec247 XL |
100 | let mut a_iter = a.chars().peekable(); |
101 | let mut b_iter = b.chars().peekable(); | |
102 | ||
103 | loop { | |
104 | let (a, b) = match (a_iter.peek(), b_iter.peek()) { | |
105 | (None, None) => return true, | |
106 | (None, _) => return false, | |
107 | (Some(&a), None) => { | |
108 | if rustc_lexer::is_whitespace(a) { | |
109 | break; // Trailing whitespace check is out of loop for borrowck. | |
110 | } else { | |
111 | return false; | |
112 | } | |
113 | } | |
114 | (Some(&a), Some(&b)) => (a, b), | |
115 | }; | |
116 | ||
117 | if rustc_lexer::is_whitespace(a) && rustc_lexer::is_whitespace(b) { | |
118 | // Skip whitespace for `a` and `b`. | |
119 | scan_for_non_ws_or_end(&mut a_iter); | |
120 | scan_for_non_ws_or_end(&mut b_iter); | |
121 | } else if rustc_lexer::is_whitespace(a) { | |
122 | // Skip whitespace for `a`. | |
123 | scan_for_non_ws_or_end(&mut a_iter); | |
124 | } else if a == b { | |
125 | a_iter.next(); | |
126 | b_iter.next(); | |
127 | } else { | |
128 | return false; | |
129 | } | |
130 | } | |
131 | ||
132 | // Check if a has *only* trailing whitespace. | |
133 | a_iter.all(rustc_lexer::is_whitespace) | |
134 | } | |
135 | ||
136 | /// Advances the given peekable `Iterator` until it reaches a non-whitespace character. | |
137 | fn scan_for_non_ws_or_end<I: Iterator<Item = char>>(iter: &mut Peekable<I>) { | |
4b012472 | 138 | while iter.peek().copied().is_some_and(rustc_lexer::is_whitespace) { |
dfeec247 XL |
139 | iter.next(); |
140 | } | |
141 | } | |
142 | ||
143 | /// Identifies a position in the text by the n'th occurrence of a string. | |
144 | struct Position { | |
145 | string: &'static str, | |
146 | count: usize, | |
147 | } | |
148 | ||
149 | struct SpanLabel { | |
150 | start: Position, | |
151 | end: Position, | |
152 | label: &'static str, | |
153 | } | |
154 | ||
923072b8 | 155 | pub(crate) struct Shared<T: Write> { |
dfeec247 XL |
156 | pub data: Arc<Mutex<T>>, |
157 | } | |
158 | ||
add651ee FG |
159 | impl<T: Write> WriteColor for Shared<T> { |
160 | fn supports_color(&self) -> bool { | |
161 | false | |
162 | } | |
163 | ||
164 | fn set_color(&mut self, _spec: &termcolor::ColorSpec) -> io::Result<()> { | |
165 | Ok(()) | |
166 | } | |
167 | ||
168 | fn reset(&mut self) -> io::Result<()> { | |
169 | Ok(()) | |
170 | } | |
171 | } | |
172 | ||
dfeec247 XL |
173 | impl<T: Write> Write for Shared<T> { |
174 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | |
175 | self.data.lock().unwrap().write(buf) | |
176 | } | |
177 | ||
178 | fn flush(&mut self) -> io::Result<()> { | |
179 | self.data.lock().unwrap().flush() | |
180 | } | |
181 | } | |
182 | ||
183 | fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &str) { | |
ed00b5ec | 184 | create_default_session_globals_then(|| { |
add651ee | 185 | let (handler, source_map, output) = create_test_handler(); |
dfeec247 XL |
186 | source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned()); |
187 | ||
188 | let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end); | |
189 | let mut msp = MultiSpan::from_span(primary_span); | |
190 | for span_label in span_labels { | |
191 | let span = make_span(&file_text, &span_label.start, &span_label.end); | |
064997fb | 192 | msp.push_span_label(span, span_label.label); |
dfeec247 XL |
193 | println!("span: {:?} label: {:?}", span, span_label.label); |
194 | println!("text: {:?}", source_map.span_to_snippet(span)); | |
195 | } | |
196 | ||
9c376795 | 197 | #[allow(rustc::untranslatable_diagnostic)] |
dfeec247 XL |
198 | handler.span_err(msp, "foo"); |
199 | ||
200 | assert!( | |
201 | expected_output.chars().next() == Some('\n'), | |
202 | "expected output should begin with newline" | |
203 | ); | |
204 | let expected_output = &expected_output[1..]; | |
205 | ||
206 | let bytes = output.lock().unwrap(); | |
207 | let actual_output = str::from_utf8(&bytes).unwrap(); | |
208 | println!("expected output:\n------\n{}------", expected_output); | |
209 | println!("actual output:\n------\n{}------", actual_output); | |
210 | ||
211 | assert!(expected_output == actual_output) | |
212 | }) | |
213 | } | |
214 | ||
215 | fn make_span(file_text: &str, start: &Position, end: &Position) -> Span { | |
216 | let start = make_pos(file_text, start); | |
217 | let end = make_pos(file_text, end) + end.string.len(); // just after matching thing ends | |
218 | assert!(start <= end); | |
219 | Span::with_root_ctxt(BytePos(start as u32), BytePos(end as u32)) | |
220 | } | |
221 | ||
222 | fn make_pos(file_text: &str, pos: &Position) -> usize { | |
223 | let mut remainder = file_text; | |
224 | let mut offset = 0; | |
225 | for _ in 0..pos.count { | |
226 | if let Some(n) = remainder.find(&pos.string) { | |
227 | offset += n; | |
228 | remainder = &remainder[n + 1..]; | |
229 | } else { | |
230 | panic!("failed to find {} instances of {:?} in {:?}", pos.count, pos.string, file_text); | |
231 | } | |
232 | } | |
233 | offset | |
234 | } | |
235 | ||
236 | #[test] | |
237 | fn ends_on_col0() { | |
238 | test_harness( | |
239 | r#" | |
240 | fn foo() { | |
241 | } | |
242 | "#, | |
243 | vec![SpanLabel { | |
244 | start: Position { string: "{", count: 1 }, | |
245 | end: Position { string: "}", count: 1 }, | |
246 | label: "test", | |
247 | }], | |
248 | r#" | |
249 | error: foo | |
250 | --> test.rs:2:10 | |
251 | | | |
252 | 2 | fn foo() { | |
253 | | __________^ | |
254 | 3 | | } | |
255 | | |_^ test | |
256 | ||
257 | "#, | |
258 | ); | |
259 | } | |
260 | ||
261 | #[test] | |
262 | fn ends_on_col2() { | |
263 | test_harness( | |
264 | r#" | |
265 | fn foo() { | |
266 | ||
267 | ||
268 | } | |
269 | "#, | |
270 | vec![SpanLabel { | |
271 | start: Position { string: "{", count: 1 }, | |
272 | end: Position { string: "}", count: 1 }, | |
273 | label: "test", | |
274 | }], | |
275 | r#" | |
276 | error: foo | |
277 | --> test.rs:2:10 | |
278 | | | |
279 | 2 | fn foo() { | |
280 | | __________^ | |
281 | 3 | | | |
282 | 4 | | | |
283 | 5 | | } | |
284 | | |___^ test | |
285 | ||
286 | "#, | |
287 | ); | |
288 | } | |
289 | #[test] | |
290 | fn non_nested() { | |
291 | test_harness( | |
292 | r#" | |
293 | fn foo() { | |
294 | X0 Y0 | |
295 | X1 Y1 | |
296 | X2 Y2 | |
297 | } | |
298 | "#, | |
299 | vec![ | |
300 | SpanLabel { | |
301 | start: Position { string: "X0", count: 1 }, | |
302 | end: Position { string: "X2", count: 1 }, | |
303 | label: "`X` is a good letter", | |
304 | }, | |
305 | SpanLabel { | |
306 | start: Position { string: "Y0", count: 1 }, | |
307 | end: Position { string: "Y2", count: 1 }, | |
308 | label: "`Y` is a good letter too", | |
309 | }, | |
310 | ], | |
311 | r#" | |
312 | error: foo | |
313 | --> test.rs:3:3 | |
314 | | | |
315 | 3 | X0 Y0 | |
487cf647 FG |
316 | | ___^__- |
317 | | |___| | |
dfeec247 XL |
318 | | || |
319 | 4 | || X1 Y1 | |
320 | 5 | || X2 Y2 | |
321 | | ||____^__- `Y` is a good letter too | |
487cf647 | 322 | | |_____| |
dfeec247 XL |
323 | | `X` is a good letter |
324 | ||
325 | "#, | |
326 | ); | |
327 | } | |
328 | ||
329 | #[test] | |
330 | fn nested() { | |
331 | test_harness( | |
332 | r#" | |
333 | fn foo() { | |
334 | X0 Y0 | |
335 | Y1 X1 | |
336 | } | |
337 | "#, | |
338 | vec![ | |
339 | SpanLabel { | |
340 | start: Position { string: "X0", count: 1 }, | |
341 | end: Position { string: "X1", count: 1 }, | |
342 | label: "`X` is a good letter", | |
343 | }, | |
344 | SpanLabel { | |
345 | start: Position { string: "Y0", count: 1 }, | |
346 | end: Position { string: "Y1", count: 1 }, | |
347 | label: "`Y` is a good letter too", | |
348 | }, | |
349 | ], | |
350 | r#" | |
351 | error: foo | |
352 | --> test.rs:3:3 | |
353 | | | |
354 | 3 | X0 Y0 | |
487cf647 FG |
355 | | ___^__- |
356 | | |___| | |
dfeec247 XL |
357 | | || |
358 | 4 | || Y1 X1 | |
359 | | ||____-__^ `X` is a good letter | |
487cf647 | 360 | | |____| |
dfeec247 XL |
361 | | `Y` is a good letter too |
362 | ||
363 | "#, | |
364 | ); | |
365 | } | |
366 | ||
367 | #[test] | |
368 | fn different_overlap() { | |
369 | test_harness( | |
370 | r#" | |
371 | fn foo() { | |
372 | X0 Y0 Z0 | |
373 | X1 Y1 Z1 | |
374 | X2 Y2 Z2 | |
375 | X3 Y3 Z3 | |
376 | } | |
377 | "#, | |
378 | vec![ | |
379 | SpanLabel { | |
380 | start: Position { string: "Y0", count: 1 }, | |
381 | end: Position { string: "X2", count: 1 }, | |
382 | label: "`X` is a good letter", | |
383 | }, | |
384 | SpanLabel { | |
385 | start: Position { string: "Z1", count: 1 }, | |
386 | end: Position { string: "X3", count: 1 }, | |
387 | label: "`Y` is a good letter too", | |
388 | }, | |
389 | ], | |
390 | r#" | |
391 | error: foo | |
392 | --> test.rs:3:6 | |
393 | | | |
394 | 3 | X0 Y0 Z0 | |
487cf647 FG |
395 | | _______^ |
396 | 4 | | X1 Y1 Z1 | |
397 | | | _________- | |
dfeec247 XL |
398 | 5 | || X2 Y2 Z2 |
399 | | ||____^ `X` is a good letter | |
487cf647 FG |
400 | 6 | | X3 Y3 Z3 |
401 | | |____- `Y` is a good letter too | |
dfeec247 XL |
402 | |
403 | "#, | |
404 | ); | |
405 | } | |
406 | ||
407 | #[test] | |
408 | fn triple_overlap() { | |
409 | test_harness( | |
410 | r#" | |
411 | fn foo() { | |
412 | X0 Y0 Z0 | |
413 | X1 Y1 Z1 | |
414 | X2 Y2 Z2 | |
415 | } | |
416 | "#, | |
417 | vec![ | |
418 | SpanLabel { | |
419 | start: Position { string: "X0", count: 1 }, | |
420 | end: Position { string: "X2", count: 1 }, | |
421 | label: "`X` is a good letter", | |
422 | }, | |
423 | SpanLabel { | |
424 | start: Position { string: "Y0", count: 1 }, | |
425 | end: Position { string: "Y2", count: 1 }, | |
426 | label: "`Y` is a good letter too", | |
427 | }, | |
428 | SpanLabel { | |
429 | start: Position { string: "Z0", count: 1 }, | |
430 | end: Position { string: "Z2", count: 1 }, | |
431 | label: "`Z` label", | |
432 | }, | |
433 | ], | |
434 | r#" | |
435 | error: foo | |
436 | --> test.rs:3:3 | |
437 | | | |
438 | 3 | X0 Y0 Z0 | |
487cf647 FG |
439 | | ___^__-__- |
440 | | |___|__| | |
441 | | ||___| | |
dfeec247 XL |
442 | | ||| |
443 | 4 | ||| X1 Y1 Z1 | |
444 | 5 | ||| X2 Y2 Z2 | |
445 | | |||____^__-__- `Z` label | |
487cf647 FG |
446 | | ||_____|__| |
447 | | |______| `Y` is a good letter too | |
dfeec247 XL |
448 | | `X` is a good letter |
449 | ||
450 | "#, | |
451 | ); | |
452 | } | |
453 | ||
454 | #[test] | |
455 | fn triple_exact_overlap() { | |
456 | test_harness( | |
457 | r#" | |
458 | fn foo() { | |
459 | X0 Y0 Z0 | |
460 | X1 Y1 Z1 | |
461 | X2 Y2 Z2 | |
462 | } | |
463 | "#, | |
464 | vec![ | |
465 | SpanLabel { | |
466 | start: Position { string: "X0", count: 1 }, | |
467 | end: Position { string: "X2", count: 1 }, | |
468 | label: "`X` is a good letter", | |
469 | }, | |
470 | SpanLabel { | |
471 | start: Position { string: "X0", count: 1 }, | |
472 | end: Position { string: "X2", count: 1 }, | |
473 | label: "`Y` is a good letter too", | |
474 | }, | |
475 | SpanLabel { | |
476 | start: Position { string: "X0", count: 1 }, | |
477 | end: Position { string: "X2", count: 1 }, | |
478 | label: "`Z` label", | |
479 | }, | |
480 | ], | |
481 | r#" | |
482 | error: foo | |
483 | --> test.rs:3:3 | |
484 | | | |
485 | 3 | / X0 Y0 Z0 | |
486 | 4 | | X1 Y1 Z1 | |
487 | 5 | | X2 Y2 Z2 | |
488 | | | ^ | |
489 | | | | | |
490 | | | `X` is a good letter | |
491 | | |____`Y` is a good letter too | |
492 | | `Z` label | |
493 | ||
494 | "#, | |
495 | ); | |
496 | } | |
497 | ||
498 | #[test] | |
499 | fn minimum_depth() { | |
500 | test_harness( | |
501 | r#" | |
502 | fn foo() { | |
503 | X0 Y0 Z0 | |
504 | X1 Y1 Z1 | |
505 | X2 Y2 Z2 | |
506 | X3 Y3 Z3 | |
507 | } | |
508 | "#, | |
509 | vec![ | |
510 | SpanLabel { | |
511 | start: Position { string: "Y0", count: 1 }, | |
512 | end: Position { string: "X1", count: 1 }, | |
513 | label: "`X` is a good letter", | |
514 | }, | |
515 | SpanLabel { | |
516 | start: Position { string: "Y1", count: 1 }, | |
517 | end: Position { string: "Z2", count: 1 }, | |
518 | label: "`Y` is a good letter too", | |
519 | }, | |
520 | SpanLabel { | |
521 | start: Position { string: "X2", count: 1 }, | |
522 | end: Position { string: "Y3", count: 1 }, | |
523 | label: "`Z`", | |
524 | }, | |
525 | ], | |
526 | r#" | |
527 | error: foo | |
528 | --> test.rs:3:6 | |
529 | | | |
530 | 3 | X0 Y0 Z0 | |
487cf647 FG |
531 | | _______^ |
532 | 4 | | X1 Y1 Z1 | |
533 | | | ____^_- | |
dfeec247 | 534 | | ||____| |
487cf647 FG |
535 | | | `X` is a good letter |
536 | 5 | | X2 Y2 Z2 | |
537 | | |___-______- `Y` is a good letter too | |
538 | | ___| | |
539 | | | | |
540 | 6 | | X3 Y3 Z3 | |
541 | | |_______- `Z` | |
dfeec247 XL |
542 | |
543 | "#, | |
544 | ); | |
545 | } | |
546 | ||
547 | #[test] | |
49aad941 | 548 | fn non_overlapping() { |
dfeec247 XL |
549 | test_harness( |
550 | r#" | |
551 | fn foo() { | |
552 | X0 Y0 Z0 | |
553 | X1 Y1 Z1 | |
554 | X2 Y2 Z2 | |
555 | X3 Y3 Z3 | |
556 | } | |
557 | "#, | |
558 | vec![ | |
559 | SpanLabel { | |
560 | start: Position { string: "X0", count: 1 }, | |
561 | end: Position { string: "X1", count: 1 }, | |
562 | label: "`X` is a good letter", | |
563 | }, | |
564 | SpanLabel { | |
565 | start: Position { string: "Y2", count: 1 }, | |
566 | end: Position { string: "Z3", count: 1 }, | |
567 | label: "`Y` is a good letter too", | |
568 | }, | |
569 | ], | |
570 | r#" | |
571 | error: foo | |
572 | --> test.rs:3:3 | |
573 | | | |
574 | 3 | / X0 Y0 Z0 | |
575 | 4 | | X1 Y1 Z1 | |
576 | | |____^ `X` is a good letter | |
577 | 5 | X2 Y2 Z2 | |
578 | | ______- | |
579 | 6 | | X3 Y3 Z3 | |
580 | | |__________- `Y` is a good letter too | |
581 | ||
582 | "#, | |
583 | ); | |
584 | } | |
585 | ||
586 | #[test] | |
49aad941 | 587 | fn overlapping_start_and_end() { |
dfeec247 XL |
588 | test_harness( |
589 | r#" | |
590 | fn foo() { | |
591 | X0 Y0 Z0 | |
592 | X1 Y1 Z1 | |
593 | X2 Y2 Z2 | |
594 | X3 Y3 Z3 | |
595 | } | |
596 | "#, | |
597 | vec![ | |
598 | SpanLabel { | |
599 | start: Position { string: "Y0", count: 1 }, | |
600 | end: Position { string: "X1", count: 1 }, | |
601 | label: "`X` is a good letter", | |
602 | }, | |
603 | SpanLabel { | |
604 | start: Position { string: "Z1", count: 1 }, | |
605 | end: Position { string: "Z3", count: 1 }, | |
606 | label: "`Y` is a good letter too", | |
607 | }, | |
608 | ], | |
609 | r#" | |
610 | error: foo | |
611 | --> test.rs:3:6 | |
612 | | | |
613 | 3 | X0 Y0 Z0 | |
487cf647 FG |
614 | | _______^ |
615 | 4 | | X1 Y1 Z1 | |
616 | | | ____^____- | |
dfeec247 | 617 | | ||____| |
487cf647 FG |
618 | | | `X` is a good letter |
619 | 5 | | X2 Y2 Z2 | |
620 | 6 | | X3 Y3 Z3 | |
621 | | |__________- `Y` is a good letter too | |
dfeec247 XL |
622 | |
623 | "#, | |
624 | ); | |
625 | } | |
626 | ||
627 | #[test] | |
628 | fn multiple_labels_primary_without_message() { | |
629 | test_harness( | |
630 | r#" | |
631 | fn foo() { | |
632 | a { b { c } d } | |
633 | } | |
634 | "#, | |
635 | vec![ | |
636 | SpanLabel { | |
637 | start: Position { string: "b", count: 1 }, | |
638 | end: Position { string: "}", count: 1 }, | |
639 | label: "", | |
640 | }, | |
641 | SpanLabel { | |
642 | start: Position { string: "a", count: 1 }, | |
643 | end: Position { string: "d", count: 1 }, | |
644 | label: "`a` is a good letter", | |
645 | }, | |
646 | SpanLabel { | |
647 | start: Position { string: "c", count: 1 }, | |
648 | end: Position { string: "c", count: 1 }, | |
649 | label: "", | |
650 | }, | |
651 | ], | |
652 | r#" | |
653 | error: foo | |
654 | --> test.rs:3:7 | |
655 | | | |
656 | 3 | a { b { c } d } | |
657 | | ----^^^^-^^-- `a` is a good letter | |
658 | ||
659 | "#, | |
660 | ); | |
661 | } | |
662 | ||
663 | #[test] | |
664 | fn multiple_labels_secondary_without_message() { | |
665 | test_harness( | |
666 | r#" | |
667 | fn foo() { | |
668 | a { b { c } d } | |
669 | } | |
670 | "#, | |
671 | vec![ | |
672 | SpanLabel { | |
673 | start: Position { string: "a", count: 1 }, | |
674 | end: Position { string: "d", count: 1 }, | |
675 | label: "`a` is a good letter", | |
676 | }, | |
677 | SpanLabel { | |
678 | start: Position { string: "b", count: 1 }, | |
679 | end: Position { string: "}", count: 1 }, | |
680 | label: "", | |
681 | }, | |
682 | ], | |
683 | r#" | |
684 | error: foo | |
685 | --> test.rs:3:3 | |
686 | | | |
687 | 3 | a { b { c } d } | |
688 | | ^^^^-------^^ `a` is a good letter | |
689 | ||
690 | "#, | |
691 | ); | |
692 | } | |
693 | ||
694 | #[test] | |
695 | fn multiple_labels_primary_without_message_2() { | |
696 | test_harness( | |
697 | r#" | |
698 | fn foo() { | |
699 | a { b { c } d } | |
700 | } | |
701 | "#, | |
702 | vec![ | |
703 | SpanLabel { | |
704 | start: Position { string: "b", count: 1 }, | |
705 | end: Position { string: "}", count: 1 }, | |
706 | label: "`b` is a good letter", | |
707 | }, | |
708 | SpanLabel { | |
709 | start: Position { string: "a", count: 1 }, | |
710 | end: Position { string: "d", count: 1 }, | |
711 | label: "", | |
712 | }, | |
713 | SpanLabel { | |
714 | start: Position { string: "c", count: 1 }, | |
715 | end: Position { string: "c", count: 1 }, | |
716 | label: "", | |
717 | }, | |
718 | ], | |
719 | r#" | |
720 | error: foo | |
721 | --> test.rs:3:7 | |
722 | | | |
723 | 3 | a { b { c } d } | |
724 | | ----^^^^-^^-- | |
725 | | | | |
726 | | `b` is a good letter | |
727 | ||
728 | "#, | |
729 | ); | |
730 | } | |
731 | ||
732 | #[test] | |
733 | fn multiple_labels_secondary_without_message_2() { | |
734 | test_harness( | |
735 | r#" | |
736 | fn foo() { | |
737 | a { b { c } d } | |
738 | } | |
739 | "#, | |
740 | vec![ | |
741 | SpanLabel { | |
742 | start: Position { string: "a", count: 1 }, | |
743 | end: Position { string: "d", count: 1 }, | |
744 | label: "", | |
745 | }, | |
746 | SpanLabel { | |
747 | start: Position { string: "b", count: 1 }, | |
748 | end: Position { string: "}", count: 1 }, | |
749 | label: "`b` is a good letter", | |
750 | }, | |
751 | ], | |
752 | r#" | |
753 | error: foo | |
754 | --> test.rs:3:3 | |
755 | | | |
756 | 3 | a { b { c } d } | |
757 | | ^^^^-------^^ | |
758 | | | | |
759 | | `b` is a good letter | |
760 | ||
761 | "#, | |
762 | ); | |
763 | } | |
764 | ||
765 | #[test] | |
766 | fn multiple_labels_secondary_without_message_3() { | |
767 | test_harness( | |
768 | r#" | |
769 | fn foo() { | |
770 | a bc d | |
771 | } | |
772 | "#, | |
773 | vec![ | |
774 | SpanLabel { | |
775 | start: Position { string: "a", count: 1 }, | |
776 | end: Position { string: "b", count: 1 }, | |
777 | label: "`a` is a good letter", | |
778 | }, | |
779 | SpanLabel { | |
780 | start: Position { string: "c", count: 1 }, | |
781 | end: Position { string: "d", count: 1 }, | |
782 | label: "", | |
783 | }, | |
784 | ], | |
785 | r#" | |
786 | error: foo | |
787 | --> test.rs:3:3 | |
788 | | | |
789 | 3 | a bc d | |
790 | | ^^^^---- | |
791 | | | | |
792 | | `a` is a good letter | |
793 | ||
794 | "#, | |
795 | ); | |
796 | } | |
797 | ||
798 | #[test] | |
799 | fn multiple_labels_without_message() { | |
800 | test_harness( | |
801 | r#" | |
802 | fn foo() { | |
803 | a { b { c } d } | |
804 | } | |
805 | "#, | |
806 | vec![ | |
807 | SpanLabel { | |
808 | start: Position { string: "a", count: 1 }, | |
809 | end: Position { string: "d", count: 1 }, | |
810 | label: "", | |
811 | }, | |
812 | SpanLabel { | |
813 | start: Position { string: "b", count: 1 }, | |
814 | end: Position { string: "}", count: 1 }, | |
815 | label: "", | |
816 | }, | |
817 | ], | |
818 | r#" | |
819 | error: foo | |
820 | --> test.rs:3:3 | |
821 | | | |
822 | 3 | a { b { c } d } | |
823 | | ^^^^-------^^ | |
824 | ||
825 | "#, | |
826 | ); | |
827 | } | |
828 | ||
829 | #[test] | |
830 | fn multiple_labels_without_message_2() { | |
831 | test_harness( | |
832 | r#" | |
833 | fn foo() { | |
834 | a { b { c } d } | |
835 | } | |
836 | "#, | |
837 | vec![ | |
838 | SpanLabel { | |
839 | start: Position { string: "b", count: 1 }, | |
840 | end: Position { string: "}", count: 1 }, | |
841 | label: "", | |
842 | }, | |
843 | SpanLabel { | |
844 | start: Position { string: "a", count: 1 }, | |
845 | end: Position { string: "d", count: 1 }, | |
846 | label: "", | |
847 | }, | |
848 | SpanLabel { | |
849 | start: Position { string: "c", count: 1 }, | |
850 | end: Position { string: "c", count: 1 }, | |
851 | label: "", | |
852 | }, | |
853 | ], | |
854 | r#" | |
855 | error: foo | |
856 | --> test.rs:3:7 | |
857 | | | |
858 | 3 | a { b { c } d } | |
859 | | ----^^^^-^^-- | |
860 | ||
861 | "#, | |
862 | ); | |
863 | } | |
864 | ||
865 | #[test] | |
866 | fn multiple_labels_with_message() { | |
867 | test_harness( | |
868 | r#" | |
869 | fn foo() { | |
870 | a { b { c } d } | |
871 | } | |
872 | "#, | |
873 | vec![ | |
874 | SpanLabel { | |
875 | start: Position { string: "a", count: 1 }, | |
876 | end: Position { string: "d", count: 1 }, | |
877 | label: "`a` is a good letter", | |
878 | }, | |
879 | SpanLabel { | |
880 | start: Position { string: "b", count: 1 }, | |
881 | end: Position { string: "}", count: 1 }, | |
882 | label: "`b` is a good letter", | |
883 | }, | |
884 | ], | |
885 | r#" | |
886 | error: foo | |
887 | --> test.rs:3:3 | |
888 | | | |
889 | 3 | a { b { c } d } | |
890 | | ^^^^-------^^ | |
891 | | | | | |
892 | | | `b` is a good letter | |
893 | | `a` is a good letter | |
894 | ||
895 | "#, | |
896 | ); | |
897 | } | |
898 | ||
899 | #[test] | |
900 | fn single_label_with_message() { | |
901 | test_harness( | |
902 | r#" | |
903 | fn foo() { | |
904 | a { b { c } d } | |
905 | } | |
906 | "#, | |
907 | vec![SpanLabel { | |
908 | start: Position { string: "a", count: 1 }, | |
909 | end: Position { string: "d", count: 1 }, | |
910 | label: "`a` is a good letter", | |
911 | }], | |
912 | r#" | |
913 | error: foo | |
914 | --> test.rs:3:3 | |
915 | | | |
916 | 3 | a { b { c } d } | |
917 | | ^^^^^^^^^^^^^ `a` is a good letter | |
918 | ||
919 | "#, | |
920 | ); | |
921 | } | |
922 | ||
923 | #[test] | |
924 | fn single_label_without_message() { | |
925 | test_harness( | |
926 | r#" | |
927 | fn foo() { | |
928 | a { b { c } d } | |
929 | } | |
930 | "#, | |
931 | vec![SpanLabel { | |
932 | start: Position { string: "a", count: 1 }, | |
933 | end: Position { string: "d", count: 1 }, | |
934 | label: "", | |
935 | }], | |
936 | r#" | |
937 | error: foo | |
938 | --> test.rs:3:3 | |
939 | | | |
940 | 3 | a { b { c } d } | |
941 | | ^^^^^^^^^^^^^ | |
942 | ||
943 | "#, | |
944 | ); | |
945 | } | |
946 | ||
947 | #[test] | |
948 | fn long_snippet() { | |
949 | test_harness( | |
950 | r#" | |
951 | fn foo() { | |
952 | X0 Y0 Z0 | |
953 | X1 Y1 Z1 | |
954 | 1 | |
955 | 2 | |
956 | 3 | |
957 | 4 | |
958 | 5 | |
959 | 6 | |
960 | 7 | |
961 | 8 | |
962 | 9 | |
963 | 10 | |
964 | X2 Y2 Z2 | |
965 | X3 Y3 Z3 | |
966 | } | |
967 | "#, | |
968 | vec![ | |
969 | SpanLabel { | |
970 | start: Position { string: "Y0", count: 1 }, | |
971 | end: Position { string: "X1", count: 1 }, | |
972 | label: "`X` is a good letter", | |
973 | }, | |
974 | SpanLabel { | |
975 | start: Position { string: "Z1", count: 1 }, | |
976 | end: Position { string: "Z3", count: 1 }, | |
977 | label: "`Y` is a good letter too", | |
978 | }, | |
979 | ], | |
980 | r#" | |
981 | error: foo | |
982 | --> test.rs:3:6 | |
983 | | | |
984 | 3 | X0 Y0 Z0 | |
487cf647 FG |
985 | | _______^ |
986 | 4 | | X1 Y1 Z1 | |
987 | | | ____^____- | |
dfeec247 | 988 | | ||____| |
487cf647 FG |
989 | | | `X` is a good letter |
990 | 5 | | 1 | |
991 | 6 | | 2 | |
992 | 7 | | 3 | |
993 | ... | | |
994 | 15 | | X2 Y2 Z2 | |
995 | 16 | | X3 Y3 Z3 | |
996 | | |__________- `Y` is a good letter too | |
dfeec247 XL |
997 | |
998 | "#, | |
999 | ); | |
1000 | } | |
1001 | ||
1002 | #[test] | |
1003 | fn long_snippet_multiple_spans() { | |
1004 | test_harness( | |
1005 | r#" | |
1006 | fn foo() { | |
1007 | X0 Y0 Z0 | |
1008 | 1 | |
1009 | 2 | |
1010 | 3 | |
1011 | X1 Y1 Z1 | |
1012 | 4 | |
1013 | 5 | |
1014 | 6 | |
1015 | X2 Y2 Z2 | |
1016 | 7 | |
1017 | 8 | |
1018 | 9 | |
1019 | 10 | |
1020 | X3 Y3 Z3 | |
1021 | } | |
1022 | "#, | |
1023 | vec![ | |
1024 | SpanLabel { | |
1025 | start: Position { string: "Y0", count: 1 }, | |
1026 | end: Position { string: "Y3", count: 1 }, | |
1027 | label: "`Y` is a good letter", | |
1028 | }, | |
1029 | SpanLabel { | |
1030 | start: Position { string: "Z1", count: 1 }, | |
1031 | end: Position { string: "Z2", count: 1 }, | |
1032 | label: "`Z` is a good letter too", | |
1033 | }, | |
1034 | ], | |
1035 | r#" | |
1036 | error: foo | |
1037 | --> test.rs:3:6 | |
1038 | | | |
1039 | 3 | X0 Y0 Z0 | |
487cf647 FG |
1040 | | _______^ |
1041 | 4 | | 1 | |
1042 | 5 | | 2 | |
1043 | 6 | | 3 | |
1044 | 7 | | X1 Y1 Z1 | |
1045 | | | _________- | |
dfeec247 XL |
1046 | 8 | || 4 |
1047 | 9 | || 5 | |
1048 | 10 | || 6 | |
1049 | 11 | || X2 Y2 Z2 | |
1050 | | ||__________- `Z` is a good letter too | |
487cf647 FG |
1051 | ... | |
1052 | 15 | | 10 | |
1053 | 16 | | X3 Y3 Z3 | |
1054 | | |________^ `Y` is a good letter | |
dfeec247 XL |
1055 | |
1056 | "#, | |
1057 | ); | |
1058 | } |