Add support for namespaces on #pragma clang attribute

Namespaces are introduced by adding an "identifier." before a
push/pop directive. Pop directives with namespaces can only pop a
attribute group that was pushed with the same namespace. Push and pop
directives that don't opt into namespaces have the same semantics.

This is necessary to prevent a pitfall of using multiple #pragma
clang attribute directives spread out in a large file, particularly
when macros are involved. It isn't easy to see which pop corripsonds
to which push, so its easy to inadvertently pop the wrong group.

Differential revision: https://reviews.llvm.org/D55628

llvm-svn: 349845
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index f6ac9b4..2bc1b76 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -631,28 +631,46 @@
       {PragmaLoc, &Attribute, std::move(SubjectMatchRules), /*IsUsed=*/false});
 }
 
-void Sema::ActOnPragmaAttributeEmptyPush(SourceLocation PragmaLoc) {
+void Sema::ActOnPragmaAttributeEmptyPush(SourceLocation PragmaLoc,
+                                         const IdentifierInfo *Namespace) {
   PragmaAttributeStack.emplace_back();
   PragmaAttributeStack.back().Loc = PragmaLoc;
+  PragmaAttributeStack.back().Namespace = Namespace;
 }
 
-void Sema::ActOnPragmaAttributePop(SourceLocation PragmaLoc) {
+void Sema::ActOnPragmaAttributePop(SourceLocation PragmaLoc,
+                                   const IdentifierInfo *Namespace) {
   if (PragmaAttributeStack.empty()) {
-    Diag(PragmaLoc, diag::err_pragma_attribute_stack_mismatch);
+    Diag(PragmaLoc, diag::err_pragma_attribute_stack_mismatch) << 1;
     return;
   }
 
-  for (const PragmaAttributeEntry &Entry :
-       PragmaAttributeStack.back().Entries) {
-    if (!Entry.IsUsed) {
-      assert(Entry.Attribute && "Expected an attribute");
-      Diag(Entry.Attribute->getLoc(), diag::warn_pragma_attribute_unused)
-          << Entry.Attribute->getName();
-      Diag(PragmaLoc, diag::note_pragma_attribute_region_ends_here);
+  // Dig back through the stack trying to find the most recently pushed group
+  // that in Namespace. Note that this works fine if no namespace is present,
+  // think of push/pops without namespaces as having an implicit "nullptr"
+  // namespace.
+  for (size_t Index = PragmaAttributeStack.size(); Index;) {
+    --Index;
+    if (PragmaAttributeStack[Index].Namespace == Namespace) {
+      for (const PragmaAttributeEntry &Entry :
+           PragmaAttributeStack[Index].Entries) {
+        if (!Entry.IsUsed) {
+          assert(Entry.Attribute && "Expected an attribute");
+          Diag(Entry.Attribute->getLoc(), diag::warn_pragma_attribute_unused)
+              << *Entry.Attribute;
+          Diag(PragmaLoc, diag::note_pragma_attribute_region_ends_here);
+        }
+      }
+      PragmaAttributeStack.erase(PragmaAttributeStack.begin() + Index);
+      return;
     }
   }
 
-  PragmaAttributeStack.pop_back();
+  if (Namespace)
+    Diag(PragmaLoc, diag::err_pragma_attribute_stack_mismatch)
+        << 0 << Namespace->getName();
+  else
+    Diag(PragmaLoc, diag::err_pragma_attribute_stack_mismatch) << 1;
 }
 
 void Sema::AddPragmaAttributes(Scope *S, Decl *D) {