forked from OSchip/llvm-project
Support for groups of attributes in #pragma clang attribute
This commit enables pushing an empty #pragma clang attribute push, then adding multiple attributes to it, then popping them all with #pragma clang attribute pop, just like #pragma clang diagnostic. We still support the current way of adding these, #pragma clang attribute push(__attribute__((...))), by treating it like a combined push/attribute. This is needed to create macros like: DO_SOMETHING_BEGIN(attr1, attr2, attr3) // ... DO_SOMETHING_END rdar://45496947 Differential revision: https://reviews.llvm.org/D53621 llvm-svn: 345486
This commit is contained in:
parent
36d9746630
commit
a7cc6b360f
|
@ -2651,17 +2651,19 @@ Specifying an attribute for multiple declarations (#pragma clang attribute)
|
|||
|
||||
The ``#pragma clang attribute`` directive can be used to apply an attribute to
|
||||
multiple declarations. The ``#pragma clang attribute push`` variation of the
|
||||
directive pushes a new attribute to the attribute stack. The declarations that
|
||||
follow the pragma receive the attributes that are on the attribute stack, until
|
||||
the stack is cleared using a ``#pragma clang attribute pop`` directive. Multiple
|
||||
push directives can be nested inside each other.
|
||||
directive pushes a new "scope" of ``#pragma clang attribute`` that attributes
|
||||
can be added to. The ``#pragma clang attribute (...)`` variation adds an
|
||||
attribute to that scope, and the ``#pragma clang attribute pop`` variation pops
|
||||
the scope. You can also use ``#pragma clang attribute push (...)``, which is a
|
||||
shorthand for when you want to add one attribute to a new scope. Multiple push
|
||||
directives can be nested inside each other.
|
||||
|
||||
The attributes that are used in the ``#pragma clang attribute`` directives
|
||||
can be written using the GNU-style syntax:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#pragma clang attribute push(__attribute__((annotate("custom"))), apply_to = function)
|
||||
#pragma clang attribute push (__attribute__((annotate("custom"))), apply_to = function)
|
||||
|
||||
void function(); // The function now has the annotate("custom") attribute
|
||||
|
||||
|
@ -2671,7 +2673,7 @@ The attributes can also be written using the C++11 style syntax:
|
|||
|
||||
.. code-block:: c++
|
||||
|
||||
#pragma clang attribute push([[noreturn]], apply_to = function)
|
||||
#pragma clang attribute push ([[noreturn]], apply_to = function)
|
||||
|
||||
void function(); // The function now has the [[noreturn]] attribute
|
||||
|
||||
|
@ -2681,7 +2683,7 @@ The ``__declspec`` style syntax is also supported:
|
|||
|
||||
.. code-block:: c++
|
||||
|
||||
#pragma clang attribute push(__declspec(dllexport), apply_to = function)
|
||||
#pragma clang attribute push (__declspec(dllexport), apply_to = function)
|
||||
|
||||
void function(); // The function now has the __declspec(dllexport) attribute
|
||||
|
||||
|
|
|
@ -86,8 +86,8 @@ Modified Compiler Flags
|
|||
New Pragmas in Clang
|
||||
--------------------
|
||||
|
||||
Clang now supports the ...
|
||||
|
||||
- Clang now supports adding multiple ``#pragma clang attribute`` attributes into
|
||||
a "scope" of ``push``ed attributes.
|
||||
|
||||
Attribute Changes in Clang
|
||||
--------------------------
|
||||
|
|
|
@ -1032,8 +1032,8 @@ def err_pragma_optimize_invalid_argument : Error<
|
|||
def err_pragma_optimize_extra_argument : Error<
|
||||
"unexpected extra argument '%0' to '#pragma clang optimize'">;
|
||||
// - #pragma clang attribute
|
||||
def err_pragma_attribute_expected_push_pop : Error<
|
||||
"expected 'push' or 'pop' after '#pragma clang attribute'">;
|
||||
def err_pragma_attribute_expected_push_pop_paren : Error<
|
||||
"expected 'push', 'pop', or '(' after '#pragma clang attribute'">;
|
||||
def err_pragma_attribute_invalid_argument : Error<
|
||||
"unexpected argument '%0' to '#pragma clang attribute'; "
|
||||
"expected 'push' or 'pop'">;
|
||||
|
|
|
@ -851,6 +851,9 @@ def err_pragma_attribute_no_pop_eof : Error<"unterminated "
|
|||
"'#pragma clang attribute push' at end of file">;
|
||||
def note_pragma_attribute_applied_decl_here : Note<
|
||||
"when applied to this declaration">;
|
||||
def err_pragma_attr_attr_no_push : Error<
|
||||
"'#pragma clang attribute' attribute with no matching "
|
||||
"'#pragma clang attribute push'">;
|
||||
|
||||
/// Objective-C parser diagnostics
|
||||
def err_duplicate_class_def : Error<
|
||||
|
|
|
@ -491,15 +491,22 @@ public:
|
|||
/// VisContext - Manages the stack for \#pragma GCC visibility.
|
||||
void *VisContext; // Really a "PragmaVisStack*"
|
||||
|
||||
/// This represents the stack of attributes that were pushed by
|
||||
/// \#pragma clang attribute.
|
||||
/// This an attribute introduced by \#pragma clang attribute.
|
||||
struct PragmaAttributeEntry {
|
||||
SourceLocation Loc;
|
||||
ParsedAttr *Attribute;
|
||||
SmallVector<attr::SubjectMatchRule, 4> MatchRules;
|
||||
bool IsUsed;
|
||||
};
|
||||
SmallVector<PragmaAttributeEntry, 2> PragmaAttributeStack;
|
||||
|
||||
/// A push'd group of PragmaAttributeEntries.
|
||||
struct PragmaAttributeGroup {
|
||||
/// The location of the push attribute.
|
||||
SourceLocation Loc;
|
||||
SmallVector<PragmaAttributeEntry, 2> Entries;
|
||||
};
|
||||
|
||||
SmallVector<PragmaAttributeGroup, 2> PragmaAttributeStack;
|
||||
|
||||
/// The declaration that is currently receiving an attribute from the
|
||||
/// #pragma attribute stack.
|
||||
|
@ -8470,9 +8477,10 @@ public:
|
|||
/// the appropriate attribute.
|
||||
void AddCFAuditedAttribute(Decl *D);
|
||||
|
||||
/// Called on well-formed '\#pragma clang attribute push'.
|
||||
void ActOnPragmaAttributePush(ParsedAttr &Attribute, SourceLocation PragmaLoc,
|
||||
attr::ParsedSubjectMatchRuleSet Rules);
|
||||
void ActOnPragmaAttributeAttribute(ParsedAttr &Attribute,
|
||||
SourceLocation PragmaLoc,
|
||||
attr::ParsedSubjectMatchRuleSet Rules);
|
||||
void ActOnPragmaAttributeEmptyPush(SourceLocation PragmaLoc);
|
||||
|
||||
/// Called on well-formed '\#pragma clang attribute pop'.
|
||||
void ActOnPragmaAttributePop(SourceLocation PragmaLoc);
|
||||
|
|
|
@ -1133,7 +1133,7 @@ bool Parser::HandlePragmaLoopHint(LoopHint &Hint) {
|
|||
|
||||
namespace {
|
||||
struct PragmaAttributeInfo {
|
||||
enum ActionType { Push, Pop };
|
||||
enum ActionType { Push, Pop, Attribute };
|
||||
ParsedAttributes &Attributes;
|
||||
ActionType Action;
|
||||
ArrayRef<Token> Tokens;
|
||||
|
@ -1394,8 +1394,16 @@ void Parser::HandlePragmaAttribute() {
|
|||
return;
|
||||
}
|
||||
// Parse the actual attribute with its arguments.
|
||||
assert(Info->Action == PragmaAttributeInfo::Push &&
|
||||
"Unexpected #pragma attribute command");
|
||||
assert(Info->Action == PragmaAttributeInfo::Push ||
|
||||
Info->Action == PragmaAttributeInfo::Attribute &&
|
||||
"Unexpected #pragma attribute command");
|
||||
|
||||
if (Info->Action == PragmaAttributeInfo::Push && Info->Tokens.empty()) {
|
||||
ConsumeAnnotationToken();
|
||||
Actions.ActOnPragmaAttributeEmptyPush(PragmaLoc);
|
||||
return;
|
||||
}
|
||||
|
||||
PP.EnterTokenStream(Info->Tokens, /*DisableMacroExpansion=*/false);
|
||||
ConsumeAnnotationToken();
|
||||
|
||||
|
@ -1542,8 +1550,12 @@ void Parser::HandlePragmaAttribute() {
|
|||
// Consume the eof terminator token.
|
||||
ConsumeToken();
|
||||
|
||||
Actions.ActOnPragmaAttributePush(Attribute, PragmaLoc,
|
||||
std::move(SubjectMatchRules));
|
||||
// Handle a mixed push/attribute by desurging to a push, then an attribute.
|
||||
if (Info->Action == PragmaAttributeInfo::Push)
|
||||
Actions.ActOnPragmaAttributeEmptyPush(PragmaLoc);
|
||||
|
||||
Actions.ActOnPragmaAttributeAttribute(Attribute, PragmaLoc,
|
||||
std::move(SubjectMatchRules));
|
||||
}
|
||||
|
||||
// #pragma GCC visibility comes in two variants:
|
||||
|
@ -3104,6 +3116,8 @@ void PragmaForceCUDAHostDeviceHandler::HandlePragma(
|
|||
/// The syntax is:
|
||||
/// \code
|
||||
/// #pragma clang attribute push(attribute, subject-set)
|
||||
/// #pragma clang attribute push
|
||||
/// #pragma clang attribute (attribute, subject-set)
|
||||
/// #pragma clang attribute pop
|
||||
/// \endcode
|
||||
///
|
||||
|
@ -3122,25 +3136,33 @@ void PragmaAttributeHandler::HandlePragma(Preprocessor &PP,
|
|||
auto *Info = new (PP.getPreprocessorAllocator())
|
||||
PragmaAttributeInfo(AttributesForPragmaAttribute);
|
||||
|
||||
// Parse the 'push' or 'pop'.
|
||||
if (Tok.isNot(tok::identifier)) {
|
||||
PP.Diag(Tok.getLocation(), diag::err_pragma_attribute_expected_push_pop);
|
||||
if (!Tok.isOneOf(tok::identifier, tok::l_paren)) {
|
||||
PP.Diag(Tok.getLocation(),
|
||||
diag::err_pragma_attribute_expected_push_pop_paren);
|
||||
return;
|
||||
}
|
||||
const auto *II = Tok.getIdentifierInfo();
|
||||
if (II->isStr("push"))
|
||||
Info->Action = PragmaAttributeInfo::Push;
|
||||
else if (II->isStr("pop"))
|
||||
Info->Action = PragmaAttributeInfo::Pop;
|
||||
|
||||
// Determine what action this pragma clang attribute represents.
|
||||
if (Tok.is(tok::l_paren))
|
||||
Info->Action = PragmaAttributeInfo::Attribute;
|
||||
else {
|
||||
PP.Diag(Tok.getLocation(), diag::err_pragma_attribute_invalid_argument)
|
||||
<< PP.getSpelling(Tok);
|
||||
return;
|
||||
const IdentifierInfo *II = Tok.getIdentifierInfo();
|
||||
if (II->isStr("push"))
|
||||
Info->Action = PragmaAttributeInfo::Push;
|
||||
else if (II->isStr("pop"))
|
||||
Info->Action = PragmaAttributeInfo::Pop;
|
||||
else {
|
||||
PP.Diag(Tok.getLocation(), diag::err_pragma_attribute_invalid_argument)
|
||||
<< PP.getSpelling(Tok);
|
||||
return;
|
||||
}
|
||||
|
||||
PP.Lex(Tok);
|
||||
}
|
||||
PP.Lex(Tok);
|
||||
|
||||
// Parse the actual attribute.
|
||||
if (Info->Action == PragmaAttributeInfo::Push) {
|
||||
if ((Info->Action == PragmaAttributeInfo::Push && Tok.isNot(tok::eod)) ||
|
||||
Info->Action == PragmaAttributeInfo::Attribute) {
|
||||
if (Tok.isNot(tok::l_paren)) {
|
||||
PP.Diag(Tok.getLocation(), diag::err_expected) << tok::l_paren;
|
||||
return;
|
||||
|
|
|
@ -520,9 +520,9 @@ attrMatcherRuleListToString(ArrayRef<attr::SubjectMatchRule> Rules) {
|
|||
|
||||
} // end anonymous namespace
|
||||
|
||||
void Sema::ActOnPragmaAttributePush(ParsedAttr &Attribute,
|
||||
SourceLocation PragmaLoc,
|
||||
attr::ParsedSubjectMatchRuleSet Rules) {
|
||||
void Sema::ActOnPragmaAttributeAttribute(
|
||||
ParsedAttr &Attribute, SourceLocation PragmaLoc,
|
||||
attr::ParsedSubjectMatchRuleSet Rules) {
|
||||
SmallVector<attr::SubjectMatchRule, 4> SubjectMatchRules;
|
||||
// Gather the subject match rules that are supported by the attribute.
|
||||
SmallVector<std::pair<attr::SubjectMatchRule, bool>, 4>
|
||||
|
@ -622,48 +622,64 @@ void Sema::ActOnPragmaAttributePush(ParsedAttr &Attribute,
|
|||
Diagnostic << attrMatcherRuleListToString(ExtraRules);
|
||||
}
|
||||
|
||||
PragmaAttributeStack.push_back(
|
||||
if (PragmaAttributeStack.empty()) {
|
||||
Diag(PragmaLoc, diag::err_pragma_attr_attr_no_push);
|
||||
return;
|
||||
}
|
||||
|
||||
PragmaAttributeStack.back().Entries.push_back(
|
||||
{PragmaLoc, &Attribute, std::move(SubjectMatchRules), /*IsUsed=*/false});
|
||||
}
|
||||
|
||||
void Sema::ActOnPragmaAttributeEmptyPush(SourceLocation PragmaLoc) {
|
||||
PragmaAttributeStack.emplace_back();
|
||||
PragmaAttributeStack.back().Loc = PragmaLoc;
|
||||
}
|
||||
|
||||
void Sema::ActOnPragmaAttributePop(SourceLocation PragmaLoc) {
|
||||
if (PragmaAttributeStack.empty()) {
|
||||
Diag(PragmaLoc, diag::err_pragma_attribute_stack_mismatch);
|
||||
return;
|
||||
}
|
||||
const PragmaAttributeEntry &Entry = PragmaAttributeStack.back();
|
||||
if (!Entry.IsUsed) {
|
||||
assert(Entry.Attribute && "Expected an attribute");
|
||||
Diag(Entry.Attribute->getLoc(), diag::warn_pragma_attribute_unused)
|
||||
<< Entry.Attribute->getName();
|
||||
Diag(PragmaLoc, diag::note_pragma_attribute_region_ends_here);
|
||||
|
||||
for (const PragmaAttributeEntry &Entry :
|
||||
PragmaAttributeStack.back().Entries) {
|
||||
if (!Entry.IsUsed) {
|
||||
assert(Entry.Attribute && "Expected an attribute");
|
||||
Diag(Entry.Attribute->getLoc(), diag::warn_pragma_attribute_unused)
|
||||
<< Entry.Attribute->getName();
|
||||
Diag(PragmaLoc, diag::note_pragma_attribute_region_ends_here);
|
||||
}
|
||||
}
|
||||
|
||||
PragmaAttributeStack.pop_back();
|
||||
}
|
||||
|
||||
void Sema::AddPragmaAttributes(Scope *S, Decl *D) {
|
||||
if (PragmaAttributeStack.empty())
|
||||
return;
|
||||
for (auto &Entry : PragmaAttributeStack) {
|
||||
ParsedAttr *Attribute = Entry.Attribute;
|
||||
assert(Attribute && "Expected an attribute");
|
||||
for (auto &Group : PragmaAttributeStack) {
|
||||
for (auto &Entry : Group.Entries) {
|
||||
ParsedAttr *Attribute = Entry.Attribute;
|
||||
assert(Attribute && "Expected an attribute");
|
||||
|
||||
// Ensure that the attribute can be applied to the given declaration.
|
||||
bool Applies = false;
|
||||
for (const auto &Rule : Entry.MatchRules) {
|
||||
if (Attribute->appliesToDecl(D, Rule)) {
|
||||
Applies = true;
|
||||
break;
|
||||
// Ensure that the attribute can be applied to the given declaration.
|
||||
bool Applies = false;
|
||||
for (const auto &Rule : Entry.MatchRules) {
|
||||
if (Attribute->appliesToDecl(D, Rule)) {
|
||||
Applies = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!Applies)
|
||||
continue;
|
||||
Entry.IsUsed = true;
|
||||
PragmaAttributeCurrentTargetDecl = D;
|
||||
ParsedAttributesView Attrs;
|
||||
Attrs.addAtEnd(Attribute);
|
||||
ProcessDeclAttributeList(S, D, Attrs);
|
||||
PragmaAttributeCurrentTargetDecl = nullptr;
|
||||
}
|
||||
if (!Applies)
|
||||
continue;
|
||||
Entry.IsUsed = true;
|
||||
PragmaAttributeCurrentTargetDecl = D;
|
||||
ParsedAttributesView Attrs;
|
||||
Attrs.addAtEnd(Attribute);
|
||||
ProcessDeclAttributeList(S, D, Attrs);
|
||||
PragmaAttributeCurrentTargetDecl = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -100,11 +100,12 @@ void function();
|
|||
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(unless(is_parameter)), variable(unless(is_parameter)) )) // expected-error {{duplicate attribute subject matcher 'variable(unless(is_parameter))'}}
|
||||
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(unless(is_parameter)), variable(unless(is_parameter)), enum, variable(unless(is_parameter)) )) // expected-error 2 {{duplicate attribute subject matcher 'variable(unless(is_parameter))'}}
|
||||
|
||||
#pragma clang attribute // expected-error {{expected 'push' or 'pop' after '#pragma clang attribute'}}
|
||||
#pragma clang attribute 42 // expected-error {{expected 'push' or 'pop' after '#pragma clang attribute'}}
|
||||
#pragma clang attribute // expected-error {{expected 'push', 'pop', or '(' after '#pragma clang attribute'}}
|
||||
#pragma clang attribute 42 // expected-error {{expected 'push', 'pop', or '(' after '#pragma clang attribute'}}
|
||||
#pragma clang attribute pushpop // expected-error {{unexpected argument 'pushpop' to '#pragma clang attribute'; expected 'push' or 'pop'}}
|
||||
|
||||
#pragma clang attribute push // expected-error {{expected '('}}
|
||||
#pragma clang attribute push
|
||||
#pragma clang attribute pop
|
||||
#pragma clang attribute push ( // expected-error {{expected an attribute after '('}}
|
||||
#pragma clang attribute push (__attribute__((annotate)) // expected-error {{expected ')'}}
|
||||
#pragma clang attribute push () // expected-error {{expected an attribute after '('}}
|
||||
|
|
|
@ -38,6 +38,29 @@ __attribute__((always_inline)) void optnone3() { } // expected-warning {{'always
|
|||
|
||||
#pragma clang attribute pop
|
||||
|
||||
#pragma clang attribute push (__attribute__((annotate())), apply_to = function) // expected-error{{'annotate' attribute takes one argument}}
|
||||
#pragma clang attribute (__attribute__((annotate())), apply_to = function) // expected-error{{'annotate' attribute takes one argument}}
|
||||
|
||||
void fun(); // expected-note 2 {{when applied to this declaration}}
|
||||
|
||||
#pragma clang attribute pop
|
||||
#pragma clang attribute pop // expected-error{{'#pragma clang attribute pop' with no matching '#pragma clang attribute push'}}
|
||||
|
||||
|
||||
#pragma clang attribute push
|
||||
#pragma clang attribute (__attribute__((annotate())), apply_to = function) // expected-error 2 {{'annotate' attribute takes one argument}}
|
||||
|
||||
void fun2(); // expected-note {{when applied to this declaration}}
|
||||
|
||||
#pragma clang attribute push (__attribute__((annotate())), apply_to = function) // expected-error{{'annotate' attribute takes one argument}}
|
||||
void fun3(); // expected-note 2 {{when applied to this declaration}}
|
||||
#pragma clang attribute pop
|
||||
|
||||
#pragma clang attribute pop
|
||||
#pragma clang attribute pop // expected-error{{'#pragma clang attribute pop' with no matching '#pragma clang attribute push'}}
|
||||
|
||||
#pragma clang attribute (__attribute__((annotate)), apply_to = function) // expected-error{{'#pragma clang attribute' attribute with no matching '#pragma clang attribute push}}
|
||||
|
||||
#pragma clang attribute push ([[]], apply_to = function) // A noop
|
||||
|
||||
#pragma clang attribute pop // expected-error {{'#pragma clang attribute pop' with no matching '#pragma clang attribute push'}}
|
||||
|
|
Loading…
Reference in New Issue