rune/ast/
item_fn.rs

1use crate::ast::prelude::*;
2
3#[test]
4#[cfg(not(miri))]
5fn ast_parse() {
6    use crate::parse;
7    use crate::SourceId;
8
9    rt::<ast::ItemFn>("async fn hello() {}");
10    assert!(
11        parse::parse_all::<ast::ItemFn>("fn async hello() {}", SourceId::EMPTY, false).is_err()
12    );
13
14    let item = rt::<ast::ItemFn>("fn hello() {}");
15    assert_eq!(item.args.len(), 0);
16
17    let item = rt::<ast::ItemFn>("fn hello(foo, bar) {}");
18    assert_eq!(item.args.len(), 2);
19
20    rt::<ast::ItemFn>("pub fn hello(foo, bar) {}");
21    rt::<ast::ItemFn>("pub async fn hello(foo, bar) {}");
22    rt::<ast::ItemFn>("#[inline] fn hello(foo, bar) {}");
23
24    let item = rt::<ast::ItemFn>("#[inline] pub async fn hello(foo, bar) {}");
25    assert!(matches!(item.visibility, ast::Visibility::Public(..)));
26
27    assert_eq!(item.args.len(), 2);
28    assert_eq!(item.attributes.len(), 1);
29    assert!(item.async_token.is_some());
30    assert!(item.const_token.is_none());
31
32    let item = rt::<ast::ItemFn>("#[inline] pub const fn hello(foo, bar) {}");
33    assert!(matches!(item.visibility, ast::Visibility::Public(..)));
34
35    assert_eq!(item.args.len(), 2);
36    assert_eq!(item.attributes.len(), 1);
37    assert!(item.async_token.is_none());
38    assert!(item.const_token.is_some());
39
40    let item_with_type = rt::<ast::ItemFn>("pub async fn hello(foo, bar) -> Type {}");
41    assert!(item_with_type.output.is_some());
42}
43
44/// A function item.
45#[derive(Debug, TryClone, PartialEq, Eq, Parse, ToTokens, Spanned)]
46#[rune(parse = "meta_only")]
47#[non_exhaustive]
48pub struct ItemFn {
49    /// The attributes for the fn
50    #[rune(iter, meta)]
51    pub attributes: Vec<ast::Attribute>,
52    /// The visibility of the `fn` item
53    #[rune(option, meta)]
54    pub visibility: ast::Visibility,
55    /// The optional `const` keyword.
56    #[rune(iter, meta)]
57    pub const_token: Option<T![const]>,
58    /// The optional `async` keyword.
59    #[rune(iter, meta)]
60    pub async_token: Option<T![async]>,
61    /// The `fn` token.
62    pub fn_token: T![fn],
63    /// The name of the function.
64    pub name: ast::Ident,
65    /// The arguments of the function.
66    pub args: ast::Parenthesized<ast::FnArg, T![,]>,
67    /// The function type.
68    #[rune(option)]
69    pub output: Option<(T![->], ast::Type)>,
70    /// The body of the function.
71    pub body: ast::Block,
72    /// Opaque identifier for fn item.
73    #[rune(skip)]
74    pub(crate) id: ItemId,
75}
76
77impl ItemFn {
78    /// Get the descriptive span of this item, e.g. `pub fn foo()` instead of
79    /// the span for the whole function declaration, body included.
80    pub(crate) fn descriptive_span(&self) -> Span {
81        if let Some(async_token) = &self.async_token {
82            async_token.span().join(self.args.span())
83        } else {
84            self.fn_token.span().join(self.args.span())
85        }
86    }
87
88    /// Test if function is an instance fn.
89    pub(crate) fn is_instance(&self) -> bool {
90        matches!(self.args.first(), Some((ast::FnArg::SelfValue(..), _)))
91    }
92}
93
94item_parse!(Fn, ItemFn, "function item");
95
96impl Peek for ItemFn {
97    fn peek(p: &mut Peeker<'_>) -> bool {
98        match (p.nth(0), p.nth(1)) {
99            (K![fn], _) => true,
100            (K![async], K![fn]) => true,
101            (K![const], K![fn]) => true,
102            _ => false,
103        }
104    }
105}