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