xref: /third_party/rust/crates/cxx/syntax/file.rs (revision 33d722a9)
1use crate::syntax::cfg::CfgExpr;
2use crate::syntax::namespace::Namespace;
3use quote::quote;
4use syn::parse::{Error, Parse, ParseStream, Result};
5use syn::{
6    braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemImpl,
7    ItemStruct, ItemUse, LitStr, Token, Visibility,
8};
9
10pub struct Module {
11    pub cfg: CfgExpr,
12    pub namespace: Namespace,
13    pub attrs: Vec<Attribute>,
14    pub vis: Visibility,
15    pub unsafety: Option<Token![unsafe]>,
16    pub mod_token: Token![mod],
17    pub ident: Ident,
18    pub brace_token: token::Brace,
19    pub content: Vec<Item>,
20}
21
22pub enum Item {
23    Struct(ItemStruct),
24    Enum(ItemEnum),
25    ForeignMod(ItemForeignMod),
26    Use(ItemUse),
27    Impl(ItemImpl),
28    Other(RustItem),
29}
30
31pub struct ItemForeignMod {
32    pub attrs: Vec<Attribute>,
33    pub unsafety: Option<Token![unsafe]>,
34    pub abi: Abi,
35    pub brace_token: token::Brace,
36    pub items: Vec<ForeignItem>,
37}
38
39impl Parse for Module {
40    fn parse(input: ParseStream) -> Result<Self> {
41        let cfg = CfgExpr::Unconditional;
42        let namespace = Namespace::ROOT;
43        let mut attrs = input.call(Attribute::parse_outer)?;
44        let vis: Visibility = input.parse()?;
45        let unsafety: Option<Token![unsafe]> = input.parse()?;
46        let mod_token: Token![mod] = input.parse()?;
47        let ident: Ident = input.parse()?;
48
49        let semi: Option<Token![;]> = input.parse()?;
50        if let Some(semi) = semi {
51            let span = quote!(#vis #mod_token #semi);
52            return Err(Error::new_spanned(
53                span,
54                "#[cxx::bridge] module must have inline contents",
55            ));
56        }
57
58        let content;
59        let brace_token = braced!(content in input);
60        attrs.extend(content.call(Attribute::parse_inner)?);
61
62        let mut items = Vec::new();
63        while !content.is_empty() {
64            items.push(content.parse()?);
65        }
66
67        Ok(Module {
68            cfg,
69            namespace,
70            attrs,
71            vis,
72            unsafety,
73            mod_token,
74            ident,
75            brace_token,
76            content: items,
77        })
78    }
79}
80
81impl Parse for Item {
82    fn parse(input: ParseStream) -> Result<Self> {
83        let attrs = input.call(Attribute::parse_outer)?;
84
85        let ahead = input.fork();
86        let unsafety = if ahead.parse::<Option<Token![unsafe]>>()?.is_some()
87            && ahead.parse::<Option<Token![extern]>>()?.is_some()
88            && ahead.parse::<Option<LitStr>>().is_ok()
89            && ahead.peek(token::Brace)
90        {
91            Some(input.parse()?)
92        } else {
93            None
94        };
95
96        let item = input.parse()?;
97        match item {
98            RustItem::Struct(mut item) => {
99                item.attrs.splice(..0, attrs);
100                Ok(Item::Struct(item))
101            }
102            RustItem::Enum(mut item) => {
103                item.attrs.splice(..0, attrs);
104                Ok(Item::Enum(item))
105            }
106            RustItem::ForeignMod(mut item) => {
107                item.attrs.splice(..0, attrs);
108                Ok(Item::ForeignMod(ItemForeignMod {
109                    attrs: item.attrs,
110                    unsafety,
111                    abi: item.abi,
112                    brace_token: item.brace_token,
113                    items: item.items,
114                }))
115            }
116            RustItem::Impl(mut item) => {
117                item.attrs.splice(..0, attrs);
118                Ok(Item::Impl(item))
119            }
120            RustItem::Use(mut item) => {
121                item.attrs.splice(..0, attrs);
122                Ok(Item::Use(item))
123            }
124            other => Ok(Item::Other(other)),
125        }
126    }
127}
128