Reject mismatched "#pragma GCC visibility push" and "#pragma GCC visibility pop".

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149559 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaAttr.cpp b/lib/Sema/SemaAttr.cpp
index 0859cb7..8b438a4 100644
--- a/lib/Sema/SemaAttr.cpp
+++ b/lib/Sema/SemaAttr.cpp
@@ -365,7 +365,7 @@
     }
     PushPragmaVisibility(*this, type, PragmaLoc);
   } else {
-    PopPragmaVisibility();
+    PopPragmaVisibility(false, PragmaLoc);
   }
 }
 
@@ -383,23 +383,44 @@
   }
 }
 
-void Sema::PushNamespaceVisibilityAttr(const VisibilityAttr *Attr) {
+void Sema::PushNamespaceVisibilityAttr(const VisibilityAttr *Attr,
+                                       SourceLocation Loc) {
   // Visibility calculations will consider the namespace's visibility.
   // Here we just want to note that we're in a visibility context
   // which overrides any enclosing #pragma context, but doesn't itself
   // contribute visibility.
-  PushPragmaVisibility(*this, NoVisibility, SourceLocation());
+  PushPragmaVisibility(*this, NoVisibility, Loc);
 }
 
-void Sema::PopPragmaVisibility() {
-  // Pop visibility from stack, if there is one on the stack.
-  if (VisContext) {
-    VisStack *Stack = static_cast<VisStack*>(VisContext);
-
-    Stack->pop_back();
-    // To simplify the implementation, never keep around an empty stack.
-    if (Stack->empty())
-      FreeVisContext();
+void Sema::PopPragmaVisibility(bool IsNamespaceEnd, SourceLocation EndLoc) {
+  if (!VisContext) {
+    Diag(EndLoc, diag::err_pragma_pop_visibility_mismatch);
+    return;
   }
-  // FIXME: Add diag for pop without push.
+
+  // Pop visibility from stack
+  VisStack *Stack = static_cast<VisStack*>(VisContext);
+
+  const std::pair<unsigned, SourceLocation> *Back = &Stack->back();
+  bool StartsWithPragma = Back->first != NoVisibility;
+  if (StartsWithPragma && IsNamespaceEnd) {
+    Diag(Back->second, diag::err_pragma_push_visibility_mismatch);
+    Diag(EndLoc, diag::note_surrounding_namespace_ends_here);
+
+    // For better error recovery, eat all pushes inside the namespace.
+    do {
+      Stack->pop_back();
+      Back = &Stack->back();
+      StartsWithPragma = Back->first != NoVisibility;
+    } while (StartsWithPragma);
+  } else if (!StartsWithPragma && !IsNamespaceEnd) {
+    Diag(EndLoc, diag::err_pragma_pop_visibility_mismatch);
+    Diag(Back->second, diag::note_surrounding_namespace_starts_here);
+    return;
+  }
+
+  Stack->pop_back();
+  // To simplify the implementation, never keep around an empty stack.
+  if (Stack->empty())
+    FreeVisContext();
 }