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}