Index: docs/LanguageExtensions.rst =================================================================== --- docs/LanguageExtensions.rst +++ docs/LanguageExtensions.rst @@ -2312,3 +2312,117 @@ proven safe to vectorize. To identify and diagnose optimization issues use `-Rpass`, `-Rpass-missed`, and `-Rpass-analysis` command line options. See the user guide for details. + +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. + +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(annotate("custom")) + + void function(); // The function now has the annotate("custom") attribute + + #pragma clang attribute pop + +The attributes can also be written using the C++11 style syntax, as long +as just one attribute is specified in the square brackets: + +.. code-block:: c++ + + #pragma clang attribute push([[noreturn]]) + + void function(); // The function now has the [[noreturn]] attribute + + #pragma clang attribute pop + +The set of declarations that receive a single attribute from the attribute +stack is attribute-dependent. For instance, an ``annotate`` attribute will be +received by all the declarations and definitions that can receive explicit +attributes. This means that the following code: + +.. code-block:: c++ + + #pragma clang attribute push(annotate("custom")) + + void function(int param); + + namespace ns { + + void function(int param) { + int localVariable = 0; + } + + } + + #pragma clang attribute pop + +Is semantically sequivalent to the code below: + +.. code-block:: c++ + + void function(int __attribute__((annotate("custom"))) param) __attribute__((annotate("custom"))); + + namespace ns __attribute__((annotate("custom"))) { + + void function(int __attribute__((annotate("custom"))) param) __attribute__((annotate("custom"))) { + int __attribute__((annotate("custom"))) localVariable = 0; + } + + } + +However, an attribute like ``[[noreturn]]`` will be received only by functions +and methods. Consequently, this means that the following code: + +.. code-block:: c++ + + #pragma clang attribute push([[noreturn]]) + + void function(int param); + + namespace ns { + + void function(int param) { + int localVariable = 0; + } + + } + + #pragma clang attribute pop + +Is equivalent to the code below: + +.. code-block:: c++ + + [[noreturn]] + void function(int param); + + namespace ns { + + [[noreturn]] + void function(int param) { + int localVariable = 0; + } + + } + +In general, the attributes are applied to a declaration only when there would +have been no error or warning for that attribute if it was specified explicitly. +An attribute is applied to each relevant declaration individually, +unless the first declaration failed to receive the attribute because of a +ompilation error. The attributes that aren't applied to any declaration are not +verified semantically. + +Not all attributes can be used with the ``#pragma clang attribute`` directive. +Notably, statement attributes like ``[[fallthrough]]`` or type attributes +like ``address_space`` aren't supported by this directive. You can determine +whether or not an attribute is supported by the pragma by referring to the +:doc:`individual documentation for that attribute `. Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -302,6 +302,14 @@ // Set to true if this attribute can be duplicated on a subject when merging // attributes. By default, attributes are not merged. bit DuplicatesAllowedWhileMerging = 0; + // Set to true if this attribute can be used with '#pragma clang attribute'. + // By default, when this value is false, an attribute is supported by the + // '#pragma clang attribute' unless one of the following statements is true: + // - An attribute requires delayed parsing (LateParsed is on) + // - An attribute has no GNU/CXX11 spelling + // - An attribute has no subject list + // - An attribute derives from a StmtAttr or a TypeAttr + bit ForcePragmaAttributeSupport = 0; // Lists language options, one of which is required to be true for the // attribute to be applicable. If empty, no language options are required. list LangOpts = []; @@ -465,6 +473,9 @@ def Annotate : InheritableParamAttr { let Spellings = [GNU<"annotate">]; let Args = [StringArgument<"Annotation">]; + // Ensure that the annotate attribute can be used with + // '#pragma clang attribute' even though it has no subject list. + let ForcePragmaAttributeSupport = 1; let Documentation = [Undocumented]; } Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -965,6 +965,22 @@ "expected 'on' or 'off'">; 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_invalid_argument : Error< + "unexpected argument '%0' to '#pragma clang attribute'; " + "expected 'push' or 'pop'">; +def err_pragma_attribute_expected_attribute : Error< + "expected an attribute after '('">; +def err_pragma_attribute_expected_attribute_name : Error< + "expected identifier that represents an attribute name">; +def err_pragma_attribute_extra_tokens_after_attribute : Error< + "extra tokens after attribute in a '#pragma clang attribute push'">; +def err_pragma_attribute_unsupported_attribute : Error< + "attribute %0 is not supported by '#pragma clang attribute'">; +def err_pragma_attribute_multiple_attributes : Error< + "more than one attribute specified in '#pragma clang attribute push'">; def err_opencl_unroll_hint_on_non_loop : Error< "OpenCL only supports 'opencl_unroll_hint' attribute on for, while, and do statements">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -748,6 +748,9 @@ def err_pragma_loop_precedes_nonloop : Error< "expected a for, while, or do-while loop to follow '%0'">; +def err_pragma_attribute_stack_mismatch : Error< + "'#pragma clang attribute pop' with no matching '#pragma clang attribute push'">; + /// Objective-C parser diagnostics def err_duplicate_class_def : Error< "duplicate interface definition for class %0">; Index: include/clang/Basic/TokenKinds.def =================================================================== --- include/clang/Basic/TokenKinds.def +++ include/clang/Basic/TokenKinds.def @@ -787,6 +787,9 @@ // handles #pragma loop ... directives. ANNOTATION(pragma_loop_hint) +// Annotation for the attribute pragma directives - #pragma clang attribute ... +ANNOTATION(pragma_attribute) + // Annotations for module import translated from #include etc. ANNOTATION(module_include) ANNOTATION(module_begin) Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -179,6 +179,7 @@ std::unique_ptr LoopHintHandler; std::unique_ptr UnrollHintHandler; std::unique_ptr NoUnrollHintHandler; + std::unique_ptr AttributePragmaHandler; std::unique_ptr CommentSemaHandler; @@ -556,6 +557,8 @@ /// #pragma clang loop and #pragma unroll. bool HandlePragmaLoopHint(LoopHint &Hint); + void HandlePragmaAttribute(); + /// GetLookAheadToken - This peeks ahead N tokens and returns that token /// without consuming any tokens. LookAhead(0) returns 'Tok', LookAhead(1) /// returns the token after Tok, etc. Index: include/clang/Sema/AttributeList.h =================================================================== --- include/clang/Sema/AttributeList.h +++ include/clang/Sema/AttributeList.h @@ -509,9 +509,11 @@ unsigned getMaxArgs() const; bool hasVariadicArg() const; bool diagnoseAppertainsTo(class Sema &S, const Decl *D) const; + bool appliesToDecl(class Sema &S, const Decl *D) const; bool diagnoseLangOpts(class Sema &S) const; bool existsInTarget(const TargetInfo &Target) const; bool isKnownToGCC() const; + bool isSupportedByPragmaAttribute() const; /// \brief If the parsed attribute has a semantic equivalent, and it would /// have a semantic Spelling enumeration (due to having semantically-distinct @@ -774,6 +776,8 @@ void clear() { list = nullptr; pool.clear(); } AttributeList *getList() const { return list; } + void clearListOnly() { list = nullptr; } + /// Returns a reference to the attribute list. Try not to introduce /// dependencies on this method, it may not be long-lived. AttributeList *&getListRef() { return list; } Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -435,6 +435,14 @@ /// VisContext - Manages the stack for \#pragma GCC visibility. void *VisContext; // Really a "PragmaVisStack*" + /// \brief This represents the stack of attributes that were pushed by + /// \#pragma clang attribute. + struct PragmaAttributeEntry { + AttributeList *Attribute; + bool IsChecked; + }; + SmallVector PragmaAttributeStack; + /// \brief This represents the last location of a "#pragma clang optimize off" /// directive if such a directive has not been closed by an "on" yet. If /// optimizations are currently "on", this is set to an invalid location. @@ -8113,6 +8121,17 @@ /// the appropriate attribute. void AddCFAuditedAttribute(Decl *D); + /// \brief Called on well formed '\#pragma clang attribute push'. + void ActOnPragmaAttributePush(AttributeList &Attribute, + SourceLocation PragmaLoc); + + /// \brief Called on well formed '\#pragma clang attribute pop'. + void ActOnPragmaAttributePop(SourceLocation PragmaLoc); + + /// \brief Adds the attributes that have been specified using the + /// '\#pragma clang attribute push' directives to the given declaration. + void AddPragmaAttributes(Scope *S, Decl *D); + /// \brief Called on well formed \#pragma clang optimize. void ActOnPragmaOptimize(bool On, SourceLocation PragmaLoc); Index: lib/Parse/ParsePragma.cpp =================================================================== --- lib/Parse/ParsePragma.cpp +++ lib/Parse/ParsePragma.cpp @@ -177,6 +177,17 @@ Sema &Actions; }; +/// PragmaAttributeHandler - "\#pragma clang attribute ...". +struct PragmaAttributeHandler : public PragmaHandler { + PragmaAttributeHandler(AttributeFactory &AttrFactory) + : PragmaHandler("attribute"), AttributesForPragmaAttribute(AttrFactory) {} + void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, + Token &FirstToken) override; + + /// A pool of attributes that were parsed in \#pragma clang attribute. + ParsedAttributes AttributesForPragmaAttribute; +}; + } // end namespace void Parser::initializePragmaHandlers() { @@ -266,6 +277,9 @@ NoUnrollHintHandler.reset(new PragmaUnrollHintHandler("nounroll")); PP.AddPragmaHandler(NoUnrollHintHandler.get()); + + AttributePragmaHandler.reset(new PragmaAttributeHandler(AttrFactory)); + PP.AddPragmaHandler("clang", AttributePragmaHandler.get()); } void Parser::resetPragmaHandlers() { @@ -344,6 +358,9 @@ PP.RemovePragmaHandler(NoUnrollHintHandler.get()); NoUnrollHintHandler.reset(); + + PP.RemovePragmaHandler("clang", AttributePragmaHandler.get()); + AttributePragmaHandler.reset(); } /// \brief Handle the annotation token produced for #pragma unused(...) @@ -940,6 +957,88 @@ return true; } +namespace { +struct PragmaAttributeInfo { + enum ActionType { Push, Pop }; + ParsedAttributes &Attributes; + ActionType Action; + ArrayRef Tokens; + + PragmaAttributeInfo(ParsedAttributes &Attributes) : Attributes(Attributes) {} +}; +} // end anonymous namespace + +void Parser::HandlePragmaAttribute() { + assert(Tok.is(tok::annot_pragma_attribute)); + SourceLocation PragmaLoc = Tok.getLocation(); + auto *Info = static_cast(Tok.getAnnotationValue()); + if (Info->Action == PragmaAttributeInfo::Pop) { + ConsumeToken(); + Actions.ActOnPragmaAttributePop(PragmaLoc); + return; + } + // Parse the actual attribute with its arguments. + assert(Info->Action == PragmaAttributeInfo::Push); + PP.EnterTokenStream(Info->Tokens, /*DisableMacroExpansion=*/false); + ConsumeToken(); + + ParsedAttributes &Attrs = Info->Attributes; + Attrs.clearListOnly(); + + if (Tok.is(tok::l_square) && NextToken().is(tok::l_square)) { + // Parse the CXX11 style attribute. + ParseCXX11AttributeSpecifier(Attrs); + // Ensure that we don't have more than one attribute. + if (Attrs.getList() && Attrs.getList()->getNext()) { + SourceLocation Loc = Attrs.getList()->getNext()->getLoc(); + Diag(Loc, diag::err_pragma_attribute_multiple_attributes); + Attrs.getList()->setNext(nullptr); + } + } else { + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_pragma_attribute_expected_attribute_name); + SkipUntil(tok::eof); + ConsumeToken(); + return; + } + IdentifierInfo *AttrName = Tok.getIdentifierInfo(); + SourceLocation AttrNameLoc = ConsumeToken(); + + if (Tok.isNot(tok::l_paren)) + Attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0, + AttributeList::AS_GNU); + else + ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, /*EndLoc=*/nullptr, + /*ScopeName=*/nullptr, + /*ScopeLoc=*/SourceLocation(), + AttributeList::AS_GNU, + /*Declarator=*/nullptr); + } + + // Tokens following an ill-formed attribute will remain in the token stream + // and must be removed. + if (Tok.isNot(tok::eof)) { + Diag(Tok, diag::err_pragma_attribute_extra_tokens_after_attribute); + SkipUntil(tok::eof); + ConsumeToken(); + return; + } + + // Consume the eof terminator token. + ConsumeToken(); + + if (!Attrs.getList() || Attrs.getList()->isInvalid()) + return; + + if (!Attrs.getList()->isSupportedByPragmaAttribute()) { + Diag(PragmaLoc, diag::err_pragma_attribute_unsupported_attribute) + << Attrs.getList()->getName(); + return; + } + assert(!Attrs.getList()->getNext()); + Actions.ActOnPragmaAttributePush(*Attrs.getList(), PragmaLoc); +} + // #pragma GCC visibility comes in two variants: // 'push' '(' [visibility] ')' // 'pop' @@ -2246,3 +2345,99 @@ PP.Diag(FirstTok.getLocation(), diag::warn_pragma_force_cuda_host_device_bad_arg); } + +/// \brief Handle the #pragma clang attribute directive. +/// +/// The syntax is: +/// \code +/// #pragma clang attribute push(attribute) +/// #pragma clang attribute pop +/// \endcode +/// +/// This directive instructs the compiler to begin/finish applying the specified +/// attribute to the set of attribute-specific declarations in the active range +/// of the pragma. +void PragmaAttributeHandler::HandlePragma(Preprocessor &PP, + PragmaIntroducerKind Introducer, + Token &FirstToken) { + Token Tok; + PP.Lex(Tok); + 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); + return; + } + const auto *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); + + // Parse the actual attribute. + if (Info->Action == PragmaAttributeInfo::Push) { + if (Tok.isNot(tok::l_paren)) { + PP.Diag(Tok.getLocation(), diag::err_expected) << tok::l_paren; + return; + } + PP.Lex(Tok); + + // Lex the attribute tokens. + SmallVector AttributeTokens; + int OpenParens = 1; + while (Tok.isNot(tok::eod)) { + if (Tok.is(tok::l_paren)) + OpenParens++; + else if (Tok.is(tok::r_paren)) { + OpenParens--; + if (OpenParens == 0) + break; + } + + AttributeTokens.push_back(Tok); + PP.Lex(Tok); + } + + if (AttributeTokens.empty()) { + PP.Diag(Tok.getLocation(), diag::err_pragma_attribute_expected_attribute); + return; + } + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok.getLocation(), diag::err_expected) << tok::r_paren; + return; + } + PP.Lex(Tok); + + // Terminate the attribute for parsing. + Token EOFTok; + EOFTok.startToken(); + EOFTok.setKind(tok::eof); + EOFTok.setLocation(Tok.getLocation()); + AttributeTokens.push_back(EOFTok); + + Info->Tokens = + llvm::makeArrayRef(AttributeTokens).copy(PP.getPreprocessorAllocator()); + } + + if (Tok.isNot(tok::eod)) + PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) + << "clang attribute"; + + // Generate the annotated pragma token. + auto TokenArray = llvm::make_unique(1); + TokenArray[0].startToken(); + TokenArray[0].setKind(tok::annot_pragma_attribute); + TokenArray[0].setLocation(FirstToken.getLocation()); + TokenArray[0].setAnnotationEndLoc(FirstToken.getLocation()); + TokenArray[0].setAnnotationValue(static_cast(Info)); + PP.EnterTokenStream(std::move(TokenArray), 1, + /*DisableMacroExpansion=*/false); +} Index: lib/Parse/ParseStmt.cpp =================================================================== --- lib/Parse/ParseStmt.cpp +++ lib/Parse/ParseStmt.cpp @@ -376,6 +376,10 @@ case tok::annot_pragma_dump: HandlePragmaDump(); return StmtEmpty(); + + case tok::annot_pragma_attribute: + HandlePragmaAttribute(); + return StmtEmpty(); } // If we reached this code, the statement must end in a semicolon. Index: lib/Parse/Parser.cpp =================================================================== --- lib/Parse/Parser.cpp +++ lib/Parse/Parser.cpp @@ -605,6 +605,10 @@ ConsumeToken(); return false; + case tok::annot_pragma_attribute: + HandlePragmaAttribute(); + return false; + case tok::eof: // Late template parsing can begin. if (getLangOpts().DelayedTemplateParsing) Index: lib/Sema/AttributeList.cpp =================================================================== --- lib/Sema/AttributeList.cpp +++ lib/Sema/AttributeList.cpp @@ -160,9 +160,11 @@ unsigned IsType : 1; unsigned IsStmt : 1; unsigned IsKnownToGCC : 1; + unsigned IsSupportedByPragmaAttribute : 1; bool (*DiagAppertainsToDecl)(Sema &S, const AttributeList &Attr, const Decl *); + bool (*AttributeAppliesToDecl)(Sema &S, const Decl *); bool (*DiagLangOpts)(Sema &S, const AttributeList &Attr); bool (*ExistsInTarget)(const TargetInfo &Target); unsigned (*SpellingIndexToSemanticSpelling)(const AttributeList &Attr); @@ -192,6 +194,10 @@ return getInfo(*this).DiagAppertainsToDecl(S, *this, D); } +bool AttributeList::appliesToDecl(Sema &S, const Decl *D) const { + return getInfo(*this).AttributeAppliesToDecl(S, D); +} + bool AttributeList::diagnoseLangOpts(Sema &S) const { return getInfo(*this).DiagLangOpts(S, *this); } @@ -216,6 +222,10 @@ return getInfo(*this).IsKnownToGCC; } +bool AttributeList::isSupportedByPragmaAttribute() const { + return getInfo(*this).IsSupportedByPragmaAttribute; +} + unsigned AttributeList::getSemanticSpelling() const { return getInfo(*this).SpellingIndexToSemanticSpelling(*this); } Index: lib/Sema/SemaAttr.cpp =================================================================== --- lib/Sema/SemaAttr.cpp +++ lib/Sema/SemaAttr.cpp @@ -367,6 +367,54 @@ D->addAttr(CFAuditedTransferAttr::CreateImplicit(Context, Loc)); } +void Sema::ActOnPragmaAttributePush(AttributeList &Attribute, + SourceLocation PragmaLoc) { + PragmaAttributeStack.push_back({&Attribute, /*IsChecked=*/false}); +} + +void Sema::ActOnPragmaAttributePop(SourceLocation PragmaLoc) { + if (PragmaAttributeStack.empty()) { + Diag(PragmaLoc, diag::err_pragma_attribute_stack_mismatch); + return; + } + PragmaAttributeStack.pop_back(); + // FIXME: Should we check the attribute even when it's not applied to any + // declaration? +} + +void Sema::AddPragmaAttributes(Scope *S, Decl *D) { + if (PragmaAttributeStack.empty()) + return; + for (auto &Entry : PragmaAttributeStack) { + if (!Entry.Attribute) + continue; + + // Ensure that the attribute can be applied to the given declaration. + if (!Entry.Attribute->appliesToDecl(*this, D)) + continue; + + // The first time the attribute is applied we also check if this attribute + // is valid. + if (!Entry.IsChecked) { + assert(Entry.Attribute); + bool HasFailed; + { + SFINAETrap Trap(*this); + ProcessDeclAttributeList(S, D, Entry.Attribute); + HasFailed = Trap.hasErrorOccurred(); + } + Entry.IsChecked = true; + if (HasFailed) { + // Reprocess the attribute to ensure that the errors are reported. + ProcessDeclAttributeList(S, D, Entry.Attribute); + Entry.Attribute = nullptr; + } + continue; + } + ProcessDeclAttributeList(S, D, Entry.Attribute); + } +} + void Sema::ActOnPragmaOptimize(bool On, SourceLocation PragmaLoc) { if(On) OptimizeOffPragmaLocation = SourceLocation(); Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -5478,6 +5478,7 @@ // Handle attributes prior to checking for duplicates in MergeVarDecl ProcessDeclAttributes(S, NewTD, D); + AddPragmaAttributes(S, NewTD); CheckTypedefForVariablyModifiedType(S, NewTD); @@ -6432,6 +6433,7 @@ // Handle attributes prior to checking for duplicates in MergeVarDecl ProcessDeclAttributes(S, NewVD, D); + AddPragmaAttributes(S, NewVD); if (getLangOpts().CUDA) { if (EmitTLSUnsupportedError && DeclAttrsMatchCUDAMode(getLangOpts(), NewVD)) @@ -8806,6 +8808,9 @@ if(D.isFunctionDefinition()) AddRangeBasedOptnone(NewFD); + // Add any attributes that were specified with '#pragma clang attribute push'. + AddPragmaAttributes(S, NewFD); + // If this is the first declaration of an extern C variable, update // the map of such variables. if (NewFD->isFirstDecl() && !NewFD->isInvalidDecl() && @@ -11443,6 +11448,7 @@ IdResolver.AddDecl(New); ProcessDeclAttributes(S, New, D); + AddPragmaAttributes(S, New); if (D.getDeclSpec().isModulePrivateSpecified()) Diag(New->getLocation(), diag::err_module_private_local) @@ -13634,6 +13640,7 @@ if (Attr) ProcessDeclAttributeList(S, New, Attr); + AddPragmaAttributes(S, New); // Set the lexical context. If the tag has a C++ scope specifier, the // lexical context will be different from the semantic context. @@ -14209,6 +14216,7 @@ if (NewFD->hasAttrs()) CheckAlignasUnderalignment(NewFD); } + AddPragmaAttributes(getCurScope(), NewFD); // In auto-retain/release, infer strong retension for fields of // retainable type. @@ -14382,6 +14390,7 @@ // Process attributes attached to the ivar. ProcessDeclAttributes(S, NewID, D); + AddPragmaAttributes(S, NewID); if (D.isInvalidType()) NewID->setInvalidDecl(); @@ -15162,6 +15171,7 @@ // Process attributes. if (Attr) ProcessDeclAttributeList(S, New, Attr); + AddPragmaAttributes(S, New); // Register this decl in the current scope stack. New->setAccess(TheEnumDecl->getAccess()); Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -8374,6 +8374,7 @@ Namespc->setInvalidDecl(); ProcessDeclAttributeList(DeclRegionScope, Namespc, AttrList); + AddPragmaAttributes(DeclRegionScope, Namespc); // FIXME: Should we be merging attributes? if (const VisibilityAttr *Attr = Namespc->getAttr()) @@ -9860,6 +9861,7 @@ NewTD->setInvalidDecl(); ProcessDeclAttributeList(S, NewTD, AttrList); + AddPragmaAttributes(S, NewTD); CheckTypedefForVariablyModifiedType(S, NewTD); Invalid |= NewTD->isInvalidDecl(); Index: lib/Sema/SemaDeclObjC.cpp =================================================================== --- lib/Sema/SemaDeclObjC.cpp +++ lib/Sema/SemaDeclObjC.cpp @@ -992,6 +992,7 @@ if (AttrList) ProcessDeclAttributeList(TUScope, IDecl, AttrList); + AddPragmaAttributes(TUScope, IDecl); PushOnScopeChains(IDecl, TUScope); // Start the definition of this class. If we're in a redefinition case, there @@ -1175,7 +1176,8 @@ if (AttrList) ProcessDeclAttributeList(TUScope, PDecl, AttrList); - + AddPragmaAttributes(TUScope, PDecl); + // Merge attributes from previous declarations. if (PrevDecl) mergeDeclAttributes(PDecl, PrevDecl); @@ -1705,7 +1707,8 @@ if (attrList) ProcessDeclAttributeList(TUScope, PDecl, attrList); - + AddPragmaAttributes(TUScope, PDecl); + if (PrevDecl) mergeDeclAttributes(PDecl, PrevDecl); @@ -1948,6 +1951,7 @@ ClassName, /*typeParamList=*/nullptr, /*PrevDecl=*/nullptr, ClassLoc, true); + AddPragmaAttributes(TUScope, IDecl); IDecl->startDefinition(); if (SDecl) { IDecl->setSuperClass(Context.getTrivialTypeSourceInfo( @@ -3037,7 +3041,7 @@ ClassName, TypeParams, PrevIDecl, IdentLocs[i]); IDecl->setAtEndRange(IdentLocs[i]); - + PushOnScopeChains(IDecl, TUScope); CheckObjCDeclScope(IDecl); DeclsInGroup.push_back(IDecl); @@ -4393,6 +4397,7 @@ // Apply the attributes to the parameter. ProcessDeclAttributeList(TUScope, Param, ArgInfo[i].ArgAttrs); + AddPragmaAttributes(TUScope, Param); if (Param->hasAttr()) { Diag(Param->getLocation(), diag::err_block_on_nonlocal); @@ -4423,6 +4428,7 @@ if (AttrList) ProcessDeclAttributeList(TUScope, ObjCMethod, AttrList); + AddPragmaAttributes(TUScope, ObjCMethod); // Add the method now. const ObjCMethodDecl *PrevMethod = nullptr; Index: lib/Sema/SemaObjCProperty.cpp =================================================================== --- lib/Sema/SemaObjCProperty.cpp +++ lib/Sema/SemaObjCProperty.cpp @@ -637,6 +637,7 @@ } ProcessDeclAttributes(S, PDecl, FD.D); + AddPragmaAttributes(S, PDecl); // Regardless of setter/getter attribute, we save the default getter/setter // selector names in anticipation of declaration of setter/getter methods. Index: test/Parser/pragma-attribute.cpp =================================================================== --- /dev/null +++ test/Parser/pragma-attribute.cpp @@ -0,0 +1,67 @@ +// RUN: %clang_cc1 -verify -std=c++11 %s + +#pragma clang attribute push(annotate("test")) + +void function(); + +#pragma clang attribute pop + +#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 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 ( // expected-error {{expected an attribute after '('}} +#pragma clang attribute push (annotate // expected-error {{expected ')'}} +#pragma clang attribute push () // expected-error {{expected an attribute after '('}} + +#pragma clang attribute push (annotate("test")) () // expected-warning {{extra tokens at end of '#pragma clang attribute'}} +// expected-error@-1 {{expected unqualified-id}} + +#pragma clang attribute pop () // expected-warning {{extra tokens at end of '#pragma clang attribute'}} + +; + +#pragma clang attribute push (42) // expected-error {{expected identifier that represents an attribute name}} + +#pragma clang attribute push (annotate foo) // expected-error {{extra tokens after attribute in a '#pragma clang attribute push'}} + +#pragma clang attribute push (availability(macos, foo=1)) // expected-error {{'foo' is not an availability stage; use 'introduced', 'deprecated', or 'obsoleted'}} +// expected-error@-1 {{attribute 'availability' is not supported by '#pragma clang attribute'}} +#pragma clang attribute push (availability(macos, 1)) // expected-error {{expected 'introduced', 'deprecated', or 'obsoleted'}} + +#pragma clang attribute push (used) // expected-error {{attribute 'used' is not supported by '#pragma clang attribute'}} + +void statementPragmasAndPragmaExpression() { +#pragma clang attribute push (annotate("hello")) +#pragma clang attribute pop +int x = 0; +_Pragma("clang attribute push (annotate(\"hi\"))"); +} + +_Pragma("clang attribute pop"); + +#pragma clang attribute push (address_space(0)) // expected-error {{attribute 'address_space' is not supported by '#pragma clang attribute'}} + +// Check support for CXX11 style attributes +#pragma clang attribute push ([[noreturn]]) +#pragma clang attribute pop + +#pragma clang attribute push ([[clang::disable_tail_calls]]) +#pragma clang attribute pop + +#pragma clang attribute push ([[gnu::alias]]) +#pragma clang attribute pop + +#pragma clang attribute push ([[clang::disable_tail_calls, noreturn]]) // expected-error {{more than one attribute specified in '#pragma clang attribute push'}} +#pragma clang attribute pop + +#pragma clang attribute push ([[gnu::alias]]) +#pragma clang attribute pop + +#pragma clang attribute push ([[fallthrough]]) // expected-error {{attribute 'fallthrough' is not supported by '#pragma clang attribute'}} +#pragma clang attribute push ([[clang::fallthrough]]) // expected-error {{attribute 'fallthrough' is not supported by '#pragma clang attribute'}} + +#pragma clang attribute push ([[]]) // A noop + +#pragma clang attribute push ([[noreturn ""]]) // expected-error {{expected ']'}} Index: test/Sema/pragma-attribute.c =================================================================== --- /dev/null +++ test/Sema/pragma-attribute.c @@ -0,0 +1,140 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: not %clang_cc1 -fsyntax-only -ast-dump -ast-dump-filter test %s | FileCheck %s + +void test0(void); +// CHECK-LABEL: FunctionDecl{{.*}}test0 +// CHECK-NOT: AnnotateAttr + +void test0() { } +// CHECK-LABEL: FunctionDecl{{.*}}test0 +// CHECK-NEXT: CompoundStmt +// CHECK-NOT: AnnotateAttr + +#pragma clang attribute push (annotate("test")) +#pragma clang attribute push (annotate("foo")) + +void test(void); +// CHECK-LABEL: FunctionDecl{{.*}}test +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" + +void test() { + int testLocal = 0; +} +// CHECK-LABEL: FunctionDecl{{.*}}test +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: DeclStmt +// CHECK-NEXT: VarDecl +// CHECK-NEXT: IntegerLiteral +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" + +void test2(int argument) { } +// CHECK-LABEL: FunctionDecl{{.*}}test2 +// CHECK-NEXT: ParmVarDecl +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" + +int testVar1 = 0; +// CHECK-LABEL: VarDecl{{.*}}testVar1 +// CHECK-NEXT: IntegerLiteral +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" + +struct testStruct1 { + int field; +}; +// CHECK-LABEL: RecordDecl{{.*}}testStruct1 +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" +// CHECK-NEXT: FieldDecl{{.*}}field +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" + +union testUnion1 { + int field1; + float field2; +}; +// CHECK-LABEL: RecordDecl{{.*}}testUnion1 +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" +// CHECK-NEXT: FieldDecl{{.*}}field1 +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" +// CHECK-NEXT: FieldDecl{{.*}}field2 +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" + +typedef struct { + int testField; +// CHECK-LABEL: FieldDecl{{.*}}testField +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" +} testTypedef1; +// CHECK-LABEL: TypedefDecl{{.*}}testTypedef1 +// CHECK: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" + +enum testEnum1 { + enumCase1, + enumCase2 +}; +// CHECK-LABEL: EnumDecl{{.*}}testEnum1 +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" +// CHECK-NEXT: EnumConstantDecl +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" +// CHECK-NEXT: EnumConstantDecl +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" + +#pragma clang attribute pop + +void test3(); +// CHECK-LABEL: FunctionDecl{{.*}}test3 +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NOT: AnnotateAttr + +#pragma clang attribute pop + +void test4(); +// CHECK-LABEL: FunctionDecl{{.*}}test4 +// CHECK-NOT: AnnotateAttr + +void test4() { } +// CHECK-LABEL: FunctionDecl{{.*}}test4 +// CHECK-NOT: AnnotateAttr + +#pragma clang attribute pop // expected-error {{'#pragma clang attribute pop' with no matching '#pragma clang attribute push'}} + +#pragma clang attribute push (annotate) +#pragma clang attribute pop // Don't verify unused attributes. + +// Ensure we only report any errors once. +#pragma clang attribute push (annotate) // expected-error {{'annotate' attribute takes one argument}} + +// CHECK-LABEL: FunctionDecl{{.*}}test5_begin +// CHECK-NOT: AnnotateAttr +void test5_begin(); +void test5_1(); + +#pragma clang attribute push (annotate()) // expected-error {{'annotate' attribute takes one argument}} + +void test5_2(); + +#pragma clang attribute push (annotate("hello", "world")) // expected-error {{'annotate' attribute takes one argument}} + +void test5_3(); + +#pragma clang attribute pop +#pragma clang attribute pop +#pragma clang attribute pop + +void test5_end(); +// CHECK-LABEL: FunctionDecl{{.*}}test5_end Index: test/SemaCXX/pragma-attribute.cpp =================================================================== --- /dev/null +++ test/SemaCXX/pragma-attribute.cpp @@ -0,0 +1,91 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -ast-dump -ast-dump-filter test -std=c++11 -DNOERROR %s | FileCheck %s + +class testClass1 { +}; +// CHECK-LABEL: CXXRecordDecl{{.*}} testClass1 +// CHECK-NOT: AnnotateAttr + +#pragma clang attribute push (annotate("test")) + +class testClass2 { + void testMethod1(int param); + + testClass2(); + + testClass2 *operator -> (); +}; +// CHECK-LABEL: CXXRecordDecl{{.*}} testClass2 +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK: CXXMethodDecl{{.*}} testMethod1 +// CHECK-NEXT: ParmVarDecl{{.*}} param +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: CXXConstructorDecl +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: CXXMethodDecl{{.*}} operator-> +// CHECK-NEXT: AnnotateAttr{{.*}} "test" + +#pragma clang attribute push (annotate("method")) + +void testClass2::testMethod1(int param) { + +#pragma clang attribute pop +} +// CHECK-LABEL: CXXMethodDecl{{.*}}prev{{.*}} testMethod1 +// CHECK-NEXT: ParmVarDecl{{.*}} param +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "method" +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "method" + +namespace testNamespace { +} +// CHECK-LABEL: NamespaceDecl{{.*}} testNamespace +// CHECK-NEXT: AnnotateAttr{{.*}} "test" + +class testClassForward; +// CHECK-LABEL: CXXRecordDecl{{.*}} testClassForward +// CHECK-NEXT: AnnotateAttr{{.*}} "test" + +namespace testNamespaceAlias = testNamespace; +// CHECK-LABEL: NamespaceAliasDecl{{.*}} testNamespaceAlias +// CHECK-NOT: AnnotateAttr + +using testTypeAlias = testClass2; +// CHECK-LABEL: TypeAliasDecl{{.*}} testTypeAlias +// CHECK: AnnotateAttr{{.*}} "test" + +#pragma clang attribute pop + +#pragma clang attribute push (require_constant_initialization) + +int testCI1 = 1; +// CHECK-LABEL: VarDecl{{.*}} testCI1 +// CHECK-NEXT: IntegerLiteral +// CHECK-NEXT: RequireConstantInitAttr + +#pragma clang attribute pop + +int testNoCI = 0; +// CHECK-LABEL: VarDecl{{.*}} testNoCI +// CHECK-NEXT: IntegerLiteral +// CHECK-NOT: RequireConstantInitAttr + +// Check support for CXX11 style attributes +#pragma clang attribute push ([[noreturn]]) + +void testNoReturn(); +// CHECK-LABEL: FunctionDecl{{.*}} testNoReturn +// CHECK-NEXT: CXX11NoReturnAttr + +#pragma clang attribute pop + +#ifndef NOERROR + +#pragma clang attribute push ([[]]) // A noop + +#pragma clang attribute pop // expected-error {{'#pragma clang attribute pop' with no matching '#pragma clang attribute push'}} + +#endif Index: test/SemaObjC/pragma-attribute.m =================================================================== --- /dev/null +++ test/SemaObjC/pragma-attribute.m @@ -0,0 +1,164 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s +// RUN: %clang_cc1 -fsyntax-only -ast-dump -ast-dump-filter test %s | FileCheck %s + +#pragma clang attribute push (annotate("test")) +#pragma clang attribute push (objc_subclassing_restricted) + +@interface testInterface1 +// CHECK-LABEL: ObjCInterfaceDecl{{.*}}testInterface1 +// CHECK-NEXT: ObjCImplementation +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: ObjCSubclassingRestrictedAttr{{.*}} + +// CHECK-NOT: AnnotateAttr +// CHECK-NOT: ObjCSubclassingRestrictedAttr + +{ + int testIvar1; + // CHECK-LABEL: ObjCIvarDecl{{.*}} testIvar1 + // CHECK-NEXT: AnnotateAttr{{.*}} "test" + // CHECK-NOT: ObjCSubclassingRestrictedAttr +} + +@property int testProp1; +// CHECK-LABEL: ObjCPropertyDecl{{.*}} testProp1 +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NOT: ObjCSubclassingRestrictedAttr + +- (void)testIm:(int) x; +// CHECK-LABEL: ObjCMethodDecl{{.*}}testIm +// CHECK-NEXT: ParmVarDecl{{.*}} x +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NOT: ObjCSubclassingRestrictedAttr + ++ (void)testCm; +// CHECK-LABEL: ObjCMethodDecl{{.*}}testCm +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NOT: ObjCSubclassingRestrictedAttr + +// Implicit getters/setters shouldn't receive the attributes. +// CHECK-LABEL: ObjCMethodDecl{{.*}}testProp1 +// CHECK-NOT: AnnotateAttr +// CHECK-LABEL: ObjCMethodDecl{{.*}}setTestProp1 +// CHECK-NOT: AnnotateAttr + +@end + +// @implementation can't receive explicit attributes, so don't add the pragma +// attributes to them. +@implementation testInterface1 +// CHECK-LABEL: ObjCImplementationDecl{{.*}}testInterface1 +// CHECK-NOT: AnnotateAttr +// CHECK-NOT: ObjCSubclassingRestrictedAttr + +{ + int testIvar2; + // CHECK-LABEL: ObjCIvarDecl{{.*}} testIvar2 + // CHECK-NEXT: AnnotateAttr{{.*}} "test" + // CHECK-NOT: ObjCSubclassingRestrictedAttr +} + +// Don't add attributes to implicit parameters! +- (void)testIm:(int) x { +// CHECK-LABEL: ObjCMethodDecl{{.*}}testIm +// CHECK-NEXT: ImplicitParamDecl +// CHECK-NEXT: ImplicitParamDecl +// CHECK-NEXT: ParmVarDecl{{.*}} x +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NOT: ObjCSubclassingRestrictedAttr +} + ++ (void)testCm { +// CHECK-LABEL: ObjCMethodDecl{{.*}}testCm +// CHECK: AnnotateAttr{{.*}} "test" +// CHECK-NOT: ObjCSubclassingRestrictedAttr +// CHECK-NOT: AnnotateAttr + _Pragma("clang attribute push (annotate(\"applied at container start\"))"); +} + +// Implicit ivars shouldn't receive the attributes. +// CHECK-LABEL: ObjCIvarDecl{{.*}}_testProp1 +// CHECK-NOT: AnnotateAttr + +@end + +@implementation testImplWithoutInterface // expected-warning {{cannot find interface declaration for 'testImplWithoutInterface'}} +// CHECK-LABEL: ObjCInterfaceDecl{{.*}}testImplWithoutInterface +// CHECK-NEXT: ObjCImplementation +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NEXT: ObjCSubclassingRestrictedAttr +// CHECK-NEXT: AnnotateAttr{{.*}} "applied at container start" + +// CHECK-LABEL: ObjCImplementationDecl{{.*}}testImplWithoutInterface +// CHECK-NOT: AnnotateAttr +// CHECK-NOT: ObjCSubclassingRestrictedAttr + +@end + +#pragma clang attribute pop + +@protocol testProtocol +// CHECK-LABEL: ObjCProtocolDecl{{.*}}testProtocol +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NOT: ObjCSubclassingRestrictedAttr +// CHECK-NOT: AnnotateAttr + +- (void)testProtIm; +// CHECK-LABEL: ObjCMethodDecl{{.*}}testProtIm +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NOT: ObjCSubclassingRestrictedAttr + +@end + +@protocol testForwardProtocol; +// CHECK-LABEL: ObjCProtocolDecl{{.*}}testForwardProtocol +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NOT: ObjCSubclassingRestrictedAttr + + +// Categories can't receive explicit attributes, so don't add pragma attributes +// to them. +@interface testInterface1(testCat) +// CHECK-LABEL: ObjCCategoryDecl{{.*}}testCat +// CHECK-NOT: AnnotateAttr +// CHECK-NOT: ObjCSubclassingRestrictedAttr + +@end + +@implementation testInterface1(testCat) +// CHECK-LABEL: ObjCCategoryImplDecl{{.*}}testInterface1 +// CHECK-NOT: AnnotateAttr +// CHECK-NOT: ObjCSubclassingRestrictedAttr + +@end + +// @class/@compatibility_alias declarations can't receive explicit attributes, +// so don't add pragma attributes to them. +@class testClass; +// CHECK-LABEL: ObjCInterfaceDecl{{.*}}testClass +// CHECK-NOT: AnnotateAttr +// CHECK-NOT: ObjCSubclassingRestrictedAttr + +@compatibility_alias testCompat testInterface1; +// CHECK-LABEL: ObjCCompatibleAliasDecl{{.*}}testCompat +// CHECK-NOT: AnnotateAttr +// CHECK-NOT: ObjCSubclassingRestrictedAttr + +#pragma clang attribute pop // objc_subclassing_restricted + +@interface testInterface3 +// CHECK-LABEL: ObjCInterfaceDecl{{.*}}testInterface3 +// CHECK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-NOT: ObjCSubclassingRestrictedAttr +@end + +#pragma clang attribute pop // annotate("test") + +@interface testInterface4 +// CHECK-LABEL: ObjCInterfaceDecl{{.*}}testInterface4 +// CHECK-NOT: AnnotateAttr +// CHECK-NOT: ObjCSubclassingRestrictedAttr +@end Index: utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- utils/TableGen/ClangAttrEmitter.cpp +++ utils/TableGen/ClangAttrEmitter.cpp @@ -1522,6 +1522,38 @@ OS << "#endif // CLANG_ATTR_LATE_PARSED_LIST\n\n"; } +static bool hasGNUorCXX11Spelling(const Record &Attribute) { + std::vector Spellings = GetFlattenedSpellings(Attribute); + for (const auto &I : Spellings) { + if (I.variety() == "GNU" || I.variety() == "CXX11") + return true; + } + return false; +} + +static bool isSupportedByPragmaAttribute(const Record &Attribute) { + if (Attribute.getValueAsBit("ForcePragmaAttributeSupport")) + return true; + // Opt-out rules: + // An attribute requires delayed parsing (LateParsed is on) + if (Attribute.getValueAsBit("LateParsed")) + return false; + // An attribute has no GNU/CXX11 spelling + if (!hasGNUorCXX11Spelling(Attribute)) + return false; + // An attribute has no subject list or its subject list is empty + if (Attribute.isValueUnset("Subjects")) + return false; + const Record *SubjectObj = Attribute.getValueAsDef("Subjects"); + std::vector Subjects = SubjectObj->getValueAsListOfDefs("Subjects"); + if (Subjects.empty()) + return false; + // An attribute derives from a StmtAttr or a TypeAttr + if (Attribute.isSubClassOf("TypeAttr") || Attribute.isSubClassOf("StmtAttr")) + return false; + return true; +} + template static void forEachUniqueSpelling(const Record &Attr, Fn &&F) { std::vector Spellings = GetFlattenedSpellings(Attr); @@ -2692,9 +2724,13 @@ return B + "Decl"; } +static std::string functionNameForCustomAppertainsTo(const Record &Subject) { + return "is" + Subject.getName().str(); +} + static std::string GenerateCustomAppertainsTo(const Record &Subject, raw_ostream &OS) { - std::string FnName = "is" + Subject.getName().str(); + std::string FnName = functionNameForCustomAppertainsTo(Subject); // If this code has already been generated, simply return the previous // instance of it. @@ -2779,6 +2815,50 @@ return FnName; } +static void GenerateDefaultAppliesTo(raw_ostream &OS) { + OS << "static bool defaultAttributeAppliesTo(Sema &, const Decl *) {\n"; + OS << " return true;\n"; + OS << "}\n\n"; +} + +static std::string GenerateAppliesTo(const Record &Attr, raw_ostream &OS) { + // If the attribute doesn't support '#pragma clang attribute' or if it doesn't + // contain a subjects definition, then use the default appliedTo logic. + if (!isSupportedByPragmaAttribute(Attr) || Attr.isValueUnset("Subjects")) + return "defaultAttributeAppliesTo"; + + const Record *SubjectObj = Attr.getValueAsDef("Subjects"); + std::vector Subjects = SubjectObj->getValueAsListOfDefs("Subjects"); + + // If the list of subjects is empty, it is assumed that the attribute applies + // to everything. + if (Subjects.empty()) + return "defaultAttributeAppliesTo"; + + // Otherwise, generate an appliesTo check specific to this attribute which + // checks all of the given subjects against the Decl passed in. Return the + // name of that check to the caller. + std::string FnName = "check" + Attr.getName().str() + "AttributeAppliesTo"; + std::stringstream SS; + SS << "static bool " << FnName << "(Sema &S, const Decl *D) {\n"; + SS << " return "; + for (auto I = Subjects.begin(), E = Subjects.end(); I != E; ++I) { + // If the subject has custom code associated with it, use the function + // that was generated for GenerateAppertainsTo to check if the declaration + // is valid. + if ((*I)->isSubClassOf("SubsetSubject")) + SS << functionNameForCustomAppertainsTo(**I) << "(D)"; + else + SS << "isa<" << GetSubjectWithSuffix(*I) << ">(D)"; + + if (I + 1 != E) + SS << " || "; + } + SS << ";\n}\n\n"; + OS << SS.str(); + return FnName; +} + static void GenerateDefaultLangOptRequirements(raw_ostream &OS) { OS << "static bool defaultDiagnoseLangOpts(Sema &, "; OS << "const AttributeList &) {\n"; @@ -2945,6 +3025,7 @@ // Generate the default appertainsTo, target and language option diagnostic, // and spelling list index mapping methods. GenerateDefaultAppertainsTo(OS); + GenerateDefaultAppliesTo(OS); GenerateDefaultLangOptRequirements(OS); GenerateDefaultTargetRequirements(OS); GenerateDefaultSpellingIndexToSemanticSpelling(OS); @@ -2970,7 +3051,9 @@ SS << ", " << I->second->isSubClassOf("TypeAttr"); SS << ", " << I->second->isSubClassOf("StmtAttr"); SS << ", " << IsKnownToGCC(*I->second); + SS << ", " << isSupportedByPragmaAttribute(*I->second); SS << ", " << GenerateAppertainsTo(*I->second, OS); + SS << ", " << GenerateAppliesTo(*I->second, OS); SS << ", " << GenerateLangOptRequirements(*I->second, OS); SS << ", " << GenerateTargetRequirements(*I->second, Dupes, OS); SS << ", " << GenerateSpellingIndexToSemanticSpelling(*I->second, OS); @@ -3238,7 +3321,7 @@ // List what spelling syntaxes the attribute supports. OS << ".. csv-table:: Supported Syntaxes\n"; OS << " :header: \"GNU\", \"C++11\", \"__declspec\", \"Keyword\","; - OS << " \"Pragma\"\n\n"; + OS << " \"Pragma\", \"Pragma clang attribute\"\n\n"; OS << " \""; if (SupportedSpellings & GNU) OS << "X"; OS << "\",\""; @@ -3249,6 +3332,9 @@ if (SupportedSpellings & Keyword) OS << "X"; OS << "\", \""; if (SupportedSpellings & Pragma) OS << "X"; + OS << "\", \""; + if (isSupportedByPragmaAttribute(*Doc.Attribute)) + OS << "X"; OS << "\"\n\n"; // If the attribute is deprecated, print a message about it, and possibly