1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
// SPDX-License-Identifier: GPL-2.0
use proc_macro::{Delimiter, Group, Ident, Spacing, Span, TokenTree};
fn concat_helper(tokens: &[TokenTree]) -> Vec<(String, Span)> {
let mut tokens = tokens.iter();
let mut segments = Vec::new();
let mut span = None;
loop {
match tokens.next() {
None => break,
Some(TokenTree::Literal(lit)) => {
// Allow us to concat string literals by stripping quotes
let mut value = lit.to_string();
if value.starts_with('"') && value.ends_with('"') {
value.remove(0);
value.pop();
}
segments.push((value, lit.span()));
}
Some(TokenTree::Ident(ident)) => {
let mut value = ident.to_string();
if value.starts_with("r#") {
value.replace_range(0..2, "");
}
segments.push((value, ident.span()));
}
Some(TokenTree::Punct(p)) if p.as_char() == ':' => {
let Some(TokenTree::Ident(ident)) = tokens.next() else {
panic!("expected identifier as modifier");
};
let (mut value, sp) = segments.pop().expect("expected identifier before modifier");
match ident.to_string().as_str() {
// Set the overall span of concatenated token as current span
"span" => {
assert!(
span.is_none(),
"span modifier should only appear at most once"
);
span = Some(sp);
}
"lower" => value = value.to_lowercase(),
"upper" => value = value.to_uppercase(),
v => panic!("unknown modifier `{v}`"),
};
segments.push((value, sp));
}
Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::None => {
let tokens = group.stream().into_iter().collect::<Vec<TokenTree>>();
segments.append(&mut concat_helper(tokens.as_slice()));
}
token => panic!("unexpected token in paste segments: {:?}", token),
};
}
segments
}
fn concat(tokens: &[TokenTree], group_span: Span) -> TokenTree {
let segments = concat_helper(tokens);
let pasted: String = segments.into_iter().map(|x| x.0).collect();
TokenTree::Ident(Ident::new(&pasted, group_span))
}
pub(crate) fn expand(tokens: &mut Vec<TokenTree>) {
for token in tokens.iter_mut() {
if let TokenTree::Group(group) = token {
let delimiter = group.delimiter();
let span = group.span();
let mut stream: Vec<_> = group.stream().into_iter().collect();
// Find groups that looks like `[< A B C D >]`
if delimiter == Delimiter::Bracket
&& stream.len() >= 3
&& matches!(&stream[0], TokenTree::Punct(p) if p.as_char() == '<')
&& matches!(&stream[stream.len() - 1], TokenTree::Punct(p) if p.as_char() == '>')
{
// Replace the group with concatenated token
*token = concat(&stream[1..stream.len() - 1], span);
} else {
// Recursively expand tokens inside the group
expand(&mut stream);
let mut group = Group::new(delimiter, stream.into_iter().collect());
group.set_span(span);
*token = TokenTree::Group(group);
}
}
}
// Path segments cannot contain invisible delimiter group, so remove them if any.
for i in (0..tokens.len().saturating_sub(3)).rev() {
// Looking for a double colon
if matches!(
(&tokens[i + 1], &tokens[i + 2]),
(TokenTree::Punct(a), TokenTree::Punct(b))
if a.as_char() == ':' && a.spacing() == Spacing::Joint && b.as_char() == ':'
) {
match &tokens[i + 3] {
TokenTree::Group(group) if group.delimiter() == Delimiter::None => {
tokens.splice(i + 3..i + 4, group.stream());
}
_ => (),
}
match &tokens[i] {
TokenTree::Group(group) if group.delimiter() == Delimiter::None => {
tokens.splice(i..i + 1, group.stream());
}
_ => (),
}
}
}
}
|