Skip to main content

rune/ast/
attribute.rs

1use crate::ast::prelude::*;
2
3#[test]
4#[cfg(not(miri))]
5fn ast_parse() {
6    rt::<ast::Attribute>("#[foo = \"foo\"]");
7    rt::<ast::Attribute>("#[foo()]");
8    rt::<ast::Attribute>("#![foo]");
9    rt::<ast::Attribute>("#![cfg(all(feature = \"potato\"))]");
10    rt::<ast::Attribute>("#[x+1]");
11
12    const TEST_STRINGS: &[&str] = &[
13        "#[foo]",
14        "#[a::b::c]",
15        "#[foo = \"hello world\"]",
16        "#[foo = 1]",
17        "#[foo = 1.3]",
18        "#[foo = true]",
19        "#[foo = b\"bytes\"]",
20        "#[foo = (1, 2, \"string\")]",
21        "#[foo = #{\"a\": 1} ]",
22        r#"#[foo = Fred {"a": 1} ]"#,
23        r#"#[foo = a::Fred {"a": #{ "b": 2 } } ]"#,
24        "#[bar()]",
25        "#[bar(baz)]",
26        "#[derive(Debug, PartialEq, PartialOrd)]",
27        "#[tracing::instrument(skip(non_debug))]",
28        "#[zanzibar(a = \"z\", both = false, sasquatch::herring)]",
29        r#"#[doc = "multiline \
30                docs are neat"
31        ]"#,
32    ];
33
34    for s in TEST_STRINGS.iter() {
35        rt::<ast::Attribute>(s);
36        let withbang = s.replacen("#[", "#![", 1);
37        rt::<ast::Attribute>(&withbang);
38    }
39}
40
41/// Attributes like:
42///
43/// * `#[derive(Debug)]`.
44/// * `#![doc = "test"]`.
45#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
46#[non_exhaustive]
47pub struct Attribute {
48    /// The `#` character
49    pub hash: T![#],
50    /// Specify if the attribute is outer `#!` or inner `#`
51    #[rune(option)]
52    pub style: AttrStyle,
53    /// The `[` character
54    pub open: T!['['],
55    /// The path of the attribute
56    pub path: ast::Path,
57    /// The input to the input of the attribute
58    #[rune(iter)]
59    pub input: TokenStream,
60    /// The `]` character
61    pub close: T![']'],
62}
63
64impl Attribute {}
65
66impl Parse for Attribute {
67    fn parse(p: &mut Parser<'_>) -> Result<Self> {
68        let hash = p.parse()?;
69        let style = p.parse()?;
70        let open = p.parse()?;
71        let path = p.parse()?;
72
73        let close;
74
75        let mut level = 1;
76        let mut input = TokenStream::new();
77
78        loop {
79            let token = p.next()?;
80
81            match token.kind {
82                K!['['] => level += 1,
83                K![']'] => {
84                    level -= 1;
85                }
86                _ => (),
87            }
88
89            if level == 0 {
90                close = ast::CloseBracket { span: token.span };
91                break;
92            }
93
94            input.push(token)?;
95        }
96
97        Ok(Attribute {
98            hash,
99            style,
100            open,
101            path,
102            input,
103            close,
104        })
105    }
106}
107
108impl Peek for Attribute {
109    fn peek(p: &mut Peeker<'_>) -> bool {
110        match (p.nth(0), p.nth(1)) {
111            (K![#], K![!]) => true,
112            (K![#], K!['[']) => true,
113            _ => false,
114        }
115    }
116}
117
118impl IntoExpectation for Attribute {
119    fn into_expectation(self) -> Expectation {
120        Expectation::Description(match &self.style {
121            AttrStyle::Inner => "inner attribute",
122            AttrStyle::Outer(_) => "outer attribute",
123        })
124    }
125}
126
127/// Whether or not the attribute is an outer `#!` or inner `#` attribute
128#[derive(Debug, TryClone, Clone, Copy, PartialEq, Eq, OptionSpanned, ToTokens)]
129#[try_clone(copy)]
130#[non_exhaustive]
131pub enum AttrStyle {
132    /// `#`
133    Inner,
134    /// `#!`
135    Outer(T![!]),
136}
137
138impl Parse for AttrStyle {
139    fn parse(p: &mut Parser<'_>) -> Result<Self> {
140        Ok(if p.peek::<T![!]>()? {
141            Self::Outer(p.parse()?)
142        } else {
143            Self::Inner
144        })
145    }
146}
147
148/// Tag struct to assist peeking for an outer `#![...]` attributes at the top of
149/// a module/file
150#[non_exhaustive]
151pub(crate) struct OuterAttribute;
152
153impl Peek for OuterAttribute {
154    fn peek(p: &mut Peeker<'_>) -> bool {
155        match (p.nth(0), p.nth(1)) {
156            (K![#], K![!]) => true,
157            _ => false,
158        }
159    }
160}