Skip to content

Commit 33a9eac

Browse files
committed
[Clang] Support multiple attributes in a single pragma
This adds support for multiple attributes in `#pragma clang attribute push`, for example: ``` ``` or ``` ``` Related attributes can now be applied with a single pragma, which makes it harder for developers to make an accidental error later when editing the code. rdar://78269653 Differential Revision: https://reviews.llvm.org/D121283
1 parent 62c4815 commit 33a9eac

12 files changed

+129
-65
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4121,8 +4121,22 @@ The ``__declspec`` style syntax is also supported:
41214121
41224122
#pragma clang attribute pop
41234123
4124-
A single push directive accepts only one attribute regardless of the syntax
4125-
used.
4124+
A single push directive can contain multiple attributes, however,
4125+
only one syntax style can be used within a single directive:
4126+
4127+
.. code-block:: c++
4128+
4129+
#pragma clang attribute push ([[noreturn, noinline]], apply_to = function)
4130+
4131+
void function1(); // The function now has the [[noreturn]] and [[noinline]] attributes
4132+
4133+
#pragma clang attribute pop
4134+
4135+
#pragma clang attribute push (__attribute((noreturn, noinline)), apply_to = function)
4136+
4137+
void function2(); // The function now has the __attribute((noreturn)) and __attribute((noinline)) attributes
4138+
4139+
#pragma clang attribute pop
41264140
41274141
Because multiple push directives can be nested, if you're writing a macro that
41284142
expands to ``_Pragma("clang attribute")`` it's good hygiene (though not

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ Attribute Changes in Clang
106106
- Statement attributes ``[[clang::noinline]]`` and ``[[clang::always_inline]]``
107107
can be used to control inlining decisions at callsites.
108108

109+
- ``#pragma clang attribute push`` now supports multiple attributes within a single directive.
110+
109111
Windows Support
110112
---------------
111113

