Skip to content

Commit e957b9c

Browse files
BennoLossinojeda
authored andcommitted
rust: macros: refactor generics parsing of #[pin_data] into its own function
Other macros might also want to parse generics. Additionally this makes the code easier to read, as the next commit will introduce more code in `#[pin_data]`. Also add more comments to explain how parsing generics work. Signed-off-by: Benno Lossin <[email protected]> Reviewed-by: Alice Ryhl <[email protected]> Reviewed-by: Gary Guo <[email protected]> Reviewed-by: Martin Rodriguez Reboredo <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Miguel Ojeda <[email protected]>
1 parent b8342ad commit e957b9c

File tree

2 files changed

+94
-62
lines changed

2 files changed

+94
-62
lines changed

rust/macros/helpers.rs

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-License-Identifier: GPL-2.0
22

3-
use proc_macro::{token_stream, Group, TokenTree};
3+
use proc_macro::{token_stream, Group, Punct, Spacing, TokenStream, TokenTree};
44

55
pub(crate) fn try_ident(it: &mut token_stream::IntoIter) -> Option<String> {
66
if let Some(TokenTree::Ident(ident)) = it.next() {
@@ -69,3 +69,87 @@ pub(crate) fn expect_end(it: &mut token_stream::IntoIter) {
6969
panic!("Expected end");
7070
}
7171
}
72+
73+
pub(crate) struct Generics {
74+
pub(crate) impl_generics: Vec<TokenTree>,
75+
pub(crate) ty_generics: Vec<TokenTree>,
76+
}
77+
78+
/// Parses the given `TokenStream` into `Generics` and the rest.
79+
///
80+
/// The generics are not present in the rest, but a where clause might remain.
81+
pub(crate) fn parse_generics(input: TokenStream) -> (Generics, Vec<TokenTree>) {
82+
// `impl_generics`, the declared generics with their bounds.
83+
let mut impl_generics = vec![];
84+
// Only the names of the generics, without any bounds.
85+
let mut ty_generics = vec![];
86+
// Tokens not related to the generics e.g. the `where` token and definition.
87+
let mut rest = vec![];
88+
// The current level of `<`.
89+
let mut nesting = 0;
90+
let mut toks = input.into_iter();
91+
// If we are at the beginning of a generic parameter.
92+
let mut at_start = true;
93+
for tt in &mut toks {
94+
match tt.clone() {
95+
TokenTree::Punct(p) if p.as_char() == '<' => {
96+
if nesting >= 1 {
97+
// This is inside of the generics and part of some bound.
98+
impl_generics.push(tt);
99+
}
100+
nesting += 1;
101+
}
102+
TokenTree::Punct(p) if p.as_char() == '>' => {
103+
// This is a parsing error, so we just end it here.
104+
if nesting == 0 {
105+
break;
106+
} else {
107+
nesting -= 1;
108+
if nesting >= 1 {
109+
// We are still inside of the generics and part of some bound.
110+
impl_generics.push(tt);
111+
}
112+
if nesting == 0 {
113+
break;
114+
}
115+
}
116+
}
117+
tt => {
118+
if nesting == 1 {
119+
// Here depending on the token, it might be a generic variable name.
120+
match &tt {
121+
// Ignore const.
122+
TokenTree::Ident(i) if i.to_string() == "const" => {}
123+
TokenTree::Ident(_) if at_start => {
124+
ty_generics.push(tt.clone());
125+
// We also already push the `,` token, this makes it easier to append
126+
// generics.
127+
ty_generics.push(TokenTree::Punct(Punct::new(',', Spacing::Alone)));
128+
at_start = false;
129+
}
130+
TokenTree::Punct(p) if p.as_char() == ',' => at_start = true,
131+
// Lifetimes begin with `'`.
132+
TokenTree::Punct(p) if p.as_char() == '\'' && at_start => {
133+
ty_generics.push(tt.clone());
134+
}
135+
_ => {}
136+
}
137+
}
138+
if nesting >= 1 {
139+
impl_generics.push(tt);
140+
} else if nesting == 0 {
141+
// If we haven't entered the generics yet, we still want to keep these tokens.
142+
rest.push(tt);
143+
}
144+
}
145+
}
146+
}
147+
rest.extend(toks);
148+
(
149+
Generics {
150+
impl_generics,
151+
ty_generics,
152+
},
153+
rest,
154+
)
155+
}

rust/macros/pin_data.rs

Lines changed: 9 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,19 @@
11
// SPDX-License-Identifier: Apache-2.0 OR MIT
22

3-
use proc_macro::{Punct, Spacing, TokenStream, TokenTree};
3+
use crate::helpers::{parse_generics, Generics};
4+
use proc_macro::TokenStream;
45

56
pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream {
67
// This proc-macro only does some pre-parsing and then delegates the actual parsing to
78
// `kernel::__pin_data!`.
8-
//
9-
// In here we only collect the generics, since parsing them in declarative macros is very
10-
// elaborate. We also do not need to analyse their structure, we only need to collect them.
119

12-
// `impl_generics`, the declared generics with their bounds.
13-
let mut impl_generics = vec![];
14-
// Only the names of the generics, without any bounds.
15-
let mut ty_generics = vec![];
16-
// Tokens not related to the generics e.g. the `impl` token.
17-
let mut rest = vec![];
18-
// The current level of `<`.
19-
let mut nesting = 0;
20-
let mut toks = input.into_iter();
21-
// If we are at the beginning of a generic parameter.
22-
let mut at_start = true;
23-
for tt in &mut toks {
24-
match tt.clone() {
25-
TokenTree::Punct(p) if p.as_char() == '<' => {
26-
if nesting >= 1 {
27-
impl_generics.push(tt);
28-
}
29-
nesting += 1;
30-
}
31-
TokenTree::Punct(p) if p.as_char() == '>' => {
32-
if nesting == 0 {
33-
break;
34-
} else {
35-
nesting -= 1;
36-
if nesting >= 1 {
37-
impl_generics.push(tt);
38-
}
39-
if nesting == 0 {
40-
break;
41-
}
42-
}
43-
}
44-
tt => {
45-
if nesting == 1 {
46-
match &tt {
47-
TokenTree::Ident(i) if i.to_string() == "const" => {}
48-
TokenTree::Ident(_) if at_start => {
49-
ty_generics.push(tt.clone());
50-
ty_generics.push(TokenTree::Punct(Punct::new(',', Spacing::Alone)));
51-
at_start = false;
52-
}
53-
TokenTree::Punct(p) if p.as_char() == ',' => at_start = true,
54-
TokenTree::Punct(p) if p.as_char() == '\'' && at_start => {
55-
ty_generics.push(tt.clone());
56-
}
57-
_ => {}
58-
}
59-
}
60-
if nesting >= 1 {
61-
impl_generics.push(tt);
62-
} else if nesting == 0 {
63-
rest.push(tt);
64-
}
65-
}
66-
}
67-
}
68-
rest.extend(toks);
10+
let (
11+
Generics {
12+
impl_generics,
13+
ty_generics,
14+
},
15+
mut rest,
16+
) = parse_generics(input);
6917
// This should be the body of the struct `{...}`.
7018
let last = rest.pop();
7119
quote!(::kernel::__pin_data! {

0 commit comments

Comments
 (0)