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
64// Events and indexing.
65pub const EVENT_ATTRIBUTE_NAME: &str = "event";
66pub const INDEXED_ATTRIBUTE_NAME: &str = "indexed";
67
68pub const KNOWN_ATTRIBUTE_NAMES: &[&str] = &[
69    STORAGE_ATTRIBUTE_NAME,
70    DOC_COMMENT_ATTRIBUTE_NAME,
71    TEST_ATTRIBUTE_NAME,
72    INLINE_ATTRIBUTE_NAME,
73    PAYABLE_ATTRIBUTE_NAME,
74    ALLOW_ATTRIBUTE_NAME,
75    CFG_ATTRIBUTE_NAME,
76    DEPRECATED_ATTRIBUTE_NAME,
77    FALLBACK_ATTRIBUTE_NAME,
78    ABI_NAME_ATTRIBUTE_NAME,
79    EVENT_ATTRIBUTE_NAME,
80    INDEXED_ATTRIBUTE_NAME,
81];
82
83/// An attribute declaration. Attribute declaration
84/// can potentially have an arbitrary number of [Attribute]s.
85/// [Attribute]s can potentially have any number of [AttributeArg]s.
86/// Each [AttributeArg] can have a value assigned.
87///
88/// E.g.:
89///
90/// ```ignore
91/// #[attribute]
92/// #[attribute_1, attribute_2]
93/// #[attribute()]
94/// #[attribute(arg)]
95/// #[attribute(arg_1, arg_2)]
96/// #[attribute(arg_1 = "value", arg_2 = true)]
97/// #[attribute_1, attribute_2(arg_1), attribute_3(arg_1, arg_2 = true)]
98/// ```
99///
100/// [AttributeDecl]s can be _inner_ or _outer_, as explained in [AttributeHashKind].
101// TODO: Currently, inner attributes are supported only on module doc comments,
102//       those starting with `//!`.
103//       See: https://github.com/FuelLabs/sway/issues/6924
104#[derive(Clone, Debug, Serialize)]
105pub struct AttributeDecl {
106    pub hash_kind: AttributeHashKind,
107    pub attribute: SquareBrackets<Punctuated<Attribute, CommaToken>>,
108}
109
110impl AttributeDecl {
111    /// Creates the `doc-comment` [AttributeDecl] for a single line of an outer comment. E.g.:
112    /// ```ignore
113    /// /// This is an outer comment.
114    /// ```
115    /// The `span` is the overall span: `/// This is an outer comment.`.
116    /// The `content_span` is the span of the content, without the leading `///`: ` This is an outer comment.`.
117    pub fn new_outer_doc_comment(span: Span, content_span: Span) -> Self {
118        Self::new_doc_comment(
119            span.clone(),
120            content_span,
121            AttributeHashKind::Outer(HashToken::new(span)),
122        )
123    }
124
125    /// Creates the `doc-comment` [AttributeDecl] for a single line of an inner comment. E.g.:
126    /// ```ignore
127    /// //! This is an inner comment.
128    /// ```
129    /// The `span` is the overall span: `//! This is an inner comment.`.
130    /// The `content_span` is the span of the content, without the leading `//!`: ` This is an inner comment.`.
131    pub fn new_inner_doc_comment(span: Span, content_span: Span) -> Self {
132        Self::new_doc_comment(
133            span.clone(),
134            content_span,
135            AttributeHashKind::Inner(HashBangToken::new(span)),
136        )
137    }
138
139    fn new_doc_comment(span: Span, content_span: Span, hash_kind: AttributeHashKind) -> Self {
140        // TODO: Store the comment line in an argument value as
141        //       discussed in https://github.com/FuelLabs/sway/issues/6938.
142        let name = Ident::new_no_trim(content_span.clone());
143        AttributeDecl {
144            hash_kind,
145            attribute: SquareBrackets::new(
146                Punctuated::single(Attribute {
147                    name: Ident::new_with_override(
148                        DOC_COMMENT_ATTRIBUTE_NAME.to_string(),
149                        span.clone(),
150                    ),
151                    args: Some(Parens::new(
152                        Punctuated::single(AttributeArg { name, value: None }),
153                        content_span,
154                    )),
155                }),
156                span,
157            ),
158        }
159    }
160
161    /// `self` is a doc comment, either an inner (`//!`) or outer (`///`).
162    pub fn is_doc_comment(&self) -> bool {
163        self.attribute.inner.value_separator_pairs.is_empty()
164            && self
165                .attribute
166                .inner
167                .final_value_opt
168                .as_ref()
169                .is_some_and(|attr| attr.is_doc_comment())
170    }
171
172    pub fn is_inner(&self) -> bool {
173        matches!(self.hash_kind, AttributeHashKind::Inner(_))
174    }
175}
176
177impl Spanned for AttributeDecl {
178    fn span(&self) -> Span {
179        let hash_span = match &self.hash_kind {
180            AttributeHashKind::Inner(hash_bang_token) => hash_bang_token.span(),
181            AttributeHashKind::Outer(hash_token) => hash_token.span(),
182        };
183        Span::join(hash_span, &self.attribute.span())
184    }
185}
186
187/// Denotes if an [AttributeDecl] is an _inner_ or _outer_ attribute declaration.
188///
189/// E.g.:
190/// ```ignore
191/// /// This is an outer doc comment.
192/// /// It annotates the struct `Foo`.
193/// struct Foo {}
194///
195/// // This is an outer attribute.
196/// // In annotates the function `fun`.
197/// #[inline(always)]
198/// fn fun() {}
199///
200/// //! This is an inner doc comment.
201/// //! It annotates the library module.
202/// library;
203///
204/// // This is an inner attribute.
205/// // In annotates whichever item it is declared in.
206/// #![allow(dead_code)]
207/// ```
208#[derive(Clone, Debug, Serialize)]
209pub enum AttributeHashKind {
210    /// Inner specifies that the attribute annotates
211    /// the item that the attribute is declared within.
212    Inner(HashBangToken),
213    /// Outer specifies that the attribute annotates
214    /// the item that immediately follows the attribute.
215    Outer(HashToken),
216}
217
218impl Spanned for AttributeHashKind {
219    fn span(&self) -> Span {
220        match self {
221            AttributeHashKind::Inner(token) => token.span(),
222            AttributeHashKind::Outer(token) => token.span(),
223        }
224    }
225}
226
227#[derive(Clone, Debug, Serialize)]
228pub struct AttributeArg {
229    pub name: Ident,
230    pub value: Option<Literal>,
231}
232
233impl Spanned for AttributeArg {
234    fn span(&self) -> Span {
235        if let Some(value) = &self.value {
236            Span::join(self.name.span(), &value.span())
237        } else {
238            self.name.span()
239        }
240    }
241}
242
243#[derive(Clone, Debug, Serialize)]
244pub struct Attribute {
245    pub name: Ident,
246    pub args: Option<Parens<Punctuated<AttributeArg, CommaToken>>>,
247}
248
249impl Attribute {
250    pub fn is_doc_comment(&self) -> bool {
251        self.name.as_str() == DOC_COMMENT_ATTRIBUTE_NAME
252    }
253    pub fn is_cfg(&self) -> bool {
254        self.name.as_str() == CFG_ATTRIBUTE_NAME
255    }
256}
257
258impl Spanned for Attribute {
259    fn span(&self) -> Span {
260        self.args
261            .as_ref()
262            .map(|args| Span::join(self.name.span(), &args.span()))
263            .unwrap_or_else(|| self.name.span())
264    }
265}