arphaman created this revision.

This patch adds support for the `#pragma clang attribute` directive that was 
proposed recently at 
http://lists.llvm.org/pipermail/cfe-dev/2017-February/052689.html.

Initially it supports the `annotate`, `require_constant_initialization` and 
`objc_subclassing_restricted` attribute (I added support for the last two to 
verify that only those declarations that are specified in the Attr.td subject 
list can receive the attribute). The attributes are parsed immediately and are 
applied individually to each relevant declaration. The attribute application 
errors aren't reported more than once. The `annotate` attribute, which doesn't 
have the subject list, is applied only to those declarations that can receive 
explicit GNU-style attributes.

Thanks


Repository:
  rL LLVM

https://reviews.llvm.org/D30009

Files:
  docs/LanguageExtensions.rst
  include/clang/Basic/Attr.td
  include/clang/Basic/DiagnosticParseKinds.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Basic/TokenKinds.def
  include/clang/Parse/Parser.h
  include/clang/Sema/AttributeList.h
  include/clang/Sema/Sema.h
  lib/Parse/ParsePragma.cpp
  lib/Parse/ParseStmt.cpp
  lib/Parse/Parser.cpp
  lib/Sema/AttributeList.cpp
  lib/Sema/SemaAttr.cpp
  lib/Sema/SemaDecl.cpp
  lib/Sema/SemaDeclCXX.cpp
  lib/Sema/SemaDeclObjC.cpp
  lib/Sema/SemaObjCProperty.cpp
  test/Parser/pragma-attribute.cpp
  test/Sema/pragma-attribute.c
  test/SemaCXX/pragma-attribute.cpp
  test/SemaObjC/pragma-attribute.m
  utils/TableGen/ClangAttrEmitter.cpp

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

Reply via email to