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