r300556 - Add #pragma clang attribute

Alex Lorenz via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 18 07:33:40 PDT 2017


Author: arphaman
Date: Tue Apr 18 09:33:39 2017
New Revision: 300556

URL: http://llvm.org/viewvc/llvm-project?rev=300556&view=rev
Log:
Add #pragma clang attribute

This is a recommit of r300539 that was reverted in r300543 due to test failures.
The original commit message is displayed below:

The new '#pragma clang attribute' directive can be used to apply attributes to
multiple declarations. An attribute must satisfy the following conditions to
be supported by the pragma:
- It must have a subject list that's defined in the TableGen file.
- It must be documented.
- It must not be late parsed.
- It must have a GNU/C++11 spelling.

Differential Revision: https://reviews.llvm.org/D30009

Added:
    cfe/trunk/include/clang/Basic/AttrSubjectMatchRules.h
    cfe/trunk/test/FixIt/fixit-pragma-attribute.c
    cfe/trunk/test/FixIt/fixit-pragma-attribute.cpp
    cfe/trunk/test/Misc/pragma-attribute-cxx-subject-match-rules.cpp
    cfe/trunk/test/Misc/pragma-attribute-cxx.cpp
    cfe/trunk/test/Misc/pragma-attribute-objc-subject-match-rules.m
    cfe/trunk/test/Misc/pragma-attribute-objc.m
    cfe/trunk/test/Misc/pragma-attribute-strict-subjects.c
    cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test
    cfe/trunk/test/Parser/pragma-attribute-declspec.cpp
    cfe/trunk/test/Parser/pragma-attribute.cpp
    cfe/trunk/test/Sema/pragma-attribute-strict-subjects.c
    cfe/trunk/test/Sema/pragma-attribute.c
Modified:
    cfe/trunk/docs/LanguageExtensions.rst
    cfe/trunk/include/clang/Basic/Attr.td
    cfe/trunk/include/clang/Basic/CMakeLists.txt
    cfe/trunk/include/clang/Basic/DiagnosticGroups.td
    cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Basic/TokenKinds.def
    cfe/trunk/include/clang/Parse/CMakeLists.txt
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/include/clang/Sema/AttributeList.h
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Basic/Attributes.cpp
    cfe/trunk/lib/Parse/ParsePragma.cpp
    cfe/trunk/lib/Parse/ParseStmt.cpp
    cfe/trunk/lib/Parse/Parser.cpp
    cfe/trunk/lib/Sema/AttributeList.cpp
    cfe/trunk/lib/Sema/Sema.cpp
    cfe/trunk/lib/Sema/SemaAttr.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaDeclAttr.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaDeclObjC.cpp
    cfe/trunk/test/lit.cfg
    cfe/trunk/test/lit.site.cfg.in
    cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp
    cfe/trunk/utils/TableGen/TableGen.cpp
    cfe/trunk/utils/TableGen/TableGenBackends.h

Modified: cfe/trunk/docs/LanguageExtensions.rst
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/LanguageExtensions.rst?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/docs/LanguageExtensions.rst (original)
+++ cfe/trunk/docs/LanguageExtensions.rst Tue Apr 18 09:33:39 2017
@@ -2346,3 +2346,178 @@ statements in C)
 The pragma can also be used with ``off`` which turns FP contraction off for a
 section of the code. This can be useful when fast contraction is otherwise
 enabled for the translation unit with the ``-ffp-contract=fast`` flag.
