Skip to main content

rucc_ast/
attr.rs

1//! Attributes, in both spellings.
2//!
3//! Design: `spec/06-lexer-and-parser.md` sections 6.6 and 6.7.
4//!
5//! C23's `[[deprecated]]` and GCC's `__attribute__((deprecated))` mean the same thing and are
6//! written in different places, so the syntax each one was written in is kept on the node. The
7//! placement rules differ between the two and GCC's are not always what its documentation says,
8//! so a diagnostic about where an attribute may go has to know which spelling it is talking
9//! about. Nothing here decides what an attribute *means*; that is `spec/13-gnu-extensions.md`
10//! and it happens in semantic analysis.
11
12use rucc_base::Symbol;
13use rucc_diag::Span;
14
15use crate::ast::AttrArgList;
16use crate::expr::ExprId;
17
18/// One attribute.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct Attribute {
21    /// The namespace in `[[gnu::packed]]`, and `None` for an attribute written without one.
22    pub namespace: Option<Symbol>,
23    /// The name, with GCC's optional pair of leading and trailing underscores already off it,
24    /// so that `__packed__` and `packed` are the same symbol here.
25    pub name: Symbol,
26    /// The arguments, which are not all expressions.
27    pub args: AttrArgList,
28    /// Which spelling it was written in.
29    pub syntax: AttrSyntax,
30    /// The whole attribute, from its name to its closing parenthesis.
31    pub span: Span,
32}
33
34/// Which spelling an attribute was written in.
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum AttrSyntax {
37    /// `[[name]]`, the C23 one.
38    Standard,
39    /// `__attribute__((name))`, the GNU one.
40    Gnu,
41    /// `__declspec(name)`, which the Windows headers are full of.
42    Declspec,
43}
44
45/// One argument of an attribute.
46///
47/// Most attribute arguments are expressions, but a few take a bare identifier that must not be
48/// looked up as one: the `printf` in `format(printf, 1, 2)` names an archetype and the `DI` in
49/// `mode(DI)` names a machine mode, and neither is a variable. Treating them as expressions is
50/// how a compiler ends up reporting an undeclared identifier inside an attribute.
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum AttrArg {
53    /// An identifier, kept as one.
54    Ident(Symbol),
55    /// An expression.
56    Expr(ExprId),
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn an_attribute_argument_is_eight_bytes() {
65        assert_eq!(size_of::<AttrArg>(), 8);
66    }
67}