arphaman updated this revision to Diff 88764.
arphaman marked an inline comment as done.
arphaman added a comment.

The updated patch switches over to the opt-out approach, allows the C++11 style 
syntax and improves documentation.


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,38 @@
   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;
+}
+
+static bool isSupportedByPragmaAttribute(const Record &Attribute) {
+  if (Attribute.getValueAsBit("ForcePragmaAttributeSupport"))
+    return true;
+  // Opt-out rules:
+  // An attribute requires delayed parsing (LateParsed is on)
+  if (Attribute.getValueAsBit("LateParsed"))
+    return false;
+  // An attribute has no GNU/CXX11 spelling
+  if (!hasGNUorCXX11Spelling(Attribute))
+    return false;
+  // An attribute has no subject list or its subject list is empty
+  if (Attribute.isValueUnset("Subjects"))
+    return false;
+  const Record *SubjectObj = Attribute.getValueAsDef("Subjects");
+  std::vector<Record *> Subjects = SubjectObj->getValueAsListOfDefs("Subjects");
+  if (Subjects.empty())
+    return false;
+  // An attribute derives from a StmtAttr or a TypeAttr
+  if (Attribute.isSubClassOf("TypeAttr") || Attribute.isSubClassOf("StmtAttr"))
+    return false;
+  return true;
+}
+
 template <typename Fn>
 static void forEachUniqueSpelling(const Record &Attr, Fn &&F) {
   std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attr);
@@ -2692,9 +2724,13 @@
   return B + "Decl";
 }
 
+static std::string functionNameForCustomAppertainsTo(const Record &Subject) {
+  return "is" + Subject.getName().str();
+}
+
 static std::string GenerateCustomAppertainsTo(const Record &Subject,
                                               raw_ostream &OS) {
-  std::string FnName = "is" + Subject.getName().str();
+  std::string FnName = functionNameForCustomAppertainsTo(Subject);
 
   // If this code has already been generated, simply return the previous
   // instance of it.
@@ -2779,6 +2815,50 @@
   return FnName;
 }
 
