Index: cfe/trunk/docs/LanguageExtensions.rst =================================================================== --- cfe/trunk/docs/LanguageExtensions.rst +++ cfe/trunk/docs/LanguageExtensions.rst @@ -2651,17 +2651,19 @@ 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 @@ .. 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 @@ .. 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 Index: cfe/trunk/docs/ReleaseNotes.rst =================================================================== --- cfe/trunk/docs/ReleaseNotes.rst +++ cfe/trunk/docs/ReleaseNotes.rst @@ -86,8 +86,8 @@ 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 -------------------------- Index: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td @@ -1032,8 +1032,8 @@ 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'">; Index: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td @@ -851,6 +851,9 @@ "'#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< Index: cfe/trunk/include/clang/Sema/Sema.h =================================================================== --- cfe/trunk/include/clang/Sema/Sema.h +++ cfe/trunk/include/clang/Sema/Sema.h @@ -491,15 +491,22 @@ /// 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 MatchRules; bool IsUsed; }; - SmallVector PragmaAttributeStack; + + /// A push'd group of PragmaAttributeEntries. + struct PragmaAttributeGroup { + /// The location of the push attribute. + SourceLocation Loc; + SmallVector Entries; + }; + + SmallVector PragmaAttributeStack; /// The declaration that is currently receiving an attribute from the /// #pragma attribute stack. @@ -8470,9 +8477,10 @@ /// 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); Index: cfe/trunk/lib/Parse/ParsePragma.cpp =================================================================== --- cfe/trunk/lib/Parse/ParsePragma.cpp +++ cfe/trunk/lib/Parse/ParsePragma.cpp @@ -1133,7 +1133,7 @@ namespace { struct PragmaAttributeInfo { - enum ActionType { Push, Pop }; + enum ActionType { Push, Pop, Attribute }; ParsedAttributes &Attributes; ActionType Action; ArrayRef Tokens; @@ -1394,8 +1394,16 @@ 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 @@ // 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 @@ /// 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 @@ 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; Index: cfe/trunk/lib/Sema/SemaAttr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaAttr.cpp +++ cfe/trunk/lib/Sema/SemaAttr.cpp @@ -520,9 +520,9 @@ } // end anonymous namespace -void Sema::ActOnPragmaAttributePush(ParsedAttr &Attribute, - SourceLocation PragmaLoc, - attr::ParsedSubjectMatchRuleSet Rules) { +void Sema::ActOnPragmaAttributeAttribute( + ParsedAttr &Attribute, SourceLocation PragmaLoc, + attr::ParsedSubjectMatchRuleSet Rules) { SmallVector SubjectMatchRules; // Gather the subject match rules that are supported by the attribute. SmallVector, 4> @@ -622,48 +622,64 @@ 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"); - - // 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; + 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; + } } + 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; } } Index: cfe/trunk/test/Parser/pragma-attribute.cpp =================================================================== --- cfe/trunk/test/Parser/pragma-attribute.cpp +++ cfe/trunk/test/Parser/pragma-attribute.cpp @@ -100,11 +100,12 @@ #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 '('}} Index: cfe/trunk/test/Sema/pragma-attribute.c =================================================================== --- cfe/trunk/test/Sema/pragma-attribute.c +++ cfe/trunk/test/Sema/pragma-attribute.c @@ -38,6 +38,29 @@ #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'}}