Skip to main content

rune/ast/
item.rs

1use core::mem::take;
2
3use crate::ast::prelude::*;
4
5/// A declaration.
6#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
7#[non_exhaustive]
8pub enum Item {
9    /// A use declaration.
10    Use(ast::ItemUse),
11    /// A function declaration.
12    // large variant, so boxed
13    Fn(ast::ItemFn),
14    /// An enum declaration.
15    Enum(ast::ItemEnum),
16    /// A struct declaration.
17    Struct(ast::ItemStruct),
18    /// An impl declaration.
19    Impl(ast::ItemImpl),
20    /// A module declaration.
21    Mod(ast::ItemMod),
22    /// A const declaration.
23    Const(ast::ItemConst),
24    /// A static declaration.
25    Static(ast::ItemStatic),
26    /// A macro call expanding into an item.
27    MacroCall(ast::MacroCall),
28}
29
30impl Item {
31    /// Indicates if the declaration needs a semi-colon or not.
32    pub(crate) fn needs_semi_colon(&self) -> bool {
33        match self {
34            Self::Use(..) => true,
35            Self::Struct(st) => st.needs_semi_colon(),
36            Self::Const(..) => true,
37            Self::Static(..) => true,
38            _ => false,
39        }
40    }
41
42    /// Test if declaration is suitable inside of a file.
43    pub(crate) fn peek_as_item(p: &mut Peeker<'_>) -> bool {
44        match p.nth(0) {
45            K![use] => true,
46            K![enum] => true,
47            K![struct] => true,
48            K![impl] => true,
49            K![async] => matches!(p.nth(1), K![fn]),
50            K![fn] => true,
51            K![mod] => true,
52            K![const] => true,
53            K![static] => true,
54            _ => false,
55        }
56    }
57
58    /// Parse an Item attaching the given meta and optional path.
59    ///
60    /// Items nest through `mod`, which does not go through expression parsing,
61    /// so how deeply they nest is bounded here as well. Every nested item is
62    /// parsed through here rather than through [`Parse::parse`], since the
63    /// bodies of a file and of a `mod` parse their meta before the item itself.
64    pub(crate) fn parse_with_meta_path(
65        p: &mut Parser<'_>,
66        attributes: Vec<ast::Attribute>,
67        visibility: ast::Visibility,
68        path: Option<ast::Path>,
69    ) -> Result<Self> {
70        p.nested(|p| Self::parse_with_meta_path_inner(p, attributes, visibility, path))
71    }
72
73    fn parse_with_meta_path_inner(
74        p: &mut Parser<'_>,
75        mut attributes: Vec<ast::Attribute>,
76        mut visibility: ast::Visibility,
77        path: Option<ast::Path>,
78    ) -> Result<Self> {
79        let item = if let Some(path) = path {
80            Self::MacroCall(ast::MacroCall::parse_with_meta_path(
81                p,
82                take(&mut attributes),
83                path,
84            )?)
85        } else {
86            let mut const_token = p.parse::<Option<T![const]>>()?;
87            let mut static_token = p.parse::<Option<T![static]>>()?;
88            let mut async_token = p.parse::<Option<T![async]>>()?;
89
90            let item = match p.nth(0)? {
91                K![use] => Self::Use(ast::ItemUse::parse_with_meta(
92                    p,
93                    take(&mut attributes),
94                    take(&mut visibility),
95                )?),
96                K![enum] => Self::Enum(ast::ItemEnum::parse_with_meta(
97                    p,
98                    take(&mut attributes),
99                    take(&mut visibility),
100                )?),
101                K![struct] => Self::Struct(ast::ItemStruct::parse_with_meta(
102                    p,
103                    take(&mut attributes),
104                    take(&mut visibility),
105                )?),
106                K![impl] => Self::Impl(ast::ItemImpl::parse_with_attributes(
107                    p,
108                    take(&mut attributes),
109                )?),
110                K![fn] => Self::Fn(ast::ItemFn::parse_with_meta(
111                    p,
112                    take(&mut attributes),
113                    take(&mut visibility),
114                    take(&mut const_token),
115                    take(&mut async_token),
116                )?),
117                K![mod] => Self::Mod(ast::ItemMod::parse_with_meta(
118                    p,
119                    take(&mut attributes),
120                    take(&mut visibility),
121                )?),
122                K![ident] => {
123                    if let Some(const_token) = const_token.take() {
124                        Self::Const(ast::ItemConst::parse_with_meta(
125                            p,
126                            take(&mut attributes),
127                            take(&mut visibility),
128                            const_token,
129                        )?)
130                    } else if let Some(static_token) = static_token.take() {
131                        Self::Static(ast::ItemStatic::parse_with_meta(
132                            p,
133                            take(&mut attributes),
134                            take(&mut visibility),
135                            static_token,
136                        )?)
137                    } else {
138                        Self::MacroCall(p.parse()?)
139                    }
140                }
141                _ => {
142                    return Err(compile::Error::expected(
143                        p.tok_at(0)?,
144                        "`fn`, `mod`, `struct`, `enum`, `use`, or macro call",
145                    ))
146                }
147            };
148
149            if let Some(span) = const_token.option_span() {
150                return Err(compile::Error::unsupported(span, "const modifier"));
151            }
152
153            if let Some(span) = static_token.option_span() {
154                return Err(compile::Error::unsupported(span, "static modifier"));
155            }
156
157            if let Some(span) = async_token.option_span() {
158                return Err(compile::Error::unsupported(span, "async modifier"));
159            }
160
161            item
162        };
163
164        if let Some(span) = attributes.option_span() {
165            return Err(compile::Error::unsupported(span, "attribute"));
166        }
167
168        if let Some(span) = visibility.option_span() {
169            return Err(compile::Error::unsupported(span, "visibility modifier"));
170        }
171
172        Ok(item)
173    }
174}
175
176impl Parse for Item {
177    fn parse(p: &mut Parser<'_>) -> Result<Self> {
178        let attributes = p.parse()?;
179        let visibility = p.parse()?;
180        let path = p.parse()?;
181        Self::parse_with_meta_path(p, attributes, visibility, path)
182    }
183}