+static void GenerateDefaultAppliesTo(raw_ostream &OS) {
+  OS << "static bool defaultAttributeAppliesTo(Sema &, const Decl *) {\n";
+  OS << "  return true;\n";
+  OS << "}\n\n";
+}
+
+static std::string GenerateAppliesTo(const Record &Attr, raw_ostream &OS) {
+  // If the attribute doesn't support '#pragma clang attribute' or if it doesn't
+  // contain a subjects definition, then use the default appliedTo logic.
+  if (!isSupportedByPragmaAttribute(Attr) || Attr.isValueUnset("Subjects"))
+    return "defaultAttributeAppliesTo";
+
+  const Record *SubjectObj = Attr.getValueAsDef("Subjects");
+  std::vector<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 +3025,7 @@
   // Generate the default appertainsTo, target and language option diagnostic,
   // and spelling list index mapping methods.
   GenerateDefaultAppertainsTo(OS);
+  GenerateDefaultAppliesTo(OS);
   GenerateDefaultLangOptRequirements(OS);
   GenerateDefaultTargetRequirements(OS);
   GenerateDefaultSpellingIndexToSemanticSpelling(OS);
@@ -2970,7 +3051,9 @@
     SS << ", " << I->second->isSubClassOf("TypeAttr");
     SS << ", " << I->second->isSubClassOf("StmtAttr");
     SS << ", " << IsKnownToGCC(*I->second);
+    SS << ", " << isSupportedByPragmaAttribute(*I->second);
     SS << ", " << GenerateAppertainsTo(*I->second, OS);
+    SS << ", " << GenerateAppliesTo(*I->second, OS);
     SS << ", " << GenerateLangOptRequirements(*I->second, OS);
     SS << ", " << GenerateTargetRequirements(*I->second, Dupes, OS);
     SS << ", " << GenerateSpellingIndexToSemanticSpelling(*I->second, OS);
@@ -3238,7 +3321,7 @@
   // List what spelling syntaxes the attribute supports.
   OS << ".. csv-table:: Supported Syntaxes\n";
   OS << "   :header: \"GNU\", \"C++11\", \"__declspec\", \"Keyword\",";
-  OS << " \"Pragma\"\n\n";
+  OS << " \"Pragma\", \"Pragma clang attribute\"\n\n";
   OS << "   \"";
   if (SupportedSpellings & GNU) OS << "X";
   OS << "\",\"";
@@ -3249,6 +3332,9 @@
   if (SupportedSpellings & Keyword) OS << "X";
   OS << "\", \"";
   if (SupportedSpellings & Pragma) OS << "X";
+  OS << "\", \"";
+  if (isSupportedByPragmaAttribute(*Doc.Attribute))
+    OS << "X";
   OS << "\"\n\n";
 
   // If the attribute is deprecated, print a message about it, and possibly
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,91 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -ast-dump -ast-dump-filter test -std=c++11 -DNOERROR %s | FileCheck %s
+
+class testClass1 {
+};
+// CHECK-LABEL: CXXRecordDecl{{.*}} testClass1
+// CHECK-NOT: AnnotateAttr
+
+#pragma clang attribute push (annotate("test"))
+
+class testClass2 {
+  void testMethod1(int param);
+
+  testClass2();
+
+  testClass2 *operator -> ();
+};
+// CHECK-LABEL: CXXRecordDecl{{.*}} testClass2
+// CHECK-NEXT: AnnotateAttr{{.*}} "test"
+// CHECK: CXXMethodDecl{{.*}} testMethod1
+// CHECK-NEXT: ParmVarDecl{{.*}} param
+// CHECK-NEXT: AnnotateAttr{{.*}} "test"
+// CHECK-NEXT: AnnotateAttr{{.*}} "test"
+// CHECK-NEXT: CXXConstructorDecl
+// CHECK-NEXT: AnnotateAttr{{.*}} "test"
+// CHECK-NEXT: CXXMethodDecl{{.*}} operator->
+// CHECK-NEXT: AnnotateAttr{{.*}} "test"
+
+#pragma clang attribute push (annotate("method"))
+
+void testClass2::testMethod1(int param) {
+
+#pragma clang attribute pop
+}
+// CHECK-LABEL: CXXMethodDecl{{.*}}prev{{.*}} testMethod1
+// CHECK-NEXT: ParmVarDecl{{.*}} param
+// CHECK-NEXT: AnnotateAttr{{.*}} "test"
+// CHECK-NEXT: AnnotateAttr{{.*}} "method"
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: AnnotateAttr{{.*}} "test"
+// CHECK-NEXT: AnnotateAttr{{.*}} "method"
+
+namespace testNamespace {
+}
+// CHECK-LABEL: NamespaceDecl{{.*}} testNamespace
+// CHECK-NEXT: AnnotateAttr{{.*}} "test"
+
+class testClassForward;
+// CHECK-LABEL: CXXRecordDecl{{.*}} testClassForward
+// CHECK-NEXT: AnnotateAttr{{.*}} "test"
+
+namespace testNamespaceAlias = testNamespace;
+// CHECK-LABEL: NamespaceAliasDecl{{.*}} testNamespaceAlias
+// CHECK-NOT: AnnotateAttr
+
+using testTypeAlias = testClass2;
+// CHECK-LABEL: TypeAliasDecl{{.*}} testTypeAlias
+// CHECK: AnnotateAttr{{.*}} "test"
+
+#pragma clang attribute pop
+
+#pragma clang attribute push (require_constant_initialization)
+
+int testCI1 = 1;
+// CHECK-LABEL: VarDecl{{.*}} testCI1
+// CHECK-NEXT: IntegerLiteral
+// CHECK-NEXT: RequireConstantInitAttr
+
+#pragma clang attribute pop
+
+int testNoCI = 0;
+// CHECK-LABEL: VarDecl{{.*}} testNoCI
+// CHECK-NEXT: IntegerLiteral
+// CHECK-NOT: RequireConstantInitAttr
+
+// Check support for CXX11 style attributes
+#pragma clang attribute push ([[noreturn]])
+
+void testNoReturn();
+// CHECK-LABEL: FunctionDecl{{.*}} testNoReturn
+// CHECK-NEXT: CXX11NoReturnAttr
+
+#pragma clang attribute pop
+
+#ifndef NOERROR
+
+#pragma clang attribute push ([[]]) // A noop
+
+#pragma clang attribute pop // expected-error {{'#pragma clang attribute pop' with no matching '#pragma clang attribute push'}}
+
+#endif
Index: test/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,67 @@
+// RUN: %clang_cc1 -verify -std=c++11 %s
+
+#pragma clang attribute push(annotate("test"))
+
+void function();
+
+#pragma clang attribute pop
+
+#pragma clang attribute // expected-error {{expected 'push' or 'pop' after '#pragma clang attribute'}}
+#pragma clang attribute 42 // expected-error {{expected 'push' or 'pop' after '#pragma clang attribute'}}
+#pragma clang attribute pushpop // expected-error {{unexpected argument 'pushpop' to '#pragma clang attribute'; expected 'push' or 'pop'}}
+
+#pragma clang attribute push // expected-error {{expected '('}}
+#pragma clang attribute push ( // expected-error {{expected an attribute after '('}}
+#pragma clang attribute push (annotate // expected-error {{expected ')'}}
+#pragma clang attribute push () // expected-error {{expected an attribute after '('}}
+
+#pragma clang attribute push (annotate("test")) () // expected-warning {{extra tokens at end of '#pragma clang attribute'}}
+// expected-error@-1 {{expected unqualified-id}}
+
+#pragma clang attribute pop () // expected-warning {{extra tokens at end of '#pragma clang attribute'}}
+
+;
+
+#pragma clang attribute push (42) // expected-error {{expected identifier that represents an attribute name}}
+
+#pragma clang attribute push (annotate foo) // expected-error {{extra tokens after attribute in a '#pragma clang attribute push'}}
+
+#pragma clang attribute push (availability(macos, foo=1)) // expected-error {{'foo' is not an availability stage; use 'introduced', 'deprecated', or 'obsoleted'}}
+// expected-error@-1 {{attribute 'availability' is not supported by '#pragma clang attribute'}}
+#pragma clang attribute push (availability(macos, 1)) // expected-error {{expected 'introduced', 'deprecated', or 'obsoleted'}}
+
+#pragma clang attribute push (used) // expected-error {{attribute 'used' is not supported by '#pragma clang attribute'}}
+
+void statementPragmasAndPragmaExpression() {
+#pragma clang attribute push (annotate("hello"))
+#pragma clang attribute pop
+int x = 0;
+_Pragma("clang attribute push (annotate(\"hi\"))");
+}
+
+_Pragma("clang attribute pop");
+
+#pragma clang attribute push (address_space(0)) // expected-error {{attribute 'address_space' is not supported by '#pragma clang attribute'}}
+
+// Check support for CXX11 style attributes
+#pragma clang attribute push ([[noreturn]])
+#pragma clang attribute pop
+
+#pragma clang attribute push ([[clang::disable_tail_calls]])
+#pragma clang attribute pop
+
+#pragma clang attribute push ([[gnu::alias]])
+#pragma clang attribute pop
+
+#pragma clang attribute push ([[clang::disable_tail_calls, noreturn]]) // expected-error {{more than one attribute specified in '#pragma clang attribute push'}}
+#pragma clang attribute pop
+
+#pragma clang attribute push ([[gnu::alias]])
+#pragma clang attribute pop
+
+#pragma clang attribute push ([[fallthrough]]) // expected-error {{attribute 'fallthrough' is not supported by '#pragma clang attribute'}}
+#pragma clang attribute push ([[clang::fallthrough]]) // expected-error {{attribute 'fallthrough' is not supported by '#pragma clang attribute'}}
+
+#pragma clang attribute push ([[]]) // A noop
+
+#pragma clang attribute push ([[noreturn ""]]) // expected-error {{expected ']'}}
Index: 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
@@ -160,9 +160,11 @@
   unsigned IsType : 1;
   unsigned IsStmt : 1;
   unsigned IsKnownToGCC : 1;
+  unsigned IsSupportedByPragmaAttribute : 1;
 
   bool (*DiagAppertainsToDecl)(Sema &S, const AttributeList &Attr,
                                const Decl *);
+  bool (*AttributeAppliesToDecl)(Sema &S, const Decl *);
   bool (*DiagLangOpts)(Sema &S, const AttributeList &Attr);
   bool (*ExistsInTarget)(const TargetInfo &Target);
   unsigned (*SpellingIndexToSemanticSpelling)(const AttributeList &Attr);
@@ -192,6 +194,10 @@
   return getInfo(*this).DiagAppertainsToDecl(S, *this, D);
 }
 
