Skip to content

Commit 9f2c7ef

Browse files
committed
Parse different attribute syntaxes in arbitrary order
In Clang today, we parse the different attribute syntaxes (__attribute__, __declspec, and [[]]) in a fairly rigid order. This leads to confusion for users when they guess the order incorrectly, and leads to bug reports like PR24559 or necessitates changes like D94788. This patch adds a helper function to allow us to more easily parse attributes in arbitrary order, and then updates all of the places where we would parse two or more different syntaxes in a rigid order to use the helper method. The patch does not attempt to handle Microsoft attributes ([]) because those are ambiguous with other code constructs and we don't have any attributes that use the syntax.
1 parent 8e67134 commit 9f2c7ef

File tree

7 files changed

+134
-45
lines changed

7 files changed

+134
-45
lines changed

clang/include/clang/Parse/Parser.h

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2656,6 +2656,61 @@ class Parser : public CodeCompletionHandler {
26562656
IdentifierInfo *ScopeName, SourceLocation ScopeLoc,
26572657
ParsedAttr::Syntax Syntax);
26582658

2659+
enum ParseAttrKindMask {
2660+
PAKM_GNU = 1 << 0,
2661+
PAKM_Declspec = 1 << 1,
2662+
PAKM_CXX11 = 1 << 2,
2663+
};
2664+
2665+
/// \brief Parse attributes based on what syntaxes are desired, allowing for
2666+
/// the order to vary. e.g. with PAKM_GNU | PAKM_Declspec:
2667+
/// __attribute__((...)) __declspec(...) __attribute__((...)))
2668+
/// Note that Microsoft attributes (spelled with single square brackets) are
2669+
/// not supported by this because of parsing ambiguities with other
2670+
/// constructs.
2671+
///
2672+
/// There are some attribute parse orderings that should not be allowed in
2673+
/// arbitrary order. e.g.,
2674+
///
2675+
/// [[]] __attribute__(()) int i; // OK
2676+
/// __attribute__(()) [[]] int i; // Not OK
2677+
///
2678+
/// Such situations should use the specific attribute parsing functionality.
2679+
void ParseAttributes(unsigned WhichAttrKinds,
2680+
ParsedAttributesWithRange &Attrs,
2681+
SourceLocation *End = nullptr,
2682+
LateParsedAttrList *LateAttrs = nullptr);
2683+
void ParseAttributes(unsigned WhichAttrKinds, ParsedAttributes &Attrs,
2684+
SourceLocation *End = nullptr,
2685+
LateParsedAttrList *LateAttrs = nullptr) {
2686+
ParsedAttributesWithRange AttrsWithRange(AttrFactory);
2687+
ParseAttributes(WhichAttrKinds, AttrsWithRange, End, LateAttrs);
2688+
Attrs.takeAllFrom(AttrsWithRange);
2689+
}
2690+
/// \brief Possibly parse attributes based on what syntaxes are desired,
2691+
/// allowing for the order to vary.
2692+
bool MaybeParseAttributes(unsigned WhichAttrKinds,
2693+
ParsedAttributesWithRange &Attrs,
2694+
SourceLocation *End = nullptr,
2695+
LateParsedAttrList *LateAttrs = nullptr) {
2696+
if (Tok.isOneOf(tok::kw___attribute, tok::kw___declspec) ||
2697+
standardAttributesAllowed() && isCXX11AttributeSpecifier()) {
2698+
ParseAttributes(WhichAttrKinds, Attrs, End, LateAttrs);
2699+
return true;
2700+
}
2701+
return false;
2702+
}
2703+
bool MaybeParseAttributes(unsigned WhichAttrKinds, ParsedAttributes &Attrs,
2704+
SourceLocation *End = nullptr,
2705+
LateParsedAttrList *LateAttrs = nullptr) {
2706+
if (Tok.isOneOf(tok::kw___attribute, tok::kw___declspec) ||
2707+
standardAttributesAllowed() && isCXX11AttributeSpecifier()) {
2708+
ParseAttributes(WhichAttrKinds, Attrs, End, LateAttrs);
2709+
return true;
2710+
}
2711+
return false;
2712+
}
2713+
26592714
void MaybeParseGNUAttributes(Declarator &D,
26602715
LateParsedAttrList *LateAttrs = nullptr) {
26612716
if (Tok.is(tok::kw___attribute)) {
@@ -2665,11 +2720,14 @@ class Parser : public CodeCompletionHandler {
26652720
D.takeAttributes(attrs, endLoc);
26662721
}
26672722
}
2668-
void MaybeParseGNUAttributes(ParsedAttributes &attrs,
2723+
bool MaybeParseGNUAttributes(ParsedAttributes &attrs,
26692724
SourceLocation *endLoc = nullptr,
26702725
LateParsedAttrList *LateAttrs = nullptr) {
2671-
if (Tok.is(tok::kw___attribute))
2726+
if (Tok.is(tok::kw___attribute)) {
26722727
ParseGNUAttributes(attrs, endLoc, LateAttrs);
2728+
return true;
2729+
}
2730+
return false;
26732731
}
26742732
void ParseGNUAttributes(ParsedAttributes &attrs,
26752733
SourceLocation *endLoc = nullptr,
@@ -2706,12 +2764,15 @@ class Parser : public CodeCompletionHandler {
27062764
}
27072765
return false;
27082766
}
2709-
void MaybeParseCXX11Attributes(ParsedAttributesWithRange &attrs,
2767+
bool MaybeParseCXX11Attributes(ParsedAttributesWithRange &attrs,
27102768
SourceLocation *endLoc = nullptr,
27112769
bool OuterMightBeMessageSend = false) {
27122770
if (standardAttributesAllowed() &&
2713-
isCXX11AttributeSpecifier(false, OuterMightBeMessageSend))
2771+
isCXX11AttributeSpecifier(false, OuterMightBeMessageSend)) {
27142772
ParseCXX11Attributes(attrs, endLoc);
2773+
return true;
2774+
}
2775+
return false;
27152776
}
27162777

27172778
void ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
@@ -2736,11 +2797,14 @@ class Parser : public CodeCompletionHandler {
27362797
void ParseMicrosoftUuidAttributeArgs(ParsedAttributes &Attrs);
27372798
void ParseMicrosoftAttributes(ParsedAttributes &attrs,
27382799
SourceLocation *endLoc = nullptr);
2739-
void MaybeParseMicrosoftDeclSpecs(ParsedAttributes &Attrs,
2800+
bool MaybeParseMicrosoftDeclSpecs(ParsedAttributes &Attrs,
27402801
SourceLocation *End = nullptr) {
27412802
const auto &LO = getLangOpts();
2742-
if (LO.DeclSpecKeyword && Tok.is(tok::kw___declspec))
2803+
if (LO.DeclSpecKeyword && Tok.is(tok::kw___declspec)) {
27432804
ParseMicrosoftDeclSpecs(Attrs, End);
2805+
return true;
2806+
}
2807+
return false;
27442808
}
27452809
void ParseMicrosoftDeclSpecs(ParsedAttributes &Attrs,
27462810
SourceLocation *End = nullptr);

clang/lib/Parse/ParseDecl.cpp

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,24 @@ static bool FindLocsWithCommonFileID(Preprocessor &PP, SourceLocation StartLoc,
103103
return AttrStartIsInMacro && AttrEndIsInMacro;
104104
}
105105

106+
void Parser::ParseAttributes(unsigned WhichAttrKinds,
107+
ParsedAttributesWithRange &Attrs,
108+
SourceLocation *End,
109+
LateParsedAttrList *LateAttrs) {
110+
bool MoreToParse;
111+
do {
112+
// Assume there's nothing left to parse, but if any attributes are in fact
113+
// parsed, loop to ensure all specified attribute combinations are parsed.
114+
MoreToParse = false;
115+
if (WhichAttrKinds & PAKM_CXX11)
116+
MoreToParse |= MaybeParseCXX11Attributes(Attrs, End);
117+
if (WhichAttrKinds & PAKM_GNU)
118+
MoreToParse |= MaybeParseGNUAttributes(Attrs, End, LateAttrs);
119+
if (WhichAttrKinds & PAKM_Declspec)
120+
MoreToParse |= MaybeParseMicrosoftDeclSpecs(Attrs, End);
121+
} while (MoreToParse);
122+
}
123+
106124
/// ParseGNUAttributes - Parse a non-empty attributes list.
107125
///
108126
/// [GNU] attributes:
@@ -3485,14 +3503,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
34853503
continue;
34863504
}
34873505

3488-
// GNU attributes support.
3506+
// Attributes support.
34893507
case tok::kw___attribute:
3490-
ParseGNUAttributes(DS.getAttributes(), nullptr, LateAttrs);
3491-
continue;
3492-
3493-
// Microsoft declspec support.
34943508
case tok::kw___declspec:
3495-
ParseMicrosoftDeclSpecs(DS.getAttributes());
3509+
ParseAttributes(PAKM_GNU | PAKM_Declspec, DS.getAttributes(), nullptr,
3510+
LateAttrs);
34963511
continue;
34973512

34983513
// Microsoft single token adornments.
@@ -4354,9 +4369,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
43544369

43554370
// If attributes exist after tag, parse them.
43564371
ParsedAttributesWithRange attrs(AttrFactory);
4357-
MaybeParseGNUAttributes(attrs);
4358-
MaybeParseCXX11Attributes(attrs);
4359-
MaybeParseMicrosoftDeclSpecs(attrs);
4372+
MaybeParseAttributes(PAKM_GNU | PAKM_Declspec | PAKM_CXX11, attrs);
43604373

43614374
SourceLocation ScopedEnumKWLoc;
43624375
bool IsScopedUsingClassTag = false;
@@ -4373,9 +4386,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
43734386
ProhibitAttributes(attrs);
43744387

43754388
// They are allowed afterwards, though.
4376-
MaybeParseGNUAttributes(attrs);
4377-
MaybeParseCXX11Attributes(attrs);
4378-
MaybeParseMicrosoftDeclSpecs(attrs);
4389+
MaybeParseAttributes(PAKM_GNU | PAKM_Declspec | PAKM_CXX11, attrs);
43794390
}
43804391

43814392
// C++11 [temp.explicit]p12:

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -684,8 +684,7 @@ Parser::ParseUsingDeclaration(DeclaratorContext Context,
684684
bool InvalidDeclarator = ParseUsingDeclarator(Context, D);
685685

686686
ParsedAttributesWithRange Attrs(AttrFactory);
687-
MaybeParseGNUAttributes(Attrs);
688-
MaybeParseCXX11Attributes(Attrs);
687+
MaybeParseAttributes(PAKM_GNU | PAKM_CXX11, Attrs);
689688

690689
// Maybe this is an alias-declaration.
691690
if (Tok.is(tok::equal)) {
@@ -1435,19 +1434,16 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
14351434

14361435
ParsedAttributesWithRange attrs(AttrFactory);
14371436
// If attributes exist after tag, parse them.
1438-
MaybeParseGNUAttributes(attrs);
1439-
MaybeParseMicrosoftDeclSpecs(attrs);
1437+
MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
14401438

14411439
// Parse inheritance specifiers.
14421440
if (Tok.isOneOf(tok::kw___single_inheritance,
14431441
tok::kw___multiple_inheritance,
14441442
tok::kw___virtual_inheritance))
14451443
ParseMicrosoftInheritanceClassAttributes(attrs);
14461444

1447-
// If C++0x attributes exist here, parse them.
1448-
// FIXME: Are we consistent with the ordering of parsing of different
1449-
// styles of attributes?
1450-
MaybeParseCXX11Attributes(attrs);
1445+
// Allow attributes to precede or succeed the inheritance specifiers.
1446+
MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
14511447

14521448
// Source location used by FIXIT to insert misplaced
14531449
// C++11 attributes

clang/lib/Parse/ParseExprCXX.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,12 +1338,9 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
13381338
SourceLocation DeclEndLoc = RParenLoc;
13391339

13401340
// GNU-style attributes must be parsed before the mutable specifier to be
1341-
// compatible with GCC.
1342-
MaybeParseGNUAttributes(Attr, &DeclEndLoc);
1343-
1344-
// MSVC-style attributes must be parsed before the mutable specifier to be
1345-
// compatible with MSVC.
1346-
MaybeParseMicrosoftDeclSpecs(Attr, &DeclEndLoc);
1341+
// compatible with GCC. MSVC-style attributes must be parsed before the
1342+
// mutable specifier to be compatible with MSVC.
1343+
MaybeParseAttributes(PAKM_GNU | PAKM_Declspec, Attr);
13471344

13481345
// Parse mutable-opt and/or constexpr-opt or consteval-opt, and update the
13491346
// DeclEndLoc.

clang/lib/Parse/ParseObjc.cpp

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,9 +1350,8 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
13501350

13511351
// If attributes exist before the method, parse them.
13521352
ParsedAttributes methodAttrs(AttrFactory);
1353-
if (getLangOpts().ObjC)
1354-
MaybeParseGNUAttributes(methodAttrs);
1355-
MaybeParseCXX11Attributes(methodAttrs);
1353+
MaybeParseAttributes(PAKM_CXX11 | (getLangOpts().ObjC ? PAKM_GNU : 0),
1354+
methodAttrs);
13561355

13571356
if (Tok.is(tok::code_completion)) {
13581357
Actions.CodeCompleteObjCMethodDecl(getCurScope(), mType == tok::minus,
@@ -1377,9 +1376,8 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
13771376
SmallVector<DeclaratorChunk::ParamInfo, 8> CParamInfo;
13781377
if (Tok.isNot(tok::colon)) {
13791378
// If attributes exist after the method, parse them.
1380-
if (getLangOpts().ObjC)
1381-
MaybeParseGNUAttributes(methodAttrs);
1382-
MaybeParseCXX11Attributes(methodAttrs);
1379+
MaybeParseAttributes(PAKM_CXX11 | (getLangOpts().ObjC ? PAKM_GNU : 0),
1380+
methodAttrs);
13831381

13841382
Selector Sel = PP.getSelectorTable().getNullarySelector(SelIdent);
13851383
Decl *Result = Actions.ActOnMethodDeclaration(
@@ -1412,9 +1410,8 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
14121410

14131411
// If attributes exist before the argument name, parse them.
14141412
// Regardless, collect all the attributes we've parsed so far.
1415-
if (getLangOpts().ObjC)
1416-
MaybeParseGNUAttributes(paramAttrs);
1417-
MaybeParseCXX11Attributes(paramAttrs);
1413+
MaybeParseAttributes(PAKM_CXX11 | (getLangOpts().ObjC ? PAKM_GNU : 0),
1414+
paramAttrs);
14181415
ArgInfo.ArgAttrs = paramAttrs;
14191416

14201417
// Code completion for the next piece of the selector.
@@ -1496,9 +1493,8 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
14961493

14971494
// FIXME: Add support for optional parameter list...
14981495
// If attributes exist after the method, parse them.
1499-
if (getLangOpts().ObjC)
1500-
MaybeParseGNUAttributes(methodAttrs);
1501-
MaybeParseCXX11Attributes(methodAttrs);
1496+
MaybeParseAttributes(PAKM_CXX11 | (getLangOpts().ObjC ? PAKM_GNU : 0),
1497+
methodAttrs);
15021498

15031499
if (KeyIdents.size() == 0)
15041500
return nullptr;

clang/test/Parser/attr-order.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %clang_cc1 -fsyntax-only -fms-extensions -verify %s
2+
3+
struct [[]] __attribute__((lockable)) __declspec(dllexport) A {}; // ok
4+
struct [[]] __declspec(dllexport) __attribute__((lockable)) B {}; // ok
5+
struct [[]] [[]] __declspec(dllexport) __attribute__((lockable)) C {}; // ok
6+
struct __declspec(dllexport) [[]] __attribute__((lockable)) D {}; // ok
7+
struct __declspec(dllexport) __attribute__((lockable)) [[]] E {}; // ok
8+
struct __attribute__((lockable)) __declspec(dllexport) [[]] F {}; // ok
9+
struct __attribute__((lockable)) [[]] __declspec(dllexport) G {}; // ok
10+
struct [[]] __attribute__((lockable)) [[]] __declspec(dllexport) H {}; // ok
11+
12+
[[noreturn]] __attribute__((cdecl)) __declspec(dllexport) void a(); // ok
13+
[[noreturn]] __declspec(dllexport) __attribute__((cdecl)) void b(); // ok
14+
[[]] [[noreturn]] __attribute__((cdecl)) __declspec(dllexport) void c(); // ok
15+
16+
// [[]] attributes before a declaration must be at the start of the line.
17+
__declspec(dllexport) [[noreturn]] __attribute__((cdecl)) void d(); // expected-error {{an attribute list cannot appear here}}
18+
__declspec(dllexport) __attribute__((cdecl)) [[noreturn]] void e(); // expected-error {{an attribute list cannot appear here}}
19+
__attribute__((cdecl)) __declspec(dllexport) [[noreturn]] void f(); // expected-error {{an attribute list cannot appear here}}
20+
__attribute__((cdecl)) [[noreturn]] __declspec(dllexport) void g(); // expected-error {{an attribute list cannot appear here}}
21+
22+
[[noreturn]] __attribute__((cdecl))
23+
[[]] // expected-error {{an attribute list cannot appear here}}
24+
__declspec(dllexport) void h();

clang/test/SemaOpenCL/address-spaces.cl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,8 @@ __kernel void k() {
256256

257257
void func_multiple_addr2(void) {
258258
typedef __private int private_int_t;
259-
__private __attribute__((opencl_global)) int var1; // expected-error {{multiple address spaces specified for type}}
259+
__private __attribute__((opencl_global)) int var1; // expected-error {{multiple address spaces specified for type}} \
260+
// expected-error {{function scope variable cannot be declared in global address space}}
260261
__private __attribute__((opencl_global)) int *var2; // expected-error {{multiple address spaces specified for type}}
261262
__attribute__((opencl_global)) private_int_t var3; // expected-error {{multiple address spaces specified for type}}
262263
__attribute__((opencl_global)) private_int_t *var4; // expected-error {{multiple address spaces specified for type}}

0 commit comments

Comments
 (0)