@@ -400,6 +400,21 @@ final class CParser(AST) : Parser!AST
400400 break ;
401401 }
402402
403+ case TOK .leftBracket:
404+ // C23 6.7.13 attribute-specifier-sequence prefixing a declaration or statement
405+ if (! isCAttributeStart())
406+ goto default ;
407+ {
408+ Specifier attrspec;
409+ attrspec.packalign.setDefault();
410+ cparseCAttributes(attrspec);
411+ if (startsDeclaration(&token))
412+ goto Ldeclaration;
413+ // attributes appertain to the following statement
414+ s = cparseStatement(0 );
415+ }
416+ break ;
417+
403418 case TOK ._Static_assert: // _Static_assert ( constant-expression, string-literal ) ;
404419 s = new AST .StaticAssertStatement(cparseStaticAssert());
405420 break ;
@@ -1888,8 +1903,8 @@ final class CParser(AST) : Parser!AST
18881903 */
18891904 first = false ;
18901905 }
1891- else if (token.value == TOK .__attribute__)
1892- cparseGnuAttributes (specifier);
1906+ else if (token.value == TOK .__attribute__ || isCAttributeStart() )
1907+ cparseAttributes (specifier); // GNU / C23 6.7.13
18931908 else
18941909 break ;
18951910 }
@@ -2517,6 +2532,14 @@ final class CParser(AST) : Parser!AST
25172532 break ;
25182533 }
25192534
2535+ case TOK .leftBracket: // C23 6.7.13 attribute-specifier-sequence
2536+ {
2537+ if (! isCAttributeStart())
2538+ goto default ;
2539+ cparseCAttributes(specifier);
2540+ break ;
2541+ }
2542+
25202543 case TOK .__declspec:
25212544 {
25222545 /* Microsoft extension
@@ -2923,8 +2946,7 @@ final class CParser(AST) : Parser!AST
29232946 const mod = cparseTypeQualifierList();
29242947 if (mod & MOD .xconst)
29252948 constTypes.push(t);
2926- if (token.value == TOK .__attribute__)
2927- cparseGnuAttributes(specifier);
2949+ cparseAttributes(specifier); // GNU / C23 6.7.13
29282950 continue ;
29292951
29302952 default :
@@ -2954,6 +2976,13 @@ final class CParser(AST) : Parser!AST
29542976 {
29552977 case TOK .leftBracket:
29562978 {
2979+ if (isCAttributeStart())
2980+ {
2981+ // C23 6.7.13 attribute-specifier-sequence appertaining to
2982+ // the declarator, e.g. `int x [[maybe_unused]]`
2983+ cparseCAttributes(specifier);
2984+ continue ;
2985+ }
29572986 // post [] syntax, pick up any leading type qualifiers, `static` and `*`
29582987 AST .Type ta;
29592988 nextToken();
@@ -3264,8 +3293,7 @@ final class CParser(AST) : Parser!AST
32643293
32653294 AST .ArgumentLabel id;
32663295 auto t = cparseDeclarator(DTR .xparameter, tspec, id, specifier);
3267- if (token.value == TOK .__attribute__)
3268- cparseGnuAttributes(specifier);
3296+ cparseAttributes(specifier); // GNU / C23 6.7.13
32693297 if (specifier.mod & MOD .xconst)
32703298 t = toConst(t);
32713299 auto param = new AST .Parameter(id.name ? id.loc : typeLoc,
@@ -3860,6 +3888,169 @@ final class CParser(AST) : Parser!AST
38603888 }
38613889 }
38623890
3891+ /* ************************
3892+ * C23 6.7.13 attribute-specifier-sequence parser.
3893+ *
3894+ * Grammar (N3220 6.7.13.2):
3895+ * attribute-specifier-sequence:
3896+ * attribute-specifier-sequence(opt) attribute-specifier
3897+ * attribute-specifier:
3898+ * [ [ attribute-list ] ]
3899+ * attribute-list:
3900+ * attribute(opt)
3901+ * attribute-list , attribute(opt)
3902+ *
3903+ * The lexer emits two `TOK.leftBracket` for `[[` and two `TOK.rightBracket`
3904+ * for `]]`; there is no combined token.
3905+ * Params:
3906+ * specifier = filled in with the recognized attribute(s)
3907+ */
3908+ private void cparseCAttributes (ref Specifier specifier)
3909+ {
3910+ while (isCAttributeStart())
3911+ {
3912+ nextToken(); // first `[`
3913+ nextToken(); // second `[`
3914+ if (token.value != TOK .rightBracket)
3915+ {
3916+ while (1 )
3917+ {
3918+ cparseCAttribute(specifier);
3919+ if (token.value != TOK .comma)
3920+ break ;
3921+ nextToken();
3922+ }
3923+ }
3924+ check(TOK .rightBracket);
3925+ check(TOK .rightBracket);
3926+ }
3927+ }
3928+
3929+ /* ************************
3930+ * True if positioned at the start of a C23 `[[` attribute-specifier-sequence.
3931+ * A lone `[` (e.g. an array subscript) is not one.
3932+ */
3933+ private bool isCAttributeStart ()
3934+ {
3935+ return token.value == TOK .leftBracket && peekNext() == TOK .leftBracket;
3936+ }
3937+
3938+ /* ************************
3939+ * Parse a single C23 attribute (N3220 6.7.13.2):
3940+ * attribute:
3941+ * attribute-token attribute-argument-clause(opt)
3942+ * attribute-token:
3943+ * standard-attribute // identifier
3944+ * attribute-prefixed-token // attribute-prefix :: identifier
3945+ * attribute-argument-clause:
3946+ * ( balanced-token-sequence(opt) )
3947+ *
3948+ * `deprecated` (6.7.13.5) and `noreturn`/`_Noreturn` (6.7.13.7) are mapped onto
3949+ * the existing Specifier fields; every other standard attribute
3950+ * (`nodiscard` 6.7.13.3, `maybe_unused` 6.7.13.4, `fallthrough` 6.7.13.6,
3951+ * `unsequenced`/`reproducible` 6.7.13.8), prefixed attribute, or unknown
3952+ * attribute is accepted and ignored (6.7.13.1). The argument clause is a
3953+ * balanced-token-sequence, not an expression, so it is skipped by paren
3954+ * balancing except for the `deprecated` message string.
3955+ * Params:
3956+ * specifier = filled in with the recognized attribute(s)
3957+ */
3958+ private void cparseCAttribute (ref Specifier specifier)
3959+ {
3960+ // C23 6.7.13.2: a keyword used as an attribute-token is treated as an
3961+ // identifier; `_Noreturn` is the only such keyword with a mapping.
3962+ if (token.value == TOK ._Noreturn)
3963+ {
3964+ specifier.noreturn = true ; // C23 6.7.13.7
3965+ nextToken();
3966+ if (token.value == TOK .leftParenthesis)
3967+ cparseParens();
3968+ return ;
3969+ }
3970+ if (token.value != TOK .identifier)
3971+ {
3972+ // empty attribute (e.g. a trailing comma) or some other keyword used as
3973+ // an attribute-token: accept and ignore
3974+ if (isGnuAttributeName())
3975+ {
3976+ nextToken();
3977+ if (token.value == TOK .leftParenthesis)
3978+ cparseParens();
3979+ }
3980+ return ;
3981+ }
3982+
3983+ Identifier ident = token.ident;
3984+ nextToken();
3985+
3986+ // attribute-prefixed-token: attribute-prefix :: identifier
3987+ // implementation-specific (e.g. `gnu::`, `clang::`): accept and ignore
3988+ if (token.value == TOK .colonColon)
3989+ {
3990+ nextToken();
3991+ if (token.value == TOK .identifier || isGnuAttributeName())
3992+ nextToken();
3993+ if (token.value == TOK .leftParenthesis)
3994+ cparseParens();
3995+ return ;
3996+ }
3997+
3998+ // standard attribute; normalize the `__attr__` spelling (C23 6.7.13.1)
3999+ const id = normalizeCAttributeName(ident);
4000+ if (id is Id._deprecated) // C23 6.7.13.5
4001+ {
4002+ specifier._deprecated = true ;
4003+ if (token.value == TOK .leftParenthesis) // optional message string
4004+ {
4005+ nextToken();
4006+ specifier.depMsg = cparseExpression();
4007+ check(TOK .rightParenthesis);
4008+ }
4009+ return ;
4010+ }
4011+ if (id is Id.noreturn) // C23 6.7.13.7
4012+ {
4013+ specifier.noreturn = true ;
4014+ if (token.value == TOK .leftParenthesis)
4015+ cparseParens();
4016+ return ;
4017+ }
4018+
4019+ // C23 6.7.13.3/.4/.6/.8 and any unknown attribute: accept and ignore
4020+ if (token.value == TOK .leftParenthesis)
4021+ cparseParens();
4022+ }
4023+
4024+ /* ************************
4025+ * C23 6.7.13.1: a standard attribute spelled `attr` and the form `__attr__`
4026+ * behave identically. Strip a surrounding `__` pair so the core name can be
4027+ * compared against the known attribute identifiers.
4028+ */
4029+ private Identifier normalizeCAttributeName (Identifier ident)
4030+ {
4031+ const s = ident.toString();
4032+ if (s.length > 4 && s[0 ] == ' _' && s[1 ] == ' _' && s[$ - 1 ] == ' _' && s[$ - 2 ] == ' _' )
4033+ return Identifier.idPool(s[2 .. $ - 2 ]);
4034+ return ident;
4035+ }
4036+
4037+ /* ************************
4038+ * Consume any mixture of GNU `__attribute__((...))` and C23 `[[...]]`
4039+ * attribute-specifier-sequences, in any order, into `specifier`.
4040+ */
4041+ private void cparseAttributes (ref Specifier specifier)
4042+ {
4043+ while (1 )
4044+ {
4045+ if (token.value == TOK .__attribute__)
4046+ cparseGnuAttributes(specifier);
4047+ else if (isCAttributeStart())
4048+ cparseCAttributes(specifier);
4049+ else
4050+ break ;
4051+ }
4052+ }
4053+
38634054 // }
38644055 /* *****************************************************************************/
38654056 /* **************************** Struct & Enum Parser ***************************/
@@ -3901,8 +4092,7 @@ final class CParser(AST) : Parser!AST
39014092 */
39024093 Specifier specifier;
39034094 specifier.packalign.setDefault();
3904- if (token.value == TOK .__attribute__)
3905- cparseGnuAttributes(specifier);
4095+ cparseAttributes(specifier); // GNU / C23 6.7.13
39064096
39074097 Identifier tag;
39084098 if (token.value == TOK .identifier)
@@ -3943,15 +4133,14 @@ final class CParser(AST) : Parser!AST
39434133 nextToken();
39444134 auto mloc = token.loc;
39454135
3946- if (token.value == TOK .__attribute__)
3947- {
3948- /* gnu-attributes can appear here, but just scan and ignore them
3949- * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3950- */
3951- Specifier specifierx;
3952- specifierx.packalign.setDefault();
3953- cparseGnuAttributes(specifierx);
3954- }
4136+ /* gnu-attributes / C23 6.7.13 attributes can appear before and after the
4137+ * (optional) value (https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html).
4138+ * `deprecated` (C23 6.7.13.5) is applied to the enumerator, since D
4139+ * supports deprecated enum members; the rest are accepted and ignored.
4140+ */
4141+ Specifier emspec;
4142+ emspec.packalign.setDefault();
4143+ cparseAttributes(emspec);
39554144
39564145 AST .Expression value;
39574146 if (token.value == TOK .assign)
@@ -3961,17 +4150,17 @@ final class CParser(AST) : Parser!AST
39614150 // TODO C11 6.7.2.2-2 value must fit into an int
39624151 }
39634152
3964- if (token.value == TOK .__attribute__)
4153+ cparseAttributes(emspec);
4154+
4155+ STC emstc = STC .none;
4156+ AST .DeprecatedDeclaration dd;
4157+ if (emspec._deprecated) // C23 6.7.13.5
39654158 {
3966- /* gnu-attributes can appear here, but just scan and ignore them
3967- * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3968- */
3969- Specifier specifierx;
3970- specifierx.packalign.setDefault();
3971- cparseGnuAttributes(specifierx);
4159+ emstc |= STC .deprecated_;
4160+ if (emspec.depMsg)
4161+ dd = new AST .DeprecatedDeclaration(emspec.depMsg, null );
39724162 }
3973-
3974- auto em = new AST .EnumMember(mloc, ident, value, null , STC .none, null , null );
4163+ auto em = new AST .EnumMember(mloc, ident, value, null , emstc, null , dd);
39754164 members.push(em);
39764165
39774166 if (token.value == TOK .comma)
@@ -3983,11 +4172,10 @@ final class CParser(AST) : Parser!AST
39834172 }
39844173 check(TOK .rightCurly);
39854174
3986- /* GNU Extensions
3987- * Parse the postfix gnu- attributes (opt)
4175+ /* GNU Extensions / C23 6.7.13
4176+ * Parse the postfix attributes (opt)
39884177 */
3989- if (token.value == TOK .__attribute__)
3990- cparseGnuAttributes(specifier);
4178+ cparseAttributes(specifier);
39914179 }
39924180 else if (! tag)
39934181 error(" missing `identifier` after `enum`" );
@@ -4035,6 +4223,8 @@ final class CParser(AST) : Parser!AST
40354223 {
40364224 if (token.value == TOK .__attribute__)
40374225 cparseGnuAttributes(tagSpecifier);
4226+ else if (isCAttributeStart()) // C23 6.7.13
4227+ cparseCAttributes(tagSpecifier);
40384228 else if (token.value == TOK .__declspec)
40394229 cparseDeclspec(tagSpecifier);
40404230 else if (token.value == TOK .__pragma)
@@ -4072,11 +4262,10 @@ final class CParser(AST) : Parser!AST
40724262 */
40734263 }
40744264
4075- /* GNU Extensions
4076- * Parse the postfix gnu- attributes (opt)
4265+ /* GNU Extensions / C23 6.7.13
4266+ * Parse the postfix attributes (opt)
40774267 */
4078- if (token.value == TOK .__attribute__)
4079- cparseGnuAttributes(tagSpecifier);
4268+ cparseAttributes(tagSpecifier);
40804269 if (! tagSpecifier.packalign.isUnknown)
40814270 {
40824271 foreach (ref d; (* members)[])
@@ -4232,13 +4421,12 @@ final class CParser(AST) : Parser!AST
42324421 width = cparseConstantExp();
42334422 }
42344423
4235- /* GNU Extensions
4424+ /* GNU Extensions / C23 6.7.13
42364425 * struct-declarator:
4237- * declarator gnu- attributes (opt)
4238- * declarator (opt) : constant-expression gnu- attributes (opt)
4426+ * declarator attributes (opt)
4427+ * declarator (opt) : constant-expression attributes (opt)
42394428 */
4240- if (token.value == TOK .__attribute__)
4241- cparseGnuAttributes(specifier);
4429+ cparseAttributes(specifier);
42424430
42434431 if (! tspec && ! specifier.scw && ! specifier.mod)
42444432 error(" specifier-qualifier-list required" );
0 commit comments