Index: clang/docs/LanguageExtensions.rst =================================================================== --- clang/docs/LanguageExtensions.rst +++ clang/docs/LanguageExtensions.rst @@ -2663,36 +2663,42 @@ shorthand for when you want to add one attribute to a new scope. Multiple push directives can be nested inside each other. +Because multiple push directives can be nested, if you're writing a macro that +expands to ``_Pragma("clang attribute")`` it's good hygiene to add a label to +your push/pop directives. A pop directive with a label will pop the innermost +push that has that same label. This will ensure that another macro's ``pop`` +won't inadvertently pop your attribute. + 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 ANNOTATE_CUSTOM (__attribute__((annotate("custom"))), apply_to = function) void function(); // The function now has the annotate("custom") attribute - #pragma clang attribute pop + #pragma clang attribute pop ANNOTATE_CUSTOM 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 NO_RETURN ([[noreturn]], apply_to = function) void function(); // The function now has the [[noreturn]] attribute - #pragma clang attribute pop + #pragma clang attribute pop NO_RETURN The ``__declspec`` style syntax is also supported: .. code-block:: c++ - #pragma clang attribute push (__declspec(dllexport), apply_to = function) + #pragma clang attribute push DLL_EXP (__declspec(dllexport), apply_to = function) void function(); // The function now has the __declspec(dllexport) attribute - #pragma clang attribute pop + #pragma clang attribute pop DLL_EXP A single push directive accepts only one attribute regardless of the syntax used. Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -837,6 +837,9 @@ "attribute %0 can't be applied to %1">; def err_pragma_attribute_stack_mismatch : Error< "'#pragma clang attribute pop' with no matching '#pragma clang attribute push'">; +def err_pragma_attribute_stack_mismatch_label : Error< + "'#pragma clang attribute pop %0' with no matching " + "'#pragma clang attribute push %0'">; def warn_pragma_attribute_unused : Warning< "unused attribute %0 in '#pragma clang attribute push' region">, InGroup; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -503,6 +503,8 @@ struct PragmaAttributeGroup { /// The location of the push attribute. SourceLocation Loc; + /// The label of this push group. + IdentifierInfo *Label; SmallVector Entries; }; @@ -8498,10 +8500,12 @@ void ActOnPragmaAttributeAttribute(ParsedAttr &Attribute, SourceLocation PragmaLoc, attr::ParsedSubjectMatchRuleSet Rules); - void ActOnPragmaAttributeEmptyPush(SourceLocation PragmaLoc); + void ActOnPragmaAttributeEmptyPush(SourceLocation PragmaLoc, + IdentifierInfo *PushLabel); /// Called on well-formed '\#pragma clang attribute pop'. - void ActOnPragmaAttributePop(SourceLocation PragmaLoc); + void ActOnPragmaAttributePop(SourceLocation PragmaLoc, + IdentifierInfo *PushLabel); /// Adds the attributes that have been specified using the /// '\#pragma clang attribute push' directives to the given declaration. Index: clang/lib/Parse/ParsePragma.cpp =================================================================== --- clang/lib/Parse/ParsePragma.cpp +++ clang/lib/Parse/ParsePragma.cpp @@ -1139,6 +1139,7 @@ enum ActionType { Push, Pop, Attribute }; ParsedAttributes &Attributes; ActionType Action; + IdentifierInfo *PushLabel = nullptr; ArrayRef Tokens; PragmaAttributeInfo(ParsedAttributes &Attributes) : Attributes(Attributes) {} @@ -1393,7 +1394,7 @@ auto *Info = static_cast(Tok.getAnnotationValue()); if (Info->Action == PragmaAttributeInfo::Pop) { ConsumeAnnotationToken(); - Actions.ActOnPragmaAttributePop(PragmaLoc); + Actions.ActOnPragmaAttributePop(PragmaLoc, Info->PushLabel); return; } // Parse the actual attribute with its arguments. @@ -1403,7 +1404,7 @@ if (Info->Action == PragmaAttributeInfo::Push && Info->Tokens.empty()) { ConsumeAnnotationToken(); - Actions.ActOnPragmaAttributeEmptyPush(PragmaLoc); + Actions.ActOnPragmaAttributeEmptyPush(PragmaLoc, Info->PushLabel); return; } @@ -1555,7 +1556,7 @@ // Handle a mixed push/attribute by desurging to a push, then an attribute. if (Info->Action == PragmaAttributeInfo::Push) - Actions.ActOnPragmaAttributeEmptyPush(PragmaLoc); + Actions.ActOnPragmaAttributeEmptyPush(PragmaLoc, Info->PushLabel); Actions.ActOnPragmaAttributeAttribute(Attribute, PragmaLoc, std::move(SubjectMatchRules)); @@ -3163,6 +3164,14 @@ PP.Lex(Tok); } + // Parse the optional push/pop label. + if ((Info->Action == PragmaAttributeInfo::Push || + Info->Action == PragmaAttributeInfo::Pop) && + Tok.is(tok::identifier)) { + Info->PushLabel = Tok.getIdentifierInfo(); + PP.Lex(Tok); + } + // Parse the actual attribute. if ((Info->Action == PragmaAttributeInfo::Push && Tok.isNot(tok::eod)) || Info->Action == PragmaAttributeInfo::Attribute) { Index: clang/lib/Sema/SemaAttr.cpp =================================================================== --- clang/lib/Sema/SemaAttr.cpp +++ clang/lib/Sema/SemaAttr.cpp @@ -631,28 +631,45 @@ {PragmaLoc, &Attribute, std::move(SubjectMatchRules), /*IsUsed=*/false}); } -void Sema::ActOnPragmaAttributeEmptyPush(SourceLocation PragmaLoc) { +void Sema::ActOnPragmaAttributeEmptyPush(SourceLocation PragmaLoc, + IdentifierInfo *PushLabel) { PragmaAttributeStack.emplace_back(); PragmaAttributeStack.back().Loc = PragmaLoc; + PragmaAttributeStack.back().Label = PushLabel; } -void Sema::ActOnPragmaAttributePop(SourceLocation PragmaLoc) { +void Sema::ActOnPragmaAttributePop(SourceLocation PragmaLoc, + IdentifierInfo *PushLabel) { if (PragmaAttributeStack.empty()) { Diag(PragmaLoc, diag::err_pragma_attribute_stack_mismatch); return; } - 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); + // Dig back through the stack trying to find the group that corrisponds to + // PushLabel. Note that this works fine if no label is present, think of + // unlabeled push/pops as having an implicit "nullptr" label. + for (size_t Index = PragmaAttributeStack.size(); Index;) { + --Index; + if (PragmaAttributeStack[Index].Label == PushLabel) { + for (const PragmaAttributeEntry &Entry : + PragmaAttributeStack[Index].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.erase(PragmaAttributeStack.begin() + Index); + return; } } - PragmaAttributeStack.pop_back(); + if (PushLabel) + Diag(PragmaLoc, diag::err_pragma_attribute_stack_mismatch_label) + << PushLabel->getName(); + else + Diag(PragmaLoc, diag::err_pragma_attribute_stack_mismatch); } void Sema::AddPragmaAttributes(Scope *S, Decl *D) { Index: clang/test/Sema/pragma-attribute-label.c =================================================================== --- /dev/null +++ clang/test/Sema/pragma-attribute-label.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#pragma clang attribute push MY_LABEL (__attribute__((annotate)), apply_to=function) // expected-error 2 {{'annotate' attribute}} + +int some_func(); // expected-note{{when applied to this declaration}} + +#pragma clang attribute pop // expected-error{{'#pragma clang attribute pop' with no matching '#pragma clang attribute push'}} +#pragma clang attribute pop NOT_MY_LABEL // expected-error{{'#pragma clang attribute pop NOT_MY_LABEL' with no matching '#pragma clang attribute push NOT_MY_LABEL'}} + +#pragma clang attribute push MY_OTHER_LABEL (__attribute__((annotate)), apply_to=function) // expected-error 2 {{'annotate' attribute}} + +int some_other_func(); // expected-note 2 {{when applied to this declaration}} + +// Out of order! +#pragma clang attribute pop MY_LABEL + +int some_other_other_func(); // expected-note 1 {{when applied to this declaration}} + +#pragma clang attribute pop MY_OTHER_LABEL