clang/include/clang/Basic/AttrSubjectMatchRules.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ namespace attr {
1818
/// A list of all the recognized kinds of attributes.
1919
enum SubjectMatchRule {
2020
#define ATTR_MATCH_RULE(X, Spelling, IsAbstract) X,
21+
#include "clang/Basic/AttrSubMatchRulesList.inc"
22+
SubjectMatchRule_Last = -1
23+
#define ATTR_MATCH_RULE(X, Spelling, IsAbstract) +1
2124
#include "clang/Basic/AttrSubMatchRulesList.inc"
2225
};
2326

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,8 +1237,6 @@ def err_pragma_attribute_extra_tokens_after_attribute : Error<
12371237
"extra tokens after attribute in a '#pragma clang attribute push'">;
12381238
def err_pragma_attribute_unsupported_attribute : Error<
12391239
"attribute %0 is not supported by '#pragma clang attribute'">;
1240-
def err_pragma_attribute_multiple_attributes : Error<
1241-
"more than one attribute specified in '#pragma clang attribute push'">;
12421240
def err_pragma_attribute_expected_attribute_syntax : Error<
12431241
"expected an attribute that is specified using the GNU, C++11 or '__declspec'"
12441242
" syntax">;

clang/lib/Parse/ParseDecl.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,8 @@ unsigned Parser::ParseClangAttributeArgs(
600600
bool Parser::ParseMicrosoftDeclSpecArgs(IdentifierInfo *AttrName,
601601
SourceLocation AttrNameLoc,
602602
ParsedAttributes &Attrs) {
603+
unsigned ExistingAttrs = Attrs.size();
604+
603605
// If the attribute isn't known, we will not attempt to parse any
604606
// arguments.
605607
if (!hasAttribute(AttrSyntax::Declspec, nullptr, AttrName,
@@ -732,7 +734,7 @@ bool Parser::ParseMicrosoftDeclSpecArgs(IdentifierInfo *AttrName,
732734

733735
// If this attribute's args were parsed, and it was expected to have
734736
// arguments but none were provided, emit a diagnostic.
735-
if (!Attrs.empty() && Attrs.begin()->getMaxArgs() && !NumArgs) {
737+
if (ExistingAttrs < Attrs.size() && Attrs.back().getMaxArgs() && !NumArgs) {
736738
Diag(OpenParenLoc, diag::err_attribute_requires_arguments) << AttrName;
737739
return false;
738740
}

clang/lib/Parse/ParsePragma.cpp

Lines changed: 55 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,7 +1561,7 @@ getAttributeSubjectRulesRecoveryPointForToken(const Token &Tok) {
15611561
/// suggests the possible attribute subject rules in a fix-it together with
15621562
/// any other missing tokens.
15631563
DiagnosticBuilder createExpectedAttributeSubjectRulesTokenDiagnostic(
1564-
unsigned DiagID, ParsedAttr &Attribute,
1564+
unsigned DiagID, ParsedAttributes &Attrs,
15651565
MissingAttributeSubjectRulesRecoveryPoint Point, Parser &PRef) {
15661566
SourceLocation Loc = PRef.getEndOfPreviousToken();
15671567
if (Loc.isInvalid())
@@ -1581,25 +1581,38 @@ DiagnosticBuilder createExpectedAttributeSubjectRulesTokenDiagnostic(
15811581
SourceRange FixItRange(Loc);
15821582
if (EndPoint == MissingAttributeSubjectRulesRecoveryPoint::None) {
15831583
// Gather the subject match rules that are supported by the attribute.
1584-
SmallVector<std::pair<attr::SubjectMatchRule, bool>, 4> SubjectMatchRuleSet;
1585-
Attribute.getMatchRules(PRef.getLangOpts(), SubjectMatchRuleSet);
1586-
if (SubjectMatchRuleSet.empty()) {
1584+
// Add all the possible rules initially.
1585+
llvm::BitVector IsMatchRuleAvailable(attr::SubjectMatchRule_Last + 1, true);
1586+
// Remove the ones that are not supported by any of the attributes.
1587+
for (const ParsedAttr &Attribute : Attrs) {
1588+
SmallVector<std::pair<attr::SubjectMatchRule, bool>, 4> MatchRules;
1589+
Attribute.getMatchRules(PRef.getLangOpts(), MatchRules);
1590+
llvm::BitVector IsSupported(attr::SubjectMatchRule_Last + 1);
1591+
for (const auto &Rule : MatchRules) {
1592+
// Ensure that the missing rule is reported in the fix-it only when it's
1593+
// supported in the current language mode.
1594+
if (!Rule.second)
1595+
continue;
1596+
IsSupported[Rule.first] = true;
1597+
}
1598+
IsMatchRuleAvailable &= IsSupported;
1599+
}
1600+
if (IsMatchRuleAvailable.count() == 0) {
15871601
// FIXME: We can emit a "fix-it" with a subject list placeholder when
15881602
// placeholders will be supported by the fix-its.
15891603
return Diagnostic;
15901604
}
15911605
FixIt += "any(";
15921606
bool NeedsComma = false;
1593-
for (const auto &I : SubjectMatchRuleSet) {
1594-
// Ensure that the missing rule is reported in the fix-it only when it's
1595-
// supported in the current language mode.
1596-
if (!I.second)
1607+
for (unsigned I = 0; I <= attr::SubjectMatchRule_Last; I++) {
1608+
if (!IsMatchRuleAvailable[I])
15971609
continue;
15981610
if (NeedsComma)
15991611
FixIt += ", ";
16001612
else
16011613
NeedsComma = true;
1602-
FixIt += attr::getSubjectMatchRuleSpelling(I.first);
1614+
FixIt += attr::getSubjectMatchRuleSpelling(
1615+
static_cast<attr::SubjectMatchRule>(I));
16031616
}
16041617
FixIt += ")";
16051618
// Check if we need to remove the range
@@ -1669,22 +1682,25 @@ void Parser::HandlePragmaAttribute() {
16691682
return SkipToEnd();
16701683
}
16711684

1672-
if (Tok.isNot(tok::identifier)) {
1673-
Diag(Tok, diag::err_pragma_attribute_expected_attribute_name);
1674-
SkipToEnd();
1675-
return;
1676-
}
1677-
IdentifierInfo *AttrName = Tok.getIdentifierInfo();
1678-
SourceLocation AttrNameLoc = ConsumeToken();
1685+
// Parse the comma-separated list of attributes.
1686+
do {
1687+
if (Tok.isNot(tok::identifier)) {
1688+
Diag(Tok, diag::err_pragma_attribute_expected_attribute_name);
1689+
SkipToEnd();
1690+
return;
1691+
}
1692+
IdentifierInfo *AttrName = Tok.getIdentifierInfo();
1693+
SourceLocation AttrNameLoc = ConsumeToken();
16791694

1680-
if (Tok.isNot(tok::l_paren))
1681-
Attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
1682-
ParsedAttr::AS_GNU);
1683-
else
1684-
ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, /*EndLoc=*/nullptr,
1685-
/*ScopeName=*/nullptr,
1686-
/*ScopeLoc=*/SourceLocation(), ParsedAttr::AS_GNU,
1687-
/*Declarator=*/nullptr);
1695+
if (Tok.isNot(tok::l_paren))
1696+
Attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
1697+
ParsedAttr::AS_GNU);
1698+
else
1699+
ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, /*EndLoc=*/nullptr,
1700+
/*ScopeName=*/nullptr,
1701+
/*ScopeLoc=*/SourceLocation(), ParsedAttr::AS_GNU,
1702+
/*Declarator=*/nullptr);
1703+
} while (TryConsumeToken(tok::comma));
16881704

16891705
if (ExpectAndConsume(tok::r_paren))
16901706
return SkipToEnd();
@@ -1722,26 +1738,19 @@ void Parser::HandlePragmaAttribute() {
17221738
return;
17231739
}
17241740

1725-
// Ensure that we don't have more than one attribute.
1726-
if (Attrs.size() > 1) {
1727-
SourceLocation Loc = Attrs[1].getLoc();
1728-
Diag(Loc, diag::err_pragma_attribute_multiple_attributes);
1729-
SkipToEnd();
1730-
return;
1731-
}
1732-
1733-
ParsedAttr &Attribute = *Attrs.begin();
1734-
if (!Attribute.isSupportedByPragmaAttribute()) {
1735-
Diag(PragmaLoc, diag::err_pragma_attribute_unsupported_attribute)
1736-
<< Attribute;
1737-
SkipToEnd();
1738-
return;
1741+
for (const ParsedAttr &Attribute : Attrs) {
1742+
if (!Attribute.isSupportedByPragmaAttribute()) {
1743+
Diag(PragmaLoc, diag::err_pragma_attribute_unsupported_attribute)
1744+
<< Attribute;
1745+
SkipToEnd();
1746+
return;
1747+
}
17391748
}
17401749

17411750
// Parse the subject-list.
17421751
if (!TryConsumeToken(tok::comma)) {
17431752
createExpectedAttributeSubjectRulesTokenDiagnostic(
1744-
diag::err_expected, Attribute,
1753+
diag::err_expected, Attrs,
17451754
MissingAttributeSubjectRulesRecoveryPoint::Comma, *this)
17461755
<< tok::comma;
17471756
SkipToEnd();
@@ -1750,15 +1759,15 @@ void Parser::HandlePragmaAttribute() {
17501759

17511760
if (Tok.isNot(tok::identifier)) {
17521761
createExpectedAttributeSubjectRulesTokenDiagnostic(
1753-
diag::err_pragma_attribute_invalid_subject_set_specifier, Attribute,
1762+
diag::err_pragma_attribute_invalid_subject_set_specifier, Attrs,
17541763
MissingAttributeSubjectRulesRecoveryPoint::ApplyTo, *this);
17551764
SkipToEnd();
17561765
return;
17571766
}
17581767
const IdentifierInfo *II = Tok.getIdentifierInfo();
17591768
if (!II->isStr("apply_to")) {
17601769
createExpectedAttributeSubjectRulesTokenDiagnostic(
1761-
diag::err_pragma_attribute_invalid_subject_set_specifier, Attribute,
1770+
diag::err_pragma_attribute_invalid_subject_set_specifier, Attrs,
17621771
MissingAttributeSubjectRulesRecoveryPoint::ApplyTo, *this);
17631772
SkipToEnd();
17641773
return;
@@ -1767,7 +1776,7 @@ void Parser::HandlePragmaAttribute() {
17671776

17681777
if (!TryConsumeToken(tok::equal)) {
17691778
createExpectedAttributeSubjectRulesTokenDiagnostic(
1770-
diag::err_expected, Attribute,
1779+
diag::err_expected, Attrs,
17711780
MissingAttributeSubjectRulesRecoveryPoint::Equals, *this)
17721781
<< tok::equal;
17731782
SkipToEnd();
@@ -1797,8 +1806,10 @@ void Parser::HandlePragmaAttribute() {
17971806
if (Info->Action == PragmaAttributeInfo::Push)
17981807
Actions.ActOnPragmaAttributeEmptyPush(PragmaLoc, Info->Namespace);
17991808

1800-
Actions.ActOnPragmaAttributeAttribute(Attribute, PragmaLoc,
1801-
std::move(SubjectMatchRules));
1809+
for (ParsedAttr &Attribute : Attrs) {
1810+
Actions.ActOnPragmaAttributeAttribute(Attribute, PragmaLoc,
1811+
SubjectMatchRules);
1812+
}
18021813
}
18031814

18041815
// #pragma GCC visibility comes in two variants:
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// RUN: %clang_cc1 -triple i386-pc-win32 -fms-extensions -fms-compatibility -fsyntax-only -ast-dump %s | FileCheck %s
2+
3+
#pragma clang attribute push (__declspec(dllexport, noinline), apply_to=function)
4+
void func1();
5+
#pragma clang attribute pop
6+
// CHECK: FunctionDecl {{.*}} func1
7+
// CHECK-NEXT: DLLExportAttr {{.*}}
8+
// CHECK-NEXT: NoInlineAttr {{.*}}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %clang_cc1 -fsyntax-only -ast-dump %s | FileCheck %s
2+
3+
#pragma clang attribute push (__attribute__((disable_sanitizer_instrumentation, annotate("test1"))), apply_to=variable(is_global))
4+
int var1;
5+
#pragma clang attribute pop
6+
// CHECK: VarDecl {{.*}} var1
7+
// CHECK-NEXT: DisableSanitizerInstrumentationAttr {{.*}}
8+
// CHECK-NEXT: AnnotateAttr {{.*}} "test1"
9+
10+
#pragma clang attribute push ([[clang::disable_sanitizer_instrumentation, clang::annotate("test2")]], apply_to=variable(is_global))
11+
int var2;
12+
#pragma clang attribute pop
13+
// CHECK: VarDecl {{.*}} var2
14+
// CHECK-NEXT: DisableSanitizerInstrumentationAttr {{.*}}
15+
// CHECK-NEXT: AnnotateAttr {{.*}} "test2"

clang/test/FixIt/fixit-pragma-attribute.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
// rules that are not applicable in the current language mode.
44

55
#pragma clang attribute push (__attribute__((abi_tag("a"))))
6-
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", apply_to = any(record(unless(is_union)), variable, function)"
6+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", apply_to = any(function, record(unless(is_union)), variable)"

clang/test/FixIt/fixit-pragma-attribute.cpp

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
#pragma clang attribute pop
4040

4141
#pragma clang attribute push (__attribute__((abi_tag("a"))))
42-
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", apply_to = any(record(unless(is_union)), variable, function, namespace)"
42+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", apply_to = any(function, namespace, record(unless(is_union)), variable)"
4343
#pragma clang attribute push (__attribute__((abi_tag("a"))) apply_to=function)
4444
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", "
4545
#pragma clang attribute push (__attribute__((abi_tag("a"))) = function)
@@ -48,36 +48,39 @@
4848
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", apply_to = "
4949

5050
#pragma clang attribute push (__attribute__((abi_tag("a"))) 22)
51-
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:63}:", apply_to = any(record(unless(is_union)), variable, function, namespace)"
51+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:63}:", apply_to = any(function, namespace, record(unless(is_union)), variable)"
5252
#pragma clang attribute push (__attribute__((abi_tag("a"))) function)
53-
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:69}:", apply_to = any(record(unless(is_union)), variable, function, namespace)"
53+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:69}:", apply_to = any(function, namespace, record(unless(is_union)), variable)"
5454
#pragma clang attribute push (__attribute__((abi_tag("a"))) (function))
55-
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:71}:", apply_to = any(record(unless(is_union)), variable, function, namespace)"
55+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:71}:", apply_to = any(function, namespace, record(unless(is_union)), variable)"
5656

5757
#pragma clang attribute push (__attribute__((abi_tag("a"))), )
58-
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:62}:"apply_to = any(record(unless(is_union)), variable, function, namespace)"
58+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:62}:"apply_to = any(function, namespace, record(unless(is_union)), variable)"
5959
#pragma clang attribute push (__attribute__((abi_tag("a"))), = function)
6060
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:61}:"apply_to"
6161
#pragma clang attribute push (__attribute__((abi_tag("a"))), any(function))
6262
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:61}:"apply_to = "
6363

6464
#pragma clang attribute push (__attribute__((abi_tag("a"))), 22)
65-
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:64}:"apply_to = any(record(unless(is_union)), variable, function, namespace)"
65+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:64}:"apply_to = any(function, namespace, record(unless(is_union)), variable)"
6666
#pragma clang attribute push (__attribute__((abi_tag("a"))), 1, 2)
67-
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:66}:"apply_to = any(record(unless(is_union)), variable, function, namespace)"
67+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:66}:"apply_to = any(function, namespace, record(unless(is_union)), variable)"
6868
#pragma clang attribute push (__attribute__((abi_tag("a"))), function)
69-
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:70}:"apply_to = any(record(unless(is_union)), variable, function, namespace)"
69+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:70}:"apply_to = any(function, namespace, record(unless(is_union)), variable)"
7070
#pragma clang attribute push (__attribute__((abi_tag("a"))), (function))
71-
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:72}:"apply_to = any(record(unless(is_union)), variable, function, namespace)"
71+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:72}:"apply_to = any(function, namespace, record(unless(is_union)), variable)"
7272

