sway_ast/
attribute.rs

1use crate::priv_prelude::*;
2
3#[derive(Clone, Debug, Serialize)]
4pub struct Annotated<T> {
5    pub attributes: Vec<AttributeDecl>,
6    pub value: T,
7}
8
9// Storage access and purity.
10pub const STORAGE_ATTRIBUTE_NAME: &str = "storage";
11pub const STORAGE_READ_ARG_NAME: &str = "read";
12pub const STORAGE_WRITE_ARG_NAME: &str = "write";
13
14// Function inlining.
15pub const INLINE_ATTRIBUTE_NAME: &str = "inline";
16pub const INLINE_NEVER_ARG_NAME: &str = "never";
17pub const INLINE_ALWAYS_ARG_NAME: &str = "always";
18
19// Payable functions.
20pub const PAYABLE_ATTRIBUTE_NAME: &str = "payable";
21
22// Fallback functions.
23pub const FALLBACK_ATTRIBUTE_NAME: &str = "fallback";
24
25// Documentation comments.
26// Note that because "doc-comment" is not a valid identifier,
27// doc-comment attributes cannot be declared in code.
28// They are exclusively created by the compiler to denote
29// doc comments, `///` and `//!`.
30pub const DOC_COMMENT_ATTRIBUTE_NAME: &str = "doc-comment";
31
32// In-language unit testing.
33pub const TEST_ATTRIBUTE_NAME: &str = "test";
34pub const TEST_SHOULD_REVERT_ARG_NAME: &str = "should_revert";
35
36// Allow warnings.
37pub const ALLOW_ATTRIBUTE_NAME: &str = "allow";
38pub const ALLOW_DEAD_CODE_ARG_NAME: &str = "dead_code";
39pub const ALLOW_DEPRECATED_ARG_NAME: &str = "deprecated";
40
41// Conditional compilation.
42pub const CFG_ATTRIBUTE_NAME: &str = "cfg";
43pub const CFG_TARGET_ARG_NAME: &str = "target";
44pub const CFG_PROGRAM_TYPE_ARG_NAME: &str = "program_type";
45
46// Deprecation.
47pub const DEPRECATED_ATTRIBUTE_NAME: &str = "deprecated";
48pub const DEPRECATED_NOTE_ARG_NAME: &str = "note";
49
50// Error types.
51pub const ERROR_TYPE_ATTRIBUTE_NAME: &str = "error_type";
52pub const ERROR_ATTRIBUTE_NAME: &str = "error";
53pub const ERROR_M_ARG_NAME: &str = "m";
54
55// Backtracing.
56pub const TRACE_ATTRIBUTE_NAME: &str = "trace";
57pub const TRACE_NEVER_ARG_NAME: &str = "never";
58pub const TRACE_ALWAYS_ARG_NAME: &str = "always";
59
60// Abi names.
61pub const ABI_NAME_ATTRIBUTE_NAME: &str = "abi_name";
62pub const ABI_NAME_NAME_ARG_NAME: &str = "name";
63
64pub const KNOWN_ATTRIBUTE_NAMES: &[&str] = &[
65    STORAGE_ATTRIBUTE_NAME,
66    DOC_COMMENT_ATTRIBUTE_NAME,
67    TEST_ATTRIBUTE_NAME,
68    INLINE_ATTRIBUTE_NAME,
69    PAYABLE_ATTRIBUTE_NAME,
70    ALLOW_ATTRIBUTE_NAME,
71    CFG_ATTRIBUTE_NAME,
72    DEPRECATED_ATTRIBUTE_NAME,
73    FALLBACK_ATTRIBUTE_NAME,
74    ABI_NAME_ATTRIBUTE_NAME,
75];
76
77/// An attribute declaration. Attribute declaration
78/// can potentially have an arbitrary number of [Attribute]s.
79/// [Attribute]s can potentially have any number of [AttributeArg]s.
80/// Each [AttributeArg] can have a value assigned.
81///
82/// E.g.:
83///
84/// ```ignore
85/// #[attribute]
86/// #[attribute_1, attribute_2]
87/// #[attribute()]
88/// #[attribute(arg)]
89/// #[attribute(arg_1, arg_2)]
90/// #[attribute(arg_1 = "value", arg_2 = true)]
91/// #[attribute_1, attribute_2(arg_1), attribute_3(arg_1, arg_2 = true)]
92/// ```
93///
94/// [AttributeDecl]s can be _inner_ or _outer_, as explained in [AttributeHashKind].
95// TODO: Currently, inner attributes are supported only on module doc comments,
96//       those starting with `//!`.
97//       See: https://github.com/FuelLabs/sway/issues/6924
98#[derive(Clone, Debug, Serialize)]
99pub struct AttributeDecl {
100    pub hash_kind: AttributeHashKind,
101    pub attribute: SquareBrackets<Punctuated<Attribute, CommaToken>>,
102}
103
104impl AttributeDecl {
105    /// Creates the `doc-comment` [AttributeDecl] for a single line of an outer comment. E.g.:
106    /// ```ignore
107    /// /// This is an outer comment.
108    /// ```
109    /// The `span` is the overall span: `/// This is an outer comment.`.
110    /// The `content_span` is the span of the content, without the leading `///`: ` This is an outer comment.`.
111    pub fn new_outer_doc_comment(span: Span, content_span: Span) -> Self {
112        Self::new_doc_comment(
113            span.clone(),
114            content_span,
115            AttributeHashKind::Outer(HashToken::new(span)),
116        )
117    }
118
119    /// Creates the `doc-comment` [AttributeDecl] for a single line of an inner comment. E.g.:
120    /// ```ignore
121    /// //! This is an inner comment.
122    /// ```
123    /// The `span` is the overall span: `//! This is an inner comment.`.
124    /// The `content_span` is the span of the content, without the leading `//!`: ` This is an inner comment.`.
125    pub fn new_inner_doc_comment(span: Span, content_span: Span) -> Self {
126        Self::new_doc_comment(
127            span.clone(),
128            content_span,
129            AttributeHashKind::Inner(HashBangToken::new(span)),
130        )
131    }
132
133    fn new_doc_comment(span: Span, content_span: Span, hash_kind: AttributeHashKind) -> Self {
134        // TODO: Store the comment line in an argument value as
135        //       discussed in https://github.com/FuelLabs/sway/issues/6938.
136        let name = Ident::new_no_trim(content_span.clone());
137        AttributeDecl {
138            hash_kind,
139            attribute: SquareBrackets::new(
140                Punctuated::single(Attribute {
141                    name: Ident::new_with_override(
142                        DOC_COMMENT_ATTRIBUTE_NAME.to_string(),
143                        span.clone(),
144                    ),
145                    args: Some(Parens::new(
146                        Punctuated::single(AttributeArg { name, value: None }),
147                        content_span,
148                    )),
149                }),
150                span,
151            ),
152        }
153    }
154
155    /// `self` is a doc comment, either an inner (`//!`) or outer (`///`).
156    pub fn is_doc_comment(&self) -> bool {
157        self.attribute.inner.value_separator_pairs.is_empty()
158            && self
159                .attribute
160                .inner
161                .final_value_opt
162                .as_ref()
163                .is_some_and(|attr| attr.is_doc_comment())
164    }
165
166    pub fn is_inner(&self) -> bool {
167        matches!(self.hash_kind, AttributeHashKind::Inner(_))
168    }
169}
170
171impl Spanned for AttributeDecl {
172    fn span(&self) -> Span {
173        let hash_span = match &self.hash_kind {
174            AttributeHashKind::Inner(hash_bang_token) => hash_bang_token.span(),
175            AttributeHashKind::Outer(hash_token) => hash_token.span(),
176        };
177        Span::join(hash_span, &self.attribute.span())
178    }
179}
180
181/// Denotes if an [AttributeDecl] is an _inner_ or _outer_ attribute declaration.
182///
183/// E.g.:
184/// ```ignore
185/// /// This is an outer doc comment.
186/// /// It annotates the struct `Foo`.
187/// struct Foo {}
188///
189/// // This is an outer attribute.
190/// // In annotates the function `fun`.
191/// #[inline(always)]
192/// fn fun() {}
193///
194/// //! This is an inner doc comment.
195/// //! It annotates the library module.
196/// library;
197///
198/// // This is an inner attribute.
199/// // In annotates whichever item it is declared in.
200/// #![allow(dead_code)]
201/// ```
202#[derive(Clone, Debug, Serialize)]
203pub enum AttributeHashKind {
204    /// Inner specifies that the attribute annotates
205    /// the item that the attribute is declared within.
206    Inner(HashBangToken),
207    /// Outer specifies that the attribute annotates
208    /// the item that immediately follows the attribute.
209    Outer(HashToken),
210}
211
212impl Spanned for AttributeHashKind {
213    fn span(&self) -> Span {
214        match self {
215            AttributeHashKind::Inner(token) => token.span(),
216            AttributeHashKind::Outer(token) => token.span(),
217        }
218    }
219}
220
221#[derive(Clone, Debug, Serialize)]
222pub struct AttributeArg {
223    pub name: Ident,
224    pub value: Option<Literal>,
225}
226
227impl Spanned for AttributeArg {
228    fn span(&self) -> Span {
229        if let Some(value) = &self.value {
230            Span::join(self.name.span(), &value.span())
231        } else {
232            self.name.span()
233        }
234    }
235}
236
237#[derive(Clone, Debug, Serialize)]
238pub struct Attribute {
239    pub name: Ident,
240    pub args: Option<Parens<Punctuated<AttributeArg, CommaToken>>>,
241}
242
243impl Attribute {
244    pub fn is_doc_comment(&self) -> bool {
245        self.name.as_str() == DOC_COMMENT_ATTRIBUTE_NAME
246    }
247    pub fn is_cfg(&self) -> bool {
248        self.name.as_str() == CFG_ATTRIBUTE_NAME
249    }
250}
251
252impl Spanned for Attribute {
253    fn span(&self) -> Span {
254        self.args
255            .as_ref()
256            .map(|args| Span::join(self.name.span(), &args.span()))
257            .unwrap_or_else(|| self.name.span())
258    }
259}