1use super::ast::*;
2use std::borrow::Cow;
3
4#[derive(Copy, Clone, PartialEq, Eq, Debug)]
6pub enum SpanKind {
7 Delimiter(usize),
11 Separator,
13 Number,
15 Literal,
17 Lifetime,
19 String,
21 Path,
23 Space(usize),
25
26 Constructor,
29
30 Surroundings,
33
34 Text,
36}
37
38#[derive(Clone, PartialEq, Eq, Debug)]
41pub struct Span<'a> {
42 pub text: Cow<'a, str>,
44 pub kind: SpanKind,
46}
47
48pub struct Config {
50 pub collapse_space: bool,
52}
53
54pub trait IntoSpans<'a>: private::IntoSpansImpl<'a> {}
55
56pub fn into_spans<'a>(ast: impl IntoSpans<'a>, config: Config) -> Vec<Span<'a>> {
58 let mut cx = private::Context {
59 config,
60 res: Vec::new(),
61 depth: 0,
62 };
63 ast.into_spans(&mut cx);
64 cx.res
65}
66
67mod private {
68 use super::*;
69
70 pub struct Context<'a> {
71 pub config: Config,
72 pub res: Vec<Span<'a>>,
73 pub depth: usize,
74 }
75
76 impl<'a> Context<'a> {
77 fn push(&mut self, text: impl Into<Cow<'a, str>>, kind: SpanKind) {
78 self.res.push(Span {
79 text: text.into(),
80 kind,
81 })
82 }
83 }
84
85 pub trait IntoSpansImpl<'a> {
86 fn into_spans(self, cx: &mut Context<'a>);
87 }
88
89 impl<'a, T> IntoSpans<'a> for T where T: IntoSpansImpl<'a> {}
90
91 impl<'a> IntoSpansImpl<'a> for Separator {
92 fn into_spans(self, cx: &mut Context<'a>) {
93 match self {
94 Separator::Eq => cx.push("=", SpanKind::Separator),
95 Separator::Colon => cx.push(":", SpanKind::Separator),
96 Separator::DoubleColon => cx.push("::", SpanKind::Separator),
97 }
98 }
99 }
100
101 impl<'a> IntoSpansImpl<'a> for QuoteType {
102 fn into_spans(self, cx: &mut Context<'a>) {
103 match self {
104 QuoteType::Single => cx.push("'", SpanKind::Separator),
105 QuoteType::Double => cx.push("\"", SpanKind::Separator),
106 QuoteType::Backtick => cx.push("`", SpanKind::Separator),
107 }
108 }
109 }
110
111 impl<'a> IntoSpansImpl<'a> for AnyString<'a> {
112 fn into_spans(self, cx: &mut Context<'a>) {
113 let Self {
114 prefix,
115 ty,
116 contents,
117 num_hashtags,
118 suffix,
119 } = self;
120 cx.push(prefix, SpanKind::Surroundings);
121 for _ in 0..num_hashtags {
122 cx.push("#", SpanKind::Surroundings)
123 }
124
125 ty.into_spans(cx);
126 cx.push(contents, SpanKind::String);
127 ty.into_spans(cx);
128
129 for _ in 0..num_hashtags {
130 cx.push("#", SpanKind::Surroundings)
131 }
132 cx.push(suffix, SpanKind::Surroundings);
133 }
134 }
135
136 impl<'a> IntoSpansImpl<'a> for Path<'a> {
137 fn into_spans(self, cx: &mut Context<'a>) {
138 cx.push(self.to_string(), SpanKind::Path)
139 }
140 }
141
142 impl<'a> IntoSpansImpl<'a> for Number<'a> {
143 fn into_spans(self, cx: &mut Context<'a>) {
144 cx.push(self.number, SpanKind::Number);
145
146 if let Some(suffix) = self.suffix_without_underscore {
147 cx.push("_", SpanKind::Surroundings);
148 cx.push(suffix, SpanKind::Surroundings);
149 }
150 }
151 }
152
153 impl<'a> IntoSpansImpl<'a> for Atom<'a> {
154 fn into_spans(self, cx: &mut Context<'a>) {
155 match self {
156 Atom::Text(text) => cx.push(text, SpanKind::Text),
157 }
158 }
159 }
160
161 impl<'a> IntoSpansImpl<'a> for Space<'a> {
162 fn into_spans(self, cx: &mut Context<'a>) {
163 match self.0.len() {
164 0 => {}
165 1 => cx.push(self.0, SpanKind::Space(1)),
166 n if cx.config.collapse_space => cx.push(" ", SpanKind::Space(n)),
167 n => cx.push(self.0, SpanKind::Space(n)),
168 }
169 }
170 }
171
172 impl<'a> IntoSpansImpl<'a> for Token<'a> {
173 fn into_spans(self, cx: &mut Context<'a>) {
174 match self {
175 Token::True => cx.push("true", SpanKind::Literal),
176 Token::False => cx.push("false", SpanKind::Literal),
177 Token::None => cx.push("None", SpanKind::Literal),
178 Token::Path(path) => path.into_spans(cx),
179 Token::String(string) => string.into_spans(cx),
180 Token::Number(number) => number.into_spans(cx),
181 Token::Separated {
182 before,
183 space_before,
184 separator,
185 after,
186 } => {
187 before.into_spans(cx);
188 space_before.into_spans(cx);
189 separator.into_spans(cx);
190 after.into_spans(cx);
191 }
192 Token::Delimited(delimited) => {
193 delimited.into_spans(cx);
194 }
195 Token::Atom(atom) => {
196 atom.into_spans(cx);
197 }
198 Token::Lifetime(lifetime) => {
199 cx.push("'", SpanKind::Surroundings);
200 cx.push(lifetime, SpanKind::Lifetime);
201 }
202 }
203 }
204 }
205
206 impl<'a> IntoSpansImpl<'a> for Segment<'a> {
207 fn into_spans(self, cx: &mut Context<'a>) {
208 let Self {
209 leading_space,
210 token,
211 } = self;
212 leading_space.into_spans(cx);
213 token.into_spans(cx);
214 }
215 }
216
217 impl<'a> IntoSpansImpl<'a> for Segments<'a> {
218 fn into_spans(self, cx: &mut Context<'a>) {
219 let Self {
220 segments,
221 trailing_space,
222 } = self;
223 for segment in segments {
224 segment.into_spans(cx);
225 }
226 trailing_space.into_spans(cx);
227 }
228 }
229
230 impl<'a> IntoSpansImpl<'a> for Delimited<'a> {
231 fn into_spans(self, cx: &mut Context<'a>) {
232 let Self {
233 prefix,
234 delimiter,
235 contents,
236 } = self;
237
238 match prefix {
239 Some((Atom::Text(text), space)) => {
240 cx.push(
241 text,
242 match delimiter {
243 Delimiter::Brace => SpanKind::Constructor,
244 Delimiter::Paren if space.0.is_empty() => SpanKind::Constructor,
245 Delimiter::Paren => SpanKind::Text,
246 Delimiter::Bracket => SpanKind::Text,
247 Delimiter::Angle if space.0.is_empty() => SpanKind::Constructor,
248 Delimiter::Angle => SpanKind::Text,
249 },
250 );
251 space.into_spans(cx);
252 }
253 None => {}
254 }
255
256 match delimiter {
257 Delimiter::Paren => cx.push("(", SpanKind::Delimiter(cx.depth)),
258 Delimiter::Bracket => cx.push("[", SpanKind::Delimiter(cx.depth)),
259 Delimiter::Brace => cx.push("{", SpanKind::Delimiter(cx.depth)),
260 Delimiter::Angle => cx.push("<", SpanKind::Delimiter(cx.depth)),
261 }
262
263 cx.depth += 1;
264 contents.into_spans(cx);
265 cx.depth -= 1;
266
267 match delimiter {
268 Delimiter::Paren => cx.push(")", SpanKind::Delimiter(cx.depth)),
269 Delimiter::Bracket => cx.push("]", SpanKind::Delimiter(cx.depth)),
270 Delimiter::Brace => cx.push("}", SpanKind::Delimiter(cx.depth)),
271 Delimiter::Angle => cx.push(">", SpanKind::Delimiter(cx.depth)),
272 }
273 }
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use insta::assert_debug_snapshot;
280
281 use super::SpanKind;
282 use crate::{Config, into_spans, parse_input};
283
284 fn spans(input: &str) -> Vec<(String, SpanKind)> {
285 let res = parse_input(input).unwrap();
286 into_spans(
287 res,
288 Config {
289 collapse_space: true,
290 },
291 )
292 .into_iter()
293 .map(|i| (i.text.into_owned(), i.kind))
294 .collect()
295 }
296
297 #[test]
298 fn spans_ex1() {
299 assert_debug_snapshot!(spans(
300 r#"def_id=DefId(0:3 ~ unsized_coercion[10fa]::Trait)"#
301 ), @r#"
302 [
303 (
304 "def_id",
305 Text,
306 ),
307 (
308 "=",
309 Separator,
310 ),
311 (
312 "DefId",
313 Constructor,
314 ),
315 (
316 "(",
317 Delimiter(
318 0,
319 ),
320 ),
321 (
322 "0",
323 Number,
324 ),
325 (
326 ":",
327 Separator,
328 ),
329 (
330 "3",
331 Number,
332 ),
333 (
334 " ",
335 Space(
336 1,
337 ),
338 ),
339 (
340 "~",
341 Text,
342 ),
343 (
344 " ",
345 Space(
346 1,
347 ),
348 ),
349 (
350 "unsized_coercion",
351 Text,
352 ),
353 (
354 "[",
355 Delimiter(
356 1,
357 ),
358 ),
359 (
360 "10fa",
361 Text,
362 ),
363 (
364 "]",
365 Delimiter(
366 1,
367 ),
368 ),
369 (
370 "::",
371 Separator,
372 ),
373 (
374 "Trait",
375 Text,
376 ),
377 (
378 ")",
379 Delimiter(
380 0,
381 ),
382 ),
383 ]
384 "#)
385 }
386
387 #[test]
388 fn spans_ex2() {
389 assert_debug_snapshot!(spans(
390 r#"data=TypeNs("MetaSized") visible_parent=DefId(2:3984 ~ core[bcc4]::marker) actual_parent=Some(DefId(2:3984 ~ core[bcc4]::marker))"#
391 ), @r#"
392 [
393 (
394 "data",
395 Text,
396 ),
397 (
398 "=",
399 Separator,
400 ),
401 (
402 "TypeNs",
403 Constructor,
404 ),
405 (
406 "(",
407 Delimiter(
408 0,
409 ),
410 ),
411 (
412 "",
413 Surroundings,
414 ),
415 (
416 "\"",
417 Separator,
418 ),
419 (
420 "MetaSized",
421 String,
422 ),
423 (
424 "\"",
425 Separator,
426 ),
427 (
428 "",
429 Surroundings,
430 ),
431 (
432 ")",
433 Delimiter(
434 0,
435 ),
436 ),
437 (
438 " ",
439 Space(
440 1,
441 ),
442 ),
443 (
444 "visible_parent",
445 Text,
446 ),
447 (
448 "=",
449 Separator,
450 ),
451 (
452 "DefId",
453 Constructor,
454 ),
455 (
456 "(",
457 Delimiter(
458 0,
459 ),
460 ),
461 (
462 "2",
463 Number,
464 ),
465 (
466 ":",
467 Separator,
468 ),
469 (
470 "3984",
471 Number,
472 ),
473 (
474 " ",
475 Space(
476 1,
477 ),
478 ),
479 (
480 "~",
481 Text,
482 ),
483 (
484 " ",
485 Space(
486 1,
487 ),
488 ),
489 (
490 "core",
491 Text,
492 ),
493 (
494 "[",
495 Delimiter(
496 1,
497 ),
498 ),
499 (
500 "bcc4",
501 Text,
502 ),
503 (
504 "]",
505 Delimiter(
506 1,
507 ),
508 ),
509 (
510 "::",
511 Separator,
512 ),
513 (
514 "marker",
515 Text,
516 ),
517 (
518 ")",
519 Delimiter(
520 0,
521 ),
522 ),
523 (
524 " ",
525 Space(
526 1,
527 ),
528 ),
529 (
530 "actual_parent",
531 Text,
532 ),
533 (
534 "=",
535 Separator,
536 ),
537 (
538 "Some",
539 Constructor,
540 ),
541 (
542 "(",
543 Delimiter(
544 0,
545 ),
546 ),
547 (
548 "DefId",
549 Constructor,
550 ),
551 (
552 "(",
553 Delimiter(
554 1,
555 ),
556 ),
557 (
558 "2",
559 Number,
560 ),
561 (
562 ":",
563 Separator,
564 ),
565 (
566 "3984",
567 Number,
568 ),
569 (
570 " ",
571 Space(
572 1,
573 ),
574 ),
575 (
576 "~",
577 Text,
578 ),
579 (
580 " ",
581 Space(
582 1,
583 ),
584 ),
585 (
586 "core",
587 Text,
588 ),
589 (
590 "[",
591 Delimiter(
592 2,
593 ),
594 ),
595 (
596 "bcc4",
597 Text,
598 ),
599 (
600 "]",
601 Delimiter(
602 2,
603 ),
604 ),
605 (
606 "::",
607 Separator,
608 ),
609 (
610 "marker",
611 Text,
612 ),
613 (
614 ")",
615 Delimiter(
616 1,
617 ),
618 ),
619 (
620 ")",
621 Delimiter(
622 0,
623 ),
624 ),
625 ]
626 "#)
627 }
628
629 #[test]
630 fn spans_ex3() {
631 assert_debug_snapshot!(spans(
632 r#"insert(DefId(0:4 ~ unsized_coercion[10fa]::{impl#0})): inserting TraitRef <u32 as Trait> into specialization graph"#
633 ), @r#"
634 [
635 (
636 "insert",
637 Constructor,
638 ),
639 (
640 "(",
641 Delimiter(
642 0,
643 ),
644 ),
645 (
646 "DefId",
647 Constructor,
648 ),
649 (
650 "(",
651 Delimiter(
652 1,
653 ),
654 ),
655 (
656 "0",
657 Number,
658 ),
659 (
660 ":",
661 Separator,
662 ),
663 (
664 "4",
665 Number,
666 ),
667 (
668 " ",
669 Space(
670 1,
671 ),
672 ),
673 (
674 "~",
675 Text,
676 ),
677 (
678 " ",
679 Space(
680 1,
681 ),
682 ),
683 (
684 "unsized_coercion",
685 Text,
686 ),
687 (
688 "[",
689 Delimiter(
690 2,
691 ),
692 ),
693 (
694 "10fa",
695 Text,
696 ),
697 (
698 "]",
699 Delimiter(
700 2,
701 ),
702 ),
703 (
704 "::",
705 Separator,
706 ),
707 (
708 "{",
709 Delimiter(
710 2,
711 ),
712 ),
713 (
714 "impl#0",
715 Text,
716 ),
717 (
718 "}",
719 Delimiter(
720 2,
721 ),
722 ),
723 (
724 ")",
725 Delimiter(
726 1,
727 ),
728 ),
729 (
730 ")",
731 Delimiter(
732 0,
733 ),
734 ),
735 (
736 ":",
737 Separator,
738 ),
739 (
740 " ",
741 Space(
742 1,
743 ),
744 ),
745 (
746 "inserting",
747 Text,
748 ),
749 (
750 " ",
751 Space(
752 1,
753 ),
754 ),
755 (
756 "TraitRef",
757 Text,
758 ),
759 (
760 " ",
761 Space(
762 1,
763 ),
764 ),
765 (
766 "<",
767 Delimiter(
768 0,
769 ),
770 ),
771 (
772 "u32",
773 Text,
774 ),
775 (
776 " ",
777 Space(
778 1,
779 ),
780 ),
781 (
782 "as",
783 Text,
784 ),
785 (
786 " ",
787 Space(
788 1,
789 ),
790 ),
791 (
792 "Trait",
793 Text,
794 ),
795 (
796 ">",
797 Delimiter(
798 0,
799 ),
800 ),
801 (
802 " ",
803 Space(
804 1,
805 ),
806 ),
807 (
808 "into",
809 Text,
810 ),
811 (
812 " ",
813 Space(
814 1,
815 ),
816 ),
817 (
818 "specialization",
819 Text,
820 ),
821 (
822 " ",
823 Space(
824 1,
825 ),
826 ),
827 (
828 "graph",
829 Text,
830 ),
831 ]
832 "#)
833 }
834
835 #[test]
836 fn spans_ex4() {
837 assert_debug_snapshot!(spans(
838 r#"inspecting def_id=DefId(3:662 ~ alloc[ef11]::boxed::Box) span=tests/ui/impl-trait/unsized_coercion.rs:12:15: 12:30 (#0)"#
839 ), @r##"
840 [
841 (
842 "inspecting",
843 Text,
844 ),
845 (
846 " ",
847 Space(
848 1,
849 ),
850 ),
851 (
852 "def_id",
853 Text,
854 ),
855 (
856 "=",
857 Separator,
858 ),
859 (
860 "DefId",
861 Constructor,
862 ),
863 (
864 "(",
865 Delimiter(
866 0,
867 ),
868 ),
869 (
870 "3",
871 Number,
872 ),
873 (
874 ":",
875 Separator,
876 ),
877 (
878 "662",
879 Number,
880 ),
881 (
882 " ",
883 Space(
884 1,
885 ),
886 ),
887 (
888 "~",
889 Text,
890 ),
891 (
892 " ",
893 Space(
894 1,
895 ),
896 ),
897 (
898 "alloc",
899 Text,
900 ),
901 (
902 "[",
903 Delimiter(
904 1,
905 ),
906 ),
907 (
908 "ef11",
909 Text,
910 ),
911 (
912 "]",
913 Delimiter(
914 1,
915 ),
916 ),
917 (
918 "::",
919 Separator,
920 ),
921 (
922 "boxed",
923 Text,
924 ),
925 (
926 "::",
927 Separator,
928 ),
929 (
930 "Box",
931 Text,
932 ),
933 (
934 ")",
935 Delimiter(
936 0,
937 ),
938 ),
939 (
940 " ",
941 Space(
942 1,
943 ),
944 ),
945 (
946 "span",
947 Text,
948 ),
949 (
950 "=",
951 Separator,
952 ),
953 (
954 "tests/ui/impl-trait/unsized_coercion.rs:12:15",
955 Path,
956 ),
957 (
958 ":",
959 Separator,
960 ),
961 (
962 " ",
963 Space(
964 1,
965 ),
966 ),
967 (
968 "12",
969 Number,
970 ),
971 (
972 ":",
973 Separator,
974 ),
975 (
976 "30",
977 Number,
978 ),
979 (
980 " ",
981 Space(
982 1,
983 ),
984 ),
985 (
986 "(",
987 Delimiter(
988 0,
989 ),
990 ),
991 (
992 "#0",
993 Text,
994 ),
995 (
996 ")",
997 Delimiter(
998 0,
999 ),
1000 ),
1001 ]
1002 "##)
1003 }
1004}