+bool AttributeList::appliesToDecl(Sema &S, const Decl *D) const {
+  return getInfo(*this).AttributeAppliesToDecl(S, D);
+}
+
 bool AttributeList::diagnoseLangOpts(Sema &S) const {
   return getInfo(*this).DiagLangOpts(S, *this);
 }
@@ -216,6 +222,10 @@
   return getInfo(*this).IsKnownToGCC;
 }
 
+bool AttributeList::isSupportedByPragmaAttribute() const {
+  return getInfo(*this).IsSupportedByPragmaAttribute;
+}
+
 unsigned AttributeList::getSemanticSpelling() const {
   return getInfo(*this).SpellingIndexToSemanticSpelling(*this);
 }
Index: lib/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,88 @@
   return true;
 }
 
+namespace {
+struct PragmaAttributeInfo {
+  enum ActionType { Push, Pop };
+  ParsedAttributes &Attributes;
+  ActionType Action;
+  ArrayRef<Token> Tokens;
+
+  PragmaAttributeInfo(ParsedAttributes &Attributes) : Attributes(Attributes) {}
+};
+} // end anonymous namespace
+
+void Parser::HandlePragmaAttribute() {
+  assert(Tok.is(tok::annot_pragma_attribute));
+  SourceLocation PragmaLoc = Tok.getLocation();
+  auto *Info = static_cast<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.is(tok::l_square) && NextToken().is(tok::l_square)) {
+    // Parse the CXX11 style attribute.
+    ParseCXX11AttributeSpecifier(Attrs);
+    // Ensure that we don't have more than one attribute.
+    if (Attrs.getList() && Attrs.getList()->getNext()) {
+      SourceLocation Loc = Attrs.getList()->getNext()->getLoc();
+      Diag(Loc, diag::err_pragma_attribute_multiple_attributes);
+      Attrs.getList()->setNext(nullptr);
+    }
+  } else {
+    if (Tok.isNot(tok::identifier)) {
+      Diag(Tok, diag::err_pragma_attribute_expected_attribute_name);
+      SkipUntil(tok::eof);
+      ConsumeToken();
+      return;
+    }
+    IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+    SourceLocation AttrNameLoc = ConsumeToken();
+
+    if (Tok.isNot(tok::l_paren))
+      Attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+                   AttributeList::AS_GNU);
+    else
+      ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, /*EndLoc=*/nullptr,
+                            /*ScopeName=*/nullptr,
+                            /*ScopeLoc=*/SourceLocation(),
+                            AttributeList::AS_GNU,
+                            /*Declarator=*/nullptr);
+  }
+
+  // Tokens following an ill-formed attribute will remain in the token stream
+  // and must be removed.
+  if (Tok.isNot(tok::eof)) {
+    Diag(Tok, diag::err_pragma_attribute_extra_tokens_after_attribute);
+    SkipUntil(tok::eof);
+    ConsumeToken();
+    return;
+  }
+
+  // Consume the eof terminator token.
+  ConsumeToken();
+
+  if (!Attrs.getList() || Attrs.getList()->isInvalid())
+    return;
+
+  if (!Attrs.getList()->isSupportedByPragmaAttribute()) {
+    Diag(PragmaLoc, diag::err_pragma_attribute_unsupported_attribute)
+        << Attrs.getList()->getName();
+    return;
+  }
+  assert(!Attrs.getList()->getNext());
+  Actions.ActOnPragmaAttributePush(*Attrs.getList(), PragmaLoc);
+}
+
 // #pragma GCC visibility comes in two variants:
 //   'push' '(' [visibility] ')'
 //   'pop'
