Index: docs/LanguageExtensions.rst =================================================================== --- docs/LanguageExtensions.rst +++ docs/LanguageExtensions.rst @@ -2312,3 +2312,192 @@ 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"), apply_only_to = function) + + 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]], apply_to = function) + + void function(); // The function now has the [[noreturn]] attribute + + #pragma clang attribute pop + +Subject Match Rules +------------------- + +The set of declarations that receive a single attribute from the attribute stack +depends on the subject match rules that were specified in the pragma. Subject +match rules are specified after the attribute. Firstly, the compiler expects an +identifier that corresponds to the subject set specifier. The ``apply_to`` +specifier tells the compiler that the match rules after ``apply_to`` represent a +**strict** subject set. Strict subject sets have to be identical to the +attribute's own subject set, otherwise a compilation error is presented. For +example, an attribute like ``[[noreturn]]`` whose subject set is just +``function``, requires ``function`` match rule to be present after ``apply_to``: + +.. code-block:: c++ + + #pragma clang attribute push([[noreturn]], apply_to = function) + + void function(int param); // The function will receive [[noreturn]] + + #pragma clang attribute pop + + // This is an error, [[noreturn]] must be applied to function: + #pragma clang attribute push([[noreturn]], apply_to = variable) + + void anotherFunction(int param) { + int localVariable = 0; + } + + #pragma clang attribute pop + + // This is an error as well, [[noreturn]] must be applied *only* to function: + #pragma clang attribute push([[noreturn]], apply_to = any(function, variable)) + + void anotherFunction(int param) { + int localVariable = 0; + } + + #pragma clang attribute pop + +The ``apply_to`` specifier should be used whenever one wants to apply an +attribute to all of the declaration that can receive that attribute. The use +of ``apply_to`` ensures that future versions of compiler will report an error +if the attribute will start supporting a new kind of declaration. + +The ``apply_only_to`` subject set specifier is a relaxed version of +``apply_to``. It allows you to specify match rules that form a subset of the +**strict** subject set, i.e. the compiler doesn't require all of the attribute's +subjects. For example, an attribute like ``[[nodiscard]]`` +whose subject set includes ``enum``, ``record`` and ``hasType(functionType)``, +requires the presence of at least one of these rules after ``apply_only_to``: + +.. code-block:: c++ + + #pragma clang attribute push([[nodiscard]], apply_only_to = enum) + + enum Enum1 { A1, B1 }; // The enum will receive [[nodiscard]] + + struct Record1 { }; // The struct will *not* receive [[nodiscard]] + + #pragma clang attribute pop + + #pragma clang attribute push([[nodiscard]], apply_only_to = any(record, enum)) + + enum Enum2 { A2, B2 }; // The enum will receive [[nodiscard]] + + struct Record2 { }; // The struct *will* receive [[nodiscard]] + + #pragma clang attribute pop + + // This is an error, since [[nodiscard]] can't be applied to namespaces: + #pragma clang attribute push([[nodiscard]], apply_to = any(record, namespace)) + + #pragma clang attribute pop + +Attributes like ``annotate`` that can apply to any declaration and don't have +a strict subject set can only be applied using the ``apply_only_to`` subject set +specifier. + +Clang supports the following match rules: + +- ``function``: Can be used to apply attributes to functions and C++ methods, + including operators and constructors/destructors. + +- ``function(is_method)``: Can be used to apply attributes to C++ methods, + including operators and constructors/destructors. + +- ``hasType(functionType)``: Can be used to apply attributes to function, C++ + methods, and variables/fields whose type is a function pointer. It does not + apply attributes to Objective-C methods or blocks! + +- ``type_alias``: Can be used to apply attributes to ``typedef`` declarations + and C++11 type aliases. + +- ``record``: Can be used to apply attributes to ``struct``, ``class`` and + ``union`` declarations. + +- ``record(unless(is_union))``: Can be used to apply attributes only to + ``struct`` and ``class`` declarations. + +- ``enum``: Can be be used to apply attributes to enumeration declarations. + +- ``enum_case``: Can be used to apply attributes to enumerators. + +- ``variable``: Can be used to apply attributes to variables, including + local variables, parameters and global variables. + +- ``variable(is_thread_local)``: Can be used to apply attributes to thread-local + variables only. + +- ``variable(is_global)``: Can be used to apply attributes to global variables + only. + +- ``variable(is_parameter)``: Can be used to apply attributes to parameters + only. + +- ``variable(unless(is_parameter))``: Can be used to apply attributes to all + the variables that are not parameters. + +- ``field``: Can be used to apply attributes to non-static field declarations + in a record. This includes Objective-C ivars. + +- ``namespace``: Can be used to apply attributes to ``namespace`` declarations. + +- ``objc_interface``: Can be used to apply attributes to ``@interface`` + declarations. + +- ``objc_protocol``: Can be used to apply attributes to ``@protocol`` + declarations. + +- ``objc_method``: Can be used to apply attributes to Objective-C methods, + including instance and class methods. Implicit methods like implicit property + getters and setters do not receive the attribute. + +- ``objc_method(is_instance)``: Can be used to apply attributes to Objective-C + instance methods. + +- ``objc_property``: Can be used to apply attributes to ``@property`` + declarations. + +- ``block``: Can be used to apply attributes to block declarations. This does + not include variables/fields of block pointer type. + +Supported Attributes +-------------------- + +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 `. + +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. Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -248,6 +248,8 @@ def CPlusPlus : LangOpt<"CPlusPlus">; def OpenCL : LangOpt<"OpenCL">; def RenderScript : LangOpt<"RenderScript">; +def ObjC : LangOpt<"ObjC1">; +def BlocksSupported : LangOpt<"Blocks">; // Defines targets for target-specific attributes. The list of strings should // specify architectures for which the target applies, based off the ArchType @@ -270,6 +272,98 @@ let CXXABIs = ["Microsoft"]; } +// Attribute subject match rules that are used for #pragma clang attribute. +// +// A instance of AttrSubjectMatcherRule represents an individual match rule. +// An individual match rule can correspond to a number of different attribute +// subjects, e.g. "record" matching rule corresponds to the Record and +// CXXRecord attribute subjects. +// +// Match rules are used in the subject list of the #pragma clang attribute. +// Match rules can have sub-match rules that are instances of +// AttrSubjectMatcherSubRule. A sub-match rule can correspond to a number +// of different attribute subjects, and it can have a negated spelling as well. +// For example, "variable(unless(is_parameter))" matching rule corresponds to +// the NonParmVar attribute subject. +class AttrSubjectMatcherSubRule subjects, + bit negated = 0> { + string Name = name; + list Subjects = subjects; + bit Negated = negated; + // Lists language options, one of which is required to be true for the + // attribute to be applicable. If empty, the language options are taken + // from the parent matcher rule. + list LangOpts = []; +} +class AttrSubjectMatcherRule subjects, + list subrules = []> { + string Name = name; + list Subjects = subjects; + list Constraints = subrules; + // 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 = []; +} + +// function(is_method) +def SubRuleForCXXMethod : AttrSubjectMatcherSubRule<"is_method", [CXXMethod]> { + let LangOpts = [CPlusPlus]; +} +def SubjectMatcherForFunction : AttrSubjectMatcherRule<"function", [Function], [ + SubRuleForCXXMethod +]>; +// hasType is abstract, it should be used with one of the sub-rules. +def SubjectMatcherForType : AttrSubjectMatcherRule<"hasType", [], [ + AttrSubjectMatcherSubRule<"functionType", [FunctionLike]> + + // FIXME: There's a matcher ambiguity with objc methods and blocks since + // functionType excludes them but functionProtoType includes them. + // AttrSubjectMatcherSubRule<"functionProtoType", [HasFunctionProto]> +]>; +def SubjectMatcherForTypedef : AttrSubjectMatcherRule<"type_alias", + [TypedefName]>; +def SubjectMatcherForRecord : AttrSubjectMatcherRule<"record", [Record, + CXXRecord], [ + // unless(is_union) + AttrSubjectMatcherSubRule<"is_union", [Struct], 1> +]>; +def SubjectMatcherForEnum : AttrSubjectMatcherRule<"enum", [Enum]>; +def SubjectMatcherForEnumConstant : AttrSubjectMatcherRule<"enum_case", + [EnumConstant]>; +def SubjectMatcherForVar : AttrSubjectMatcherRule<"variable", [Var], [ + AttrSubjectMatcherSubRule<"is_thread_local", [TLSVar]>, + AttrSubjectMatcherSubRule<"is_global", [GlobalVar]>, + AttrSubjectMatcherSubRule<"is_parameter", [ParmVar]>, + // unless(is_parameter) + AttrSubjectMatcherSubRule<"is_parameter", [NonParmVar], 1> +]>; +def SubjectMatcherForField : AttrSubjectMatcherRule<"field", [Field]>; +def SubjectMatcherForNamespace : AttrSubjectMatcherRule<"namespace", + [Namespace]> { + let LangOpts = [CPlusPlus]; +} +def SubjectMatcherForObjCInterface : AttrSubjectMatcherRule<"objc_interface", + [ObjCInterface]> { + let LangOpts = [ObjC]; +} +def SubjectMatcherForObjCProtocol : AttrSubjectMatcherRule<"objc_protocol", + [ObjCProtocol]> { + let LangOpts = [ObjC]; +} +def SubjectMatcherForObjCMethod : AttrSubjectMatcherRule<"objc_method", + [ObjCMethod], [ + AttrSubjectMatcherSubRule<"is_instance", [ObjCInstanceMethod]> +]> { + let LangOpts = [ObjC]; +} +def SubjectMatcherForObjCProperty : AttrSubjectMatcherRule<"objc_property", + [ObjCProperty]> { + let LangOpts = [ObjC]; +} +def SubjectMatcherForBlock : AttrSubjectMatcherRule<"block", [Block]> { + let LangOpts = [BlocksSupported]; +} + class Attr { // The various ways in which an attribute can be spelled in source list Spellings; @@ -302,6 +396,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' only when: + // - It has documentation. + // - It has a subject list whose subjects can be represented using subject + // match rules. + // - It has GNU/CXX11 spelling and doesn't require delayed parsing. + 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 +567,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/AttrSubjectMatchRules.h =================================================================== --- /dev/null +++ include/clang/Basic/AttrSubjectMatchRules.h @@ -0,0 +1,47 @@ +//===-- AttrSubjectMatchRules.h - Attribute subject match rules -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_ATTR_SUBJECT_MATCH_RULES_H +#define LLVM_CLANG_BASIC_ATTR_SUBJECT_MATCH_RULES_H + +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/DenseMap.h" + +namespace clang { + +namespace attr { + +/// \brief A list of all the recognized kinds of attributes. +enum SubjectMatchRule { +#define ATTR_MATCH_RULE(X, Spelling, IsAbstract) X, +#include "clang/Basic/AttrSubMatchRulesList.inc" +}; + +const char *getSubjectMatchRuleSpelling(SubjectMatchRule Rule); + +using ParsedSubjectMatchRuleSet = llvm::DenseMap; + +} // end namespace attr +} // end namespace clang + +namespace llvm { + +template <> +struct DenseMapInfo : DenseMapInfo { + static inline clang::attr::SubjectMatchRule getEmptyKey() { + return (clang::attr::SubjectMatchRule)DenseMapInfo::getEmptyKey(); + } + static inline clang::attr::SubjectMatchRule getTombstoneKey() { + return (clang::attr::SubjectMatchRule)DenseMapInfo::getTombstoneKey(); + } +}; + +} // end namespace llvm + +#endif Index: include/clang/Basic/CMakeLists.txt =================================================================== --- include/clang/Basic/CMakeLists.txt +++ include/clang/Basic/CMakeLists.txt @@ -28,6 +28,11 @@ SOURCE Attr.td TARGET ClangAttrList) +clang_tablegen(AttrSubMatchRulesList.inc -gen-clang-attr-subject-match-rule-list + -I ${CMAKE_CURRENT_SOURCE_DIR}/../../ + SOURCE Attr.td + TARGET ClangAttrSubjectMatchRuleList) + clang_tablegen(AttrHasAttributeImpl.inc -gen-clang-attr-has-attribute-impl -I ${CMAKE_CURRENT_SOURCE_DIR}/../../ SOURCE Attr.td Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -974,6 +974,42 @@ "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_pragma_attribute_invalid_subject_set_specifier : Error< + "expected attribute subject set specifier 'apply_to' or 'apply_only_to'">; +def err_pragma_attribute_expected_subject_identifier : Error< + "expected an identifier that corresponds to an attribute subject rule">; +def err_pragma_attribute_unknown_subject_rule : Error< + "unknown attribute subject rule '%0'">; +def err_pragma_attribute_expected_subject_sub_identifier_supported : Error< + "expected an identifier that corresponds to an attribute subject matcher " + "sub-rule; '%0' matcher supports the following sub-rules: %1">; +def err_pragma_attribute_expected_subject_sub_identifier_unsupported : Error< + "expected an identifier that corresponds to an attribute subject matcher " + "sub-rule; '%0' matcher does not support sub-rules">; +def err_pragma_attribute_unknown_subject_sub_rule_supported : Error< + "unknown attribute subject matcher sub-rule '%0'; '%1' matcher supports " + "the following sub-rules: %2">; +def err_pragma_attribute_unknown_subject_sub_rule_unsupported : Error< + "invalid use of attribute subject matcher sub-rule '%0'; '%1' matcher " + "does not support sub-rules">; +def err_pragma_attribute_duplicate_subject : Error< + "duplicate attribute subject matcher '%0'">; 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,24 @@ def err_pragma_loop_precedes_nonloop : Error< "expected a for, while, or do-while loop to follow '%0'">; +def err_pragma_attribute_invalid_strict_subject_specifier : Error< + "use of 'apply_to' subject set specifier for an attribute without a strict " + "subject set">; +def note_pragma_attribute_use_apply_only_to_instead : Note< + "use 'apply_only_to' instead">; +def err_pragma_attribute_matcher_subrule_contradicts_rule : Error< + "redundant attribute subject matcher sub-rule '%0'; '%1' already matches " + "those declarations">; +def err_pragma_attribute_matcher_negated_subrule_contradicts_subrule : Error< + "negated attribute subject matcher sub-rule '%0' contradicts sub-rule '%1'">; +def err_pragma_attribute_invalid_matchers : Error< + "attribute %0 can't be applied to %1">; +def err_pragma_attribute_missing_matchers : Error< + "attribute %0 must also apply to %1">; +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/CMakeLists.txt =================================================================== --- include/clang/Parse/CMakeLists.txt +++ include/clang/Parse/CMakeLists.txt @@ -2,3 +2,9 @@ -I ${CMAKE_CURRENT_SOURCE_DIR}/../../ SOURCE ../Basic/Attr.td TARGET ClangAttrParserStringSwitches) + +clang_tablegen(AttrSubMatchRulesParserStringSwitches.inc + -gen-clang-attr-subject-match-rules-parser-string-switches + -I ${CMAKE_CURRENT_SOURCE_DIR}/../../ + SOURCE ../Basic/Attr.td + TARGET ClangAttrSubMatchRulesParserStringSwitches) Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -183,6 +183,7 @@ std::unique_ptr LoopHintHandler; std::unique_ptr UnrollHintHandler; std::unique_ptr NoUnrollHintHandler; + std::unique_ptr AttributePragmaHandler; std::unique_ptr CommentSemaHandler; @@ -560,6 +561,12 @@ /// #pragma clang loop and #pragma unroll. bool HandlePragmaLoopHint(LoopHint &Hint); + bool ParsePragmaAttributeSubjectMatchRuleSet( + attr::ParsedSubjectMatchRuleSet &SubjectMatchRules, + SourceLocation &AnyLoc, SourceLocation &LastMatchRuleEndLoc); + + 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 @@ -15,6 +15,7 @@ #ifndef LLVM_CLANG_SEMA_ATTRIBUTELIST_H #define LLVM_CLANG_SEMA_ATTRIBUTELIST_H +#include "clang/Basic/AttrSubjectMatchRules.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/VersionTuple.h" @@ -509,9 +510,14 @@ unsigned getMaxArgs() const; bool hasVariadicArg() const; bool diagnoseAppertainsTo(class Sema &S, const Decl *D) const; + bool appliesToDecl(const Decl *D, attr::SubjectMatchRule MatchRule) const; + void getMatchRules(class Sema &S, + SmallVectorImpl> + &MatchRules) 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 +780,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,15 @@ /// 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; + SmallVector MatchRules; + 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. @@ -8132,6 +8141,22 @@ /// the appropriate attribute. void AddCFAuditedAttribute(Decl *D); + /// \brief Called on well formed '\#pragma clang attribute push'. + void ActOnPragmaAttributePush(AttributeList &Attribute, + SourceLocation PragmaLoc, + attr::ParsedSubjectMatchRuleSet Rules, + SourceLocation SubjectSetSpecifierLoc, + SourceLocation AnyLoc, + SourceLocation LastMatchRuleEndLoc, + bool IsStrict); + + /// \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/Basic/Attributes.cpp =================================================================== --- lib/Basic/Attributes.cpp +++ lib/Basic/Attributes.cpp @@ -1,4 +1,5 @@ #include "clang/Basic/Attributes.h" +#include "clang/Basic/AttrSubjectMatchRules.h" #include "clang/Basic/IdentifierTable.h" #include "llvm/ADT/StringSwitch.h" using namespace clang; @@ -15,3 +16,13 @@ return 0; } + +const char *attr::getSubjectMatchRuleSpelling(attr::SubjectMatchRule Rule) { + switch (Rule) { +#define ATTR_MATCH_RULE(NAME, SPELLING, IsAbstract) \ + case attr::NAME: \ + return SPELLING; +#include "clang/Basic/AttrSubMatchRulesList.inc" + } + llvm_unreachable("Invalid subject match rule"); +} 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,291 @@ return true; } +namespace { +struct PragmaAttributeInfo { + enum ActionType { Push, Pop }; + ParsedAttributes &Attributes; + ActionType Action; + ArrayRef Tokens; + + PragmaAttributeInfo(ParsedAttributes &Attributes) : Attributes(Attributes) {} +}; + +#include "clang/Parse/AttrSubMatchRulesParserStringSwitches.inc" + +} // end anonymous namespace + +static StringRef getIdentifier(const Token &Tok) { + if (Tok.is(tok::identifier)) + return Tok.getIdentifierInfo()->getName(); + const char *S = tok::getKeywordSpelling(Tok.getKind()); + if (!S) + return ""; + return S; +} + +static bool isAbstractAttrMatcherRule(attr::SubjectMatchRule Rule) { + using namespace attr; + switch (Rule) { +#define ATTR_MATCH_RULE(Value, Spelling, IsAbstract) \ + case Value: \ + return IsAbstract; +#include "clang/Basic/AttrSubMatchRulesList.inc" + } +} + +bool Parser::ParsePragmaAttributeSubjectMatchRuleSet( + attr::ParsedSubjectMatchRuleSet &SubjectMatchRules, SourceLocation &AnyLoc, + SourceLocation &LastMatchRuleEndLoc) { + bool IsAny = false; + BalancedDelimiterTracker AnyParens(*this, tok::l_paren); + if (getIdentifier(Tok) == "any") { + AnyLoc = ConsumeToken(); + IsAny = true; + if (AnyParens.expectAndConsume()) + return true; + } + + do { + // Parse the subject matcher rule. + StringRef Name = getIdentifier(Tok); + if (Name.empty()) { + Diag(Tok, diag::err_pragma_attribute_expected_subject_identifier); + return true; + } + auto Rule = isAttributeSubjectMatchRule(Name); + if (!Rule.first) { + Diag(Tok, diag::err_pragma_attribute_unknown_subject_rule) << Name; + return true; + } + attr::SubjectMatchRule PrimaryRule = *Rule.first; + SourceLocation RuleLoc = ConsumeToken(); + + BalancedDelimiterTracker Parens(*this, tok::l_paren); + if (isAbstractAttrMatcherRule(PrimaryRule)) { + if (Parens.expectAndConsume()) + return true; + } else if (Parens.consumeOpen()) { + if (!SubjectMatchRules + .insert( + std::make_pair(PrimaryRule, SourceRange(RuleLoc, RuleLoc))) + .second) + Diag(RuleLoc, diag::err_pragma_attribute_duplicate_subject) + << Name + << FixItHint::CreateRemoval(SourceRange( + RuleLoc, Tok.is(tok::comma) ? Tok.getLocation() : RuleLoc)); + LastMatchRuleEndLoc = RuleLoc; + continue; + } + + // Parse the sub-rules. + StringRef SubRuleName = getIdentifier(Tok); + if (SubRuleName.empty()) { + if (const char *SubRules = + validAttributeSubjectMatchSubRules(PrimaryRule)) + Diag(Tok, + diag:: + err_pragma_attribute_expected_subject_sub_identifier_supported) + << Name << SubRules; + else + Diag( + Tok, + diag:: + err_pragma_attribute_expected_subject_sub_identifier_unsupported) + << Name; + return true; + } + attr::SubjectMatchRule SubRule; + if (SubRuleName == "unless") { + SourceLocation SubRuleLoc = ConsumeToken(); + BalancedDelimiterTracker Parens(*this, tok::l_paren); + if (Parens.expectAndConsume()) + return true; + SubRuleName = getIdentifier(Tok); + if (SubRuleName.empty()) { + if (const char *SubRules = + validAttributeSubjectMatchSubRules(PrimaryRule)) + Diag( + SubRuleLoc, + diag:: + err_pragma_attribute_expected_subject_sub_identifier_supported) + << Name << SubRules; + else + Diag( + SubRuleLoc, + diag:: + err_pragma_attribute_expected_subject_sub_identifier_unsupported) + << Name; + return true; + } + auto SubRuleOrNone = Rule.second(SubRuleName, /*IsUnless=*/true); + if (!SubRuleOrNone) { + std::string SubRuleUnlessName = "unless(" + SubRuleName.str() + ")"; + if (const char *SubRules = + validAttributeSubjectMatchSubRules(PrimaryRule)) + Diag(SubRuleLoc, + diag::err_pragma_attribute_unknown_subject_sub_rule_supported) + << SubRuleUnlessName << Name << SubRules; + else + Diag(SubRuleLoc, + diag::err_pragma_attribute_unknown_subject_sub_rule_unsupported) + << SubRuleUnlessName << Name; + return true; + } + SubRule = *SubRuleOrNone; + ConsumeToken(); + if (Parens.consumeClose()) + return true; + } else { + auto SubRuleOrNone = Rule.second(SubRuleName, /*IsUnless=*/false); + if (!SubRuleOrNone) { + if (const char *SubRules = + validAttributeSubjectMatchSubRules(PrimaryRule)) + Diag(Tok, + diag::err_pragma_attribute_unknown_subject_sub_rule_supported) + << SubRuleName << Name << SubRules; + else + Diag(Tok, + diag::err_pragma_attribute_unknown_subject_sub_rule_unsupported) + << SubRuleName << Name; + return true; + } + SubRule = *SubRuleOrNone; + ConsumeToken(); + } + SourceLocation RuleEndLoc = Tok.getLocation(); + LastMatchRuleEndLoc = RuleEndLoc; + if (Parens.consumeClose()) + return true; + if (!SubjectMatchRules + .insert(std::make_pair(SubRule, SourceRange(RuleLoc, RuleEndLoc))) + .second) { + Diag(RuleLoc, diag::err_pragma_attribute_duplicate_subject) + << attr::getSubjectMatchRuleSpelling(SubRule) + << FixItHint::CreateRemoval(SourceRange( + RuleLoc, Tok.is(tok::comma) ? Tok.getLocation() : RuleEndLoc)); + continue; + } + } while (IsAny && TryConsumeToken(tok::comma)); + + if (IsAny) + if (AnyParens.consumeClose()) + return true; + + return false; +} + +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, StopBeforeMatch); + 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); + } + + // Parse the subject-list. + if (ExpectAndConsume(tok::comma)) { + SkipUntil(tok::eof, StopBeforeMatch); + ConsumeToken(); + return; + } + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_pragma_attribute_invalid_subject_set_specifier); + SkipUntil(tok::eof, StopBeforeMatch); + ConsumeToken(); + return; + } + const IdentifierInfo *II = Tok.getIdentifierInfo(); + if (!II->isStr("apply_to") && !II->isStr("apply_only_to")) { + Diag(Tok, diag::err_pragma_attribute_invalid_subject_set_specifier); + SkipUntil(tok::eof, StopBeforeMatch); + ConsumeToken(); + return; + } + bool IsStrict = II->isStr("apply_to"); + SourceLocation SubjectSetSpecifierLoc = ConsumeToken(); + + if (ExpectAndConsume(tok::equal)) { + SkipUntil(tok::eof, StopBeforeMatch); + ConsumeToken(); + return; + } + + attr::ParsedSubjectMatchRuleSet SubjectMatchRules; + SourceLocation AnyLoc, LastMatchRuleEndLoc; + if (ParsePragmaAttributeSubjectMatchRuleSet(SubjectMatchRules, AnyLoc, + LastMatchRuleEndLoc)) { + SkipUntil(tok::eof, StopBeforeMatch); + ConsumeToken(); + return; + } + + // 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, StopBeforeMatch); + 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, std::move(SubjectMatchRules), + SubjectSetSpecifierLoc, AnyLoc, LastMatchRuleEndLoc, IsStrict); +} + // #pragma GCC visibility comes in two variants: // 'push' '(' [visibility] ')' // 'pop' @@ -2246,3 +2548,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, subject-set) +/// #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 @@ -607,6 +607,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 @@ -16,6 +16,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" +#include "clang/Basic/AttrSubjectMatchRules.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/TargetInfo.h" #include "clang/Sema/SemaInternal.h" @@ -160,12 +161,16 @@ unsigned IsType : 1; unsigned IsStmt : 1; unsigned IsKnownToGCC : 1; + unsigned IsSupportedByPragmaAttribute : 1; bool (*DiagAppertainsToDecl)(Sema &S, const AttributeList &Attr, const Decl *); bool (*DiagLangOpts)(Sema &S, const AttributeList &Attr); bool (*ExistsInTarget)(const TargetInfo &Target); unsigned (*SpellingIndexToSemanticSpelling)(const AttributeList &Attr); + void (*GetPragmaAttributeMatchRules)( + llvm::SmallVectorImpl> &Rules, + const LangOptions &LangOpts); }; namespace { @@ -192,6 +197,19 @@ return getInfo(*this).DiagAppertainsToDecl(S, *this, D); } +bool AttributeList::appliesToDecl(const Decl *D, + attr::SubjectMatchRule MatchRule) const { + return checkAttributeMatchRuleAppliesTo(D, MatchRule); +} + +void AttributeList::getMatchRules( + Sema &S, + SmallVectorImpl> &MatchRules) + const { + return getInfo(*this).GetPragmaAttributeMatchRules(MatchRules, + S.getLangOpts()); +} + bool AttributeList::diagnoseLangOpts(Sema &S) const { return getInfo(*this).DiagLangOpts(S, *this); } @@ -216,6 +234,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,281 @@ D->addAttr(CFAuditedTransferAttr::CreateImplicit(Context, Loc)); } +namespace { + +Optional +getParentAttrMatcherRule(attr::SubjectMatchRule Rule) { + using namespace attr; + switch (Rule) { + default: + return None; +#define ATTR_MATCH_RULE(Value, Spelling, IsAbstract) +#define ATTR_MATCH_SUB_RULE(Value, Spelling, IsAbstract, Parent, IsNegated) \ + case Value: \ + return Parent; +#include "clang/Basic/AttrSubMatchRulesList.inc" + } +} + +bool isNegatedAttrMatcherSubRule(attr::SubjectMatchRule Rule) { + using namespace attr; + switch (Rule) { + default: + return false; +#define ATTR_MATCH_RULE(Value, Spelling, IsAbstract) +#define ATTR_MATCH_SUB_RULE(Value, Spelling, IsAbstract, Parent, IsNegated) \ + case Value: \ + return IsNegated; +#include "clang/Basic/AttrSubMatchRulesList.inc" + } +} + +} // end anonymous namespace + +static CharSourceRange replacementRangeForListElement(const Sema &S, + SourceRange Range) { + // Make sure that the ',' is removed as well. + SourceLocation AfterCommaLoc = Lexer::findLocationAfterToken( + Range.getEnd(), tok::comma, S.getSourceManager(), S.getLangOpts(), + /*SkipTrailingWhitespaceAndNewLine=*/false); + if (AfterCommaLoc.isValid()) + return CharSourceRange::getCharRange(Range.getBegin(), AfterCommaLoc); + else + return CharSourceRange::getTokenRange(Range); +} + +static std::string +attrMatcherRuleListToString(ArrayRef Rules) { + std::string Result; + llvm::raw_string_ostream OS(Result); + for (const auto &I : llvm::enumerate(Rules)) { + if (I.Index) + OS << (I.Index == Rules.size() - 1 ? " and " : ", "); + OS << "'" << attr::getSubjectMatchRuleSpelling(I.Value) << "'"; + } + return OS.str(); +} + +static std::string +attrMatcherRuleListToCodeString(ArrayRef Rules) { + std::string Result; + llvm::raw_string_ostream OS(Result); + OS << ", "; + for (const auto &I : llvm::enumerate(Rules)) { + if (I.Index) + OS << ", "; + OS << attr::getSubjectMatchRuleSpelling(I.Value); + } + return OS.str(); +} + +void Sema::ActOnPragmaAttributePush(AttributeList &Attribute, + SourceLocation PragmaLoc, + attr::ParsedSubjectMatchRuleSet Rules, + SourceLocation SubjectSetSpecifierLoc, + SourceLocation AnyLoc, + SourceLocation LastMatchRuleEndLoc, + bool IsStrict) { + SmallVector SubjectMatchRules; + // Gather the subject match rules that are supported by the attribute. + SmallVector, 4> + StrictSubjectMatchRuleSet; + Attribute.getMatchRules(*this, StrictSubjectMatchRuleSet); + + // Figure out which subject matching rules are valid. + if (StrictSubjectMatchRuleSet.empty()) { + // This is an attribute like 'annotate' which doesn't have a strict subject + // set. + if (IsStrict) { + SourceRange SpecifierRange = SubjectSetSpecifierLoc; + Diag(PragmaLoc, + diag::err_pragma_attribute_invalid_strict_subject_specifier) + << SpecifierRange; + Diag(SubjectSetSpecifierLoc, + diag::note_pragma_attribute_use_apply_only_to_instead) + << FixItHint::CreateReplacement(SpecifierRange, "apply_only_to"); + // Assume that the non-strict version was used and keep going. + IsStrict = false; + } + + // Check for contradicting match rules. Contradicting match rules are + // either: + // - a top-level rule and one of its sub-rules. E.g. variable and + // variable(is_parameter). + // - a sub-rule and a sibling that's negated. E.g. + // variable(is_thread_local) and variable(unless(is_parameter)) + llvm::SmallDenseMap, 2> + RulesToFirstSpecifiedNegatedSubRule; + for (const auto &Rule : Rules) { + auto ParentRule = getParentAttrMatcherRule(Rule.first); + if (!ParentRule) + continue; + auto It = Rules.find(*ParentRule); + if (It != Rules.end()) { + // A sub-rule contradicts a parent rule. + Diag(Rule.second.getBegin(), + diag::err_pragma_attribute_matcher_subrule_contradicts_rule) + << attr::getSubjectMatchRuleSpelling(Rule.first) + << attr::getSubjectMatchRuleSpelling(*ParentRule) << It->second + << FixItHint::CreateRemoval( + replacementRangeForListElement(*this, Rule.second)); + // Keep going without removing this rule as it won't change the set of + // declarations that receive the attribute. + continue; + } + if (isNegatedAttrMatcherSubRule(Rule.first)) + RulesToFirstSpecifiedNegatedSubRule.insert( + std::make_pair(*ParentRule, Rule)); + } + bool IgnoreNegatedSubRules = false; + for (const auto &Rule : Rules) { + auto ParentRule = getParentAttrMatcherRule(Rule.first); + if (!ParentRule) + continue; + auto It = RulesToFirstSpecifiedNegatedSubRule.find(*ParentRule); + if (It != RulesToFirstSpecifiedNegatedSubRule.end() && + It->second != Rule) { + // Negated sub-rule contradicts another sub-rule. + Diag( + It->second.second.getBegin(), + diag:: + err_pragma_attribute_matcher_negated_subrule_contradicts_subrule) + << attr::getSubjectMatchRuleSpelling(It->second.first) + << attr::getSubjectMatchRuleSpelling(Rule.first) << Rule.second + << FixItHint::CreateRemoval( + replacementRangeForListElement(*this, It->second.second)); + // Keep going but ignore all of the negated sub-rules. + IgnoreNegatedSubRules = true; + RulesToFirstSpecifiedNegatedSubRule.erase(It); + } + } + + if (!IgnoreNegatedSubRules) { + for (const auto &Rule : Rules) + SubjectMatchRules.push_back(Rule.first); + } else { + for (const auto &Rule : Rules) { + if (!isNegatedAttrMatcherSubRule(Rule.first)) + SubjectMatchRules.push_back(Rule.first); + } + } + Rules.clear(); + } else if (IsStrict) { + SmallVector MissingRules; + SourceLocation FirstRuleLoc; + for (const auto &Rule : StrictSubjectMatchRuleSet) { + auto It = Rules.find(Rule.first); + if (It != Rules.end()) { + FirstRuleLoc = It->second.getBegin(); + Rules.erase(It); + // Add the rule to the set of attribute receivers only if it's supported + // in the current language mode. + if (Rule.second) + SubjectMatchRules.push_back(Rule.first); + continue; + } + // This rule is not part of pragma's subject set when it should be. + // Ensure that the missing rule is reported as an error only when it's + // supported in the current language mode. + if (Rule.second) + MissingRules.push_back(Rule.first); + } + if (!MissingRules.empty()) { + auto Diagnostic = + Diag(PragmaLoc, diag::err_pragma_attribute_missing_matchers) + << Attribute.getName() << attrMatcherRuleListToString(MissingRules); + // Insert the diagnostics. + if (AnyLoc.isInvalid()) + Diagnostic << FixItHint::CreateInsertion(FirstRuleLoc, "any("); + auto MissingCode = attrMatcherRuleListToCodeString(MissingRules); + if (AnyLoc.isInvalid()) + MissingCode += ")"; + Diagnostic << FixItHint::CreateInsertion( + Lexer::getLocForEndOfToken(LastMatchRuleEndLoc, 0, SourceMgr, + LangOpts), + MissingCode); + // Keep going but pretend the missing rules where specified. + for (const auto &Rule : MissingRules) + SubjectMatchRules.push_back(Rule); + } + } else { + for (const auto &Rule : StrictSubjectMatchRuleSet) { + if (Rules.erase(Rule.first)) { + // Add the rule to the set of attribute receivers only if it's supported + // in the current language mode. + if (Rule.second) + SubjectMatchRules.push_back(Rule.first); + } + } + } + + if (!Rules.empty()) { + auto Diagnostic = + Diag(PragmaLoc, diag::err_pragma_attribute_invalid_matchers) + << Attribute.getName(); + SmallVector ExtraRules; + for (const auto &Rule : Rules) { + ExtraRules.push_back(Rule.first); + Diagnostic << FixItHint::CreateRemoval( + replacementRangeForListElement(*this, Rule.second)); + } + Diagnostic << attrMatcherRuleListToString(ExtraRules); + } + + PragmaAttributeStack.push_back( + {&Attribute, std::move(SubjectMatchRules), /*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. + bool Applies = false; + for (const auto &Rule : Entry.MatchRules) { + if (Entry.Attribute->appliesToDecl(D, Rule)) { + Applies = true; + break; + } + } + if (!Applies) + continue; + + // The first time the attribute is applied we also check if this attribute + // is valid. + if (!Entry.IsChecked) { + 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)) @@ -8802,6 +8804,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() && @@ -11441,6 +11446,7 @@ IdResolver.AddDecl(New); ProcessDeclAttributes(S, New, D); + AddPragmaAttributes(S, New); if (D.getDeclSpec().isModulePrivateSpecified()) Diag(New->getLocation(), diag::err_module_private_local) @@ -13657,6 +13663,7 @@ if (Attr) ProcessDeclAttributeList(S, New, Attr); + AddPragmaAttributes(S, New); // If this has an identifier, add it to the scope stack. if (TUK == TUK_Friend) { @@ -14217,6 +14224,7 @@ if (NewFD->hasAttrs()) CheckAlignasUnderalignment(NewFD); } + AddPragmaAttributes(getCurScope(), NewFD); // In auto-retain/release, infer strong retension for fields of // retainable type. @@ -14390,6 +14398,7 @@ // Process attributes attached to the ivar. ProcessDeclAttributes(S, NewID, D); + AddPragmaAttributes(S, NewID); if (D.isInvalidType()) NewID->setInvalidDecl(); @@ -15170,6 +15179,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 @@ -8448,6 +8448,7 @@ Namespc->setInvalidDecl(); ProcessDeclAttributeList(DeclRegionScope, Namespc, AttrList); + AddPragmaAttributes(DeclRegionScope, Namespc); // FIXME: Should we be merging attributes? if (const VisibilityAttr *Attr = Namespc->getAttr()) @@ -9934,6 +9935,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/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -12325,6 +12325,7 @@ // Finally we can process decl attributes. ProcessDeclAttributes(CurScope, CurBlock->TheDecl, ParamInfo); + AddPragmaAttributes(CurScope, CurBlock->TheDecl); // Put the parameter variables in scope. for (auto AI : CurBlock->TheDecl->parameters()) { 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/FixIt/fixit-pragma-attribute.cpp =================================================================== --- /dev/null +++ test/FixIt/fixit-pragma-attribute.cpp @@ -0,0 +1,58 @@ +// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits test %s 2>&1 | FileCheck %s + +#pragma clang attribute push(annotate("test"), apply_only_to = any( enum, function, function, namespace, function )) +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:85-[[@LINE-1]]:95}:"" +// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:106-[[@LINE-2]]:115}:"" + +#pragma clang attribute push(annotate("test"), apply_only_to = any( variable(is_global), function, variable(is_global), variable(is_global) )) +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:100-[[@LINE-1]]:121}:"" +// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:121-[[@LINE-2]]:141}:"" + +#pragma clang attribute push (annotate("recoverStrict"), apply_to = any(function, variable)) +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:58-[[@LINE-1]]:66}:"apply_only_to" + +#pragma clang attribute pop + +#pragma clang attribute push (annotate("subRuleContradictions"), apply_only_to = any(variable, variable(is_parameter), function(is_method), variable(is_global))) +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:96-[[@LINE-1]]:120}:"" +// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:141-[[@LINE-2]]:160}:"" + +#pragma clang attribute pop + +#pragma clang attribute push (annotate("subRuleContradictions2"), apply_only_to = any(function(is_method),function)) +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:87-[[@LINE-1]]:107}:"" + +#pragma clang attribute pop + +#pragma clang attribute push (annotate("negatedSubRuleContradictions1"), apply_only_to = any(variable(is_parameter), variable(unless(is_parameter)))) +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:118-[[@LINE-1]]:148}:"" +#pragma clang attribute pop + +#pragma clang attribute push (annotate("negatedSubRuleContradictions2"), apply_only_to = any(variable(unless(is_parameter)), variable(is_thread_local), function, variable(is_global))) +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:94-[[@LINE-1]]:125}:"" +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_to = any(function)) +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:68-[[@LINE-1]]:68}:", record(unless(is_union)), variable" +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_to = any(function, record(unless(is_union)))) +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:94-[[@LINE-1]]:94}:", variable" +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_to = any(variable, record(unless(is_union)))) +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:94-[[@LINE-1]]:94}:", function" +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_to = any(record(unless(is_union)))) +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:84-[[@LINE-1]]:84}:", variable, function" +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_to = variable) +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:56-[[@LINE-1]]:56}:"any(" +// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:64-[[@LINE-2]]:64}:", record(unless(is_union)), function)" +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_only_to = any(enum, variable)) +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:65-[[@LINE-1]]:70}:"" +#pragma clang attribute pop Index: test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- /dev/null +++ test/Misc/pragma-attribute-supported-attributes-list.test @@ -0,0 +1,60 @@ +// RUN: clang-tblgen -gen-clang-test-pragma-attribute-supported-attributes -I%src_include_dir %src_include_dir/clang/Basic/Attr.td -o - | FileCheck %s + +// The number of supported attributes should never go down! + +// CHECK: #pragma clang attribute supports 55 attributes: +// CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) +// CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) +// CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) +// CHECK-NEXT: AMDGPUWavesPerEU (SubjectMatchRule_function) +// CHECK-NEXT: AVRSignal (SubjectMatchRule_function) +// CHECK-NEXT: AbiTag (SubjectMatchRule_record_not_is_union, SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_namespace) +// CHECK-NEXT: AlignValue (SubjectMatchRule_variable, SubjectMatchRule_type_alias) +// CHECK-NEXT: AllocSize (SubjectMatchRule_function) +// CHECK-NEXT: Annotate () +// CHECK-NEXT: AssumeAligned (SubjectMatchRule_objc_method, SubjectMatchRule_function) +// CHECK-NEXT: CXX11NoReturn (SubjectMatchRule_function) +// CHECK-NEXT: CallableWhen (SubjectMatchRule_function_is_method) +// CHECK-NEXT: CarriesDependency (SubjectMatchRule_variable_is_parameter, SubjectMatchRule_objc_method, SubjectMatchRule_function) +// CHECK-NEXT: Consumable (SubjectMatchRule_record) +// CHECK-NEXT: Convergent (SubjectMatchRule_function) +// CHECK-NEXT: DLLExport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface) +// CHECK-NEXT: DLLImport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface) +// CHECK-NEXT: DisableTailCalls (SubjectMatchRule_function, SubjectMatchRule_objc_method) +// CHECK-NEXT: EnableIf (SubjectMatchRule_function) +// CHECK-NEXT: FlagEnum (SubjectMatchRule_enum) +// CHECK-NEXT: Flatten (SubjectMatchRule_function) +// CHECK-NEXT: IFunc (SubjectMatchRule_function) +// CHECK-NEXT: InternalLinkage (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record) +// CHECK-NEXT: LTOVisibilityPublic (SubjectMatchRule_record) +// CHECK-NEXT: NoDebug (SubjectMatchRule_hasType_functionType, SubjectMatchRule_objc_method, SubjectMatchRule_variable_not_is_parameter) +// CHECK-NEXT: NoDuplicate (SubjectMatchRule_function) +// CHECK-NEXT: NoSanitize (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_variable_is_global) +// CHECK-NEXT: NoSanitizeSpecific (SubjectMatchRule_function, SubjectMatchRule_variable_is_global) +// CHECK-NEXT: NoSplitStack (SubjectMatchRule_function) +// CHECK-NEXT: NotTailCalled (SubjectMatchRule_function) +// CHECK-NEXT: ObjCBoxable (SubjectMatchRule_record) +// CHECK-NEXT: ObjCMethodFamily (SubjectMatchRule_objc_method) +// CHECK-NEXT: ObjCRequiresSuper (SubjectMatchRule_objc_method) +// CHECK-NEXT: ObjCRuntimeName (SubjectMatchRule_objc_interface, SubjectMatchRule_objc_protocol) +// CHECK-NEXT: ObjCRuntimeVisible (SubjectMatchRule_objc_interface) +// CHECK-NEXT: ObjCSubclassingRestricted (SubjectMatchRule_objc_interface) +// CHECK-NEXT: OpenCLNoSVM (SubjectMatchRule_variable) +// CHECK-NEXT: OptimizeNone (SubjectMatchRule_function, SubjectMatchRule_objc_method) +// CHECK-NEXT: Overloadable (SubjectMatchRule_function) +// CHECK-NEXT: ParamTypestate (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: PassObjectSize (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: RenderScriptKernel (SubjectMatchRule_function) +// CHECK-NEXT: RequireConstantInit (SubjectMatchRule_variable_is_global) +// CHECK-NEXT: ReturnTypestate (SubjectMatchRule_function, SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: ReturnsNonNull (SubjectMatchRule_objc_method, SubjectMatchRule_function) +// CHECK-NEXT: Section (SubjectMatchRule_function, SubjectMatchRule_variable_is_global, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property) +// CHECK-NEXT: SetTypestate (SubjectMatchRule_function_is_method) +// CHECK-NEXT: SwiftContext (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: SwiftErrorResult (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: SwiftIndirectResult (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: TLSModel (SubjectMatchRule_variable_is_thread_local) +// CHECK-NEXT: Target (SubjectMatchRule_function) +// CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_method) +// CHECK-NEXT: WarnUnusedResult (SubjectMatchRule_objc_method, SubjectMatchRule_enum, SubjectMatchRule_record, SubjectMatchRule_hasType_functionType) +// CHECK-NEXT: XRayInstrument (SubjectMatchRule_function_is_method, SubjectMatchRule_objc_method, SubjectMatchRule_function) Index: test/Parser/pragma-attribute.cpp =================================================================== --- /dev/null +++ test/Parser/pragma-attribute.cpp @@ -0,0 +1,154 @@ +// RUN: %clang_cc1 -verify -std=c++11 %s + +#pragma clang attribute push(annotate("test"), apply_only_to = function) + +void function(); + +#pragma clang attribute pop + +#pragma clang attribute push(annotate("test"), apply_only_to = any(variable(is_parameter), function)) +#pragma clang attribute pop + +#pragma clang attribute push(annotate("test"), apply_only_to = variable(unless(is_parameter))) +#pragma clang attribute pop + +#pragma clang attribute push(annotate("test"), apply_only_to = any(variable(unless(is_parameter)))) +#pragma clang attribute pop + +#pragma clang attribute push(annotate("test"), = any(function)) // expected-error {{expected attribute subject set specifier 'apply_to' or 'apply_only_to'}} +#pragma clang attribute push(annotate("test"), = function) // expected-error {{expected attribute subject set specifier 'apply_to' or 'apply_only_to'}} +#pragma clang attribute push(annotate("test"), any(function)) // expected-error {{expected attribute subject set specifier 'apply_to' or 'apply_only_to'}} +#pragma clang attribute push(annotate("test"), function) // expected-error {{expected attribute subject set specifier 'apply_to' or 'apply_only_to'}} +#pragma clang attribute push(annotate("test"), apply = any(function )) // expected-error {{expected attribute subject set specifier 'apply_to' or 'apply_only_to'}} +#pragma clang attribute push(annotate("test"), to = function) // expected-error {{expected attribute subject set specifier 'apply_to' or 'apply_only_to'}} + +#pragma clang attribute push(annotate("test"), apply_to any(function)) // expected-error {{expected '='}} +#pragma clang attribute push(annotate("test"), apply_only_to function) // expected-error {{expected '='}} +#pragma clang attribute push(annotate("test"), apply_to) // expected-error {{expected '='}} + +#pragma clang attribute push(annotate("test"), apply_to = any) // expected-error {{expected '('}} +#pragma clang attribute push(annotate("test"), apply_to = any {) // expected-error {{expected '('}} +#pragma clang attribute push(annotate("test"), apply_to = any function) // expected-error {{expected '('}} + +#pragma clang attribute push(annotate("test"), apply_to = { function, enum }) // expected-error {{expected an identifier that corresponds to an attribute subject rule}} + +#pragma clang attribute push(annotate("test"), apply_to = any(function ) // expected-error {{expected ')'}} +#pragma clang attribute push(annotate("test"), apply_to = any(function, )) // expected-error {{expected an identifier that corresponds to an attribute subject rule}} +#pragma clang attribute push(annotate("test"), apply_to = any( function, enum ) // expected-error {{expected ')'}} + +#pragma clang attribute push(annotate("test"), apply_to = () ) // expected-error {{expected an identifier that corresponds to an attribute subject rule}} + +#pragma clang attribute push(annotate("test"), apply_to = any( + ) ) // expected-error {{expected an identifier that corresponds to an attribute subject rule}} +#pragma clang attribute push(annotate("test"), apply_to = any()) // expected-error {{expected an identifier that corresponds to an attribute subject rule}} +#pragma clang attribute push(annotate("test"), apply_to = any( function, 42 )) // expected-error {{expected an identifier that corresponds to an attribute subject rule}} + +#pragma clang attribute push(annotate("test"), apply_to = any( diag )) // expected-error {{unknown attribute subject rule 'diag'}} +#pragma clang attribute push(annotate("test"), apply_to = any( a )) // expected-error {{unknown attribute subject rule 'a'}} +#pragma clang attribute push(annotate("test"), apply_to = any( function, for)) // expected-error {{unknown attribute subject rule 'for'}} +#pragma clang attribute push(annotate("test"), apply_to = any( function42, for )) // expected-error {{unknown attribute subject rule 'function42'}} + +#pragma clang attribute push(annotate("test"), apply_to = any(hasType)) // expected-error {{expected '('}} +#pragma clang attribute push(annotate("test"), apply_to = hasType) // expected-error {{expected '('}} +#pragma clang attribute push(annotate("test"), apply_only_to = hasType(functionType)) // OK + +#pragma clang attribute push(annotate("test"), apply_to = any( variable( )) // expected-error {{expected ')'}} +#pragma clang attribute push(annotate("test"), apply_to = any( variable( ) )) // expected-error {{expected an identifier that corresponds to an attribute subject matcher sub-rule; 'variable' matcher supports the following sub-rules: 'is_thread_local', 'is_global', 'is_parameter', 'unless(is_parameter)'}} +#pragma clang attribute push(annotate("test"), apply_to = any( variable(is ) )) // expected-error {{unknown attribute subject matcher sub-rule 'is'; 'variable' matcher supports the following sub-rules: 'is_thread_local', 'is_global', 'is_parameter', 'unless(is_parameter)'}} +#pragma clang attribute push(annotate("test"), apply_to = any( variable(is_parameter, not) )) // expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma clang attribute push(annotate("test"), apply_to = any( variable is_parameter )) // expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma clang attribute push(annotate("test"), apply_to = any( variable ( ) // expected-error {{expected ')'}} +#pragma clang attribute push(annotate("test"), apply_to = variable ( // expected-error {{expected ')'}} +#pragma clang attribute push(annotate("test"), apply_to = any( function, variable (is ()) )) // expected-error {{unknown attribute subject matcher sub-rule 'is'; 'variable' matcher supports the following sub-rules: 'is_thread_local', 'is_global', 'is_parameter', 'unless(is_parameter)'}} +#pragma clang attribute push(annotate("test"), apply_to = any( function, variable (42) )) // expected-error {{expected an identifier that corresponds to an attribute subject matcher sub-rule; 'variable' matcher supports the following sub-rules: 'is_thread_local', 'is_global', 'is_parameter', 'unless(is_parameter)'}} +#pragma clang attribute push(annotate("test"), apply_to = any( function, namespace("test") )) // expected-error {{expected an identifier that corresponds to an attribute subject matcher sub-rule; 'namespace' matcher does not support sub-rules}} +#pragma clang attribute push(annotate("test"), apply_to = any( function, variable ("test" )) // expected-error {{expected ')'}} + +#pragma clang attribute push(annotate("test"), apply_to = enum(is_parameter)) // expected-error {{invalid use of attribute subject matcher sub-rule 'is_parameter'; 'enum' matcher does not support sub-rules}} +#pragma clang attribute push(annotate("test"), apply_to = any(enum(is_parameter))) // expected-error {{invalid use of attribute subject matcher sub-rule 'is_parameter'; 'enum' matcher does not support sub-rules}} + +#pragma clang attribute push(annotate("test"), apply_to = any (function, variable (unless) )) // expected-error {{expected '('}} +#pragma clang attribute push(annotate("test"), apply_to = any (function, variable (unless() )) // expected-error {{expected ')'}} +#pragma clang attribute push(annotate("test"), apply_to = any ( function, variable (unless(is)) )) // expected-error {{unknown attribute subject matcher sub-rule 'unless(is)'; 'variable' matcher supports the following sub-rules: 'is_thread_local', 'is_global', 'is_parameter', 'unless(is_parameter)'}} +#pragma clang attribute push(annotate("test"), apply_to = any( variable(unless(is_parameter, not)) )) // expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma clang attribute push(annotate("test"), apply_to = any( variable(unless(is_parameter), not) ) // expected-error {{expected ')'}} +#pragma clang attribute push(annotate("test"), apply_to = any( variable unless is_parameter )) // expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma clang attribute push(annotate("test"), apply_to = any( variable(unless is_parameter) )) // expected-error {{expected '('}} +#pragma clang attribute push(annotate("test"), apply_to = any( function, variable (unless(42)) )) // expected-error {{expected an identifier that corresponds to an attribute subject matcher sub-rule; 'variable' matcher supports the following sub-rules: 'is_thread_local', 'is_global', 'is_parameter', 'unless(is_parameter)'}} +#pragma clang attribute push(annotate("test"), apply_to = any( function, enum(unless("test")) )) // expected-error {{expected an identifier that corresponds to an attribute subject matcher sub-rule; 'enum' matcher does not support sub-rules}} + +#pragma clang attribute push(annotate("test"), apply_to = any( function, variable (unless(is_global)) )) // expected-error {{unknown attribute subject matcher sub-rule 'unless(is_global)'; 'variable' matcher supports the following sub-rules: 'is_thread_local', 'is_global', 'is_parameter', 'unless(is_parameter)'}} +#pragma clang attribute push(annotate("test"), apply_to = any( enum(unless(is_parameter)) )) // expected-error {{invalid use of attribute subject matcher sub-rule 'unless(is_parameter)'; 'enum' matcher does not support sub-rules}} + +#pragma clang attribute push(annotate("test"), apply_only_to = any( function, function )) // expected-error {{duplicate attribute subject matcher 'function'}} +#pragma clang attribute push(annotate("test"), apply_only_to = any( function, function, function )) // expected-error 2 {{duplicate attribute subject matcher 'function'}} +#pragma clang attribute push(annotate("test"), apply_only_to = any( function, enum, function )) // expected-error {{duplicate attribute subject matcher 'function'}} +#pragma clang attribute push(annotate("test"), apply_only_to = any( enum, enum, function )) // expected-error {{duplicate attribute subject matcher 'enum'}} + +#pragma clang attribute push(annotate("test"), apply_only_to = any( variable(is_global), variable(is_global) )) // expected-error {{duplicate attribute subject matcher 'variable(is_global)'}} +#pragma clang attribute push(annotate("test"), apply_only_to = any( variable(is_global), function, variable(is_global), variable(is_global) )) // expected-error 2 {{duplicate attribute subject matcher 'variable(is_global)'}} +#pragma clang attribute push(annotate("test"), apply_only_to = any( variable(unless(is_parameter)), variable(unless(is_parameter)) )) // expected-error {{duplicate attribute subject matcher 'variable(unless(is_parameter))'}} +#pragma clang attribute push(annotate("test"), apply_only_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 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"), apply_only_to = function) () // 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 {{expected ','}} +#pragma clang attribute push (annotate, apply_to=function foo) // expected-error {{extra tokens after attribute in a '#pragma clang attribute push'}} + +#pragma clang attribute push (availability(macos, foo=1), apply_to=function) // 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), apply_to=function) // expected-error {{expected 'introduced', 'deprecated', or 'obsoleted'}} + +#pragma clang attribute push (used, apply_to=function) // expected-error {{attribute 'used' is not supported by '#pragma clang attribute'}} + +void statementPragmasAndPragmaExpression() { +#pragma clang attribute push (annotate("hello"), apply_only_to=variable) +#pragma clang attribute pop +int x = 0; +_Pragma("clang attribute push (annotate(\"hi\"), apply_only_to = function)"); + +_Pragma("clang attribute push (annotate(\"hi\"), apply_only_to = any(function(is_method ))"); // expected-error {{expected ')'}} +} + +_Pragma("clang attribute pop"); + +#pragma clang attribute push (address_space(0), apply_to=variable) // expected-error {{attribute 'address_space' is not supported by '#pragma clang attribute'}} + +// Check support for CXX11 style attributes +#pragma clang attribute push ([[noreturn]], apply_to = any(function)) +#pragma clang attribute pop + +#pragma clang attribute push ([[clang::disable_tail_calls]], apply_to = function) +#pragma clang attribute pop + +#pragma clang attribute push ([[gnu::abi_tag]], apply_only_to=any(function)) +#pragma clang attribute pop + +#pragma clang attribute push ([[clang::disable_tail_calls, noreturn]], apply_to = function) // expected-error {{more than one attribute specified in '#pragma clang attribute push'}} +#pragma clang attribute pop + +#pragma clang attribute push ([[gnu::abi_tag]], apply_only_to=namespace) +#pragma clang attribute pop + +#pragma clang attribute push ([[fallthrough]], apply_to=function) // expected-error {{attribute 'fallthrough' is not supported by '#pragma clang attribute'}} +#pragma clang attribute push ([[clang::fallthrough]], apply_to=function) // expected-error {{attribute 'fallthrough' is not supported by '#pragma clang attribute'}} + +#pragma clang attribute push ([[]], apply_to = function) // A noop + +#pragma clang attribute push ([[noreturn ""]], apply_to=function) // expected-error {{expected ']'}} +#pragma clang attribute push ([[noreturn 42]]) // expected-error {{expected ']'}} expected-error {{expected ','}} Index: test/Sema/pragma-attribute-strict-subjects.c =================================================================== --- /dev/null +++ test/Sema/pragma-attribute-strict-subjects.c @@ -0,0 +1,330 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: not %clang_cc1 -fsyntax-only -ast-dump -ast-dump-filter test %s | FileCheck %s + +// Verify the subject set verification. + +#pragma clang attribute push (annotate("test"), apply_only_to = any(function, variable)) + +#pragma clang attribute pop + +#pragma clang attribute push (annotate("recoverStrict"), apply_to = any(function, variable)) +// expected-error@-1 {{use of 'apply_to' subject set specifier for an attribute without a strict subject set}} +// expected-note@-2 {{use 'apply_only_to' instead}} + +// Ensure that we've recovered from the error: +void testRecoverUseOfStrict(void); +// CHECK-LABEL: FunctionDecl{{.*}} testRecoverUseOfStrict +// CHECK-NEXT: AnnotateAttr{{.*}} "recoverStrict" + +#pragma clang attribute pop + +// Check for contradictions in rules for attribute without a strict subject set: + +#pragma clang attribute push (annotate("subRuleContradictions"), apply_only_to = any(variable, variable(is_parameter), function(is_method), variable(is_global))) +// expected-error@-1 {{redundant attribute subject matcher sub-rule 'variable(is_parameter)'; 'variable' already matches those declarations}} +// expected-error@-2 {{redundant attribute subject matcher sub-rule 'variable(is_global)'; 'variable' already matches those declarations}} + +// Ensure that we've recovered from the error: +int testRecoverSubRuleContradiction = 0; +// CHECK-LABEL: VarDecl{{.*}} testRecoverSubRuleContradiction +// CHECK-NEXT: IntegerLiteral +// CHECK-NEXT: AnnotateAttr{{.*}} "subRuleContradictions" + +#pragma clang attribute pop + +#pragma clang attribute push (annotate("subRuleContradictions2"), apply_only_to = any(function(is_method), function)) +// expected-error@-1 {{redundant attribute subject matcher sub-rule 'function(is_method)'; 'function' already matches those declarations}} + +#pragma clang attribute pop + +#pragma clang attribute push (annotate("subRuleContradictions3"), apply_only_to = any(variable, variable(unless(is_parameter)))) +// expected-error@-1 {{redundant attribute subject matcher sub-rule 'variable(unless(is_parameter))'; 'variable' already matches those declarations}} + +#pragma clang attribute pop + +#pragma clang attribute push (annotate("negatedSubRuleContradictions1"), apply_only_to = any(variable(is_parameter), variable(unless(is_parameter)))) +// expected-error@-1 {{negated attribute subject matcher sub-rule 'variable(unless(is_parameter))' contradicts sub-rule 'variable(is_parameter)'}} + +#pragma clang attribute pop + +#pragma clang attribute push (annotate("negatedSubRuleContradictions2"), apply_only_to = any(variable(unless(is_parameter)), variable(is_thread_local), function, variable(is_global))) +// expected-error@-1 {{negated attribute subject matcher sub-rule 'variable(unless(is_parameter))' contradicts sub-rule 'variable(is_thread_local)'}} +// We have just one error, don't error on 'variable(is_global)' + +// Ensure that we've recovered from the error: +int testRecoverNegatedContradiction = 0; +// CHECK-LABEL: VarDecl{{.*}} testRecoverNegatedContradiction +// CHECK-NEXT: IntegerLiteral +// CHECK-NEXT: AnnotateAttr{{.*}} "negatedSubRuleContradictions2" + +void testRecoverNegatedContradictionFunc(void); +// CHECK-LABEL: FunctionDecl{{.*}} testRecoverNegatedContradictionFunc +// CHECK-NEXT: AnnotateAttr{{.*}} "negatedSubRuleContradictions2" + +#pragma clang attribute pop + +// Verify the strict subject set verification. + +#pragma clang attribute push (abi_tag("a"), apply_to = any(function)) +// expected-error@-1 {{attribute 'abi_tag' must also apply to 'record(unless(is_union))' and 'variable'}} + +int testRecoverStrictnessVar = 0; +// CHECK-LABEL: VarDecl{{.*}} testRecoverStrictnessVar +// CHECK-NEXT: IntegerLiteral +// CHECK-NEXT: AbiTagAttr + +void testRecoverStrictnessFunc(void); +// CHECK-LABEL: FunctionDecl{{.*}} testRecoverStrictnessFunc +// CHECK-NEXT: AbiTagAttr + +struct testRecoverStrictnessStruct { }; +// CHECK-LABEL: RecordDecl{{.*}} testRecoverStrictnessStruct +// CHECK-NEXT: AbiTagAttr + +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_to = any(function, record(unless(is_union)))) +// expected-error@-1 {{attribute 'abi_tag' must also apply to 'variable'}} +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_to = any(variable, record(unless(is_union)))) +// expected-error@-1 {{attribute 'abi_tag' must also apply to 'function'}} +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_to = any(record(unless(is_union)))) +// expected-error@-1 {{attribute 'abi_tag' must also apply to 'variable' and 'function'}} +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_to = variable) +// expected-error@-1 {{attribute 'abi_tag' must also apply to 'record(unless(is_union))' and 'function'}} +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_to = any(record(unless(is_union)), function, variable)) +// No error +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_to = any(function, variable, record(unless(is_union)))) +// No error +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_to = any(variable, record(unless(is_union)), function)) +// No error +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_to = any(function, record(unless(is_union)), variable, enum)) +// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum'}} + +int testRecoverExtraVar = 0; +// CHECK-LABEL: VarDecl{{.*}} testRecoverExtraVar +// CHECK-NEXT: IntegerLiteral +// CHECK-NEXT: AbiTagAttr + +void testRecoverExtraFunc(void); +// CHECK-LABEL: FunctionDecl{{.*}} testRecoverExtraFunc +// CHECK-NEXT: AbiTagAttr + +struct testRecoverExtraStruct { }; +// CHECK-LABEL: RecordDecl{{.*}} testRecoverExtraStruct +// CHECK-NEXT: AbiTagAttr + +enum testNoEnumAbiTag { CaseCase }; +// CHECK-LABEL: EnumDecl{{.*}} testNoEnumAbiTag +// CHECK-NO: AbiTagAttr + +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_to = any(enum_case, function, record(unless(is_union)), variable, variable(is_parameter))) +// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum_case' and 'variable(is_parameter)'}} +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_to = any(function, record(unless(is_union)), enum)) +// expected-error@-1 {{attribute 'abi_tag' must also apply to 'variable'}} +// expected-error@-2 {{attribute 'abi_tag' can't be applied to 'enum'}} +#pragma clang attribute pop + +// Verify the non-strict subject set verification. + +#pragma clang attribute push (abi_tag("a"), apply_only_to = any(function)) + +int testSubset1Var; +// CHECK-LABEL: VarDecl{{.*}} testSubset1Var +// CHECK-NOT: AbiTagAttr + +void testSubset1Func(void); +// CHECK-LABEL: FunctionDecl{{.*}} testSubset1Func +// CHECK-NEXT: AbiTagAttr + +struct testSubset1Struct { }; +// CHECK-LABEL: RecordDecl{{.*}} testSubset1Struct +// CHECK-NOT: AbiTagAttr + +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_only_to = variable) + +int testSubset2Var; +// CHECK-LABEL: VarDecl{{.*}} testSubset2Var +// CHECK-NEXT: AbiTagAttr + +void testSubset2Func(void); +// CHECK-LABEL: FunctionDecl{{.*}} testSubset2Func +// CHECK-NOT: AbiTagAttr + +struct testSubset2Struct { }; +// CHECK-LABEL: RecordDecl{{.*}} testSubset2Struct +// CHECK-NOT: AbiTagAttr + +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_only_to = any(record(unless(is_union)))) + +int testSubset3Var; +// CHECK-LABEL: VarDecl{{.*}} testSubset3Var +// CHECK-NOT: AbiTagAttr + +void testSubset3Func(void); +// CHECK-LABEL: FunctionDecl{{.*}} testSubset3Func +// CHECK-NOT: AbiTagAttr + +struct testSubset3Struct { }; +// CHECK-LABEL: RecordDecl{{.*}} testSubset3Struct +// CHECK-NEXT: AbiTagAttr + +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_only_to = any(function, variable)) + +int testSubset4Var; +// CHECK-LABEL: VarDecl{{.*}} testSubset4Var +// CHECK-NEXT: AbiTagAttr + +void testSubset4Func(void); +// CHECK-LABEL: FunctionDecl{{.*}} testSubset4Func +// CHECK-NEXT: AbiTagAttr + +struct testSubset4Struct { }; +// CHECK-LABEL: RecordDecl{{.*}} testSubset4Struct +// CHECK-NOT: AbiTagAttr + +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_only_to = any(variable, record(unless(is_union)))) + +int testSubset5Var; +// CHECK-LABEL: VarDecl{{.*}} testSubset5Var +// CHECK-NEXT: AbiTagAttr + +void testSubset5Func(void); +// CHECK-LABEL: FunctionDecl{{.*}} testSubset5Func +// CHECK-NOT: AbiTagAttr + +struct testSubset5Struct { }; +// CHECK-LABEL: RecordDecl{{.*}} testSubset5Struct +// CHECK-NEXT: AbiTagAttr + +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_only_to = any(record(unless(is_union)), function)) + +int testSubset6Var; +// CHECK-LABEL: VarDecl{{.*}} testSubset6Var +// CHECK-NOT: AbiTagAttr + +void testSubset6Func(void); +// CHECK-LABEL: FunctionDecl{{.*}} testSubset6Func +// CHECK-NEXT: AbiTagAttr + +struct testSubset6Struct { }; +// CHECK-LABEL: RecordDecl{{.*}} testSubset6Struct +// CHECK-NEXT: AbiTagAttr + +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_only_to = any(record(unless(is_union)), function, variable)) + +int testSubset7Var; +// CHECK-LABEL: VarDecl{{.*}} testSubset7Var +// CHECK-NEXT: AbiTagAttr + +void testSubset7Func(void); +// CHECK-LABEL: FunctionDecl{{.*}} testSubset7Func +// CHECK-NEXT: AbiTagAttr + +struct testSubset7Struct { }; +// CHECK-LABEL: RecordDecl{{.*}} testSubset7Struct +// CHECK-NEXT: AbiTagAttr + +#pragma clang attribute pop + + +#pragma clang attribute push (abi_tag("a"), apply_only_to = any(record(unless(is_union)), function, variable, enum, enum_case)) +// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum_case' and 'enum'}} + +int testSubsetRecoverVar; +// CHECK-LABEL: VarDecl{{.*}} testSubsetRecoverVar +// CHECK-NEXT: AbiTagAttr + +void testSubsetRecoverFunc(void); +// CHECK-LABEL: FunctionDecl{{.*}} testSubsetRecoverFunc +// CHECK-NEXT: AbiTagAttr + +struct testSubsetRecoverStruct { }; +// CHECK-LABEL: RecordDecl{{.*}} testSubsetRecoverStruct +// CHECK-NEXT: AbiTagAttr + +#pragma clang attribute pop + +#pragma clang attribute push (abi_tag("a"), apply_only_to = enum) +// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum'}} + +int testSubsetNoVar; +// CHECK-LABEL: VarDecl{{.*}} testSubsetNoVar +// CHECK-NOT: AbiTagAttr + +void testSubsetNoFunc(void); +// CHECK-LABEL: FunctionDecl{{.*}} testSubsetNoFunc +// CHECK-NOT: AbiTagAttr + +struct testSubsetNoStruct { }; +// CHECK-LABEL: RecordDecl{{.*}} testSubsetNoStruct +// CHECK-NOT: AbiTagAttr + +#pragma clang attribute pop + +// Handle attributes whose subjects are supported only in other language modes: + +#pragma clang attribute push(abi_tag("b"), apply_to = any(namespace, record(unless(is_union)), variable, function)) +// 'namespace' is accepted! +#pragma clang attribute pop + +#pragma clang attribute push(abi_tag("b"), apply_only_to = any(namespace)) +// 'namespace' is accepted! +#pragma clang attribute pop + +#pragma clang attribute push(objc_subclassing_restricted, apply_to = objc_interface) +// No error! +#pragma clang attribute pop + +#pragma clang attribute push(objc_subclassing_restricted, apply_only_to = objc_interface) +// No error! +#pragma clang attribute pop + +#pragma clang attribute push(objc_subclassing_restricted, apply_to = any(objc_interface, objc_protocol)) +// expected-error@-1 {{attribute 'objc_subclassing_restricted' can't be applied to 'objc_protocol'}} +#pragma clang attribute pop + +#pragma clang attribute push(objc_subclassing_restricted, apply_to = any(objc_protocol)) +// expected-error@-1 {{attribute 'objc_subclassing_restricted' can't be applied to 'objc_protocol'}} +// Don't report an error about missing 'objc_interface' as we aren't parsing +// Objective-C. +#pragma clang attribute pop + +#pragma clang attribute push(objc_subclassing_restricted, apply_only_to = any(objc_interface, objc_protocol)) +// expected-error@-1 {{attribute 'objc_subclassing_restricted' can't be applied to 'objc_protocol'}} +#pragma clang attribute pop + +#pragma clang attribute push(objc_subclassing_restricted, apply_only_to = any(objc_protocol)) +// expected-error@-1 {{attribute 'objc_subclassing_restricted' can't be applied to 'objc_protocol'}} +// Don't report an error about missing 'objc_interface' as we aren't parsing +// Objective-C. +#pragma clang attribute pop Index: test/Sema/pragma-attribute.c =================================================================== --- /dev/null +++ test/Sema/pragma-attribute.c @@ -0,0 +1,139 @@ +// 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"), apply_only_to = any(function, variable, record, type_alias, enum, enum_case)) +#pragma clang attribute push (annotate("foo"), apply_only_to = any(function, variable, record, type_alias, field, enum, enum_case)) + +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" + +// The fields should not receive "test"! +// CHECK-NEXT: FieldDecl{{.*}}field +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" +// CHECK-NOT: AnnotateAttr + +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{{.*}} "foo" +// CHECK-NEXT: FieldDecl{{.*}}field2 +// CHECK-NEXT: AnnotateAttr{{.*}} "foo" + +typedef struct { + int testField; +// CHECK-LABEL: FieldDecl{{.*}}testField +// 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, apply_only_to = function) +#pragma clang attribute pop // Don't verify unused attributes. + +// Ensure we only report any errors once. +#pragma clang attribute push (annotate, apply_only_to = function) // 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(), apply_only_to = function) // expected-error {{'annotate' attribute takes one argument}} + +void test5_2(); + +#pragma clang attribute push (annotate("hello", "world"), apply_only_to = function) // 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-subject-match-rules.cpp =================================================================== --- /dev/null +++ test/SemaCXX/pragma-attribute-subject-match-rules.cpp @@ -0,0 +1,169 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=namespace" %s | FileCheck --check-prefix=CHECK-NAMESPACE %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=type_alias" %s | FileCheck --check-prefix=CHECK-TYPE_ALIAS %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=enum" %s | FileCheck --check-prefix=CHECK-ENUM %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=enum_case" %s | FileCheck --check-prefix=CHECK-ENUM_CASE %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=record" %s | FileCheck --check-prefix=CHECK-RECORD %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=record(unless(is_union))" %s | FileCheck --check-prefix=CHECK-RECORD_UNLESS_IS_UNION %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=field" %s | FileCheck --check-prefix=CHECK-FIELD %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=function" %s | FileCheck --check-prefix=CHECK-FUNCTION %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=hasType(functionType)" %s | FileCheck --check-prefix=CHECK-HAS_TYPE_FUNCTION_TYPE %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=function(is_method)" %s | FileCheck --check-prefix=CHECK-FUNCTION_IS_METHOD %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=variable" %s | FileCheck --check-prefix=CHECK-VARIABLE %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=variable(is_global)" %s | FileCheck --check-prefix=CHECK-VARIABLE_IS_GLOBAL %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=variable(is_parameter)" %s | FileCheck --check-prefix=CHECK-VARIABLE_IS_PARAMETER %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=variable(unless(is_parameter))" %s | FileCheck --check-prefix=CHECK-VARIABLE_UNLESS_IS_PARAMETER %s + +#pragma clang attribute push (annotate("test"), apply_only_to = any(SUBJECT)) + +namespace testNamespace { +// CHECK-NAMESPACE: NamespaceDecl{{.*}} testNamespace +// CHECK-NAMESPACE-NEXT: AnnotateAttr{{.*}} "test" + +typedef int testTypedef; +// CHECK-TYPE_ALIAS: TypedefDecl{{.*}} testTypedef +// CHECK-TYPE_ALIAS-NEXT: BuiltinType +// CHECK-TYPE_ALIAS-NEXT: AnnotateAttr{{.*}} "test" + +using testTypeAlias = double; +// CHECK-TYPE_ALIAS: TypeAliasDecl{{.*}} testTypeAlias +// CHECK-TYPE_ALIAS-NEXT: BuiltinType +// CHECK-TYPE_ALIAS-NEXT: AnnotateAttr{{.*}} "test" + +enum testEnum { + testEnumCase1, + testEnumCase2 +}; +// CHECK-ENUM: EnumDecl{{.*}} testEnum +// CHECK-ENUM-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-ENUM_CASE: EnumConstantDecl{{.*}} testEnumCase1 +// CHECK-ENUM_CASE-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-ENUM_CASE: EnumConstantDecl{{.*}} testEnumCase2 +// CHECK-ENUM_CASE-NEXT: AnnotateAttr{{.*}} "test" + +struct testStructRecord { + int testStructRecordField; +}; +// CHECK-RECORD: CXXRecordDecl{{.*}} testStructRecord +// CHECK-RECORD-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-RECORD_UNLESS_IS_UNION-LABEL: CXXRecordDecl{{.*}} testStructRecord +// CHECK-RECORD_UNLESS_IS_UNION-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-FIELD: FieldDecl{{.*}} testStructRecordField +// CHECK-FIELD-NEXT: AnnotateAttr{{.*}} "test" + +class testClassRecord { + int testClassRecordField; +}; +// CHECK-RECORD: CXXRecordDecl{{.*}} testClassRecord +// CHECK-RECORD-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-RECORD_UNLESS_IS_UNION-LABEL: CXXRecordDecl{{.*}} testClassRecord +// CHECK-RECORD_UNLESS_IS_UNION-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-FIELD: FieldDecl{{.*}} testClassRecordField +// CHECK-FIELD-NEXT: AnnotateAttr{{.*}} "test" + +union testUnionRecord { + int testUnionRecordField; +}; +// CHECK-RECORD: CXXRecordDecl{{.*}} testUnionRecord +// CHECK-RECORD-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-RECORD_UNLESS_IS_UNION-LABEL: CXXRecordDecl{{.*}} testUnionRecord +// CHECK-RECORD_UNLESS_IS_UNION-NOT: AnnotateAttr{{.*}} "test" +// CHECK-FIELD: FieldDecl{{.*}} testUnionRecordField +// CHECK-FIELD-NEXT: AnnotateAttr{{.*}} "test" + +// CHECK-RECORD_UNLESS_IS_UNION-LABEL: CXXRecordDecl +void testFunctionDecl(); +// CHECK-FUNCTION: FunctionDecl{{.*}} testFunctionDecl +// CHECK-FUNCTION-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-HAS_TYPE_FUNCTION_TYPE: FunctionDecl{{.*}} testFunctionDecl +// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: AnnotateAttr{{.*}} "test" + +void testFunctionDecl() { } +// CHECK-FUNCTION: FunctionDecl{{.*}} testFunctionDecl +// CHECK-FUNCTION-NEXT: CompoundStmt +// CHECK-FUNCTION-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-HAS_TYPE_FUNCTION_TYPE: FunctionDecl{{.*}} testFunctionDecl +// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: CompoundStmt +// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: AnnotateAttr{{.*}} "test" + +void (*testFunctionVar)(); +// CHECK-HAS_TYPE_FUNCTION_TYPE: VarDecl{{.*}} testFunctionVar +// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: AnnotateAttr{{.*}} "test" +// 'function' should not apply to variables with a function type! +// CHECK-FUNCTION: VarDecl{{.*}} testFunctionVar +// CHECK-FUNCTION-NOT: AnnotateAttr{{.*}} "test" + +class testMethods { + testMethods(); + void testMethod(); +}; +void testMethods::testMethod() { } +void testFunctionNotMethod(); +// CHECK-FUNCTION-LABEL: CXXConstructorDecl{{.*}} testMethods +// CHECK-FUNCTION-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-FUNCTION_IS_METHOD: CXXConstructorDecl{{.*}} testMethods +// CHECK-FUNCTION_IS_METHOD-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-HAS_TYPE_FUNCTION_TYPE: CXXConstructorDecl{{.*}} testMethods +// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-FUNCTION: CXXMethodDecl{{.*}} testMethod +// CHECK-FUNCTION-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-FUNCTION_IS_METHOD: CXXMethodDecl{{.*}} testMethod +// CHECK-FUNCTION_IS_METHOD-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-HAS_TYPE_FUNCTION_TYPE: CXXMethodDecl{{.*}} testMethod +// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-FUNCTION: CXXMethodDecl{{.*}} testMethod +// CHECK-FUNCTION-NEXT: CompoundStmt +// CHECK-FUNCTION-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-FUNCTION_IS_METHOD: CXXMethodDecl{{.*}} testMethod +// CHECK-FUNCTION_IS_METHOD-NEXT: CompoundStmt +// CHECK-CXX_METHOD-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-HAS_TYPE_FUNCTION_TYPE: CXXMethodDecl{{.*}} testMethod +// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: CompoundStmt +// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-FUNCTION_IS_METHOD: FunctionDecl{{.*}} testFunctionNotMethod +// CHECK-FUNCTION_IS_METHOD-NOT: AnnotateAttr{{.*}} "test" + +int testVariable; +// CHECK-VARIABLE: VarDecl{{.*}} testVariable +// CHECK-VARIABLE-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-VARIABLE_IS_GLOBAL-LABEL: VarDecl{{.*}} testVariable +// CHECK-VARIABLE_IS_GLOBAL-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-VARIABLE_IS_PARAMETER-LABEL: VarDecl{{.*}} testVariable +// CHECK-VARIABLE_IS_PARAMETER-NOT: AnnotateAttr{{.*}} "test" +// CHECK-VARIABLE_UNLESS_IS_PARAMETER-LABEL: VarDecl{{.*}} testVariable +// CHECK-VARIABLE_UNLESS_IS_PARAMETER-NEXT: AnnotateAttr{{.*}} "test" +void testVarFunction(int testParam) { +// CHECK-VARIABLE: VarDecl{{.*}} testParam +// CHECK-VARIABLE-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-VARIABLE_IS_GLOBAL-LABEL: VarDecl{{.*}} testParam +// CHECK-VARIABLE_IS_GLOBAL-NOT: AnnotateAttr{{.*}} "test" +// CHECK-VARIABLE_IS_PARAMETER-LABEL: VarDecl{{.*}} testParam +// CHECK-VARIABLE_IS_PARAMETER-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-VARIABLE_UNLESS_IS_PARAMETER-LABEL: VarDecl{{.*}} testParam +// CHECK-VARIABLE_UNLESS_IS_PARAMETER-NOT: AnnotateAttr{{.*}} "test" + + int testLocalVariable; +// CHECK-VARIABLE: VarDecl{{.*}} testLocalVariable +// CHECK-VARIABLE-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-VARIABLE_IS_GLOBAL-LABEL: VarDecl{{.*}} testLocalVariable +// CHECK-VARIABLE_IS_GLOBAL-NOT: AnnotateAttr{{.*}} "test" +// CHECK-VARIABLE_IS_PARAMETER-LABEL: VarDecl{{.*}} testLocalVariable +// CHECK-VARIABLE_IS_PARAMETER-NOT: AnnotateAttr{{.*}} "test" +// CHECK-VARIABLE_UNLESS_IS_PARAMETER-LABEL: VarDecl{{.*}} testLocalVariable +// CHECK-VARIABLE_UNLESS_IS_PARAMETER-NEXT: AnnotateAttr{{.*}} "test" +} +class testVarClass { + static int testStaticVar; +}; +// CHECK-VARIABLE: VarDecl{{.*}} testStaticVar +// CHECK-VARIABLE-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-VARIABLE_IS_GLOBAL-LABEL: VarDecl{{.*}} testStaticVar +// CHECK-VARIABLE_IS_GLOBAL-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-VARIABLE_IS_PARAMETER-LABEL: VarDecl{{.*}} testStaticVar +// CHECK-VARIABLE_IS_PARAMETER-NOT: AnnotateAttr{{.*}} "test" +// CHECK-VARIABLE_UNLESS_IS_PARAMETER-LABEL: VarDecl{{.*}} testStaticVar +// CHECK-VARIABLE_UNLESS_IS_PARAMETER-NEXT: AnnotateAttr{{.*}} "test" + + +} + +#pragma clang attribute pop \ No newline at end of file Index: test/SemaCXX/pragma-attribute.cpp =================================================================== --- /dev/null +++ test/SemaCXX/pragma-attribute.cpp @@ -0,0 +1,101 @@ +// 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"), apply_only_to=any(record, field, variable, function, namespace, type_alias)) + +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"), apply_only_to=any(record, field, variable, function, namespace, type_alias)) + +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, apply_to=variable(is_global)) + +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]], apply_to = function) + +void testNoReturn(); +// CHECK-LABEL: FunctionDecl{{.*}} testNoReturn +// CHECK-NEXT: CXX11NoReturnAttr + +#pragma clang attribute pop + +#ifndef NOERROR + +#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'}} + +#endif + +// Use of matchers from other language modes should not cause errors in-non +// strict mode for attributes without subject list: +#pragma clang attribute push (annotate("test"), apply_only_to = objc_method) + +#pragma clang attribute pop + +#pragma clang attribute push (annotate("test"), apply_only_to = any(objc_interface, objc_protocol)) + +#pragma clang attribute pop Index: test/SemaObjC/pragma-attribute-subject-match-rules.m =================================================================== --- /dev/null +++ test/SemaObjC/pragma-attribute-subject-match-rules.m @@ -0,0 +1,106 @@ +// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump "-DSUBJECT=objc_interface" %s | FileCheck --check-prefix=CHECK-OBJC_INTERFACE %s +// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=objc_protocol" %s | FileCheck --check-prefix=CHECK-OBJC_PROTOCOL %s +// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=objc_method" %s | FileCheck --check-prefix=CHECK-OBJC_METHOD %s +// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=objc_method(is_instance)" %s | FileCheck --check-prefix=CHECK-OBJC_METHOD_IS_INSTANCE %s +// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=field" %s | FileCheck --check-prefix=CHECK-FIELD %s +// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=objc_property" %s | FileCheck --check-prefix=CHECK-OBJC_PROPERTY %s +// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=block" %s | FileCheck --check-prefix=CHECK-BLOCK %s +// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=hasType(functionType)" %s | FileCheck --check-prefix=CHECK-HAS_TYPE_FUNCTION_TYPE %s + +#pragma clang attribute push (annotate("test"), apply_only_to = any(SUBJECT)) + +@interface testInterface +@end +// CHECK-OBJC_INTERFACE: ObjCInterfaceDecl{{.*}} testInterface +// CHECK-OBJC_INTERFACE-NEXT: AnnotateAttr{{.*}} "test" + +@interface testInterface () +@end +// CHECK-OBJC_INTERFACE: ObjCCategoryDecl +// CHECK-OBJC_INTERFACE-NOT: AnnotateAttr{{.*}} "test" + +@interface testInterface (testCategory) +@end +// CHECK-OBJC_INTERFACE: ObjCCategoryDecl{{.*}} testCategory +// CHECK-OBJC_INTERFACE-NOT: AnnotateAttr{{.*}} "test" + +// CHECK-OBJC_INTERFACE-LABEL: ObjCProtocolDecl +@protocol testProtocol +@end +// CHECK-OBJC_PROTOCOL: ObjCProtocolDecl{{.*}} testProtocol +// CHECK-OBJC_PROTOCOL-NEXT: AnnotateAttr{{.*}} "test" + +@interface methodContainer +- (void) testInstanceMethod; ++ (void) testClassMethod; +@end +// CHECK-OBJC_METHOD: ObjCMethodDecl{{.*}} testInstanceMethod +// CHECK-OBJC_METHOD-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-OBJC_METHOD: ObjCMethodDecl{{.*}} testClassMethod +// CHECK-OBJC_METHOD-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-OBJC_METHOD_IS_INSTANCE: ObjCMethodDecl{{.*}} testInstanceMethod +// CHECK-OBJC_METHOD_IS_INSTANCE-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-OBJC_METHOD_IS_INSTANCE-LABEL: ObjCMethodDecl{{.*}} testClassMethod +// CHECK-OBJC_METHOD_IS_INSTANCE-NOT: AnnotateAttr{{.*}} "test" +// CHECK-HAS_TYPE_FUNCTION_TYPE-LABEL: ObjCMethodDecl{{.*}} testInstanceMethod +// CHECK-HAS_TYPE_FUNCTION_TYPE-NOT: AnnotateAttr{{.*}} "test" +// CHECK-HAS_TYPE_FUNCTION_TYPE-LABEL: ObjCMethodDecl{{.*}} testClassMethod +// CHECK-HAS_TYPE_FUNCTION_TYPE-NOT: AnnotateAttr{{.*}} "test" + +@implementation methodContainer +- (void) testInstanceMethod { } ++ (void) testClassMethod { } +@end +// CHECK-OBJC_METHOD: ObjCMethodDecl{{.*}} testInstanceMethod +// CHECK-OBJC_METHOD-NEXT: ImplicitParamDecl +// CHECK-OBJC_METHOD-NEXT: ImplicitParamDecl +// CHECK-OBJC_METHOD-NEXT: CompoundStmt +// CHECK-OBJC_METHOD-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-OBJC_METHOD: ObjCMethodDecl{{.*}} testClassMethod +// CHECK-OBJC_METHOD-NEXT: ImplicitParamDecl +// CHECK-OBJC_METHOD-NEXT: ImplicitParamDecl +// CHECK-OBJC_METHOD-NEXT: CompoundStmt +// CHECK-OBJC_METHOD-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-OBJC_METHOD_IS_INSTANCE-LABEL: ObjCMethodDecl{{.*}} testInstanceMethod +// CHECK-OBJC_METHOD_IS_INSTANCE-NEXT: ImplicitParamDecl +// CHECK-OBJC_METHOD_IS_INSTANCE-NEXT: ImplicitParamDecl +// CHECK-OBJC_METHOD_IS_INSTANCE-NEXT: CompoundStmt +// CHECK-OBJC_METHOD_IS_INSTANCE-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-OBJC_METHOD_IS_INSTANCE: ObjCMethodDecl{{.*}} testClassMethod +// CHECK-OBJC_METHOD_IS_INSTANCE-NOT: AnnotateAttr{{.*}} "test" + +// CHECK-HAS_TYPE_FUNCTION_TYPE-LABEL: ObjCMethodDecl{{.*}} testInstanceMethod +// CHECK-HAS_TYPE_FUNCTION_TYPE-NOT: AnnotateAttr{{.*}} "test" +// CHECK-HAS_TYPE_FUNCTION_TYPE-LABEL: ObjCMethodDecl{{.*}} testClassMethod +// CHECK-HAS_TYPE_FUNCTION_TYPE-NOT: AnnotateAttr{{.*}} "test" +@interface propertyContainer { + int testIvar; +// CHECK-FIELD: ObjCIvarDecl{{.*}} testIvar +// CHECK-FIELD-NEXT: AnnotateAttr{{.*}} "test" + +} +@property int testProperty; +// CHECK-OBJC_PROPERTY: ObjCPropertyDecl{{.*}} testProperty +// CHECK-OBJC_PROPERTY-NEXT: AnnotateAttr{{.*}} "test" + +@end + +void (^testBlockVar)(); +// CHECK-BLOCK: VarDecl{{.*}} testBlockVar +// CHECK-BLOCK-NOT: AnnotateAttr{{.*}} "test" + +void testBlock() { + (void)(^ { }); +} +// CHECK-BLOCK-LABEL: BlockDecl +// CHECK-BLOCK-NEXT: CompoundStmt +// CHECK-BLOCK-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-HAS_TYPE_FUNCTION_TYPE-LABEL: FunctionDecl{{.*}} testBlock +// CHECK-HAS_TYPE_FUNCTION_TYPE: BlockDecl +// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: CompoundStmt +// The attribute applies to function, but not to block: +// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: AnnotateAttr{{.*}} "test" +// CHECK-HAS_TYPE_FUNCTION_TYPE-NOT: AnnotateAttr{{.*}} "test" + + +#pragma clang attribute pop \ No newline at end of file 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 -Wno-objc-root-class -ast-dump -ast-dump-filter test %s | FileCheck %s + +#pragma clang attribute push (annotate("test"), apply_only_to = any(objc_interface, objc_protocol, objc_property, field, objc_method, variable)) +#pragma clang attribute push (objc_subclassing_restricted, apply_to = objc_interface) + +@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\"), apply_only_to=objc_interface)"); +} + +// 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: test/lit.cfg =================================================================== --- test/lit.cfg +++ test/lit.cfg @@ -282,6 +282,8 @@ else: config.substitutions.append( ('%target_itanium_abi_host_triple', '') ) +config.substitutions.append( ('%src_include_dir', config.clang_src_dir + '/include') ) + # FIXME: Find nicer way to prohibit this. config.substitutions.append( (' clang ', """*** Do not use 'clang' in tests, use '%clang'. ***""") ) Index: test/lit.site.cfg.in =================================================================== --- test/lit.site.cfg.in +++ test/lit.site.cfg.in @@ -10,6 +10,7 @@ config.llvm_plugin_ext = "@LLVM_PLUGIN_EXT@" config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@" config.clang_obj_root = "@CLANG_BINARY_DIR@" +config.clang_src_dir = "@CLANG_SOURCE_DIR@" config.clang_tools_dir = "@CLANG_TOOLS_DIR@" config.host_triple = "@LLVM_HOST_TRIPLE@" config.target_triple = "@TARGET_TRIPLE@" Index: utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- utils/TableGen/ClangAttrEmitter.cpp +++ utils/TableGen/ClangAttrEmitter.cpp @@ -12,13 +12,15 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/iterator_range.h" -#include "llvm/ADT/SmallString.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/iterator_range.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TableGen/Error.h" @@ -1522,6 +1524,335 @@ 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; +} + +namespace { + +struct AttributeSubjectMatchRule { + const Record *MetaSubject; + const Record *Constraint; + + AttributeSubjectMatchRule(const Record *MetaSubject, const Record *Constraint) + : MetaSubject(MetaSubject), Constraint(Constraint) { + assert(MetaSubject && "Missing subject"); + } + + bool isSubRule() const { return Constraint != nullptr; } + + std::vector getSubjects() const { + return (Constraint ? Constraint : MetaSubject) + ->getValueAsListOfDefs("Subjects"); + } + + std::vector getLangOpts() const { + if (Constraint) { + // Lookup the options in the sub-rule first, in case the sub-rule + // overrides the rules options. + auto Opts = Constraint->getValueAsListOfDefs("LangOpts"); + if (!Opts.empty()) + return Opts; + } + return MetaSubject->getValueAsListOfDefs("LangOpts"); + } + + // Abstract rules are used only for sub-rules + bool isAbstractRule() const { return getSubjects().empty(); } + + std::string getName() const { + return (Constraint ? Constraint : MetaSubject)->getValueAsString("Name"); + } + + bool isNegatedSubRule() const { + assert(isSubRule() && "Not a sub-rule"); + return Constraint->getValueAsBit("Negated"); + } + + std::string getSpelling() const { + std::string Result = MetaSubject->getValueAsString("Name"); + if (isSubRule()) { + Result += '('; + if (isNegatedSubRule()) + Result += "unless("; + Result += getName(); + if (isNegatedSubRule()) + Result += ')'; + Result += ')'; + } + return Result; + } + + std::string getEnumValueName() const { + std::string Result = + "SubjectMatchRule_" + MetaSubject->getValueAsString("Name"); + if (isSubRule()) { + Result += "_"; + if (isNegatedSubRule()) + Result += "not_"; + Result += Constraint->getValueAsString("Name"); + } + if (isAbstractRule()) + Result += "_abstract"; + return Result; + } + + std::string getEnumValue() const { return "attr::" + getEnumValueName(); } + + static const char *EnumName; +}; + +const char *AttributeSubjectMatchRule::EnumName = "attr::SubjectMatchRule"; + +struct PragmaClangAttributeSupport { + std::vector Rules; + llvm::DenseMap SubjectsToRules; + + PragmaClangAttributeSupport(RecordKeeper &Records); + + bool isAttributedSupported(const Record &Attribute); + + void emitMatchRuleList(raw_ostream &OS); + + std::string generateStrictConformsTo(const Record &Attr, raw_ostream &OS); + + void generateParsingHelpers(raw_ostream &OS); +}; + +} // end anonymous namespace + +PragmaClangAttributeSupport::PragmaClangAttributeSupport( + RecordKeeper &Records) { + std::vector MetaSubjects = + Records.getAllDerivedDefinitions("AttrSubjectMatcherRule"); + auto MapFromSubjectsToRules = [this](const Record *SubjectContainer, + const Record *MetaSubject, + const Record *Constraint = nullptr) { + Rules.emplace_back(MetaSubject, Constraint); + std::vector ApplicableSubjects = + SubjectContainer->getValueAsListOfDefs("Subjects"); + for (const Record *Subject : ApplicableSubjects) { + bool Inserted = + SubjectsToRules.try_emplace(Subject, MetaSubject, Constraint).second; + if (!Inserted) { + PrintFatalError("Attribute subject match rules should not represent" + "same attribute subjects."); + } + } + }; + for (const Record *MetaSubject : MetaSubjects) { + MapFromSubjectsToRules(MetaSubject, MetaSubject); + std::vector Constraints = + MetaSubject->getValueAsListOfDefs("Constraints"); + for (const Record *Constraint : Constraints) + MapFromSubjectsToRules(Constraint, MetaSubject, Constraint); + } +} + +static PragmaClangAttributeSupport & +getPragmaAttributeSupport(RecordKeeper &Records) { + static PragmaClangAttributeSupport Instance(Records); + return Instance; +} + +void PragmaClangAttributeSupport::emitMatchRuleList(raw_ostream &OS) { + OS << "#ifndef ATTR_MATCH_SUB_RULE\n"; + OS << "#define ATTR_MATCH_SUB_RULE(Value, Spelling, IsAbstract, Parent, " + "IsNegated) " + << "ATTR_MATCH_RULE(Value, Spelling, IsAbstract)\n"; + OS << "#endif\n"; + for (const AttributeSubjectMatchRule &Rule : Rules) { + OS << (Rule.isSubRule() ? "ATTR_MATCH_SUB_RULE" : "ATTR_MATCH_RULE") << '('; + OS << Rule.getEnumValueName() << ", \"" << Rule.getSpelling() << "\", " + << Rule.isAbstractRule(); + if (Rule.isSubRule()) + OS << ", " + << AttributeSubjectMatchRule(Rule.MetaSubject, nullptr).getEnumValue() + << ", " << Rule.isNegatedSubRule(); + OS << ")\n"; + } + OS << "#undef ATTR_MATCH_SUB_RULE\n"; +} + +bool PragmaClangAttributeSupport::isAttributedSupported( + const Record &Attribute) { + if (Attribute.getValueAsBit("ForcePragmaAttributeSupport")) + return true; + // Opt-out rules: + // FIXME: The documentation check should be moved before + // the ForcePragmaAttributeSupport check after annotate is documented. + // No documentation present. + if (Attribute.isValueUnset("Documentation")) + return false; + std::vector Docs = Attribute.getValueAsListOfDefs("Documentation"); + if (Docs.empty()) + return false; + if (Docs.size() == 1 && Docs[0]->getName() == "Undocumented") + return false; + // 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 subject list has a subject that isn't covered by one of the + // subject match rules or has no subjects at all. + if (Attribute.isValueUnset("Subjects")) + return false; + const Record *SubjectObj = Attribute.getValueAsDef("Subjects"); + std::vector Subjects = SubjectObj->getValueAsListOfDefs("Subjects"); + if (Subjects.empty()) + return false; + for (const Record *Subject : Subjects) { + auto It = SubjectsToRules.find(Subject); + if (It == SubjectsToRules.end()) + return false; + } + return true; +} + +std::string +PragmaClangAttributeSupport::generateStrictConformsTo(const Record &Attr, + raw_ostream &OS) { + if (!isAttributedSupported(Attr)) + return "nullptr"; + // Generate a function that constructs a set of matching rules that describe + // to which declarations the attribute should apply to. + std::string FnName = "matchRulesFor" + Attr.getName().str(); + std::stringstream SS; + SS << "static void " << FnName << "(llvm::SmallVectorImpl> &MatchRules, const LangOptions &LangOpts) {\n"; + if (Attr.isValueUnset("Subjects")) { + SS << "}\n\n"; + OS << SS.str(); + return FnName; + } + const Record *SubjectObj = Attr.getValueAsDef("Subjects"); + std::vector Subjects = SubjectObj->getValueAsListOfDefs("Subjects"); + for (const Record *Subject : Subjects) { + auto It = SubjectsToRules.find(Subject); + assert(It != SubjectsToRules.end() && + "This attribute is unsupported by #pragma clang attribute"); + AttributeSubjectMatchRule Rule = It->getSecond(); + // The rule might be language specific, so only subtract it from the given + // rules if the specific language options are specified. + std::vector LangOpts = Rule.getLangOpts(); + SS << " MatchRules.push_back(std::make_pair(" << Rule.getEnumValue() + << ", /*IsSupported=*/"; + if (!LangOpts.empty()) { + for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) { + std::string Part = (*I)->getValueAsString("Name"); + if ((*I)->getValueAsBit("Negated")) + SS << "!"; + SS << "LangOpts." + Part; + if (I + 1 != E) + SS << " || "; + } + } else + SS << "true"; + SS << "));\n"; + } + SS << "}\n\n"; + OS << SS.str(); + return FnName; +} + +void PragmaClangAttributeSupport::generateParsingHelpers(raw_ostream &OS) { + // Generate routines that check the names of sub-rules. + OS << "Optional " + "defaultIsAttributeSubjectMatchSubRuleFor(StringRef, bool) {\n"; + OS << " return None;\n"; + OS << "}\n\n"; + + std::map> + SubMatchRules; + for (const auto &Rule : Rules) { + if (!Rule.isSubRule()) + continue; + SubMatchRules[Rule.MetaSubject].push_back(Rule); + } + + for (const auto &SubMatchRule : SubMatchRules) { + OS << "Optional isAttributeSubjectMatchSubRuleFor_" + << SubMatchRule.first->getValueAsString("Name") + << "(StringRef Name, bool IsUnless) {\n"; + OS << " if (IsUnless)\n"; + OS << " return " + "llvm::StringSwitch>(Name).\n"; + for (const auto &Rule : SubMatchRule.second) { + if (Rule.isNegatedSubRule()) + OS << " Case(\"" << Rule.getName() << "\", " << Rule.getEnumValue() + << ").\n"; + } + OS << " Default(None);\n"; + OS << " return " + "llvm::StringSwitch>(Name).\n"; + for (const auto &Rule : SubMatchRule.second) { + if (!Rule.isNegatedSubRule()) + OS << " Case(\"" << Rule.getName() << "\", " << Rule.getEnumValue() + << ").\n"; + } + OS << " Default(None);\n"; + OS << "}\n\n"; + } + + // Generate the function that checks for the top-level rules. + OS << "std::pair, " + "llvm::function_ref (StringRef, " + "bool)>> isAttributeSubjectMatchRule(StringRef Name) {\n"; + OS << " return " + "llvm::StringSwitch, " + "llvm::function_ref (StringRef, " + "bool)>>>(Name).\n"; + for (const auto &Rule : Rules) { + if (Rule.isSubRule()) + continue; + std::string SubRuleFunction; + if (SubMatchRules.count(Rule.MetaSubject)) + SubRuleFunction = "isAttributeSubjectMatchSubRuleFor_" + Rule.getName(); + else + SubRuleFunction = "defaultIsAttributeSubjectMatchSubRuleFor"; + OS << " Case(\"" << Rule.getName() << "\", std::make_pair(" + << Rule.getEnumValue() << ", " << SubRuleFunction << ")).\n"; + } + OS << " Default(std::make_pair(None, " + "defaultIsAttributeSubjectMatchSubRuleFor));\n"; + OS << "}\n\n"; + + // Generate the function that checks for the submatch rules. + OS << "const char *validAttributeSubjectMatchSubRules(" + << AttributeSubjectMatchRule::EnumName << " Rule) {\n"; + OS << " switch (Rule) {\n"; + for (const auto &SubMatchRule : SubMatchRules) { + OS << " case " + << AttributeSubjectMatchRule(SubMatchRule.first, nullptr).getEnumValue() + << ":\n"; + OS << " return \"'"; + bool IsFirst = true; + for (const auto &Rule : SubMatchRule.second) { + if (!IsFirst) + OS << ", '"; + IsFirst = false; + if (Rule.isNegatedSubRule()) + OS << "unless("; + OS << Rule.getName(); + if (Rule.isNegatedSubRule()) + OS << ')'; + OS << "'"; + } + OS << "\";\n"; + } + OS << " default: return nullptr;\n"; + OS << " }\n"; + OS << "}\n\n"; +} + template static void forEachUniqueSpelling(const Record &Attr, Fn &&F) { std::vector Spellings = GetFlattenedSpellings(Attr); @@ -2109,6 +2440,17 @@ OS << "#undef PRAGMA_SPELLING_ATTR\n"; } +// Emits the enumeration list for attributes. +void EmitClangAttrSubjectMatchRuleList(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader( + "List of all attribute subject matching rules that Clang recognizes", OS); + PragmaClangAttributeSupport &PragmaAttributeSupport = + getPragmaAttributeSupport(Records); + emitDefaultDefine(OS, "ATTR_MATCH_RULE", nullptr); + PragmaAttributeSupport.emitMatchRuleList(OS); + OS << "#undef ATTR_MATCH_RULE\n"; +} + // Emits the code to read an attribute from a precompiled header. void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS) { emitSourceFileHeader("Attribute deserialization code", OS); @@ -2692,9 +3034,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 +3125,40 @@ return FnName; } +static void +emitAttributeMatchRules(PragmaClangAttributeSupport &PragmaAttributeSupport, + raw_ostream &OS) { + OS << "static bool checkAttributeMatchRuleAppliesTo(const Decl *D, " + << AttributeSubjectMatchRule::EnumName << " rule) {\n"; + OS << " switch (rule) {\n"; + for (const auto &Rule : PragmaAttributeSupport.Rules) { + if (Rule.isAbstractRule()) { + OS << " case " << Rule.getEnumValue() << ":\n"; + OS << " assert(false && \"Abstract matcher rule isn't allowed\");\n"; + OS << " return false;\n"; + continue; + } + std::vector Subjects = Rule.getSubjects(); + assert(!Subjects.empty()); + OS << " case " << Rule.getEnumValue() << ":\n"; + OS << " 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")) + OS << functionNameForCustomAppertainsTo(**I) << "(D)"; + else + OS << "isa<" << GetSubjectWithSuffix(*I) << ">(D)"; + + if (I + 1 != E) + OS << " || "; + } + OS << ";\n"; + } + OS << " }\n}\n\n"; +} + static void GenerateDefaultLangOptRequirements(raw_ostream &OS) { OS << "static bool defaultDiagnoseLangOpts(Sema &, "; OS << "const AttributeList &) {\n"; @@ -2937,6 +3317,9 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) { emitSourceFileHeader("Parsed attribute helpers", OS); + PragmaClangAttributeSupport &PragmaAttributeSupport = + getPragmaAttributeSupport(Records); + // Get the list of parsed attributes, and accept the optional list of // duplicates due to the ParseKind. ParsedAttrMap Dupes; @@ -2970,10 +3353,13 @@ SS << ", " << I->second->isSubClassOf("TypeAttr"); SS << ", " << I->second->isSubClassOf("StmtAttr"); SS << ", " << IsKnownToGCC(*I->second); + SS << ", " << PragmaAttributeSupport.isAttributedSupported(*I->second); SS << ", " << GenerateAppertainsTo(*I->second, OS); SS << ", " << GenerateLangOptRequirements(*I->second, OS); SS << ", " << GenerateTargetRequirements(*I->second, Dupes, OS); SS << ", " << GenerateSpellingIndexToSemanticSpelling(*I->second, OS); + SS << ", " + << PragmaAttributeSupport.generateStrictConformsTo(*I->second, OS); SS << " }"; if (I + 1 != E) @@ -2985,6 +3371,9 @@ OS << "static const ParsedAttrInfo AttrInfoMap[AttributeList::UnknownAttribute + 1] = {\n"; OS << SS.str(); OS << "};\n\n"; + + // Generate the attribute match rules. + emitAttributeMatchRules(PragmaAttributeSupport, OS); } // Emits the kind list of parsed attributes @@ -3124,6 +3513,11 @@ emitClangAttrLateParsedList(Records, OS); } +void EmitClangAttrSubjectMatchRulesParserStringSwitches(RecordKeeper &Records, + raw_ostream &OS) { + getPragmaAttributeSupport(Records).generateParsingHelpers(OS); +} + class DocumentationData { public: const Record *Documentation; @@ -3155,8 +3549,8 @@ Pragma = 1 << 5 }; -static void WriteDocumentation(const DocumentationData &Doc, - raw_ostream &OS) { +static void WriteDocumentation(RecordKeeper &Records, + const DocumentationData &Doc, raw_ostream &OS) { // FIXME: there is no way to have a per-spelling category for the attribute // documentation. This may not be a limiting factor since the spellings // should generally be consistently applied across the category. @@ -3238,7 +3632,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 +3643,9 @@ if (SupportedSpellings & Keyword) OS << "X"; OS << "\", \""; if (SupportedSpellings & Pragma) OS << "X"; + OS << "\", \""; + if (getPragmaAttributeSupport(Records).isAttributedSupported(*Doc.Attribute)) + OS << "X"; OS << "\"\n\n"; // If the attribute is deprecated, print a message about it, and possibly @@ -3315,7 +3712,40 @@ // Walk over each of the attributes in the category and write out their // documentation. for (const auto &Doc : I.second) - WriteDocumentation(Doc, OS); + WriteDocumentation(Records, Doc, OS); + } +} + +void EmitTestPragmaAttributeSupportedAttributes(RecordKeeper &Records, + raw_ostream &OS) { + PragmaClangAttributeSupport Support = getPragmaAttributeSupport(Records); + ParsedAttrMap Attrs = getParsedAttrList(Records); + unsigned NumAttrs = 0; + for (const auto &I : Attrs) { + if (Support.isAttributedSupported(*I.second)) + ++NumAttrs; + } + OS << "#pragma clang attribute supports " << NumAttrs << " attributes:\n"; + for (const auto &I : Attrs) { + if (!Support.isAttributedSupported(*I.second)) + continue; + OS << I.first; + if (I.second->isValueUnset("Subjects")) { + OS << " ()\n"; + continue; + } + const Record *SubjectObj = I.second->getValueAsDef("Subjects"); + std::vector Subjects = + SubjectObj->getValueAsListOfDefs("Subjects"); + OS << " ("; + for (const auto &Subject : llvm::enumerate(Subjects)) { + if (Subject.Index) + OS << ", "; + OS << Support.SubjectsToRules.find(Subject.Value) + ->getSecond() + .getEnumValueName(); + } + OS << ")\n"; } } Index: utils/TableGen/TableGen.cpp =================================================================== --- utils/TableGen/TableGen.cpp +++ utils/TableGen/TableGen.cpp @@ -25,8 +25,10 @@ enum ActionType { GenClangAttrClasses, GenClangAttrParserStringSwitches, + GenClangAttrSubjectMatchRulesParserStringSwitches, GenClangAttrImpl, GenClangAttrList, + GenClangAttrSubjectMatchRuleList, GenClangAttrPCHRead, GenClangAttrPCHWrite, GenClangAttrHasAttributeImpl, @@ -54,7 +56,8 @@ GenArmNeonTest, GenAttrDocs, GenDiagDocs, - GenOptDocs + GenOptDocs, + GenTestPragmaAttributeSupportedAttributes }; namespace { @@ -66,10 +69,17 @@ clEnumValN(GenClangAttrParserStringSwitches, "gen-clang-attr-parser-string-switches", "Generate all parser-related attribute string switches"), + clEnumValN(GenClangAttrSubjectMatchRulesParserStringSwitches, + "gen-clang-attr-subject-match-rules-parser-string-switches", + "Generate all parser-related attribute subject match rule" + "string switches"), clEnumValN(GenClangAttrImpl, "gen-clang-attr-impl", "Generate clang attribute implementations"), clEnumValN(GenClangAttrList, "gen-clang-attr-list", "Generate a clang attribute list"), + clEnumValN(GenClangAttrSubjectMatchRuleList, + "gen-clang-attr-subject-match-rule-list", + "Generate a clang attribute subject match rule list"), clEnumValN(GenClangAttrPCHRead, "gen-clang-attr-pch-read", "Generate clang PCH attribute reader"), clEnumValN(GenClangAttrPCHWrite, "gen-clang-attr-pch-write", @@ -80,8 +90,7 @@ clEnumValN(GenClangAttrSpellingListIndex, "gen-clang-attr-spelling-index", "Generate a clang attribute spelling index"), - clEnumValN(GenClangAttrASTVisitor, - "gen-clang-attr-ast-visitor", + clEnumValN(GenClangAttrASTVisitor, "gen-clang-attr-ast-visitor", "Generate a recursive AST visitor for clang attributes"), clEnumValN(GenClangAttrTemplateInstantiate, "gen-clang-attr-template-instantiate", @@ -137,8 +146,11 @@ "Generate attribute documentation"), clEnumValN(GenDiagDocs, "gen-diag-docs", "Generate diagnostic documentation"), - clEnumValN(GenOptDocs, "gen-opt-docs", - "Generate option documentation"))); + clEnumValN(GenOptDocs, "gen-opt-docs", "Generate option documentation"), + clEnumValN(GenTestPragmaAttributeSupportedAttributes, + "gen-clang-test-pragma-attribute-supported-attributes", + "Generate a list of attributes supported by #pragma clang " + "attribute for testing purposes"))); cl::opt ClangComponent("clang-component", @@ -153,12 +165,18 @@ case GenClangAttrParserStringSwitches: EmitClangAttrParserStringSwitches(Records, OS); break; + case GenClangAttrSubjectMatchRulesParserStringSwitches: + EmitClangAttrSubjectMatchRulesParserStringSwitches(Records, OS); + break; case GenClangAttrImpl: EmitClangAttrImpl(Records, OS); break; case GenClangAttrList: EmitClangAttrList(Records, OS); break; + case GenClangAttrSubjectMatchRuleList: + EmitClangAttrSubjectMatchRuleList(Records, OS); + break; case GenClangAttrPCHRead: EmitClangAttrPCHRead(Records, OS); break; @@ -244,6 +262,9 @@ case GenOptDocs: EmitClangOptDocs(Records, OS); break; + case GenTestPragmaAttributeSupportedAttributes: + EmitTestPragmaAttributeSupportedAttributes(Records, OS); + break; } return false; Index: utils/TableGen/TableGenBackends.h =================================================================== --- utils/TableGen/TableGenBackends.h +++ utils/TableGen/TableGenBackends.h @@ -33,9 +33,12 @@ const std::string &N, const std::string &S); void EmitClangAttrParserStringSwitches(RecordKeeper &Records, raw_ostream &OS); +void EmitClangAttrSubjectMatchRulesParserStringSwitches(RecordKeeper &Records, + raw_ostream &OS); void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS); void EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS); void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS); +void EmitClangAttrSubjectMatchRuleList(RecordKeeper &Records, raw_ostream &OS); void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS); void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS); void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS); @@ -72,6 +75,9 @@ void EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS); void EmitClangOptDocs(RecordKeeper &Records, raw_ostream &OS); +void EmitTestPragmaAttributeSupportedAttributes(RecordKeeper &Records, + raw_ostream &OS); + } // end namespace clang #endif