xref: /linux/rust/macros/helpers.rs (revision c84d574698bad2c02aad506dfe712f83cbe3b771)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 use proc_macro::{token_stream, Group, Ident, TokenStream, TokenTree};
4 
try_ident(it: &mut token_stream::IntoIter) -> Option<String>5 pub(crate) fn try_ident(it: &mut token_stream::IntoIter) -> Option<String> {
6     if let Some(TokenTree::Ident(ident)) = it.next() {
7         Some(ident.to_string())
8     } else {
9         None
10     }
11 }
12 
try_sign(it: &mut token_stream::IntoIter) -> Option<char>13 pub(crate) fn try_sign(it: &mut token_stream::IntoIter) -> Option<char> {
14     let peek = it.clone().next();
15     match peek {
16         Some(TokenTree::Punct(punct)) if punct.as_char() == '-' => {
17             let _ = it.next();
18             Some(punct.as_char())
19         }
20         _ => None,
21     }
22 }
23 
try_literal(it: &mut token_stream::IntoIter) -> Option<String>24 pub(crate) fn try_literal(it: &mut token_stream::IntoIter) -> Option<String> {
25     if let Some(TokenTree::Literal(literal)) = it.next() {
26         Some(literal.to_string())
27     } else {
28         None
29     }
30 }
31 
try_string(it: &mut token_stream::IntoIter) -> Option<String>32 pub(crate) fn try_string(it: &mut token_stream::IntoIter) -> Option<String> {
33     try_literal(it).and_then(|string| {
34         if string.starts_with('\"') && string.ends_with('\"') {
35             let content = &string[1..string.len() - 1];
36             if content.contains('\\') {
37                 panic!("Escape sequences in string literals not yet handled");
38             }
39             Some(content.to_string())
40         } else if string.starts_with("r\"") {
41             panic!("Raw string literals are not yet handled");
42         } else {
43             None
44         }
45     })
46 }
47 
expect_ident(it: &mut token_stream::IntoIter) -> String48 pub(crate) fn expect_ident(it: &mut token_stream::IntoIter) -> String {
49     try_ident(it).expect("Expected Ident")
50 }
51 
expect_punct(it: &mut token_stream::IntoIter) -> char52 pub(crate) fn expect_punct(it: &mut token_stream::IntoIter) -> char {
53     if let TokenTree::Punct(punct) = it.next().expect("Reached end of token stream for Punct") {
54         punct.as_char()
55     } else {
56         panic!("Expected Punct");
57     }
58 }
59 
expect_string(it: &mut token_stream::IntoIter) -> String60 pub(crate) fn expect_string(it: &mut token_stream::IntoIter) -> String {
61     try_string(it).expect("Expected string")
62 }
63 
expect_string_ascii(it: &mut token_stream::IntoIter) -> String64 pub(crate) fn expect_string_ascii(it: &mut token_stream::IntoIter) -> String {
65     let string = try_string(it).expect("Expected string");
66     assert!(string.is_ascii(), "Expected ASCII string");
67     string
68 }
69 
expect_group(it: &mut token_stream::IntoIter) -> Group70 pub(crate) fn expect_group(it: &mut token_stream::IntoIter) -> Group {
71     if let TokenTree::Group(group) = it.next().expect("Reached end of token stream for Group") {
72         group
73     } else {
74         panic!("Expected Group");
75     }
76 }
77 
expect_end(it: &mut token_stream::IntoIter)78 pub(crate) fn expect_end(it: &mut token_stream::IntoIter) {
79     if it.next().is_some() {
80         panic!("Expected end");
81     }
82 }
83 
84 /// Given a function declaration, finds the name of the function.
function_name(input: TokenStream) -> Option<Ident>85 pub(crate) fn function_name(input: TokenStream) -> Option<Ident> {
86     let mut input = input.into_iter();
87     while let Some(token) = input.next() {
88         match token {
89             TokenTree::Ident(i) if i.to_string() == "fn" => {
90                 if let Some(TokenTree::Ident(i)) = input.next() {
91                     return Some(i);
92                 }
93                 return None;
94             }
95             _ => continue,
96         }
97     }
98     None
99 }
100 
file() -> String101 pub(crate) fn file() -> String {
102     #[cfg(not(CONFIG_RUSTC_HAS_SPAN_FILE))]
103     {
104         proc_macro::Span::call_site()
105             .source_file()
106             .path()
107             .to_string_lossy()
108             .into_owned()
109     }
110 
111     #[cfg(CONFIG_RUSTC_HAS_SPAN_FILE)]
112     #[allow(clippy::incompatible_msrv)]
113     {
114         proc_macro::Span::call_site().file()
115     }
116 }
117 
118 /// Parse a token stream of the form `expected_name: "value",` and return the
119 /// string in the position of "value".
120 ///
121 /// # Panics
122 ///
123 /// - On parse error.
expect_string_field(it: &mut token_stream::IntoIter, expected_name: &str) -> String124 pub(crate) fn expect_string_field(it: &mut token_stream::IntoIter, expected_name: &str) -> String {
125     assert_eq!(expect_ident(it), expected_name);
126     assert_eq!(expect_punct(it), ':');
127     let string = expect_string(it);
128     assert_eq!(expect_punct(it), ',');
129     string
130 }
131