@@ -2246,3 +2345,99 @@
     PP.Diag(FirstTok.getLocation(),
             diag::warn_pragma_force_cuda_host_device_bad_arg);
 }
+
+/// \brief Handle the #pragma clang attribute directive.
+///
+/// The syntax is:
+/// \code
+///  #pragma clang attribute push(attribute)
+///  #pragma clang attribute pop
+/// \endcode
+///
+/// This directive instructs the compiler to begin/finish applying the specified
+/// attribute to the set of attribute-specific declarations in the active range
+/// of the pragma.
+void PragmaAttributeHandler::HandlePragma(Preprocessor &PP,
+                                          PragmaIntroducerKind Introducer,
+                                          Token &FirstToken) {
+  Token Tok;
+  PP.Lex(Tok);
+  auto *Info = new (PP.getPreprocessorAllocator())
+      PragmaAttributeInfo(AttributesForPragmaAttribute);
+
+  // Parse the 'push' or 'pop'.
+  if (Tok.isNot(tok::identifier)) {
+    PP.Diag(Tok.getLocation(), diag::err_pragma_attribute_expected_push_pop);
+    return;
+  }
+  const auto *II = Tok.getIdentifierInfo();
+  if (II->isStr("push"))
+    Info->Action = PragmaAttributeInfo::Push;
+  else if (II->isStr("pop"))
+    Info->Action = PragmaAttributeInfo::Pop;
+  else {
+    PP.Diag(Tok.getLocation(), diag::err_pragma_attribute_invalid_argument)
+        << PP.getSpelling(Tok);
+    return;
+  }
+  PP.Lex(Tok);
+
+  // Parse the actual attribute.
+  if (Info->Action == PragmaAttributeInfo::Push) {
+    if (Tok.isNot(tok::l_paren)) {
+      PP.Diag(Tok.getLocation(), diag::err_expected) << tok::l_paren;
+      return;
+    }
+    PP.Lex(Tok);
+
+    // Lex the attribute tokens.
+    SmallVector<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,9 +509,11 @@
   unsigned getMaxArgs() const;
   bool hasVariadicArg() const;
   bool diagnoseAppertainsTo(class Sema &S, const Decl *D) const;
+  bool appliesToDecl(class Sema &S, const Decl *D) const;
   bool diagnoseLangOpts(class Sema &S) const;
   bool existsInTarget(const TargetInfo &Target) const;
   bool isKnownToGCC() const;
+  bool isSupportedByPragmaAttribute() const;
 
   /// \brief If the parsed attribute has a semantic equivalent, and it would
   /// have a semantic Spelling enumeration (due to having semantically-distinct
@@ -774,6 +776,8 @@
   void clear() { list = nullptr; pool.clear(); }
   AttributeList *getList() const { return list; }
 
+  void clearListOnly() { list = nullptr; }
+
   /// Returns a reference to the attribute list.  Try not to introduce
   /// dependencies on this method, it may not be long-lived.
   AttributeList *&getListRef() { return list; }
Index: include/clang/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,22 @@
   "expected 'on' or 'off'">;
 def err_pragma_optimize_extra_argument : Error<
   "unexpected extra argument '%0' to '#pragma clang optimize'">;
+// - #pragma clang attribute
+def err_pragma_attribute_expected_push_pop : Error<
+  "expected 'push' or 'pop' after '#pragma clang attribute'">;
+def err_pragma_attribute_invalid_argument : Error<
+  "unexpected argument '%0' to '#pragma clang attribute'; "
+  "expected 'push' or 'pop'">;
+def err_pragma_attribute_expected_attribute : Error<
+  "expected an attribute after '('">;
+def err_pragma_attribute_expected_attribute_name : Error<
+  "expected identifier that represents an attribute name">;
+def err_pragma_attribute_extra_tokens_after_attribute : Error<
+  "extra tokens after attribute in a '#pragma clang attribute push'">;
+def err_pragma_attribute_unsupported_attribute : Error<
+  "attribute %0 is not supported by '#pragma clang attribute'">;
+def err_pragma_attribute_multiple_attributes : Error<
+  "more than one attribute specified in '#pragma clang attribute push'">;
 
 def err_opencl_unroll_hint_on_non_loop : Error<
   "OpenCL only supports 'opencl_unroll_hint' attribute on for, while, and do statements">;
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -302,6 +302,14 @@
   // Set to true if this attribute can be duplicated on a subject when merging
   // attributes. By default, attributes are not merged.
   bit DuplicatesAllowedWhileMerging = 0;
+  // Set to true if this attribute can be used with '#pragma clang attribute'.
+  // By default, when this value is false, an attribute is supported by the
+  // '#pragma clang attribute' unless one of the following statements is true:
+  // - An attribute requires delayed parsing (LateParsed is on)
+  // - An attribute has no GNU/CXX11 spelling
+  // - An attribute has no subject list
+  // - An attribute derives from a StmtAttr or a TypeAttr
+  bit ForcePragmaAttributeSupport = 0;
   // Lists language options, one of which is required to be true for the
   // attribute to be applicable. If empty, no language options are required.
   list<LangOpt> LangOpts = [];
@@ -465,6 +473,9 @@
 def Annotate : InheritableParamAttr {
   let Spellings = [GNU<"annotate">];
   let Args = [StringArgument<"Annotation">];
+  // Ensure that the annotate attribute can be used with
+  // '#pragma clang attribute' even though it has no subject list.
+  let ForcePragmaAttributeSupport = 1;
   let Documentation = [Undocumented];
 }
 
Index: docs/LanguageExtensions.rst
===================================================================
--- docs/LanguageExtensions.rst
+++ docs/LanguageExtensions.rst
@@ -2312,3 +2312,117 @@
 proven safe to vectorize. To identify and diagnose optimization issues use
 `-Rpass`, `-Rpass-missed`, and `-Rpass-analysis` command line options. See the
 user guide for details.
+
+Specifying an attribute for multiple declarations (#pragma clang attribute)
+===========================================================================
+
+The ``#pragma clang attribute`` directive can be used to apply an attribute to
+multiple declarations. The ``#pragma clang attribute push`` variation of the
+directive pushes a new attribute to the attribute stack. The declarations that
+follow the pragma receive the attributes that are on the attribute stack, until
+the stack is cleared using a ``#pragma clang attribute pop`` directive.
+
+The attributes that are used in the ``#pragma clang attribute`` directives
+can be written using the GNU-style syntax:
+
+.. code-block:: c++
+
+  #pragma clang attribute push(annotate("custom"))
+
+  void function(); // The function now has the annotate("custom") attribute
+
+  #pragma clang attribute pop
+
+The attributes can also be written using the C++11 style syntax, as long
+as just one attribute is specified in the square brackets:
+
+.. code-block:: c++
+
+  #pragma clang attribute push([[noreturn]])
+
+  void function(); // The function now has the [[noreturn]] attribute
+
+  #pragma clang attribute pop
+
+The set of declarations that receive a single attribute from the attribute
+stack is attribute-dependent. For instance, an ``annotate`` attribute will be
+received by all the declarations and definitions that can receive explicit
+attributes. This means that the following code:
+
+.. code-block:: c++
+
+  #pragma clang attribute push(annotate("custom"))
+
+  void function(int param);
+
+  namespace ns {
+
+  void function(int param) {
+    int localVariable = 0;
+  }
+
+  }
+
+  #pragma clang attribute pop
+
+Is semantically sequivalent to the code below:
+
+.. code-block:: c++
+
+  void function(int __attribute__((annotate("custom"))) param) __attribute__((annotate("custom")));
+
+  namespace ns __attribute__((annotate("custom"))) {
+
+  void function(int __attribute__((annotate("custom"))) param) __attribute__((annotate("custom"))) {
+    int __attribute__((annotate("custom"))) localVariable = 0;
+  }
+
+  }
+
+However, an attribute like ``[[noreturn]]`` will be received only by functions
+and methods. Consequently, this means that the following code:
+
+.. code-block:: c++
+
+  #pragma clang attribute push([[noreturn]])
+
+  void function(int param);
+
+  namespace ns {
+
+  void function(int param) {
+    int localVariable = 0;
+  }
+
+  }
+
+  #pragma clang attribute pop
+
+Is equivalent to the code below:
+
+.. code-block:: c++
+
+  [[noreturn]]
+  void function(int param);
+
+  namespace ns {
+
+  [[noreturn]]
+  void function(int param) {
+    int localVariable = 0;
+  }
+
+  }
+
+In general, the attributes are applied to a declaration only when there would
+have been no error or warning for that attribute if it was specified explicitly.
+An attribute is applied to each relevant declaration individually,
+unless the first declaration failed to receive the attribute because of a
+ompilation error. The attributes that aren't applied to any declaration are not
+verified semantically.
+
+Not all attributes can be used with the ``#pragma clang attribute`` directive.
+Notably, statement attributes like ``[[fallthrough]]`` or type attributes
+like ``address_space`` aren't supported by this directive. You can determine
+whether or not an attribute is supported by the pragma by referring to the
+:doc:`individual documentation for that attribute <AttributeReference>`.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to