7373
#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to)
74-
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:70-[[@LINE-1]]:70}:" = any(record(unless(is_union)), variable, function, namespace)"
74+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:70-[[@LINE-1]]:70}:" = any(function, namespace, record(unless(is_union)), variable)"
7575
#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to any(function))
7676
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:70-[[@LINE-1]]:70}:" = "
7777

7878
#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to 41 (22))
79-
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:70-[[@LINE-1]]:78}:" = any(record(unless(is_union)), variable, function, namespace)"
79+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:70-[[@LINE-1]]:78}:" = any(function, namespace, record(unless(is_union)), variable)"
8080

8181
// Don't give fix-it to attributes without a strict subject set
8282
#pragma clang attribute push (__attribute__((annotate("a"))))
8383
// CHECK-NO: [[@LINE-1]]:61
84+
85+
#pragma clang attribute push (__attribute__((objc_externally_retained)), apply_to)
86+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:82-[[@LINE-1]]:82}:" = any(function, variable(unless(is_parameter)))"

clang/test/Parser/pragma-attribute-declspec.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ void function();
66

77
#pragma clang attribute pop
88

9-
#pragma clang attribute push(__declspec(dllexport, dllimport), apply_to = function) // expected-error {{more than one attribute specified in '#pragma clang attribute push'}}
9+
#pragma clang attribute push(__declspec(dllexport, dllimport), apply_to = function)
10+
#pragma clang attribute pop
1011

