macros/
helpers.rs

1// SPDX-License-Identifier: GPL-2.0
2
3use proc_macro::{token_stream, Group, Ident, TokenStream, TokenTree};
4
5pub(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
13pub(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
24pub(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
32pub(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
48pub(crate) fn expect_ident(it: &mut token_stream::IntoIter) -> String {
49    try_ident(it).expect("Expected Ident")
50}
51
52pub(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
60pub(crate) fn expect_string(it: &mut token_stream::IntoIter) -> String {
61    try_string(it).expect("Expected string")
62}
63
64pub(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
70pub(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
78pub(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.
85pub(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
101pub(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.
124pub(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}