logviewer/logparse/src/spans.rs

1393 lines
35 KiB
Rust

use super::ast::*;
use std::borrow::Cow;
/// Text categories, based on the parsing.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum SpanKind {
/// Parentheses e.g.
///
/// Stores the delimiter depth, for e.g. rainbow delimiters.
Delimiter(usize),
/// Separators like `=`, ':' and ','
Separator,
/// Numbers
Number,
/// Known literals, like `true`, `false`, `None`, `Ok`, `Err`
Literal,
/// Lifetimes (`'foo`)
Lifetime,
/// Strings
String,
/// Paths
Path,
/// Spaces (returns original number of spaces)
Space(usize),
/// Constructor: the prefix of a delimited block.
/// i.e. `Some` in `Some(3)`
Constructor,
/// String prefix, suffix, hashtags, etc.
/// Also number suffix
Surroundings,
/// Any other text (the default)
Text,
}
/// A `Span` is a piece of categorized text, based on the parsing done by
/// [`parse_input`](crate::parse_input).
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Span<'a> {
/// The segment of text.
pub text: Cow<'a, str>,
/// Its category.
pub kind: SpanKind,
}
/// Configuration options for [`into_spans`]
#[derive(Default)]
pub struct Config {
/// Turn sequences of more than 1 space into exactly 1 space.
pub collapse_space: bool,
/// Pretty print: wrap at braces etc to the given width
pub pretty_print: Option<usize>,
}
pub trait IntoSpans<'a>: private::IntoSpansImpl<'a> {}
/// Turn an ast node into [`Span`]s.
pub fn into_spans<'a>(ast: impl IntoSpans<'a>, config: Config) -> Vec<Span<'a>> {
let mut cx = private::Context {
config,
res: Vec::new(),
depth: 0,
ignore_next_space: false,
};
if let Some(width) = cx.config.pretty_print {
let res = ast.into_pretty_spans(&mut cx);
let _ = res.render_vec(width, &mut cx.res);
} else {
ast.into_spans(&mut cx)
}
cx.res
}
mod private {
use logparse_pretty_print::{PrettyBuilder, PrettyTree, Text};
use super::*;
pub struct Context<'a> {
pub config: Config,
pub res: Vec<Span<'a>>,
pub depth: usize,
pub ignore_next_space: bool,
}
impl<'a> Context<'a> {
fn push(&mut self, text: impl Into<Cow<'a, str>>, kind: SpanKind) {
self.res.push(Span {
text: text.into(),
kind,
})
}
}
impl<'a> Text<'a> for Span<'a> {
fn from_static_str(s: &'static str) -> Self {
Span {
text: Cow::Borrowed(s),
kind: SpanKind::Text,
}
}
fn as_str(&self) -> Cow<'_, str> {
self.text.clone()
}
}
pub trait IntoSpansImpl<'a> {
fn into_spans(self, cx: &mut Context<'a>);
fn into_pretty_spans(self, cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>>;
}
impl<'a, T> IntoSpans<'a> for T where T: IntoSpansImpl<'a> {}
impl<'a> IntoSpansImpl<'a> for Separator {
fn into_spans(self, cx: &mut Context<'a>) {
match self {
Separator::Eq => cx.push("=", SpanKind::Separator),
Separator::Colon => cx.push(":", SpanKind::Separator),
Separator::DoubleColon => cx.push("::", SpanKind::Separator),
}
}
fn into_pretty_spans(self, _cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> {
match self {
Separator::Eq => PrettyTree::text(Span {
text: Cow::Borrowed("="),
kind: SpanKind::Separator,
}),
Separator::Colon => PrettyTree::text(Span {
text: Cow::Borrowed(":"),
kind: SpanKind::Separator,
}),
Separator::DoubleColon => PrettyTree::text(Span {
text: Cow::Borrowed("::"),
kind: SpanKind::Separator,
}),
}
}
}
impl<'a> IntoSpansImpl<'a> for QuoteType {
fn into_spans(self, cx: &mut Context<'a>) {
match self {
QuoteType::Single => cx.push("'", SpanKind::Surroundings),
QuoteType::Double => cx.push("\"", SpanKind::Surroundings),
QuoteType::Backtick => cx.push("`", SpanKind::Surroundings),
}
}
fn into_pretty_spans(self, _cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> {
match self {
QuoteType::Single => PrettyTree::text(Span {
text: Cow::Borrowed("'"),
kind: SpanKind::Surroundings,
}),
QuoteType::Double => PrettyTree::text(Span {
text: Cow::Borrowed("\""),
kind: SpanKind::Surroundings,
}),
QuoteType::Backtick => PrettyTree::text(Span {
text: Cow::Borrowed("`"),
kind: SpanKind::Surroundings,
}),
}
}
}
impl<'a> IntoSpansImpl<'a> for AnyString<'a> {
fn into_spans(self, cx: &mut Context<'a>) {
let Self {
prefix,
ty,
contents,
num_hashtags,
suffix,
} = self;
cx.push(prefix, SpanKind::Surroundings);
for _ in 0..num_hashtags {
cx.push("#", SpanKind::Surroundings)
}
ty.into_spans(cx);
cx.push(contents, SpanKind::String);
ty.into_spans(cx);
for _ in 0..num_hashtags {
cx.push("#", SpanKind::Surroundings)
}
cx.push(suffix, SpanKind::Surroundings);
}
fn into_pretty_spans(self, cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> {
let Self {
prefix,
ty,
contents,
num_hashtags,
suffix,
} = self;
let hashtags = "#".repeat(num_hashtags);
PrettyTree::text(Span {
text: prefix,
kind: SpanKind::Surroundings,
})
.append(PrettyTree::text(Span {
text: Cow::Owned(hashtags.clone()),
kind: SpanKind::Surroundings,
}))
.group()
.append(ty.into_pretty_spans(cx))
.group()
.append(PrettyTree::text(Span {
text: contents,
kind: SpanKind::String,
}))
.group()
.append(ty.into_pretty_spans(cx))
.group()
.append(PrettyTree::text(Span {
text: suffix,
kind: SpanKind::Surroundings,
}))
.group()
}
}
impl<'a> IntoSpansImpl<'a> for Path<'a> {
fn into_spans(self, cx: &mut Context<'a>) {
cx.push(self.to_string(), SpanKind::Path)
}
fn into_pretty_spans(self, _cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> {
PrettyTree::text(Span {
text: Cow::Owned(self.to_string()),
kind: SpanKind::Path,
})
}
}
impl<'a> IntoSpansImpl<'a> for Number<'a> {
fn into_spans(self, cx: &mut Context<'a>) {
cx.push(self.number, SpanKind::Number);
if let Some(suffix) = self.suffix_without_underscore {
cx.push("_", SpanKind::Surroundings);
cx.push(suffix, SpanKind::Surroundings);
}
}
fn into_pretty_spans(self, _cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> {
let main = PrettyTree::text(Span {
text: self.number,
kind: SpanKind::Number,
});
let suffix = if let Some(suffix) = self.suffix_without_underscore {
PrettyTree::text(Span {
text: Cow::Borrowed("_"),
kind: SpanKind::Surroundings,
})
.append(PrettyTree::text(Span {
text: suffix,
kind: SpanKind::Surroundings,
}))
.group()
} else {
PrettyTree::Nil
};
main.append(suffix).group()
}
}
impl<'a> IntoSpansImpl<'a> for Atom<'a> {
fn into_spans(self, cx: &mut Context<'a>) {
match self {
Atom::Text(text) => cx.push(text, SpanKind::Text),
}
}
fn into_pretty_spans(self, _cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> {
match self {
Atom::Text(text) => PrettyTree::text(Span {
text,
kind: SpanKind::Text,
}),
}
}
}
impl<'a> IntoSpansImpl<'a> for Space<'a> {
fn into_spans(self, cx: &mut Context<'a>) {
match self.0.len() {
0 => {}
1 => cx.push(self.0, SpanKind::Space(1)),
n if cx.config.collapse_space => cx.push(" ", SpanKind::Space(n)),
n => cx.push(self.0, SpanKind::Space(n)),
}
}
fn into_pretty_spans(self, cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> {
if cx.ignore_next_space {
cx.ignore_next_space = false;
return PrettyTree::Nil;
}
match self.0.len() {
0 => PrettyTree::Nil,
1 => PrettyTree::line_or_space(),
n if cx.config.collapse_space => PrettyTree::line_or_space(),
n => PrettyTree::text(Span {
text: self.0,
kind: SpanKind::Space(n),
}),
}
}
}
impl<'a> IntoSpansImpl<'a> for Token<'a> {
fn into_spans(self, cx: &mut Context<'a>) {
match self {
Token::True => cx.push("true", SpanKind::Literal),
Token::False => cx.push("false", SpanKind::Literal),
Token::None => cx.push("None", SpanKind::Literal),
Token::Path(path) => path.into_spans(cx),
Token::String(string) => string.into_spans(cx),
Token::Number(number) => number.into_spans(cx),
Token::Separated {
before,
space_before,
separator,
after,
} => {
before.into_spans(cx);
space_before.into_spans(cx);
separator.into_spans(cx);
after.into_spans(cx);
}
Token::Delimited(delimited) => {
delimited.into_spans(cx);
}
Token::Atom(atom) => {
atom.into_spans(cx);
}
Token::Lifetime(lifetime) => {
cx.push("'", SpanKind::Surroundings);
cx.push(lifetime, SpanKind::Lifetime);
}
}
}
fn into_pretty_spans(self, cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> {
match self {
Token::True => PrettyTree::text(Span {
text: Cow::Borrowed("true"),
kind: SpanKind::Literal,
}),
Token::False => PrettyTree::text(Span {
text: Cow::Borrowed("true"),
kind: SpanKind::Literal,
}),
Token::None => PrettyTree::text(Span {
text: Cow::Borrowed("None"),
kind: SpanKind::Literal,
}),
Token::Lifetime(lifetime) => PrettyTree::Text(Span {
text: Cow::Borrowed("'"),
kind: SpanKind::Surroundings,
})
.append(PrettyTree::text(Span {
text: lifetime,
kind: SpanKind::Lifetime,
}))
.group(),
Token::Path(path) => path.into_pretty_spans(cx),
Token::String(string) => string.into_pretty_spans(cx),
Token::Number(number) => number.into_pretty_spans(cx),
Token::Separated {
before,
space_before,
separator,
after,
} => before
.into_pretty_spans(cx)
.append(space_before.into_pretty_spans(cx))
.group()
.append(separator.into_pretty_spans(cx))
.group()
.append(after.into_pretty_spans(cx))
.group(),
Token::Delimited(delimited) => delimited.into_pretty_spans(cx),
Token::Atom(atom) => atom.into_pretty_spans(cx),
}
}
}
impl<'a> IntoSpansImpl<'a> for Segment<'a> {
fn into_spans(self, cx: &mut Context<'a>) {
let Self {
leading_space,
token,
} = self;
leading_space.into_spans(cx);
token.into_spans(cx);
}
fn into_pretty_spans(self, cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> {
let Self {
leading_space,
token,
} = self;
leading_space
.into_pretty_spans(cx)
.append(token.into_pretty_spans(cx))
.group()
}
}
impl<'a> IntoSpansImpl<'a> for Segments<'a> {
fn into_spans(self, cx: &mut Context<'a>) {
let Self {
segments,
trailing_space,
} = self;
for segment in segments {
segment.into_spans(cx);
}
trailing_space.into_spans(cx);
}
fn into_pretty_spans(self, cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> {
let ignore_start = cx.ignore_next_space;
let Self {
segments,
trailing_space,
} = self;
let mut res = PrettyTree::Nil;
for segment in segments {
res = res.append(segment.into_pretty_spans(cx)).group();
}
// if we ignore the first space, also ignore the last one.
if ignore_start {
cx.ignore_next_space = true;
}
res.append(trailing_space.into_pretty_spans(cx)).group()
}
}
fn prefix_kind(delimiter: Delimiter, space: &Space<'_>) -> SpanKind {
match delimiter {
Delimiter::Brace => SpanKind::Constructor,
Delimiter::Paren if space.0.is_empty() => SpanKind::Constructor,
Delimiter::Paren => SpanKind::Text,
Delimiter::Bracket => SpanKind::Text,
Delimiter::Angle if space.0.is_empty() => SpanKind::Constructor,
Delimiter::Angle => SpanKind::Text,
}
}
impl<'a> IntoSpansImpl<'a> for Delimited<'a> {
fn into_spans(self, cx: &mut Context<'a>) {
let Self {
prefix,
delimiter,
contents,
} = self;
match prefix {
Some((Atom::Text(text), space)) => {
cx.push(text, prefix_kind(delimiter, &space));
space.into_spans(cx);
}
None => {}
}
match delimiter {
Delimiter::Paren => cx.push("(", SpanKind::Delimiter(cx.depth)),
Delimiter::Bracket => cx.push("[", SpanKind::Delimiter(cx.depth)),
Delimiter::Brace => cx.push("{", SpanKind::Delimiter(cx.depth)),
Delimiter::Angle => cx.push("<", SpanKind::Delimiter(cx.depth)),
}
cx.depth += 1;
contents.into_spans(cx);
cx.depth -= 1;
match delimiter {
Delimiter::Paren => cx.push(")", SpanKind::Delimiter(cx.depth)),
Delimiter::Bracket => cx.push("]", SpanKind::Delimiter(cx.depth)),
Delimiter::Brace => cx.push("}", SpanKind::Delimiter(cx.depth)),
Delimiter::Angle => cx.push(">", SpanKind::Delimiter(cx.depth)),
}
}
fn into_pretty_spans(self, cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> {
let Self {
prefix,
delimiter,
contents,
} = self;
let group = contents
.segments
.iter()
.filter(|i| !matches!(i.token, Token::Atom(..)))
.count()
<= 1;
let prefix = match prefix {
Some((Atom::Text(text), space)) => PrettyTree::text(Span {
text,
kind: prefix_kind(delimiter, &space),
})
.append(space.into_pretty_spans(cx))
.group(),
None => PrettyTree::Nil,
};
let open = match delimiter {
Delimiter::Paren => Span {
text: Cow::Borrowed("("),
kind: SpanKind::Delimiter(cx.depth),
},
Delimiter::Bracket => Span {
text: Cow::Borrowed("["),
kind: SpanKind::Delimiter(cx.depth),
},
Delimiter::Brace => Span {
text: Cow::Borrowed("{"),
kind: SpanKind::Delimiter(cx.depth),
},
Delimiter::Angle => Span {
text: Cow::Borrowed("<"),
kind: SpanKind::Delimiter(cx.depth),
},
};
let close = match delimiter {
Delimiter::Paren => Span {
text: Cow::Borrowed(")"),
kind: SpanKind::Delimiter(cx.depth),
},
Delimiter::Bracket => Span {
text: Cow::Borrowed("]"),
kind: SpanKind::Delimiter(cx.depth),
},
Delimiter::Brace => Span {
text: Cow::Borrowed("}"),
kind: SpanKind::Delimiter(cx.depth),
},
Delimiter::Angle => Span {
text: Cow::Borrowed(">"),
kind: SpanKind::Delimiter(cx.depth),
},
};
cx.depth += 1;
if !group {
cx.ignore_next_space = true;
}
let contents = contents.into_pretty_spans(cx);
cx.depth -= 1;
if group {
prefix
.append(PrettyTree::text(open))
.group()
.append(contents)
.group()
.append(PrettyTree::text(close))
} else {
prefix
.append(PrettyTree::text(open))
.group()
.append(PrettyTree::Hardline)
.group()
.append(contents)
.group()
.nest(4)
.group()
.append(PrettyTree::Hardline)
.group()
.append(PrettyTree::text(close))
.group()
}
}
}
}
#[cfg(test)]
mod tests {
use insta::{assert_debug_snapshot, assert_snapshot};
use super::SpanKind;
use crate::{Config, into_spans, parse_input};
fn spans(input: &str) -> Vec<(String, SpanKind)> {
let res = parse_input(input).unwrap();
into_spans(
res,
Config {
collapse_space: true,
..Default::default()
},
)
.into_iter()
.map(|i| (i.text.into_owned(), i.kind))
.collect()
}
fn pretty_print(input: &str, n: usize) -> String {
let res = parse_input(input).unwrap();
into_spans(
res,
Config {
pretty_print: Some(n),
..Default::default()
},
)
.into_iter()
.map(|i| i.text.into_owned())
.collect::<String>()
}
#[test]
fn pretty_print_ex1() {
assert_snapshot!(pretty_print(
r#"def_id=DefId(0:3 ~ unsized_coercion[10fa]::Trait)"#, 60
), @"
def_id=DefId(
0:3 ~ unsized_coercion[10fa]::Trait
)
");
assert_snapshot!(pretty_print(
r#"def_id=DefId(0:3 ~ unsized_coercion[10fa]::Trait)"#, 30
), @"
def_id=DefId(
0:3 ~
unsized_coercion[10fa]::Trait
)
");
assert_snapshot!(pretty_print(
r#"def_id=DefId(0:3 ~ unsized_coercion[10fa]::Trait)"#, 10
), @"
def_id=DefId(
0:3 ~
unsized_coercion[10fa]::Trait
)
");
}
#[test]
fn pretty_print_ex2() {
assert_snapshot!(pretty_print(
r#"stability: inspecting def_id=DefId(3:662 ~ alloc[ef11]::boxed::Box) span=tests/ui/impl-trait/unsized_coercion.rs:16:16: 16:30 (#0) of stability=Some(Stability { level: Stable { since: Version(RustcVersion { major: 1, minor: 0, patch: 0 }), allowed_through_unstable_modules: None }, feature: "rust1" })"#, 60
), @r#"
stability: inspecting
def_id=DefId(
3:662 ~ alloc[ef11]::boxed::Box
) span=tests/ui/impl-trait/unsized_coercion.rs:16:16: 16:30
(#0) of
stability=Some(Stability {
level:
Stable {
since:
Version(RustcVersion {
major: 1, minor: 0, patch: 0
}), allowed_through_unstable_modules: None
}, feature: "rust1"
})
"#);
}
#[test]
fn spans_ex1() {
assert_debug_snapshot!(spans(
r#"def_id=DefId(0:3 ~ unsized_coercion[10fa]::Trait)"#
), @r#"
[
(
"def_id",
Text,
),
(
"=",
Separator,
),
(
"DefId",
Constructor,
),
(
"(",
Delimiter(
0,
),
),
(
"0",
Number,
),
(
":",
Separator,
),
(
"3",
Number,
),
(
" ",
Space(
1,
),
),
(
"~",
Text,
),
(
" ",
Space(
1,
),
),
(
"unsized_coercion",
Text,
),
(
"[",
Delimiter(
1,
),
),
(
"10fa",
Text,
),
(
"]",
Delimiter(
1,
),
),
(
"::",
Separator,
),
(
"Trait",
Text,
),
(
")",
Delimiter(
0,
),
),
]
"#)
}
#[test]
fn spans_ex2() {
assert_debug_snapshot!(spans(
r#"data=TypeNs("MetaSized") visible_parent=DefId(2:3984 ~ core[bcc4]::marker) actual_parent=Some(DefId(2:3984 ~ core[bcc4]::marker))"#
), @r#"
[
(
"data",
Text,
),
(
"=",
Separator,
),
(
"TypeNs",
Constructor,
),
(
"(",
Delimiter(
0,
),
),
(
"",
Surroundings,
),
(
"\"",
Surroundings,
),
(
"MetaSized",
String,
),
(
"\"",
Surroundings,
),
(
"",
Surroundings,
),
(
")",
Delimiter(
0,
),
),
(
" ",
Space(
1,
),
),
(
"visible_parent",
Text,
),
(
"=",
Separator,
),
(
"DefId",
Constructor,
),
(
"(",
Delimiter(
0,
),
),
(
"2",
Number,
),
(
":",
Separator,
),
(
"3984",
Number,
),
(
" ",
Space(
1,
),
),
(
"~",
Text,
),
(
" ",
Space(
1,
),
),
(
"core",
Text,
),
(
"[",
Delimiter(
1,
),
),
(
"bcc4",
Text,
),
(
"]",
Delimiter(
1,
),
),
(
"::",
Separator,
),
(
"marker",
Text,
),
(
")",
Delimiter(
0,
),
),
(
" ",
Space(
1,
),
),
(
"actual_parent",
Text,
),
(
"=",
Separator,
),
(
"Some",
Constructor,
),
(
"(",
Delimiter(
0,
),
),
(
"DefId",
Constructor,
),
(
"(",
Delimiter(
1,
),
),
(
"2",
Number,
),
(
":",
Separator,
),
(
"3984",
Number,
),
(
" ",
Space(
1,
),
),
(
"~",
Text,
),
(
" ",
Space(
1,
),
),
(
"core",
Text,
),
(
"[",
Delimiter(
2,
),
),
(
"bcc4",
Text,
),
(
"]",
Delimiter(
2,
),
),
(
"::",
Separator,
),
(
"marker",
Text,
),
(
")",
Delimiter(
1,
),
),
(
")",
Delimiter(
0,
),
),
]
"#)
}
#[test]
fn spans_ex3() {
assert_debug_snapshot!(spans(
r#"insert(DefId(0:4 ~ unsized_coercion[10fa]::{impl#0})): inserting TraitRef <u32 as Trait> into specialization graph"#
), @r#"
[
(
"insert",
Constructor,
),
(
"(",
Delimiter(
0,
),
),
(
"DefId",
Constructor,
),
(
"(",
Delimiter(
1,
),
),
(
"0",
Number,
),
(
":",
Separator,
),
(
"4",
Number,
),
(
" ",
Space(
1,
),
),
(
"~",
Text,
),
(
" ",
Space(
1,
),
),
(
"unsized_coercion",
Text,
),
(
"[",
Delimiter(
2,
),
),
(
"10fa",
Text,
),
(
"]",
Delimiter(
2,
),
),
(
"::",
Separator,
),
(
"{",
Delimiter(
2,
),
),
(
"impl#0",
Text,
),
(
"}",
Delimiter(
2,
),
),
(
")",
Delimiter(
1,
),
),
(
")",
Delimiter(
0,
),
),
(
":",
Separator,
),
(
" ",
Space(
1,
),
),
(
"inserting",
Text,
),
(
" ",
Space(
1,
),
),
(
"TraitRef",
Text,
),
(
" ",
Space(
1,
),
),
(
"<",
Delimiter(
0,
),
),
(
"u32",
Text,
),
(
" ",
Space(
1,
),
),
(
"as",
Text,
),
(
" ",
Space(
1,
),
),
(
"Trait",
Text,
),
(
">",
Delimiter(
0,
),
),
(
" ",
Space(
1,
),
),
(
"into",
Text,
),
(
" ",
Space(
1,
),
),
(
"specialization",
Text,
),
(
" ",
Space(
1,
),
),
(
"graph",
Text,
),
]
"#)
}
#[test]
fn spans_ex4() {
assert_debug_snapshot!(spans(
r#"inspecting def_id=DefId(3:662 ~ alloc[ef11]::boxed::Box) span=tests/ui/impl-trait/unsized_coercion.rs:12:15: 12:30 (#0)"#
), @r##"
[
(
"inspecting",
Text,
),
(
" ",
Space(
1,
),
),
(
"def_id",
Text,
),
(
"=",
Separator,
),
(
"DefId",
Constructor,
),
(
"(",
Delimiter(
0,
),
),
(
"3",
Number,
),
(
":",
Separator,
),
(
"662",
Number,
),
(
" ",
Space(
1,
),
),
(
"~",
Text,
),
(
" ",
Space(
1,
),
),
(
"alloc",
Text,
),
(
"[",
Delimiter(
1,
),
),
(
"ef11",
Text,
),
(
"]",
Delimiter(
1,
),
),
(
"::",
Separator,
),
(
"boxed",
Text,
),
(
"::",
Separator,
),
(
"Box",
Text,
),
(
")",
Delimiter(
0,
),
),
(
" ",
Space(
1,
),
),
(
"span",
Text,
),
(
"=",
Separator,
),
(
"tests/ui/impl-trait/unsized_coercion.rs:12:15",
Path,
),
(
":",
Separator,
),
(
" ",
Space(
1,
),
),
(
"12",
Number,
),
(
":",
Separator,
),
(
"30",
Number,
),
(
" ",
Space(
1,
),
),
(
"(",
Delimiter(
0,
),
),
(
"#0",
Text,
),
(
")",
Delimiter(
0,
),
),
]
"##)
}
}