+
+Specifying an attribute for multiple declarations (#pragma clang attribute)
+===========================================================================
+
+The ``#pragma clang attribute`` directive can be used to apply an attribute to
+multiple declarations. The ``#pragma clang attribute push`` variation of the
+directive pushes a new attribute to the attribute stack. The declarations that
+follow the pragma receive the attributes that are on the attribute stack, until
+the stack is cleared using a ``#pragma clang attribute pop`` directive. Multiple
+push directives can be nested inside each other.
+
+The attributes that are used in the ``#pragma clang attribute`` directives
+can be written using the GNU-style syntax:
+
+.. code-block:: c++
+
+  #pragma clang attribute push(__attribute__((annotate("custom"))), apply_to = function)
+
+  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:
+
+.. code-block:: c++
+
+  #pragma clang attribute push([[noreturn]], apply_to = function)
+
+  void function(); // The function now has the [[noreturn]] attribute
+
+  #pragma clang attribute pop
+
+The ``__declspec`` style syntax is also supported:
+
+.. code-block:: c++
+
+  #pragma clang attribute push(__declspec(dllexport), apply_to = function)
+
+  void function(); // The function now has the __declspec(dllexport) attribute
+
+  #pragma clang attribute pop
+
+A single push directive accepts only one attribute regardless of the syntax
+used.
+
+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. The compiler expects an
+identifier that corresponds to the subject set specifier. The ``apply_to``
+specifier is currently the only supported subject set specifier. It allows you
+to specify match rules that form a subset of the attribute's allowed 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_to``:
+
+.. code-block:: c++
+
+  #pragma clang attribute push([[nodiscard]], apply_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_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
+
+Multiple match rules can be specified using the ``any`` match rule, as shown
+in the example above. The ``any`` rule applies attributes to all declarations
+that are matched by at least one of the rules in the ``any``. It doesn't nest
+and can't be used inside the other match rules. Redundant match rules or rules
+that conflict with one another should not be used inside of ``any``.
+
+Clang supports the following match rules:
+
+- ``function``: Can be used to apply attributes to functions. This includes C++
+  member functions, static functions, operators, and constructors/destructors.
+
+- ``function(is_member)``: Can be used to apply attributes to C++ member
+  functions. This includes members like static functions, operators, and
+  constructors/destructors.
+
+- ``hasType(functionType)``: Can be used to apply attributes to functions, C++
+  member functions, 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_constant``: Can be used to apply attributes to enumerators.
+
+- ``variable``: Can be used to apply attributes to variables, including
+  local variables, parameters, global variables, and static member variables.
+  It does not apply attributes to instance member variables or Objective-C
+  ivars.
+
+- ``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 member variables
+  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_category``: Can be used to apply attributes to category declarations,
+  including class extensions.
+
+- ``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.
+
+The use of ``unless`` in match rules is currently restricted to a strict set of
+sub-rules that are used by the supported attributes. That means that even though
+``variable(unless(is_parameter))`` is a valid match rule,
+``variable(unless(is_thread_local))`` is not.
+
+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 <AttributeReference>`.
+
+The attributes are applied to all matching declarations individually, even when
+the attribute is semantically incorrect. The attributes that aren't applied to
+any declaration are not verified semantically.

Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Tue Apr 18 09:33:39 2017
@@ -248,6 +248,8 @@ def COnly : LangOpt<"CPlusPlus", 1>;
 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,102 @@ def TargetMicrosoftCXXABI : TargetArch<[
   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<string name, list<AttrSubject> subjects,
+                                bit negated = 0> {
+  string Name = name;
+  list<AttrSubject> 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<LangOpt> LangOpts = [];
+}
+class AttrSubjectMatcherRule<string name, list<AttrSubject> subjects,
+                             list<AttrSubjectMatcherSubRule> subrules = []> {
+  string Name = name;
+  list<AttrSubject> Subjects = subjects;
+  list<AttrSubjectMatcherSubRule> 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<LangOpt> LangOpts = [];
+}
+
+// function(is_member)
+def SubRuleForCXXMethod : AttrSubjectMatcherSubRule<"is_member", [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_constant",
+                                                           [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 SubjectMatcherForObjCCategory : AttrSubjectMatcherRule<"objc_category",
+                                                           [ObjCCategory]> {
+  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<Spelling> Spellings;
@@ -305,6 +403,14 @@ class Attr {
   // Set to true if this attribute meaningful when applied to or inherited 
   // in a class template definition.
   bit MeaningfulToClassTemplateDefinition = 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<LangOpt> LangOpts = [];
@@ -478,6 +584,9 @@ def AnalyzerNoReturn : InheritableAttr {
 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];
 }
 

Added: cfe/trunk/include/clang/Basic/AttrSubjectMatchRules.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/AttrSubjectMatchRules.h?rev=300556&view=auto
==============================================================================
--- cfe/trunk/include/clang/Basic/AttrSubjectMatchRules.h (added)
+++ cfe/trunk/include/clang/Basic/AttrSubjectMatchRules.h Tue Apr 18 09:33:39 2017
@@ -0,0 +1,46 @@
+//===-- 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<SubjectMatchRule, SourceRange>;
+
+} // end namespace attr
+} // end namespace clang
+
+namespace llvm {
+
+template <>
+struct DenseMapInfo<clang::attr::SubjectMatchRule> : DenseMapInfo<int> {
+  static inline clang::attr::SubjectMatchRule getEmptyKey() {
+    return (clang::attr::SubjectMatchRule)DenseMapInfo<int>::getEmptyKey();
+  }
+  static inline clang::attr::SubjectMatchRule getTombstoneKey() {
+    return (clang::attr::SubjectMatchRule)DenseMapInfo<int>::getTombstoneKey();
+  }
+};
+
+} // end namespace llvm
+
+#endif

Modified: cfe/trunk/include/clang/Basic/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/CMakeLists.txt?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/CMakeLists.txt (original)
+++ cfe/trunk/include/clang/Basic/CMakeLists.txt Tue Apr 18 09:33:39 2017
@@ -28,6 +28,11 @@ clang_tablegen(AttrList.inc -gen-clang-a
   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

Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Tue Apr 18 09:33:39 2017
@@ -461,7 +461,9 @@ def Uninitialized  : DiagGroup<"uninitia
 def IgnoredPragmaIntrinsic : DiagGroup<"ignored-pragma-intrinsic">;
 def UnknownPragmas : DiagGroup<"unknown-pragmas">;
 def IgnoredPragmas : DiagGroup<"ignored-pragmas", [IgnoredPragmaIntrinsic]>;
-def Pragmas : DiagGroup<"pragmas", [UnknownPragmas, IgnoredPragmas]>;
+def PragmaClangAttribute : DiagGroup<"pragma-clang-attribute">;
+def Pragmas : DiagGroup<"pragmas", [UnknownPragmas, IgnoredPragmas,
+                                    PragmaClangAttribute]>;
 def UnknownWarningOption : DiagGroup<"unknown-warning-option">;
 def NSobjectAttribute : DiagGroup<"NSObject-attribute">;
 def IndependentClassAttribute : DiagGroup<"IndependentClass-attribute">;

Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Tue Apr 18 09:33:39 2017
@@ -979,6 +979,43 @@ def err_pragma_optimize_invalid_argument
   "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_expected_attribute_syntax : Error<
+  "expected an attribute that is specified using the GNU, C++11 or '__declspec'"
+  " syntax">;
+def note_pragma_attribute_use_attribute_kw : Note<"use the GNU '__attribute__' "
+  "syntax">;
+def err_pragma_attribute_invalid_subject_set_specifier : Error<
+  "expected attribute subject set specifier 'apply_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 : Error<
+  "expected an identifier that corresponds to an attribute subject matcher "
+  "sub-rule; '%0' matcher %select{does not support sub-rules|supports the "
+  "following sub-rules: %2|}1">;
+def err_pragma_attribute_unknown_subject_sub_rule : Error<
+  "%select{invalid use of|unknown}2 attribute subject matcher sub-rule '%0'; "
+  "'%1' matcher %select{does not support sub-rules|supports the following "
+  "sub-rules: %3}2">;
+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">;

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Apr 18 09:33:39 2017
@@ -750,6 +750,25 @@ def err_pragma_loop_compatibility : Erro
 def err_pragma_loop_precedes_nonloop : Error<
   "expected a for, while, or do-while loop to follow '%0'">;
 
+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_stack_mismatch : Error<
+  "'#pragma clang attribute pop' with no matching '#pragma clang attribute push'">;
+def warn_pragma_attribute_unused : Warning<
+  "unused attribute %0 in '#pragma clang attribute push' region">,
+  InGroup<PragmaClangAttribute>;
+def note_pragma_attribute_region_ends_here : Note<
+  "'#pragma clang attribute push' regions ends here">;
+def err_pragma_attribute_no_pop_eof : Error<"unterminated "
+  "'#pragma clang attribute push' at end of file">;
+def note_pragma_attribute_applied_decl_here : Note<
+  "when applied to this declaration">;
+
 /// Objective-C parser diagnostics
 def err_duplicate_class_def : Error<
   "duplicate interface definition for class %0">;

Modified: cfe/trunk/include/clang/Basic/TokenKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/TokenKinds.def?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/TokenKinds.def (original)
+++ cfe/trunk/include/clang/Basic/TokenKinds.def Tue Apr 18 09:33:39 2017
@@ -790,6 +790,9 @@ ANNOTATION(pragma_loop_hint)
 
 ANNOTATION(pragma_fp)
 
+// 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)

Modified: cfe/trunk/include/clang/Parse/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/CMakeLists.txt?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/CMakeLists.txt (original)
+++ cfe/trunk/include/clang/Parse/CMakeLists.txt Tue Apr 18 09:33:39 2017
@@ -2,3 +2,9 @@ clang_tablegen(AttrParserStringSwitches.
   -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)

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Tue Apr 18 09:33:39 2017
@@ -184,6 +184,7 @@ class Parser : public CodeCompletionHand
   std::unique_ptr<PragmaHandler> UnrollHintHandler;
   std::unique_ptr<PragmaHandler> NoUnrollHintHandler;
   std::unique_ptr<PragmaHandler> FPHandler;
+  std::unique_ptr<PragmaHandler> AttributePragmaHandler;
 
   std::unique_ptr<CommentHandler> CommentSemaHandler;
 
@@ -565,6 +566,12 @@ private:
   /// #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.

Modified: cfe/trunk/include/clang/Sema/AttributeList.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/AttributeList.h?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/AttributeList.h (original)
+++ cfe/trunk/include/clang/Sema/AttributeList.h Tue Apr 18 09:33:39 2017
@@ -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 @@ public:
   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(const LangOptions &LangOpts,
+                     SmallVectorImpl<std::pair<attr::SubjectMatchRule, bool>>
+                         &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 @@ public:
   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; }

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Tue Apr 18 09:33:39 2017
@@ -437,6 +437,20 @@ public:
   /// 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 {
+    SourceLocation Loc;
+    AttributeList *Attribute;
+    SmallVector<attr::SubjectMatchRule, 4> MatchRules;
+    bool IsUsed;
+  };
+  SmallVector<PragmaAttributeEntry, 2> PragmaAttributeStack;
+
+  /// \brief The declaration that is currently receiving an attribute from the
+  /// #pragma attribute stack.
+  const Decl *PragmaAttributeCurrentTargetDecl;
+
   /// \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.
@@ -7206,9 +7220,13 @@ public:
       PrintInstantiationStack();
       LastEmittedCodeSynthesisContextDepth = CodeSynthesisContexts.size();
     }
+    if (PragmaAttributeCurrentTargetDecl)
+      PrintPragmaAttributeInstantiationPoint();
   }
   void PrintInstantiationStack();
 
+  void PrintPragmaAttributeInstantiationPoint();
+
   /// \brief Determines whether we are currently in a context where
   /// template argument substitution failures are not considered
   /// errors.
@@ -8152,6 +8170,20 @@ public:
   /// 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);
+
+  /// \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);
+
+  void DiagnoseUnterminatedPragmaAttribute();
+
   /// \brief Called on well formed \#pragma clang optimize.
   void ActOnPragmaOptimize(bool On, SourceLocation PragmaLoc);
 

Modified: cfe/trunk/lib/Basic/Attributes.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/Attributes.cpp?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/lib/Basic/Attributes.cpp (original)
+++ cfe/trunk/lib/Basic/Attributes.cpp Tue Apr 18 09:33:39 2017
@@ -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 @@ int clang::hasAttribute(AttrSyntax Synta
 
   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");
+}

Modified: cfe/trunk/lib/Parse/ParsePragma.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParsePragma.cpp?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParsePragma.cpp (original)
+++ cfe/trunk/lib/Parse/ParsePragma.cpp Tue Apr 18 09:33:39 2017
@@ -183,6 +183,17 @@ private:
   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() {
@@ -275,6 +286,9 @@ void Parser::initializePragmaHandlers()
 
   FPHandler.reset(new PragmaFPHandler());
   PP.AddPragmaHandler("clang", FPHandler.get());
+
+  AttributePragmaHandler.reset(new PragmaAttributeHandler(AttrFactory));
+  PP.AddPragmaHandler("clang", AttributePragmaHandler.get());
 }
 
 void Parser::resetPragmaHandlers() {
@@ -356,6 +370,9 @@ void Parser::resetPragmaHandlers() {
 
   PP.RemovePragmaHandler("clang", FPHandler.get());
   FPHandler.reset();
+
+  PP.RemovePragmaHandler("clang", AttributePragmaHandler.get());
+  AttributePragmaHandler.reset();
 }
 
 /// \brief Handle the annotation token produced for #pragma unused(...)
@@ -966,6 +983,422 @@ bool Parser::HandlePragmaLoopHint(LoopHi
   return true;
 }
 
+namespace {
+struct PragmaAttributeInfo {
+  enum ActionType { Push, Pop };
+  ParsedAttributes &Attributes;
+  ActionType Action;
+  ArrayRef<Token> 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"
+  }
+  llvm_unreachable("Invalid attribute subject match rule");
+  return false;
+}
+
+static void diagnoseExpectedAttributeSubjectSubRule(
+    Parser &PRef, attr::SubjectMatchRule PrimaryRule, StringRef PrimaryRuleName,
+    SourceLocation SubRuleLoc) {
+  auto Diagnostic =
+      PRef.Diag(SubRuleLoc,
+                diag::err_pragma_attribute_expected_subject_sub_identifier)
+      << PrimaryRuleName;
+  if (const char *SubRules = validAttributeSubjectMatchSubRules(PrimaryRule))
+    Diagnostic << /*SubRulesSupported=*/1 << SubRules;
+  else
+    Diagnostic << /*SubRulesSupported=*/0;
+}
+
+static void diagnoseUnknownAttributeSubjectSubRule(
+    Parser &PRef, attr::SubjectMatchRule PrimaryRule, StringRef PrimaryRuleName,
+    StringRef SubRuleName, SourceLocation SubRuleLoc) {
+
+  auto Diagnostic =
+      PRef.Diag(SubRuleLoc, diag::err_pragma_attribute_unknown_subject_sub_rule)
+      << SubRuleName << PrimaryRuleName;
+  if (const char *SubRules = validAttributeSubjectMatchSubRules(PrimaryRule))
+    Diagnostic << /*SubRulesSupported=*/1 << SubRules;
+  else
+    Diagnostic << /*SubRulesSupported=*/0;
+}
+
+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;
+    }
+    std::pair<Optional<attr::SubjectMatchRule>,
+              Optional<attr::SubjectMatchRule> (*)(StringRef, bool)>
+        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()) {
+      diagnoseExpectedAttributeSubjectSubRule(*this, PrimaryRule, Name,
+                                              Tok.getLocation());
+      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()) {
+        diagnoseExpectedAttributeSubjectSubRule(*this, PrimaryRule, Name,
+                                                SubRuleLoc);
+        return true;
+      }
+      auto SubRuleOrNone = Rule.second(SubRuleName, /*IsUnless=*/true);
+      if (!SubRuleOrNone) {
+        std::string SubRuleUnlessName = "unless(" + SubRuleName.str() + ")";
+        diagnoseUnknownAttributeSubjectSubRule(*this, PrimaryRule, Name,
+                                               SubRuleUnlessName, SubRuleLoc);
+        return true;
+      }
+      SubRule = *SubRuleOrNone;
+      ConsumeToken();
+      if (Parens.consumeClose())
+        return true;
+    } else {
+      auto SubRuleOrNone = Rule.second(SubRuleName, /*IsUnless=*/false);
+      if (!SubRuleOrNone) {
+        diagnoseUnknownAttributeSubjectSubRule(*this, PrimaryRule, Name,
+                                               SubRuleName, Tok.getLocation());
+        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;
+}
+
+namespace {
+
+/// Describes the stage at which attribute subject rule parsing was interruped.
+enum class MissingAttributeSubjectRulesRecoveryPoint {
+  Comma,
+  ApplyTo,
+  Equals,
+  Any,
+  None,
+};
+
+MissingAttributeSubjectRulesRecoveryPoint
+getAttributeSubjectRulesRecoveryPointForToken(const Token &Tok) {
+  if (const auto *II = Tok.getIdentifierInfo()) {
+    if (II->isStr("apply_to"))
+      return MissingAttributeSubjectRulesRecoveryPoint::ApplyTo;
+    if (II->isStr("any"))
+      return MissingAttributeSubjectRulesRecoveryPoint::Any;
+  }
+  if (Tok.is(tok::equal))
+    return MissingAttributeSubjectRulesRecoveryPoint::Equals;
+  return MissingAttributeSubjectRulesRecoveryPoint::None;
+}
+
+/// Creates a diagnostic for the attribute subject rule parsing diagnostic that
+/// suggests the possible attribute subject rules in a fix-it together with
+/// any other missing tokens.
+DiagnosticBuilder createExpectedAttributeSubjectRulesTokenDiagnostic(
+    unsigned DiagID, AttributeList &Attribute,
+    MissingAttributeSubjectRulesRecoveryPoint Point, Parser &PRef) {
+  SourceLocation Loc = PRef.getEndOfPreviousToken();
+  if (Loc.isInvalid())
+    Loc = PRef.getCurToken().getLocation();
+  auto Diagnostic = PRef.Diag(Loc, DiagID);
+  std::string FixIt;
+  MissingAttributeSubjectRulesRecoveryPoint EndPoint =
+      getAttributeSubjectRulesRecoveryPointForToken(PRef.getCurToken());
+  if (Point == MissingAttributeSubjectRulesRecoveryPoint::Comma)
+    FixIt = ", ";
+  if (Point <= MissingAttributeSubjectRulesRecoveryPoint::ApplyTo &&
+      EndPoint > MissingAttributeSubjectRulesRecoveryPoint::ApplyTo)
+    FixIt += "apply_to";
+  if (Point <= MissingAttributeSubjectRulesRecoveryPoint::Equals &&
+      EndPoint > MissingAttributeSubjectRulesRecoveryPoint::Equals)
+    FixIt += " = ";
+  SourceRange FixItRange(Loc);
+  if (EndPoint == MissingAttributeSubjectRulesRecoveryPoint::None) {
+    // Gather the subject match rules that are supported by the attribute.
+    SmallVector<std::pair<attr::SubjectMatchRule, bool>, 4> SubjectMatchRuleSet;
+    Attribute.getMatchRules(PRef.getLangOpts(), SubjectMatchRuleSet);
+    if (SubjectMatchRuleSet.empty()) {
+      // FIXME: We can emit a "fix-it" with a subject list placeholder when
+      // placeholders will be supported by the fix-its.
+      return Diagnostic;
+    }
+    FixIt += "any(";
+    bool NeedsComma = false;
+    for (const auto &I : SubjectMatchRuleSet) {
+      // Ensure that the missing rule is reported in the fix-it only when it's
+      // supported in the current language mode.
+      if (!I.second)
+        continue;
+      if (NeedsComma)
+        FixIt += ", ";
+      else
+        NeedsComma = true;
+      FixIt += attr::getSubjectMatchRuleSpelling(I.first);
+    }
+    FixIt += ")";
+    // Check if we need to remove the range
+    PRef.SkipUntil(tok::eof, Parser::StopBeforeMatch);
+    FixItRange.setEnd(PRef.getCurToken().getLocation());
+  }
+  if (FixItRange.getBegin() == FixItRange.getEnd())
+    Diagnostic << FixItHint::CreateInsertion(FixItRange.getBegin(), FixIt);
+  else
+    Diagnostic << FixItHint::CreateReplacement(
+        CharSourceRange::getCharRange(FixItRange), FixIt);
+  return Diagnostic;
+}
+
+} // end anonymous namespace
+
+void Parser::HandlePragmaAttribute() {
+  assert(Tok.is(tok::annot_pragma_attribute) &&
+         "Expected #pragma attribute annotation token");
+  SourceLocation PragmaLoc = Tok.getLocation();
+  auto *Info = static_cast<PragmaAttributeInfo *>(Tok.getAnnotationValue());
+  if (Info->Action == PragmaAttributeInfo::Pop) {
+    ConsumeToken();
+    Actions.ActOnPragmaAttributePop(PragmaLoc);
+    return;
+  }
+  // Parse the actual attribute with its arguments.
+  assert(Info->Action == PragmaAttributeInfo::Push &&
+         "Unexpected #pragma attribute command");
+  PP.EnterTokenStream(Info->Tokens, /*DisableMacroExpansion=*/false);
+  ConsumeToken();
+
+  ParsedAttributes &Attrs = Info->Attributes;
+  Attrs.clearListOnly();
+
+  auto SkipToEnd = [this]() {
+    SkipUntil(tok::eof, StopBeforeMatch);
+    ConsumeToken();
+  };
+
+  if (Tok.is(tok::l_square) && NextToken().is(tok::l_square)) {
+    // Parse the CXX11 style attribute.
+    ParseCXX11AttributeSpecifier(Attrs);
+  } else if (Tok.is(tok::kw___attribute)) {
+    ConsumeToken();
+    if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after,
+                         "attribute"))
+      return SkipToEnd();
+    if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after, "("))
+      return SkipToEnd();
+
+    if (Tok.isNot(tok::identifier)) {
+      Diag(Tok, diag::err_pragma_attribute_expected_attribute_name);
+      SkipToEnd();
+      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);
+
+    if (ExpectAndConsume(tok::r_paren))
+      return SkipToEnd();
+    if (ExpectAndConsume(tok::r_paren))
+      return SkipToEnd();
+  } else if (Tok.is(tok::kw___declspec)) {
+    ParseMicrosoftDeclSpecs(Attrs);
+  } else {
+    Diag(Tok, diag::err_pragma_attribute_expected_attribute_syntax);
+    if (Tok.getIdentifierInfo()) {
+      // If we suspect that this is an attribute suggest the use of
+      // '__attribute__'.
+      if (AttributeList::getKind(Tok.getIdentifierInfo(), /*ScopeName=*/nullptr,
+                                 AttributeList::AS_GNU) !=
+          AttributeList::UnknownAttribute) {
+        SourceLocation InsertStartLoc = Tok.getLocation();
+        ConsumeToken();
+        if (Tok.is(tok::l_paren)) {
+          ConsumeAnyToken();
+          SkipUntil(tok::r_paren, StopBeforeMatch);
+          if (Tok.isNot(tok::r_paren))
+            return SkipToEnd();
+        }
+        Diag(Tok, diag::note_pragma_attribute_use_attribute_kw)
+            << FixItHint::CreateInsertion(InsertStartLoc, "__attribute__((")
+            << FixItHint::CreateInsertion(Tok.getEndLoc(), "))");
+      }
+    }
+    SkipToEnd();
+    return;
+  }
+
+  if (!Attrs.getList() || Attrs.getList()->isInvalid()) {
+    SkipToEnd();
+    return;
+  }
+
+  // Ensure that we don't have more than one attribute.
+  if (Attrs.getList()->getNext()) {
+    SourceLocation Loc = Attrs.getList()->getNext()->getLoc();
+    Diag(Loc, diag::err_pragma_attribute_multiple_attributes);
+    SkipToEnd();
+    return;
+  }
+
+  if (!Attrs.getList()->isSupportedByPragmaAttribute()) {
+    Diag(PragmaLoc, diag::err_pragma_attribute_unsupported_attribute)
+        << Attrs.getList()->getName();
+    SkipToEnd();
+    return;
+  }
+  AttributeList &Attribute = *Attrs.getList();
+
+  // Parse the subject-list.
+  if (!TryConsumeToken(tok::comma)) {
+    createExpectedAttributeSubjectRulesTokenDiagnostic(
+        diag::err_expected, Attribute,
+        MissingAttributeSubjectRulesRecoveryPoint::Comma, *this)
+        << tok::comma;
+    SkipToEnd();
+    return;
+  }
+
+  if (Tok.isNot(tok::identifier)) {
+    createExpectedAttributeSubjectRulesTokenDiagnostic(
+        diag::err_pragma_attribute_invalid_subject_set_specifier, Attribute,
+        MissingAttributeSubjectRulesRecoveryPoint::ApplyTo, *this);
+    SkipToEnd();
+    return;
+  }
+  const IdentifierInfo *II = Tok.getIdentifierInfo();
+  if (!II->isStr("apply_to")) {
+    createExpectedAttributeSubjectRulesTokenDiagnostic(
+        diag::err_pragma_attribute_invalid_subject_set_specifier, Attribute,
+        MissingAttributeSubjectRulesRecoveryPoint::ApplyTo, *this);
+    SkipToEnd();
+    return;
+  }
+  ConsumeToken();
+
+  if (!TryConsumeToken(tok::equal)) {
+    createExpectedAttributeSubjectRulesTokenDiagnostic(
+        diag::err_expected, Attribute,
+        MissingAttributeSubjectRulesRecoveryPoint::Equals, *this)
+        << tok::equal;
+    SkipToEnd();
+    return;
+  }
+
+  attr::ParsedSubjectMatchRuleSet SubjectMatchRules;
+  SourceLocation AnyLoc, LastMatchRuleEndLoc;
+  if (ParsePragmaAttributeSubjectMatchRuleSet(SubjectMatchRules, AnyLoc,
+                                              LastMatchRuleEndLoc)) {
+    SkipToEnd();
+    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);
+    SkipToEnd();
+    return;
+  }
+
+  // Consume the eof terminator token.
+  ConsumeToken();
+
+  Actions.ActOnPragmaAttributePush(Attribute, PragmaLoc,
+                                   std::move(SubjectMatchRules));
+}
+
 // #pragma GCC visibility comes in two variants:
 //   'push' '(' [visibility] ')'
 //   'pop'
@@ -2395,3 +2828,104 @@ void PragmaForceCUDAHostDeviceHandler::H
     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
+///
+/// The subject-set clause defines the set of declarations which receive the
+/// attribute. Its exact syntax is described in the LanguageExtensions document
+/// in Clang's documentation.
+///
+/// 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<Token, 16> 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;
+    }
+    SourceLocation EndLoc = Tok.getLocation();
+    PP.Lex(Tok);
+
+    // Terminate the attribute for parsing.
+    Token EOFTok;
+    EOFTok.startToken();
+    EOFTok.setKind(tok::eof);
+    EOFTok.setLocation(EndLoc);
+    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<Token[]>(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<void *>(Info));
+  PP.EnterTokenStream(std::move(TokenArray), 1,
+                      /*DisableMacroExpansion=*/false);
+}

Modified: cfe/trunk/lib/Parse/ParseStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseStmt.cpp?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseStmt.cpp (original)
+++ cfe/trunk/lib/Parse/ParseStmt.cpp Tue Apr 18 09:33:39 2017
@@ -382,6 +382,10 @@ Retry:
   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.

Modified: cfe/trunk/lib/Parse/Parser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/Parser.cpp?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/Parser.cpp (original)
+++ cfe/trunk/lib/Parse/Parser.cpp Tue Apr 18 09:33:39 2017
@@ -602,6 +602,10 @@ bool Parser::ParseTopLevelDecl(DeclGroup
     ConsumeToken();
     return false;
 
+  case tok::annot_pragma_attribute:
+    HandlePragmaAttribute();
+    return false;
+
   case tok::eof:
     // Late template parsing can begin.
     if (getLangOpts().DelayedTemplateParsing)

Modified: cfe/trunk/lib/Sema/AttributeList.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AttributeList.cpp?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AttributeList.cpp (original)
+++ cfe/trunk/lib/Sema/AttributeList.cpp Tue Apr 18 09:33:39 2017
@@ -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 @@ struct ParsedAttrInfo {
   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<std::pair<attr::SubjectMatchRule, bool>> &Rules,
+      const LangOptions &LangOpts);
 };
 
 namespace {
@@ -192,6 +197,18 @@ bool AttributeList::diagnoseAppertainsTo
   return getInfo(*this).DiagAppertainsToDecl(S, *this, D);
 }
 
+bool AttributeList::appliesToDecl(const Decl *D,
+                                  attr::SubjectMatchRule MatchRule) const {
+  return checkAttributeMatchRuleAppliesTo(D, MatchRule);
+}
+
+void AttributeList::getMatchRules(
+    const LangOptions &LangOpts,
+    SmallVectorImpl<std::pair<attr::SubjectMatchRule, bool>> &MatchRules)
+    const {
+  return getInfo(*this).GetPragmaAttributeMatchRules(MatchRules, LangOpts);
+}
+
 bool AttributeList::diagnoseLangOpts(Sema &S) const {
   return getInfo(*this).DiagLangOpts(S, *this);
 }
@@ -216,6 +233,10 @@ bool AttributeList::isKnownToGCC() const
   return getInfo(*this).IsKnownToGCC;
 }
 
+bool AttributeList::isSupportedByPragmaAttribute() const {
+  return getInfo(*this).IsSupportedByPragmaAttribute;
+}
+
 unsigned AttributeList::getSemanticSpelling() const {
   return getInfo(*this).SpellingIndexToSemanticSpelling(*this);
 }

Modified: cfe/trunk/lib/Sema/Sema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.cpp (original)
+++ cfe/trunk/lib/Sema/Sema.cpp Tue Apr 18 09:33:39 2017
@@ -71,42 +71,35 @@ void Sema::ActOnTranslationUnitScope(Sco
 }
 
 Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
-           TranslationUnitKind TUKind,
-           CodeCompleteConsumer *CodeCompleter)
-  : ExternalSource(nullptr),
-    isMultiplexExternalSource(false), FPFeatures(pp.getLangOpts()),
-    LangOpts(pp.getLangOpts()), PP(pp), Context(ctxt), Consumer(consumer),
-    Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()),
-    CollectStats(false), CodeCompleter(CodeCompleter),
-    CurContext(nullptr), OriginalLexicalContext(nullptr),
-    MSStructPragmaOn(false),
-    MSPointerToMemberRepresentationMethod(
-        LangOpts.getMSPointerToMemberRepresentationMethod()),
-    VtorDispStack(MSVtorDispAttr::Mode(LangOpts.VtorDispMode)),
-    PackStack(0), DataSegStack(nullptr), BSSSegStack(nullptr),
-    ConstSegStack(nullptr), CodeSegStack(nullptr), CurInitSeg(nullptr),
-    VisContext(nullptr),
-    IsBuildingRecoveryCallExpr(false),
-    Cleanup{}, LateTemplateParser(nullptr),
-    LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr), IdResolver(pp),
-    StdExperimentalNamespaceCache(nullptr), StdInitializerList(nullptr),
-    CXXTypeInfoDecl(nullptr), MSVCGuidDecl(nullptr),
-    NSNumberDecl(nullptr), NSValueDecl(nullptr),
-    NSStringDecl(nullptr), StringWithUTF8StringMethod(nullptr),
-    ValueWithBytesObjCTypeMethod(nullptr),
-    NSArrayDecl(nullptr), ArrayWithObjectsMethod(nullptr),
-    NSDictionaryDecl(nullptr), DictionaryWithObjectsMethod(nullptr),
-    GlobalNewDeleteDeclared(false),
-    TUKind(TUKind),
-    NumSFINAEErrors(0),
-    CachedFakeTopLevelModule(nullptr),
-    AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false),
-    NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1),
-    CurrentInstantiationScope(nullptr), DisableTypoCorrection(false),
-    TyposCorrected(0), AnalysisWarnings(*this), ThreadSafetyDeclCache(nullptr),
-    VarDataSharingAttributesStack(nullptr), CurScope(nullptr),
-    Ident_super(nullptr), Ident___float128(nullptr)
-{
+           TranslationUnitKind TUKind, CodeCompleteConsumer *CodeCompleter)
+    : ExternalSource(nullptr), isMultiplexExternalSource(false),
+      FPFeatures(pp.getLangOpts()), LangOpts(pp.getLangOpts()), PP(pp),
+      Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()),
+      SourceMgr(PP.getSourceManager()), CollectStats(false),
+      CodeCompleter(CodeCompleter), CurContext(nullptr),
+      OriginalLexicalContext(nullptr), MSStructPragmaOn(false),
+      MSPointerToMemberRepresentationMethod(
+          LangOpts.getMSPointerToMemberRepresentationMethod()),
+      VtorDispStack(MSVtorDispAttr::Mode(LangOpts.VtorDispMode)), PackStack(0),
+      DataSegStack(nullptr), BSSSegStack(nullptr), ConstSegStack(nullptr),
+      CodeSegStack(nullptr), CurInitSeg(nullptr), VisContext(nullptr),
+      PragmaAttributeCurrentTargetDecl(nullptr),
+      IsBuildingRecoveryCallExpr(false), Cleanup{}, LateTemplateParser(nullptr),
+      LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr), IdResolver(pp),
+      StdExperimentalNamespaceCache(nullptr), StdInitializerList(nullptr),
+      CXXTypeInfoDecl(nullptr), MSVCGuidDecl(nullptr), NSNumberDecl(nullptr),
+      NSValueDecl(nullptr), NSStringDecl(nullptr),
+      StringWithUTF8StringMethod(nullptr),
+      ValueWithBytesObjCTypeMethod(nullptr), NSArrayDecl(nullptr),
+      ArrayWithObjectsMethod(nullptr), NSDictionaryDecl(nullptr),
+      DictionaryWithObjectsMethod(nullptr), GlobalNewDeleteDeclared(false),
+      TUKind(TUKind), NumSFINAEErrors(0), CachedFakeTopLevelModule(nullptr),
+      AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false),
+      NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1),
+      CurrentInstantiationScope(nullptr), DisableTypoCorrection(false),
+      TyposCorrected(0), AnalysisWarnings(*this),
+      ThreadSafetyDeclCache(nullptr), VarDataSharingAttributesStack(nullptr),
+      CurScope(nullptr), Ident_super(nullptr), Ident___float128(nullptr) {
   TUScope = nullptr;
 
   LoadedExternalKnownNamespaces = false;
@@ -731,6 +724,8 @@ void Sema::ActOnEndOfTranslationUnit() {
     CheckDelayedMemberExceptionSpecs();
   }
 
+  DiagnoseUnterminatedPragmaAttribute();
+
   // All delayed member exception specs should be checked or we end up accepting
   // incompatible declarations.
   // FIXME: This is wrong for TUKind == TU_Prefix. In that case, we need to

Modified: cfe/trunk/lib/Sema/SemaAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaAttr.cpp?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaAttr.cpp Tue Apr 18 09:33:39 2017
@@ -368,6 +368,217 @@ void Sema::AddCFAuditedAttribute(Decl *D
   D->addAttr(CFAuditedTransferAttr::CreateImplicit(Context, Loc));
 }
 
+namespace {
+
+Optional<attr::SubjectMatchRule>
+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"
+  }
+}
+
+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);
+}
+
+std::string
+attrMatcherRuleListToString(ArrayRef<attr::SubjectMatchRule> 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();
+}
+
+} // end anonymous namespace
+
+void Sema::ActOnPragmaAttributePush(AttributeList &Attribute,
+                                    SourceLocation PragmaLoc,
+                                    attr::ParsedSubjectMatchRuleSet Rules) {
+  SmallVector<attr::SubjectMatchRule, 4> SubjectMatchRules;
+  // Gather the subject match rules that are supported by the attribute.
+  SmallVector<std::pair<attr::SubjectMatchRule, bool>, 4>
+      StrictSubjectMatchRuleSet;
+  Attribute.getMatchRules(LangOpts, StrictSubjectMatchRuleSet);
+
+  // Figure out which subject matching rules are valid.
+  if (StrictSubjectMatchRuleSet.empty()) {
+    // 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<attr::SubjectMatchRule,
+                        std::pair<attr::SubjectMatchRule, SourceRange>, 2>
+        RulesToFirstSpecifiedNegatedSubRule;
+    for (const auto &Rule : Rules) {
+      Optional<attr::SubjectMatchRule> 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) {
+      Optional<attr::SubjectMatchRule> 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 {
+    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<attr::SubjectMatchRule, 2> ExtraRules;
+    for (const auto &Rule : Rules) {
+      ExtraRules.push_back(Rule.first);
+      Diagnostic << FixItHint::CreateRemoval(
+          replacementRangeForListElement(*this, Rule.second));
+    }
+    Diagnostic << attrMatcherRuleListToString(ExtraRules);
+  }
+
+  PragmaAttributeStack.push_back(
+      {PragmaLoc, &Attribute, std::move(SubjectMatchRules), /*IsUsed=*/false});
+}
+
+void Sema::ActOnPragmaAttributePop(SourceLocation PragmaLoc) {
+  if (PragmaAttributeStack.empty()) {
+    Diag(PragmaLoc, diag::err_pragma_attribute_stack_mismatch);
+    return;
+  }
+  const PragmaAttributeEntry &Entry = PragmaAttributeStack.back();
+  if (!Entry.IsUsed) {
+    assert(Entry.Attribute && "Expected an attribute");
+    Diag(Entry.Attribute->getLoc(), diag::warn_pragma_attribute_unused)
+        << Entry.Attribute->getName();
+    Diag(PragmaLoc, diag::note_pragma_attribute_region_ends_here);
+  }
+  PragmaAttributeStack.pop_back();
+}
+
+void Sema::AddPragmaAttributes(Scope *S, Decl *D) {
+  if (PragmaAttributeStack.empty())
+    return;
+  for (auto &Entry : PragmaAttributeStack) {
+    const AttributeList *Attribute = Entry.Attribute;
+    assert(Attribute && "Expected an attribute");
+
+    // Ensure that the attribute can be applied to the given declaration.
+    bool Applies = false;
+    for (const auto &Rule : Entry.MatchRules) {
+      if (Attribute->appliesToDecl(D, Rule)) {
+        Applies = true;
+        break;
+      }
+    }
+    if (!Applies)
+      continue;
+    Entry.IsUsed = true;
+    assert(!Attribute->getNext() && "Expected just one attribute");
+    PragmaAttributeCurrentTargetDecl = D;
+    ProcessDeclAttributeList(S, D, Attribute);
+    PragmaAttributeCurrentTargetDecl = nullptr;
+  }
+}
+
+void Sema::PrintPragmaAttributeInstantiationPoint() {
+  assert(PragmaAttributeCurrentTargetDecl && "Expected an active declaration");
+  Diags.Report(PragmaAttributeCurrentTargetDecl->getLocStart(),
+               diag::note_pragma_attribute_applied_decl_here);
+}
+
+void Sema::DiagnoseUnterminatedPragmaAttribute() {
+  if (PragmaAttributeStack.empty())
+    return;
+  Diag(PragmaAttributeStack.back().Loc, diag::err_pragma_attribute_no_pop_eof);
+}
+
 void Sema::ActOnPragmaOptimize(bool On, SourceLocation PragmaLoc) {
   if(On)
     OptimizeOffPragmaLocation = SourceLocation();

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Tue Apr 18 09:33:39 2017
@@ -13674,6 +13674,7 @@ CreateNewDecl:
 
   if (Attr)
     ProcessDeclAttributeList(S, New, Attr);
+  AddPragmaAttributes(S, New);
 
   // If this has an identifier, add it to the scope stack.
   if (TUK == TUK_Friend) {
@@ -15185,6 +15186,7 @@ Decl *Sema::ActOnEnumConstant(Scope *S,
 
   // Process attributes.
   if (Attr) ProcessDeclAttributeList(S, New, Attr);
+  AddPragmaAttributes(S, New);
 
   // Register this decl in the current scope stack.
   New->setAccess(TheEnumDecl->getAccess());

Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Tue Apr 18 09:33:39 2017
@@ -6676,6 +6676,9 @@ void Sema::ProcessDeclAttributes(Scope *
   // Finally, apply any attributes on the decl itself.
   if (const AttributeList *Attrs = PD.getAttributes())
     ProcessDeclAttributeList(S, D, Attrs);
+
+  // Apply additional attributes specified by '#pragma clang attribute'.
+  AddPragmaAttributes(S, D);
 }
 
 /// Is the given declaration allowed to use a forbidden type?

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Tue Apr 18 09:33:39 2017
@@ -8445,6 +8445,7 @@ Decl *Sema::ActOnStartNamespaceDef(Scope
     Namespc->setInvalidDecl();
   
   ProcessDeclAttributeList(DeclRegionScope, Namespc, AttrList);
+  AddPragmaAttributes(DeclRegionScope, Namespc);
 
   // FIXME: Should we be merging attributes?
   if (const VisibilityAttr *Attr = Namespc->getAttr<VisibilityAttr>())
@@ -9931,6 +9932,7 @@ Decl *Sema::ActOnAliasDeclaration(Scope
     NewTD->setInvalidDecl();
 
   ProcessDeclAttributeList(S, NewTD, AttrList);
+  AddPragmaAttributes(S, NewTD);
 
   CheckTypedefForVariablyModifiedType(S, NewTD);
   Invalid |= NewTD->isInvalidDecl();

Modified: cfe/trunk/lib/Sema/SemaDeclObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclObjC.cpp?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclObjC.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclObjC.cpp Tue Apr 18 09:33:39 2017
@@ -993,6 +993,7 @@ ActOnStartClassInterface(Scope *S, Sourc
   
   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 
@@ -1176,7 +1177,8 @@ Sema::ActOnStartProtocolInterface(Source
   
   if (AttrList)
     ProcessDeclAttributeList(TUScope, PDecl, AttrList);
-  
+  AddPragmaAttributes(TUScope, PDecl);
+
   // Merge attributes from previous declarations.
   if (PrevDecl)
     mergeDeclAttributes(PDecl, PrevDecl);
@@ -1706,7 +1708,8 @@ Sema::ActOnForwardProtocolDeclaration(So
     
     if (attrList)
       ProcessDeclAttributeList(TUScope, PDecl, attrList);
-    
+    AddPragmaAttributes(TUScope, PDecl);
+
     if (PrevDecl)
       mergeDeclAttributes(PDecl, PrevDecl);
 
@@ -1805,6 +1808,7 @@ ActOnStartCategoryInterface(SourceLocati
 
   if (AttrList)
     ProcessDeclAttributeList(TUScope, CDecl, AttrList);
+  AddPragmaAttributes(TUScope, CDecl);
 
   CheckObjCDeclScope(CDecl);
   return ActOnObjCContainerStartDefinition(CDecl);
@@ -1954,6 +1958,7 @@ Decl *Sema::ActOnStartClassImplementatio
                                       ClassName, /*typeParamList=*/nullptr,
                                       /*PrevDecl=*/nullptr, ClassLoc,
                                       true);
+    AddPragmaAttributes(TUScope, IDecl);
     IDecl->startDefinition();
     if (SDecl) {
       IDecl->setSuperClass(Context.getTrivialTypeSourceInfo(
@@ -3043,7 +3048,7 @@ Sema::ActOnForwardClassDeclaration(Sourc
                                   ClassName, TypeParams, PrevIDecl,
                                   IdentLocs[i]);
     IDecl->setAtEndRange(IdentLocs[i]);
-    
+
     PushOnScopeChains(IDecl, TUScope);
     CheckObjCDeclScope(IDecl);
     DeclsInGroup.push_back(IDecl);
@@ -4399,6 +4404,7 @@ Decl *Sema::ActOnMethodDeclaration(
 
     // Apply the attributes to the parameter.
     ProcessDeclAttributeList(TUScope, Param, ArgInfo[i].ArgAttrs);
+    AddPragmaAttributes(TUScope, Param);
 
     if (Param->hasAttr<BlocksAttr>()) {
       Diag(Param->getLocation(), diag::err_block_on_nonlocal);
@@ -4429,6 +4435,7 @@ Decl *Sema::ActOnMethodDeclaration(
 
   if (AttrList)
     ProcessDeclAttributeList(TUScope, ObjCMethod, AttrList);
+  AddPragmaAttributes(TUScope, ObjCMethod);
 
   // Add the method now.
   const ObjCMethodDecl *PrevMethod = nullptr;

Added: cfe/trunk/test/FixIt/fixit-pragma-attribute.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/FixIt/fixit-pragma-attribute.c?rev=300556&view=auto
==============================================================================
--- cfe/trunk/test/FixIt/fixit-pragma-attribute.c (added)
+++ cfe/trunk/test/FixIt/fixit-pragma-attribute.c Tue Apr 18 09:33:39 2017
@@ -0,0 +1,6 @@
+// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+// Verify that the suggested attribute subject match rules don't include the
+// rules that are not applicable in the current language mode.
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))))
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", apply_to = any(record(unless(is_union)), variable, function)"

Added: cfe/trunk/test/FixIt/fixit-pragma-attribute.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/FixIt/fixit-pragma-attribute.cpp?rev=300556&view=auto
==============================================================================
--- cfe/trunk/test/FixIt/fixit-pragma-attribute.cpp (added)
+++ cfe/trunk/test/FixIt/fixit-pragma-attribute.cpp Tue Apr 18 09:33:39 2017
@@ -0,0 +1,83 @@
+// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits -Wno-pragma-clang-attribute %s 2>&1 | FileCheck %s
+
+#pragma clang attribute push (annotate)
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:31-[[@LINE-1]]:31}:"__attribute__(("
+// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:39-[[@LINE-2]]:39}:"))"
+#pragma clang attribute push (annotate(("test")))
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:31-[[@LINE-1]]:31}:"__attribute__(("
+// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:49-[[@LINE-2]]:49}:"))"
+
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( enum, function, function, namespace, function ))
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:97-[[@LINE-1]]:107}:""
+// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:118-[[@LINE-2]]:127}:""
+
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(is_global), function, variable(is_global), variable(is_global) ))
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:112-[[@LINE-1]]:133}:""
+// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:133-[[@LINE-2]]:153}:""
+
+#pragma clang attribute push (__attribute__((annotate("subRuleContradictions"))), apply_to = any(variable, variable(is_parameter), function(is_member), variable(is_global)))
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:108-[[@LINE-1]]:132}:""
+// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:153-[[@LINE-2]]:172}:""
+
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((annotate("subRuleContradictions2"))), apply_to = any(function(is_member),function))
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:99-[[@LINE-1]]:119}:""
+
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((annotate("negatedSubRuleContradictions1"))), apply_to = any(variable(is_parameter), variable(unless(is_parameter))))
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:130-[[@LINE-1]]:160}:""
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((annotate("negatedSubRuleContradictions2"))), apply_to = any(variable(unless(is_parameter)), variable(is_thread_local), function, variable(is_global)))
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:106-[[@LINE-1]]:137}:""
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(enum, variable))
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:77-[[@LINE-1]]:82}:""
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))))
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", apply_to = any(record(unless(is_union)), variable, function, namespace)"
+#pragma clang attribute push (__attribute__((abi_tag("a"))) apply_to=function)
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", "
+#pragma clang attribute push (__attribute__((abi_tag("a"))) = function)
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", apply_to"
+#pragma clang attribute push (__attribute__((abi_tag("a"))) any(function))
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", apply_to = "
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))) 22)
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:63}:", apply_to = any(record(unless(is_union)), variable, function, namespace)"
+#pragma clang attribute push (__attribute__((abi_tag("a"))) function)
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:69}:", apply_to = any(record(unless(is_union)), variable, function, namespace)"
+#pragma clang attribute push (__attribute__((abi_tag("a"))) (function))
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:71}:", apply_to = any(record(unless(is_union)), variable, function, namespace)"
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), )
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:62}:"apply_to = any(record(unless(is_union)), variable, function, namespace)"
+#pragma clang attribute push (__attribute__((abi_tag("a"))), = function)
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:61}:"apply_to"
+#pragma clang attribute push (__attribute__((abi_tag("a"))), any(function))
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:61}:"apply_to = "
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), 22)
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:64}:"apply_to = any(record(unless(is_union)), variable, function, namespace)"
+#pragma clang attribute push (__attribute__((abi_tag("a"))), 1, 2)
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:66}:"apply_to = any(record(unless(is_union)), variable, function, namespace)"
+#pragma clang attribute push (__attribute__((abi_tag("a"))), function)
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:70}:"apply_to = any(record(unless(is_union)), variable, function, namespace)"
+#pragma clang attribute push (__attribute__((abi_tag("a"))), (function))
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:72}:"apply_to = any(record(unless(is_union)), variable, function, namespace)"
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to)
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:70-[[@LINE-1]]:70}:" = any(record(unless(is_union)), variable, function, namespace)"
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to any(function))
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:70-[[@LINE-1]]:70}:" = "
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to 41 (22))
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:70-[[@LINE-1]]:78}:" = any(record(unless(is_union)), variable, function, namespace)"
+
+// Don't give fix-it to attributes without a strict subject set
+#pragma clang attribute push (__attribute__((annotate("a"))))
+// CHECK-NO: [[@LINE-1]]:61

Added: cfe/trunk/test/Misc/pragma-attribute-cxx-subject-match-rules.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Misc/pragma-attribute-cxx-subject-match-rules.cpp?rev=300556&view=auto
==============================================================================
--- cfe/trunk/test/Misc/pragma-attribute-cxx-subject-match-rules.cpp (added)
+++ cfe/trunk/test/Misc/pragma-attribute-cxx-subject-match-rules.cpp Tue Apr 18 09:33:39 2017
@@ -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_constant" %s | FileCheck --check-prefix=CHECK-ENUM_CONSTANT %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_member)" %s | FileCheck --check-prefix=CHECK-FUNCTION_IS_MEMBER %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 (__attribute__((annotate("test"))), apply_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_CONSTANT: EnumConstantDecl{{.*}} testEnumCase1
+// CHECK-ENUM_CONSTANT-NEXT: AnnotateAttr{{.*}} "test"
+// CHECK-ENUM_CONSTANT: EnumConstantDecl{{.*}} testEnumCase2
+// CHECK-ENUM_CONSTANT-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_MEMBER: CXXConstructorDecl{{.*}} testMethods
+// CHECK-FUNCTION_IS_MEMBER-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_MEMBER: CXXMethodDecl{{.*}} testMethod
+// CHECK-FUNCTION_IS_MEMBER-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_MEMBER: CXXMethodDecl{{.*}} testMethod
+// CHECK-FUNCTION_IS_MEMBER-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_MEMBER: FunctionDecl{{.*}} testFunctionNotMethod
+// CHECK-FUNCTION_IS_MEMBER-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

Added: cfe/trunk/test/Misc/pragma-attribute-cxx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Misc/pragma-attribute-cxx.cpp?rev=300556&view=auto
==============================================================================
--- cfe/trunk/test/Misc/pragma-attribute-cxx.cpp (added)
+++ cfe/trunk/test/Misc/pragma-attribute-cxx.cpp Tue Apr 18 09:33:39 2017
@@ -0,0 +1,106 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -fcxx-exceptions %s
+// RUN: %clang_cc1 -fsyntax-only -ast-dump -ast-dump-filter test -std=c++11 -fcxx-exceptions %s | FileCheck %s
+// expected-no-diagnostics
+
+class testClass1 {
+};
+// CHECK-LABEL: CXXRecordDecl{{.*}} testClass1
+// CHECK-NOT: AnnotateAttr
+
+#pragma clang attribute push (__attribute__((annotate("test"))), apply_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 (__attribute__((annotate("method"))), apply_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"
+
+void testCatchVariable() {
+  try {
+  } catch (int testCatch) {
+  }
+  testCatchVariable();
+}
+// CHECK-LABEL: FunctionDecl{{.*}} testCatchVariable
+// CHECK: CXXCatchStmt
+// CHECK-NEXT: VarDecl{{.*}} testCatch
+// CHECK-NEXT: AnnotateAttr{{.*}} "test"
+
+void testLambdaMethod() {
+  auto l = [] () { };
+  testLambdaMethod();
+}
+// CHECK-LABEL: FunctionDecl{{.*}} testLambdaMethod
+// CHECK: LambdaExpr
+// CHECK-NEXT: CXXRecordDecl
+// CHECK-NEXT: CXXMethodDecl{{.*}} operator()
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: AnnotateAttr{{.*}} "test"
+
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((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

Added: cfe/trunk/test/Misc/pragma-attribute-objc-subject-match-rules.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Misc/pragma-attribute-objc-subject-match-rules.m?rev=300556&view=auto
==============================================================================
--- cfe/trunk/test/Misc/pragma-attribute-objc-subject-match-rules.m (added)
+++ cfe/trunk/test/Misc/pragma-attribute-objc-subject-match-rules.m Tue Apr 18 09:33:39 2017
@@ -0,0 +1,113 @@
+// 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 "-DSUBJECT=objc_category" %s | FileCheck --check-prefix=CHECK-OBJC_CATEGORY %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 (__attribute__((annotate("test"))), apply_to = any(SUBJECT))
+
+ at interface testInterface
+ at end
+// CHECK-OBJC_INTERFACE: ObjCInterfaceDecl{{.*}} testInterface
+// CHECK-OBJC_INTERFACE-NEXT: AnnotateAttr{{.*}} "test"
+
+ at interface testInterface ()
+ at end
+// CHECK-OBJC_INTERFACE: ObjCCategoryDecl
+// CHECK-OBJC_INTERFACE-NOT: AnnotateAttr{{.*}} "test"
+// CHECK-OBJC_CATEGORY: ObjCCategoryDecl
+// CHECK-OBJC_CATEGORY-NEXT: ObjCInterface
+// CHECK-OBJC_CATEGORY-NEXT: AnnotateAttr{{.*}} "test"
+
+ at interface testInterface (testCategory)
+ at end
+// CHECK-OBJC_INTERFACE: ObjCCategoryDecl{{.*}} testCategory
+// CHECK-OBJC_INTERFACE-NOT: AnnotateAttr{{.*}} "test"
+// CHECK-OBJC_CATEGORY: ObjCCategoryDecl{{.*}} testCategory
+// CHECK-OBJC_CATEGORY-NEXT: ObjCInterface
+// CHECK-OBJC_CATEGORY-NEXT: AnnotateAttr{{.*}} "test"
+
+// CHECK-OBJC_INTERFACE-LABEL: ObjCProtocolDecl
+ at protocol testProtocol
+ at end
+// CHECK-OBJC_PROTOCOL: ObjCProtocolDecl{{.*}} testProtocol
+// CHECK-OBJC_PROTOCOL-NEXT: AnnotateAttr{{.*}} "test"
+
+ at interface methodContainer
+- (void) testInstanceMethod;
++ (void) testClassMethod;
+ at 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"
+
+ at implementation methodContainer
+- (void) testInstanceMethod { }
++ (void) testClassMethod { }
+ at 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"
+ at interface propertyContainer {
+  int testIvar;
+// CHECK-FIELD: ObjCIvarDecl{{.*}} testIvar
+// CHECK-FIELD-NEXT: AnnotateAttr{{.*}} "test"
+
+}
+ at property int testProperty;
+// CHECK-OBJC_PROPERTY: ObjCPropertyDecl{{.*}} testProperty
+// CHECK-OBJC_PROPERTY-NEXT: AnnotateAttr{{.*}} "test"
+
+ at 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

Added: cfe/trunk/test/Misc/pragma-attribute-objc.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Misc/pragma-attribute-objc.m?rev=300556&view=auto
==============================================================================
--- cfe/trunk/test/Misc/pragma-attribute-objc.m (added)
+++ cfe/trunk/test/Misc/pragma-attribute-objc.m Tue Apr 18 09:33:39 2017
@@ -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 (__attribute__((annotate("test"))), apply_to = any(objc_interface, objc_protocol, objc_property, field, objc_method, variable))
+#pragma clang attribute push (__attribute__((objc_subclassing_restricted)), apply_to = objc_interface)
+
+ at 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
+}
+
+ at 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
+
+ at end
+
+// @implementation can't receive explicit attributes, so don't add the pragma
+// attributes to them.
+ at 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 (__attribute__((annotate(\"applied at container start\"))), apply_to=objc_interface)");
+}
+
+// Implicit ivars shouldn't receive the attributes.
+// CHECK-LABEL: ObjCIvarDecl{{.*}}_testProp1
+// CHECK-NOT: AnnotateAttr
+
+ at end
+
+ at 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
+
+ at end
+
+#pragma clang attribute pop
+
+ at 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
+
+ at end
+
+ at 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.
+ at interface testInterface1(testCat)
+// CHECK-LABEL: ObjCCategoryDecl{{.*}}testCat
+// CHECK-NOT: AnnotateAttr
+// CHECK-NOT: ObjCSubclassingRestrictedAttr
+
+ at end
+
+ at implementation testInterface1(testCat)
+// CHECK-LABEL: ObjCCategoryImplDecl{{.*}}testCat
+// CHECK-NOT: AnnotateAttr
+// CHECK-NOT: ObjCSubclassingRestrictedAttr
+
+ at end
+
+// @class/@compatibility_alias declarations can't receive explicit attributes,
+// so don't add pragma attributes to them.
+ at class testClass;
+// CHECK-LABEL: ObjCInterfaceDecl{{.*}}testClass
+// CHECK-NOT: AnnotateAttr
+// CHECK-NOT: ObjCSubclassingRestrictedAttr
+
+ at compatibility_alias testCompat testInterface1;
+// CHECK-LABEL: ObjCCompatibleAliasDecl{{.*}}testCompat
+// CHECK-NOT: AnnotateAttr
+// CHECK-NOT: ObjCSubclassingRestrictedAttr
+
+#pragma clang attribute pop // objc_subclassing_restricted
+
+ at interface testInterface3
+// CHECK-LABEL: ObjCInterfaceDecl{{.*}}testInterface3
+// CHECK-NEXT: AnnotateAttr{{.*}} "test"
+// CHECK-NOT: ObjCSubclassingRestrictedAttr
+ at end
+
+#pragma clang attribute pop // annotate("test")
+
+ at interface testInterface4
+// CHECK-LABEL: ObjCInterfaceDecl{{.*}}testInterface4
+// CHECK-NOT: AnnotateAttr
+// CHECK-NOT: ObjCSubclassingRestrictedAttr
+ at end

Added: cfe/trunk/test/Misc/pragma-attribute-strict-subjects.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Misc/pragma-attribute-strict-subjects.c?rev=300556&view=auto
==============================================================================
--- cfe/trunk/test/Misc/pragma-attribute-strict-subjects.c (added)
+++ cfe/trunk/test/Misc/pragma-attribute-strict-subjects.c Tue Apr 18 09:33:39 2017
@@ -0,0 +1,222 @@
+// RUN: %clang_cc1 -fsyntax-only -Wno-pragma-clang-attribute -verify %s
+// RUN: not %clang_cc1 -fsyntax-only -ast-dump -ast-dump-filter test %s | FileCheck %s
+
+// Check for contradictions in rules for attribute without a strict subject set:
+
+#pragma clang attribute push (__attribute__((annotate("subRuleContradictions"))), apply_to = any(variable, variable(is_parameter), function(is_member), variable(is_global)))
+// expected-error at -1 {{redundant attribute subject matcher sub-rule 'variable(is_parameter)'; 'variable' already matches those declarations}}
+// expected-error at -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 (__attribute__((annotate("negatedSubRuleContradictions2"))), apply_to = any(variable(unless(is_parameter)), variable(is_thread_local), function, variable(is_global)))
+// expected-error at -1 {{negated attribute subject matcher sub-rule 'variable(unless(is_parameter))' contradicts sub-rule 'variable(is_global)'}}
+// 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 (__attribute__((abi_tag("a"))), apply_to = any(function))
+
+int testRecoverStrictnessVar = 0;
+// CHECK-LABEL: VarDecl{{.*}} testRecoverStrictnessVar
+// CHECK-NEXT: IntegerLiteral
+// CHECK-NOT: AbiTagAttr
+
+void testRecoverStrictnessFunc(void);
+// CHECK-LABEL: FunctionDecl{{.*}} testRecoverStrictnessFunc
+// CHECK-NEXT: AbiTagAttr
+
+struct testRecoverStrictnessStruct { };
+// CHECK-LABEL: RecordDecl{{.*}} testRecoverStrictnessStruct
+// CHECK-NOT: AbiTagAttr
+
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function, record(unless(is_union)), variable, enum))
+// expected-error at -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
+
+// Verify the non-strict subject set verification.
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_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 (__attribute__((abi_tag("a"))), apply_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 (__attribute__((abi_tag("a"))), apply_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 (__attribute__((abi_tag("a"))), apply_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 (__attribute__((abi_tag("a"))), apply_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 (__attribute__((abi_tag("a"))), apply_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 (__attribute__((abi_tag("a"))), apply_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 (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union)), function, variable, enum, enum_constant))
+// expected-error at -1 {{attribute 'abi_tag' can't be applied to 'enum_constant', 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 (__attribute__((abi_tag("a"))), apply_to = enum)
+// expected-error at -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

Added: cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test?rev=300556&view=auto
==============================================================================
--- cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test (added)
+++ cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test Tue Apr 18 09:33:39 2017
@@ -0,0 +1,62 @@
+// 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 57 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_member)
+// 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: EnumExtensibility (SubjectMatchRule_enum)
+// 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_member)
+// 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_member)
+// CHECK-NEXT: WarnUnusedResult (SubjectMatchRule_objc_method, SubjectMatchRule_enum, SubjectMatchRule_record, SubjectMatchRule_hasType_functionType)
+// CHECK-NEXT: XRayInstrument (SubjectMatchRule_function_is_member, SubjectMatchRule_objc_method, SubjectMatchRule_function)
+// CHECK-NEXT: XRayLogArgs (SubjectMatchRule_function_is_member, SubjectMatchRule_objc_method, SubjectMatchRule_function)

Added: cfe/trunk/test/Parser/pragma-attribute-declspec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/pragma-attribute-declspec.cpp?rev=300556&view=auto
==============================================================================
--- cfe/trunk/test/Parser/pragma-attribute-declspec.cpp (added)
+++ cfe/trunk/test/Parser/pragma-attribute-declspec.cpp Tue Apr 18 09:33:39 2017
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -triple i386-pc-win32 -std=c++11  -verify -Wno-pragma-clang-attribute -fms-extensions -fms-compatibility %s
+
+#pragma clang attribute push(__declspec(dllexport), apply_to = function)
+
+void function();
+
+#pragma clang attribute pop
+
+#pragma clang attribute push(__declspec(dllexport, dllimport), apply_to = function) // expected-error {{more than one attribute specified in '#pragma clang attribute push'}}
+
+#pragma clang attribute push(__declspec(align), apply_to = variable) // expected-error {{attribute 'align' is not supported by '#pragma clang attribute'}}
+
+#pragma clang attribute push(__declspec(), apply_to = variable) // A noop

Added: cfe/trunk/test/Parser/pragma-attribute.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/pragma-attribute.cpp?rev=300556&view=auto
==============================================================================
--- cfe/trunk/test/Parser/pragma-attribute.cpp (added)
+++ cfe/trunk/test/Parser/pragma-attribute.cpp Tue Apr 18 09:33:39 2017
@@ -0,0 +1,181 @@
+// RUN: %clang_cc1 -Wno-pragma-clang-attribute -verify -std=c++11 %s
+
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = function)
+
+void function();
+
+#pragma clang attribute pop
+
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any(variable(is_parameter), function))
+#pragma clang attribute pop
+
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = variable(unless(is_parameter)))
+#pragma clang attribute pop
+
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any(variable(unless(is_parameter))))
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a")))) // expected-error {{expected ','}}
+#pragma clang attribute push (__attribute__((abi_tag("a"))) apply_to=function) // expected-error {{expected ','}}
+#pragma clang attribute push (__attribute__((abi_tag("a"))) = function) // expected-error {{expected ','}}
+#pragma clang attribute push (__attribute__((abi_tag("a"))) any(function)) // expected-error {{expected ','}}
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))) 22) // expected-error {{expected ','}}
+#pragma clang attribute push (__attribute__((abi_tag("a"))) function) // expected-error {{expected ','}}
+#pragma clang attribute push (__attribute__((abi_tag("a"))) (function)) // expected-error {{expected ','}}
+
+#pragma clang attribute push(__attribute__((annotate("test"))), ) // expected-error {{expected attribute subject set specifier 'apply_to'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), = any(function)) // expected-error {{expected attribute subject set specifier 'apply_to'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), = function) // expected-error {{expected attribute subject set specifier 'apply_to'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), any(function)) // expected-error {{expected attribute subject set specifier 'apply_to'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), function) // expected-error {{expected attribute subject set specifier 'apply_to'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply = any(function )) // expected-error {{expected attribute subject set specifier 'apply_to'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), to = function) // expected-error {{expected attribute subject set specifier 'apply_to'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_only_to = function) // expected-error {{expected attribute subject set specifier 'apply_to'}}
+
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to any(function)) // expected-error {{expected '='}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to function) // expected-error {{expected '='}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to) // expected-error {{expected '='}}
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to 41 (22)) // expected-error {{expected '='}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any) // expected-error {{expected '('}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any {) // expected-error {{expected '('}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any function) // expected-error {{expected '('}}
+
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = { function, enum }) // expected-error {{expected an identifier that corresponds to an attribute subject rule}}
+
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any(function ) // expected-error {{expected ')'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any(function, )) // expected-error {{expected an identifier that corresponds to an attribute subject rule}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, enum ) // expected-error {{expected ')'}}
+
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = () ) // expected-error {{expected an identifier that corresponds to an attribute subject rule}}
+
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( + ) ) // expected-error {{expected an identifier that corresponds to an attribute subject rule}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any()) // expected-error {{expected an identifier that corresponds to an attribute subject rule}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, 42 )) // expected-error {{expected an identifier that corresponds to an attribute subject rule}}
+
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( diag )) // expected-error {{unknown attribute subject rule 'diag'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( a )) // expected-error {{unknown attribute subject rule 'a'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, for)) // expected-error {{unknown attribute subject rule 'for'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function42, for )) // expected-error {{unknown attribute subject rule 'function42'}}
+
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any(hasType)) // expected-error {{expected '('}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = hasType) // expected-error {{expected '('}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = hasType(functionType)) // OK
+
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable( )) // expected-error {{expected ')'}}
+#pragma clang attribute push(__attribute__((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(__attribute__((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(__attribute__((annotate("test"))), apply_to = any( variable(is_parameter, not) )) // expected-error {{expected ')'}} expected-note {{to match this '('}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable is_parameter )) // expected-error {{expected ')'}} expected-note {{to match this '('}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable ( ) // expected-error {{expected ')'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = variable (  // expected-error {{expected ')'}}
+#pragma clang attribute push(__attribute__((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(__attribute__((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(__attribute__((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(__attribute__((annotate("test"))), apply_to = any( function, variable ("test" )) // expected-error {{expected ')'}}
+
+#pragma clang attribute push(__attribute__((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(__attribute__((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(__attribute__((annotate("test"))), apply_to = any (function, variable (unless) )) // expected-error {{expected '('}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any (function, variable (unless() )) // expected-error {{expected ')'}}
+#pragma clang attribute push(__attribute__((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(__attribute__((annotate("test"))), apply_to = any( variable(unless(is_parameter, not)) )) // expected-error {{expected ')'}} expected-note {{to match this '('}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(unless(is_parameter), not) ) // expected-error {{expected ')'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable unless is_parameter )) // expected-error {{expected ')'}} expected-note {{to match this '('}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(unless is_parameter) )) // expected-error {{expected '('}}
+#pragma clang attribute push(__attribute__((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(__attribute__((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(__attribute__((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(__attribute__((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(__attribute__((annotate("test"))), apply_to = any( function, function )) // expected-error {{duplicate attribute subject matcher 'function'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, function, function )) // expected-error 2 {{duplicate attribute subject matcher 'function'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, enum, function )) // expected-error {{duplicate attribute subject matcher 'function'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( enum, enum, function )) // expected-error {{duplicate attribute subject matcher 'enum'}}
+
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(is_global), variable(is_global) )) // expected-error {{duplicate attribute subject matcher 'variable(is_global)'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_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(__attribute__((annotate("test"))), apply_to = any( variable(unless(is_parameter)), variable(unless(is_parameter)) )) // expected-error {{duplicate attribute subject matcher 'variable(unless(is_parameter))'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(unless(is_parameter)), variable(unless(is_parameter)), enum, variable(unless(is_parameter)) )) // expected-error 2 {{duplicate attribute subject matcher 'variable(unless(is_parameter))'}}
+
+#pragma clang attribute // expected-error {{expected 'push' or 'pop' after '#pragma clang attribute'}}
+#pragma clang attribute 42 // expected-error {{expected 'push' or 'pop' after '#pragma clang attribute'}}
+#pragma clang attribute 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 (__attribute__((annotate)) // expected-error {{expected ')'}}
+#pragma clang attribute push () // expected-error {{expected an attribute after '('}}
+
+#pragma clang attribute push (__attribute__((annotate("test"))), apply_to = function) () // expected-warning {{extra tokens at end of '#pragma clang attribute'}}
+// expected-error at -1 {{expected unqualified-id}}
+// expected-error at -2 {{unterminated '#pragma clang attribute push' at end of file}}
+
+#pragma clang attribute pop () // expected-warning {{extra tokens at end of '#pragma clang attribute'}}
+
+;
+
+#pragma clang attribute push (__attribute__((42))) // expected-error {{expected identifier that represents an attribute name}}
+
+#pragma clang attribute push (__attribute__((annotate)) foo) // expected-error {{expected ','}}
+#pragma clang attribute push (__attribute__((annotate)), apply_to=function foo) // expected-error {{extra tokens after attribute in a '#pragma clang attribute push'}}
+
+#pragma clang attribute push (__attribute__((availability(macos, foo=1))), apply_to=function) // expected-error {{'foo' is not an availability stage; use 'introduced', 'deprecated', or 'obsoleted'}}
+// expected-error at -1 {{attribute 'availability' is not supported by '#pragma clang attribute'}}
+#pragma clang attribute push (__attribute__((availability(macos, 1))), apply_to=function) // expected-error {{expected 'introduced', 'deprecated', or 'obsoleted'}}
+
+#pragma clang attribute push (__attribute__((used)), apply_to=function) // expected-error {{attribute 'used' is not supported by '#pragma clang attribute'}}
+
+void statementPragmasAndPragmaExpression() {
+#pragma clang attribute push (__attribute__((annotate("hello"))), apply_to=variable)
+#pragma clang attribute pop
+int x = 0;
+_Pragma("clang attribute push (__attribute__((annotate(\"hi\"))), apply_to = function)");
+
+_Pragma("clang attribute push (__attribute__((annotate(\"hi\"))), apply_to = any(function(is_method ))"); // expected-error {{expected ')'}}
+}
+
+_Pragma("clang attribute pop");
+
+#pragma clang attribute push (__attribute__((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_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 push ([[clang::disable_tail_calls, noreturn]]) // expected-error {{more than one attribute specified in '#pragma clang attribute push'}}
+
+#pragma clang attribute push ([[gnu::abi_tag]], apply_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 pop
+#pragma clang attribute push ([[noreturn 42]]) // expected-error {{expected ']'}} expected-error {{expected ','}}
+
+#pragma clang attribute push(__attribute__, apply_to=function) // expected-error {{expected '(' after 'attribute'}}
+#pragma clang attribute push(__attribute__(), apply_to=function) // expected-error {{expected '(' after '('}}
+#pragma clang attribute push(__attribute__(()), apply_to=function) // expected-error {{expected identifier that represents an attribute name}}
+#pragma clang attribute push(__attribute__((annotate, apply_to=function))) // expected-error {{expected ')'}}
+#pragma clang attribute push(__attribute__((annotate("test"), apply_to=function))) // expected-error {{expected ')'}}
+#pragma clang attribute push(__attribute__((annotate), apply_to=function)) // expected-error {{expected ')'}}
+
+#pragma clang attribute push (42) // expected-error {{expected an attribute that is specified using the GNU, C++11 or '__declspec' syntax}}
+#pragma clang attribute push (test) // expected-error {{expected an attribute that is specified using the GNU, C++11 or '__declspec' syntax}}
+#pragma clang attribute push (annotate) // expected-error {{expected an attribute that is specified using the GNU, C++11 or '__declspec' syntax}}
+// expected-note at -1 {{use the GNU '__attribute__' syntax}}
+#pragma clang attribute push (annotate("test")) // expected-error {{expected an attribute that is specified using the GNU, C++11 or '__declspec' syntax}}
+// expected-note at -1 {{use the GNU '__attribute__' syntax}}

Added: cfe/trunk/test/Sema/pragma-attribute-strict-subjects.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/pragma-attribute-strict-subjects.c?rev=300556&view=auto
==============================================================================
--- cfe/trunk/test/Sema/pragma-attribute-strict-subjects.c (added)
+++ cfe/trunk/test/Sema/pragma-attribute-strict-subjects.c Tue Apr 18 09:33:39 2017
@@ -0,0 +1,153 @@
+// RUN: %clang_cc1 -fsyntax-only -Wno-pragmas -verify %s
+
+#pragma clang attribute push (__attribute__((annotate("test"))), apply_to = any(function, variable))
+
+#pragma clang attribute pop
+
+// Check for contradictions in rules for attribute without a strict subject set:
+
+#pragma clang attribute push (__attribute__((annotate("subRuleContradictions"))), apply_to = any(variable, variable(is_parameter), function(is_member), variable(is_global)))
+// expected-error at -1 {{redundant attribute subject matcher sub-rule 'variable(is_parameter)'; 'variable' already matches those declarations}}
+// expected-error at -2 {{redundant attribute subject matcher sub-rule 'variable(is_global)'; 'variable' already matches those declarations}}
+
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((annotate("subRuleContradictions2"))), apply_to = any(function(is_member), function))
+// expected-error at -1 {{redundant attribute subject matcher sub-rule 'function(is_member)'; 'function' already matches those declarations}}
+
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((annotate("subRuleContradictions3"))), apply_to = any(variable, variable(unless(is_parameter))))
+// expected-error at -1 {{redundant attribute subject matcher sub-rule 'variable(unless(is_parameter))'; 'variable' already matches those declarations}}
+
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((annotate("negatedSubRuleContradictions1"))), apply_to = any(variable(is_parameter), variable(unless(is_parameter))))
+// expected-error at -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 (__attribute__((annotate("negatedSubRuleContradictions2"))), apply_to = any(variable(unless(is_parameter)), variable(is_thread_local), function, variable(is_global)))
+// expected-error at -1 {{negated attribute subject matcher sub-rule 'variable(unless(is_parameter))' contradicts sub-rule 'variable(is_global)'}}
+// We have just one error, don't error on 'variable(is_global)'
+
+#pragma clang attribute pop
+
+// Verify the strict subject set verification.
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function))
+// No error
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union)), function, variable))
+// No error
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function, variable, record(unless(is_union))))
+// No error
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(variable, record(unless(is_union)), function))
+// No error
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function, record(unless(is_union)), variable, enum))
+// expected-error at -1 {{attribute 'abi_tag' can't be applied to 'enum'}}
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(enum_constant, function, record(unless(is_union)), variable, variable(is_parameter)))
+// expected-error at -1 {{attribute 'abi_tag' can't be applied to 'variable(is_parameter)', and 'enum_constant'}}
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function, record(unless(is_union)), enum))
+// expected-error at -1 {{attribute 'abi_tag' can't be applied to 'enum'}}
+#pragma clang attribute pop
+
+// Verify the non-strict subject set verification.
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function))
+
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = variable)
+
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union))))
+
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function, variable))
+
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(variable, record(unless(is_union))))
+
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union)), function))
+
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union)), function, variable))
+
+#pragma clang attribute pop
+
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union)), function, variable, enum, enum_constant))
+// expected-error at -1 {{attribute 'abi_tag' can't be applied to 'enum_constant', and 'enum'}}
+
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = enum)
+// expected-error at -1 {{attribute 'abi_tag' can't be applied to 'enum'}}
+
+#pragma clang attribute pop
+
+// Handle attributes whose subjects are supported only in other language modes:
+
+#pragma clang attribute push(__attribute__((abi_tag("b"))), apply_to = any(namespace, record(unless(is_union)), variable, function))
+// 'namespace' is accepted!
+#pragma clang attribute pop
+
+#pragma clang attribute push(__attribute__((abi_tag("b"))), apply_to = any(namespace))
+// 'namespace' is accepted!
+#pragma clang attribute pop
+
+#pragma clang attribute push(__attribute__((objc_subclassing_restricted)), apply_to = objc_interface)
+// No error!
+#pragma clang attribute pop
+
+#pragma clang attribute push(__attribute__((objc_subclassing_restricted)), apply_to = objc_interface)
+// No error!
+#pragma clang attribute pop
+
+#pragma clang attribute push(__attribute__((objc_subclassing_restricted)), apply_to = any(objc_interface, objc_protocol))
+// expected-error at -1 {{attribute 'objc_subclassing_restricted' can't be applied to 'objc_protocol'}}
+#pragma clang attribute pop
+
+#pragma clang attribute push(__attribute__((objc_subclassing_restricted)), apply_to = any(objc_protocol))
+// expected-error at -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(__attribute__((objc_subclassing_restricted)), apply_to = any(objc_interface, objc_protocol))
+// expected-error at -1 {{attribute 'objc_subclassing_restricted' can't be applied to 'objc_protocol'}}
+#pragma clang attribute pop
+
+#pragma clang attribute push(__attribute__((objc_subclassing_restricted)), apply_to = any(objc_protocol))
+// expected-error at -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
+
+// Use of matchers from other language modes should not cause for attributes
+// without subject list:
+#pragma clang attribute push (__attribute__((annotate("test"))), apply_to = objc_method)
+
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((annotate("test"))), apply_to = any(objc_interface, objc_protocol))
+
+#pragma clang attribute pop

Added: cfe/trunk/test/Sema/pragma-attribute.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/pragma-attribute.c?rev=300556&view=auto
==============================================================================
--- cfe/trunk/test/Sema/pragma-attribute.c (added)
+++ cfe/trunk/test/Sema/pragma-attribute.c Tue Apr 18 09:33:39 2017
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+#pragma clang attribute pop // expected-error {{'#pragma clang attribute pop' with no matching '#pragma clang attribute push'}}
+
+// Don't verify unused attributes.
+#pragma clang attribute push (__attribute__((annotate)), apply_to = function) // expected-warning {{unused attribute 'annotate' in '#pragma clang attribute push' region}}
+#pragma clang attribute pop // expected-note {{'#pragma clang attribute push' regions ends here}}
+
+// Ensure we only report any errors once.
+#pragma clang attribute push (__attribute__((annotate)), apply_to = function) // expected-error 4 {{'annotate' attribute takes one argument}}
+
+void test5_begin(); // expected-note {{when applied to this declaration}}
+void test5_1(); // expected-note {{when applied to this declaration}}
+
+#pragma clang attribute push (__attribute__((annotate())), apply_to = function) // expected-error 2 {{'annotate' attribute takes one argument}}
+
+void test5_2(); // expected-note 2 {{when applied to this declaration}}
+
+#pragma clang attribute push (__attribute__((annotate("hello", "world"))), apply_to = function) // expected-error {{'annotate' attribute takes one argument}}
+
+void test5_3(); // expected-note 3 {{when applied to this declaration}}
+
+#pragma clang attribute pop
+#pragma clang attribute pop
+#pragma clang attribute pop
+
+// Verify that the warnings are reported for each receiver declaration
+
+#pragma clang attribute push (__attribute__((optnone)), apply_to = function) // expected-note 2 {{conflicting attribute is here}}
+
+__attribute__((always_inline)) void optnone1() { } // expected-warning {{'always_inline' attribute ignored}}
+// expected-note at -1 {{when applied to this declaration}}
+
+void optnone2() { }
+
+__attribute__((always_inline)) void optnone3() { } // expected-warning {{'always_inline' attribute ignored}}
+// expected-note at -1 {{when applied to this declaration}}
+
+#pragma clang attribute pop
+
+#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'}}
+
+#pragma clang attribute push (__attribute__((annotate("func"))), apply_to = function) // expected-error {{unterminated '#pragma clang attribute push' at end of file}}
+
+void function();

Modified: cfe/trunk/test/lit.cfg
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/lit.cfg?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/test/lit.cfg (original)
+++ cfe/trunk/test/lit.cfg Tue Apr 18 09:33:39 2017
@@ -284,6 +284,8 @@ if config.host_triple and config.host_tr
 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'. ***""") )

Modified: cfe/trunk/test/lit.site.cfg.in
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/lit.site.cfg.in?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/test/lit.site.cfg.in (original)
+++ cfe/trunk/test/lit.site.cfg.in Tue Apr 18 09:33:39 2017
@@ -10,6 +10,7 @@ config.llvm_shlib_dir = "@SHLIBDIR@"
 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@"

Modified: cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp (original)
+++ cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp Tue Apr 18 09:33:39 2017
@@ -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,334 @@ static void emitClangAttrLateParsedList(
   OS << "#endif // CLANG_ATTR_LATE_PARSED_LIST\n\n";
 }
 
+static bool hasGNUorCXX11Spelling(const Record &Attribute) {
+  std::vector<FlattenedSpelling> 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<Record *> getSubjects() const {
+    return (Constraint ? Constraint : MetaSubject)
+        ->getValueAsListOfDefs("Subjects");
+  }
+
+  std::vector<Record *> getLangOpts() const {
+    if (Constraint) {
+      // Lookup the options in the sub-rule first, in case the sub-rule
+      // overrides the rules options.
+      std::vector<Record *> 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<AttributeSubjectMatchRule> Rules;
+  llvm::DenseMap<const Record *, AttributeSubjectMatchRule> 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<Record *> MetaSubjects =
+      Records.getAllDerivedDefinitions("AttrSubjectMatcherRule");
+  auto MapFromSubjectsToRules = [this](const Record *SubjectContainer,
+                                       const Record *MetaSubject,
+                                       const Record *Constraint = nullptr) {
+    Rules.emplace_back(MetaSubject, Constraint);
+    std::vector<Record *> ApplicableSubjects =
+        SubjectContainer->getValueAsListOfDefs("Subjects");
+    for (const auto *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 auto *MetaSubject : MetaSubjects) {
+    MapFromSubjectsToRules(MetaSubject, MetaSubject);
+    std::vector<Record *> Constraints =
+        MetaSubject->getValueAsListOfDefs("Constraints");
+    for (const auto *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 auto &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<Record *> 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<Record *> Subjects = SubjectObj->getValueAsListOfDefs("Subjects");
+  if (Subjects.empty())
+    return false;
+  for (const auto *Subject : Subjects) {
+    if (SubjectsToRules.find(Subject) == 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<std::pair<"
+     << AttributeSubjectMatchRule::EnumName
+     << ", bool>> &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<Record *> Subjects = SubjectObj->getValueAsListOfDefs("Subjects");
+  for (const auto *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<Record *> 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<attr::SubjectMatchRule> "
+        "defaultIsAttributeSubjectMatchSubRuleFor(StringRef, bool) {\n";
+  OS << "  return None;\n";
+  OS << "}\n\n";
+
+  std::map<const Record *, std::vector<AttributeSubjectMatchRule>>
+      SubMatchRules;
+  for (const auto &Rule : Rules) {
+    if (!Rule.isSubRule())
+      continue;
+    SubMatchRules[Rule.MetaSubject].push_back(Rule);
+  }
+
+  for (const auto &SubMatchRule : SubMatchRules) {
+    OS << "Optional<attr::SubjectMatchRule> isAttributeSubjectMatchSubRuleFor_"
+       << SubMatchRule.first->getValueAsString("Name")
+       << "(StringRef Name, bool IsUnless) {\n";
+    OS << "  if (IsUnless)\n";
+    OS << "    return "
+          "llvm::StringSwitch<Optional<attr::SubjectMatchRule>>(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<Optional<attr::SubjectMatchRule>>(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<Optional<attr::SubjectMatchRule>, "
+        "Optional<attr::SubjectMatchRule> (*)(StringRef, "
+        "bool)> isAttributeSubjectMatchRule(StringRef Name) {\n";
+  OS << "  return "
+        "llvm::StringSwitch<std::pair<Optional<attr::SubjectMatchRule>, "
+        "Optional<attr::SubjectMatchRule> (*) (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 <typename Fn>
 static void forEachUniqueSpelling(const Record &Attr, Fn &&F) {
   std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attr);
@@ -2109,6 +2439,17 @@ void EmitClangAttrList(RecordKeeper &Rec
   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);
@@ -2704,9 +3045,13 @@ static std::string GetSubjectWithSuffix(
   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.
@@ -2791,6 +3136,42 @@ static std::string GenerateAppertainsTo(
   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<Record *> Subjects = Rule.getSubjects();
+    assert(!Subjects.empty() && "Missing subjects");
+    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";
+  OS << "  llvm_unreachable(\"Invalid match rule\");\nreturn false;\n";
+  OS << "}\n\n";
+}
+
 static void GenerateDefaultLangOptRequirements(raw_ostream &OS) {
   OS << "static bool defaultDiagnoseLangOpts(Sema &, ";
   OS << "const AttributeList &) {\n";
@@ -2949,6 +3330,9 @@ static bool IsKnownToGCC(const Record &A
 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;
@@ -2982,10 +3366,13 @@ void EmitClangAttrParsedAttrImpl(RecordK
     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)
@@ -2997,6 +3384,9 @@ void EmitClangAttrParsedAttrImpl(RecordK
   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
@@ -3136,6 +3526,11 @@ void EmitClangAttrParserStringSwitches(R
   emitClangAttrLateParsedList(Records, OS);
 }
 
+void EmitClangAttrSubjectMatchRulesParserStringSwitches(RecordKeeper &Records,
+                                                        raw_ostream &OS) {
+  getPragmaAttributeSupport(Records).generateParsingHelpers(OS);
+}
+
 class DocumentationData {
 public:
   const Record *Documentation;
@@ -3167,8 +3562,8 @@ enum SpellingKind {
   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.
@@ -3250,7 +3645,7 @@ static void WriteDocumentation(const Doc
   // 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 << "\",\"";
@@ -3261,6 +3656,9 @@ static void WriteDocumentation(const Doc
   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
@@ -3327,7 +3725,40 @@ void EmitClangAttrDocs(RecordKeeper &Rec
     // 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<Record *> 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";
   }
 }
 

Modified: cfe/trunk/utils/TableGen/TableGen.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/TableGen/TableGen.cpp?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/utils/TableGen/TableGen.cpp (original)
+++ cfe/trunk/utils/TableGen/TableGen.cpp Tue Apr 18 09:33:39 2017
@@ -25,8 +25,10 @@ using namespace clang;
 enum ActionType {
   GenClangAttrClasses,
   GenClangAttrParserStringSwitches,
+  GenClangAttrSubjectMatchRulesParserStringSwitches,
   GenClangAttrImpl,
   GenClangAttrList,
+  GenClangAttrSubjectMatchRuleList,
   GenClangAttrPCHRead,
   GenClangAttrPCHWrite,
   GenClangAttrHasAttributeImpl,
@@ -54,7 +56,8 @@ enum ActionType {
   GenArmNeonTest,
   GenAttrDocs,
   GenDiagDocs,
-  GenOptDocs
+  GenOptDocs,
+  GenTestPragmaAttributeSupportedAttributes
 };
 
 namespace {
@@ -66,10 +69,17 @@ cl::opt<ActionType> Action(
         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 @@ cl::opt<ActionType> Action(
         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 @@ cl::opt<ActionType> Action(
                    "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<std::string>
 ClangComponent("clang-component",
@@ -153,12 +165,18 @@ bool ClangTableGenMain(raw_ostream &OS,
   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 @@ bool ClangTableGenMain(raw_ostream &OS,
   case GenOptDocs:
     EmitClangOptDocs(Records, OS);
     break;
+  case GenTestPragmaAttributeSupportedAttributes:
+    EmitTestPragmaAttributeSupportedAttributes(Records, OS);
+    break;
   }
 
   return false;

Modified: cfe/trunk/utils/TableGen/TableGenBackends.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/TableGen/TableGenBackends.h?rev=300556&r1=300555&r2=300556&view=diff
==============================================================================
--- cfe/trunk/utils/TableGen/TableGenBackends.h (original)
+++ cfe/trunk/utils/TableGen/TableGenBackends.h Tue Apr 18 09:33:39 2017
@@ -33,9 +33,12 @@ void EmitClangASTNodes(RecordKeeper &RK,
                        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 EmitClangAttrDocs(RecordKeeper &Rec
 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




More information about the cfe-commits mailing list