xref: /openbmc/linux/rust/macros/helpers.rs (revision e957b9cd)
11fbde52bSMiguel Ojeda // SPDX-License-Identifier: GPL-2.0
21fbde52bSMiguel Ojeda 
3*e957b9cdSBenno Lossin use proc_macro::{token_stream, Group, Punct, Spacing, TokenStream, TokenTree};
41fbde52bSMiguel Ojeda 
try_ident(it: &mut token_stream::IntoIter) -> Option<String>51fbde52bSMiguel Ojeda pub(crate) fn try_ident(it: &mut token_stream::IntoIter) -> Option<String> {
61fbde52bSMiguel Ojeda     if let Some(TokenTree::Ident(ident)) = it.next() {
71fbde52bSMiguel Ojeda         Some(ident.to_string())
81fbde52bSMiguel Ojeda     } else {
91fbde52bSMiguel Ojeda         None
101fbde52bSMiguel Ojeda     }
111fbde52bSMiguel Ojeda }
121fbde52bSMiguel Ojeda 
try_literal(it: &mut token_stream::IntoIter) -> Option<String>131fbde52bSMiguel Ojeda pub(crate) fn try_literal(it: &mut token_stream::IntoIter) -> Option<String> {
141fbde52bSMiguel Ojeda     if let Some(TokenTree::Literal(literal)) = it.next() {
151fbde52bSMiguel Ojeda         Some(literal.to_string())
161fbde52bSMiguel Ojeda     } else {
171fbde52bSMiguel Ojeda         None
181fbde52bSMiguel Ojeda     }
191fbde52bSMiguel Ojeda }
201fbde52bSMiguel Ojeda 
try_string(it: &mut token_stream::IntoIter) -> Option<String>21b13c9880SGary Guo pub(crate) fn try_string(it: &mut token_stream::IntoIter) -> Option<String> {
22b13c9880SGary Guo     try_literal(it).and_then(|string| {
23b13c9880SGary Guo         if string.starts_with('\"') && string.ends_with('\"') {
24b13c9880SGary Guo             let content = &string[1..string.len() - 1];
25b13c9880SGary Guo             if content.contains('\\') {
26b13c9880SGary Guo                 panic!("Escape sequences in string literals not yet handled");
27b13c9880SGary Guo             }
28b13c9880SGary Guo             Some(content.to_string())
29b13c9880SGary Guo         } else if string.starts_with("r\"") {
30b13c9880SGary Guo             panic!("Raw string literals are not yet handled");
311fbde52bSMiguel Ojeda         } else {
321fbde52bSMiguel Ojeda             None
331fbde52bSMiguel Ojeda         }
341fbde52bSMiguel Ojeda     })
351fbde52bSMiguel Ojeda }
361fbde52bSMiguel Ojeda 
expect_ident(it: &mut token_stream::IntoIter) -> String371fbde52bSMiguel Ojeda pub(crate) fn expect_ident(it: &mut token_stream::IntoIter) -> String {
381fbde52bSMiguel Ojeda     try_ident(it).expect("Expected Ident")
391fbde52bSMiguel Ojeda }
401fbde52bSMiguel Ojeda 
expect_punct(it: &mut token_stream::IntoIter) -> char411fbde52bSMiguel Ojeda pub(crate) fn expect_punct(it: &mut token_stream::IntoIter) -> char {
421fbde52bSMiguel Ojeda     if let TokenTree::Punct(punct) = it.next().expect("Reached end of token stream for Punct") {
431fbde52bSMiguel Ojeda         punct.as_char()
441fbde52bSMiguel Ojeda     } else {
451fbde52bSMiguel Ojeda         panic!("Expected Punct");
461fbde52bSMiguel Ojeda     }
471fbde52bSMiguel Ojeda }
481fbde52bSMiguel Ojeda 
expect_string(it: &mut token_stream::IntoIter) -> String49b13c9880SGary Guo pub(crate) fn expect_string(it: &mut token_stream::IntoIter) -> String {
50b13c9880SGary Guo     try_string(it).expect("Expected string")
51b13c9880SGary Guo }
52b13c9880SGary Guo 
expect_string_ascii(it: &mut token_stream::IntoIter) -> String53b13c9880SGary Guo pub(crate) fn expect_string_ascii(it: &mut token_stream::IntoIter) -> String {
54b13c9880SGary Guo     let string = try_string(it).expect("Expected string");
55b13c9880SGary Guo     assert!(string.is_ascii(), "Expected ASCII string");
56b13c9880SGary Guo     string
571fbde52bSMiguel Ojeda }
581fbde52bSMiguel Ojeda 
expect_group(it: &mut token_stream::IntoIter) -> Group5939867fecSAsahi Lina pub(crate) fn expect_group(it: &mut token_stream::IntoIter) -> Group {
6039867fecSAsahi Lina     if let TokenTree::Group(group) = it.next().expect("Reached end of token stream for Group") {
6139867fecSAsahi Lina         group
6239867fecSAsahi Lina     } else {
6339867fecSAsahi Lina         panic!("Expected Group");
6439867fecSAsahi Lina     }
6539867fecSAsahi Lina }
6639867fecSAsahi Lina 
expect_end(it: &mut token_stream::IntoIter)671fbde52bSMiguel Ojeda pub(crate) fn expect_end(it: &mut token_stream::IntoIter) {
681fbde52bSMiguel Ojeda     if it.next().is_some() {
691fbde52bSMiguel Ojeda         panic!("Expected end");
701fbde52bSMiguel Ojeda     }
711fbde52bSMiguel Ojeda }
72*e957b9cdSBenno Lossin 
73*e957b9cdSBenno Lossin pub(crate) struct Generics {
74*e957b9cdSBenno Lossin     pub(crate) impl_generics: Vec<TokenTree>,
75*e957b9cdSBenno Lossin     pub(crate) ty_generics: Vec<TokenTree>,
76*e957b9cdSBenno Lossin }
77*e957b9cdSBenno Lossin 
78*e957b9cdSBenno Lossin /// Parses the given `TokenStream` into `Generics` and the rest.
79*e957b9cdSBenno Lossin ///
80*e957b9cdSBenno Lossin /// The generics are not present in the rest, but a where clause might remain.
parse_generics(input: TokenStream) -> (Generics, Vec<TokenTree>)81*e957b9cdSBenno Lossin pub(crate) fn parse_generics(input: TokenStream) -> (Generics, Vec<TokenTree>) {
82*e957b9cdSBenno Lossin     // `impl_generics`, the declared generics with their bounds.
83*e957b9cdSBenno Lossin     let mut impl_generics = vec![];
84*e957b9cdSBenno Lossin     // Only the names of the generics, without any bounds.
85*e957b9cdSBenno Lossin     let mut ty_generics = vec![];
86*e957b9cdSBenno Lossin     // Tokens not related to the generics e.g. the `where` token and definition.
87*e957b9cdSBenno Lossin     let mut rest = vec![];
88*e957b9cdSBenno Lossin     // The current level of `<`.
89*e957b9cdSBenno Lossin     let mut nesting = 0;
90*e957b9cdSBenno Lossin     let mut toks = input.into_iter();
91*e957b9cdSBenno Lossin     // If we are at the beginning of a generic parameter.
92*e957b9cdSBenno Lossin     let mut at_start = true;
93*e957b9cdSBenno Lossin     for tt in &mut toks {
94*e957b9cdSBenno Lossin         match tt.clone() {
95*e957b9cdSBenno Lossin             TokenTree::Punct(p) if p.as_char() == '<' => {
96*e957b9cdSBenno Lossin                 if nesting >= 1 {
97*e957b9cdSBenno Lossin                     // This is inside of the generics and part of some bound.
98*e957b9cdSBenno Lossin                     impl_generics.push(tt);
99*e957b9cdSBenno Lossin                 }
100*e957b9cdSBenno Lossin                 nesting += 1;
101*e957b9cdSBenno Lossin             }
102*e957b9cdSBenno Lossin             TokenTree::Punct(p) if p.as_char() == '>' => {
103*e957b9cdSBenno Lossin                 // This is a parsing error, so we just end it here.
104*e957b9cdSBenno Lossin                 if nesting == 0 {
105*e957b9cdSBenno Lossin                     break;
106*e957b9cdSBenno Lossin                 } else {
107*e957b9cdSBenno Lossin                     nesting -= 1;
108*e957b9cdSBenno Lossin                     if nesting >= 1 {
109*e957b9cdSBenno Lossin                         // We are still inside of the generics and part of some bound.
110*e957b9cdSBenno Lossin                         impl_generics.push(tt);
111*e957b9cdSBenno Lossin                     }
112*e957b9cdSBenno Lossin                     if nesting == 0 {
113*e957b9cdSBenno Lossin                         break;
114*e957b9cdSBenno Lossin                     }
115*e957b9cdSBenno Lossin                 }
116*e957b9cdSBenno Lossin             }
117*e957b9cdSBenno Lossin             tt => {
118*e957b9cdSBenno Lossin                 if nesting == 1 {
119*e957b9cdSBenno Lossin                     // Here depending on the token, it might be a generic variable name.
120*e957b9cdSBenno Lossin                     match &tt {
121*e957b9cdSBenno Lossin                         // Ignore const.
122*e957b9cdSBenno Lossin                         TokenTree::Ident(i) if i.to_string() == "const" => {}
123*e957b9cdSBenno Lossin                         TokenTree::Ident(_) if at_start => {
124*e957b9cdSBenno Lossin                             ty_generics.push(tt.clone());
125*e957b9cdSBenno Lossin                             // We also already push the `,` token, this makes it easier to append
126*e957b9cdSBenno Lossin                             // generics.
127*e957b9cdSBenno Lossin                             ty_generics.push(TokenTree::Punct(Punct::new(',', Spacing::Alone)));
128*e957b9cdSBenno Lossin                             at_start = false;
129*e957b9cdSBenno Lossin                         }
130*e957b9cdSBenno Lossin                         TokenTree::Punct(p) if p.as_char() == ',' => at_start = true,
131*e957b9cdSBenno Lossin                         // Lifetimes begin with `'`.
132*e957b9cdSBenno Lossin                         TokenTree::Punct(p) if p.as_char() == '\'' && at_start => {
133*e957b9cdSBenno Lossin                             ty_generics.push(tt.clone());
134*e957b9cdSBenno Lossin                         }
135*e957b9cdSBenno Lossin                         _ => {}
136*e957b9cdSBenno Lossin                     }
137*e957b9cdSBenno Lossin                 }
138*e957b9cdSBenno Lossin                 if nesting >= 1 {
139*e957b9cdSBenno Lossin                     impl_generics.push(tt);
140*e957b9cdSBenno Lossin                 } else if nesting == 0 {
141*e957b9cdSBenno Lossin                     // If we haven't entered the generics yet, we still want to keep these tokens.
142*e957b9cdSBenno Lossin                     rest.push(tt);
143*e957b9cdSBenno Lossin                 }
144*e957b9cdSBenno Lossin             }
145*e957b9cdSBenno Lossin         }
146*e957b9cdSBenno Lossin     }
147*e957b9cdSBenno Lossin     rest.extend(toks);
148*e957b9cdSBenno Lossin     (
149*e957b9cdSBenno Lossin         Generics {
150*e957b9cdSBenno Lossin             impl_generics,
151*e957b9cdSBenno Lossin             ty_generics,
152*e957b9cdSBenno Lossin         },
153*e957b9cdSBenno Lossin         rest,
154*e957b9cdSBenno Lossin     )
155*e957b9cdSBenno Lossin }
156