1112
#pragma clang attribute push(__declspec(align), apply_to = variable) // expected-error {{attribute 'align' is not supported by '#pragma clang attribute'}}
1213

clang/test/Parser/pragma-attribute.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,6 @@ _Pragma("clang attribute pop");
154154
#pragma clang attribute push ([[gnu::abi_tag]], apply_to=any(function))
155155
#pragma clang attribute pop
156156

157-
#pragma clang attribute push ([[clang::disable_tail_calls, noreturn]], apply_to = function) // expected-error {{more than one attribute specified in '#pragma clang attribute push'}}
158-
#pragma clang attribute push ([[clang::disable_tail_calls, noreturn]]) // expected-error {{more than one attribute specified in '#pragma clang attribute push'}}
159-
160157
#pragma clang attribute push ([[gnu::abi_tag]], apply_to=namespace)
161158
#pragma clang attribute pop
162159

@@ -210,3 +207,13 @@ _Pragma("clang attribute pop");
210207
#pragma clang attribute push([[clang::uninitialized]], apply_to=any) // expected-error {{expected '('}}
211208
#pragma clang attribute push([[clang::uninitialized]], apply_to = any()) // expected-error {{expected an identifier that corresponds to an attribute subject rule}}
212209
// NB: neither of these need to be popped; they were never successfully pushed.
210+
211+
#pragma clang attribute push ([[clang::disable_tail_calls, noreturn]], apply_to = function)
212+
#pragma clang attribute pop
213+
214+
#pragma clang attribute push (__attribute__((disable_tail_calls, annotate("test"))), apply_to = function)
215+
#pragma clang attribute pop
216+
217+
#pragma clang attribute push (__attribute__((disable_tail_calls,)), apply_to = function) // expected-error {{expected identifier that represents an attribute name}}
218+
219+
#pragma clang attribute push ([[clang::disable_tail_calls, noreturn]]) // expected-error {{expected ','}}

0 commit comments

Comments
 (0)