[clang] Add `pragma clang scope [push|pop]` (PR #121025)

Chris B via cfe-commits cfe-commits at lists.llvm.org
Mon Dec 23 20:13:55 PST 2024


https://github.com/llvm-beanz created https://github.com/llvm/llvm-project/pull/121025

Have you ever had that horrible realization that some header you included defines a macro that matches a commonly used word that appears throughout your codebase? What kind of horrible person would define `max` or `min` as a macro and put it into a public header that ships in an SDK?!?!

Well, I have the solution for you!

Enter "Macro Scopes": with this new preprocessor extension you can wrap pesky includes with `#pragma clang scope push` and `#pragma clang scope pop` to protect your carefully curated source from preprocessor macros that bleed from your dependencies.

>From 5433a9134beb8faf1e2b662c96f76d7e8bc814de Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Mon, 23 Dec 2024 21:22:28 -0600
Subject: [PATCH] Add `pragma clang scope [push|pop]`

Have you ever had that horrible realization that some header you
included defines a macro that matches a commonly used word that appears
throughout your codebase? What kind of horrible person would define
`max` or `min` as a macro and put it into a public header that ships in
an SDK?!?!

Well, I have the solution for you!

Enter "Macro Scopes": with this new preprocessor extension you can wrap
pesky includes with `#pragma clang scope push` and `#pragma clang scope
pop` to protect your carefully curated source from preprocessor macros
that bleed from your dependencies.
---
 clang/docs/LanguageExtensions.rst             |  34 ++
 .../include/clang/Basic/DiagnosticLexKinds.td |   4 +-
 clang/include/clang/Lex/Preprocessor.h        |   7 +
 clang/lib/Lex/PPMacroExpansion.cpp            | 499 ++++++++++--------
 clang/lib/Lex/Pragma.cpp                      |  25 +-
 .../Inputs/SomeHeaderThatDefinesAwfulThings.h |   1 +
 clang/test/Lexer/pragma-scope.c               |  36 ++
 7 files changed, 369 insertions(+), 237 deletions(-)
 create mode 100644 clang/test/Lexer/Inputs/SomeHeaderThatDefinesAwfulThings.h
 create mode 100644 clang/test/Lexer/pragma-scope.c

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index cc5f1d4ddf4477..a81fa833eafdc9 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -5738,6 +5738,40 @@ in user headers or code. This is controlled by ``-Wpedantic-macros``. Final
 macros will always warn on redefinition, including situations with identical
 bodies and in system headers.
 
+Macro Scope
+===========
+
+Clang supports the pragma ``#pragma clang scope`` which is provided with an
+argument ``push`` or ``pop`` to denote entering and leaving macro scopes. On
+entering a macro scope all macro definitions and undefinitions are recorded so
+that they can be reverted on leaving the scope.
+
+.. code-block:: c
+
+   #define NUM_DOGGOS 2
+
+   #pragma clang scope push
+   #define NUM_DOGGOS 3
+   #pragma clang scope pop // NUM_DOGGOS is restored to 2
+
+   #pragma clang scope push
+   #undef NUM_DOGGOS
+   #pragma clang scope pop // NUM_DOGGOS is restored to 2
+
+   #undef NUM_DOGGOS
+   #pragma clang scope push
+   #define NUM_DOGGOS 1
+   #pragma clang scope pop // NUM_DOGGOS is restored to undefined
+
+A macro scope can be used to wrap header includes to isolate headers from
+leaking macros to the outer source file.
+
+.. code-block:: c
+
+   #pragma clang scope push
+   #include <SomeSystemHeader.h>
+   #pragma clang scope pop // None of the defines from the included header persist.
+
 Line Control
 ============
 
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 959376b0847216..a1f57aafb51bf7 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -693,8 +693,8 @@ def warn_pragma_diagnostic_invalid :
    ExtWarn<"pragma diagnostic expected 'error', 'warning', 'ignored', 'fatal',"
             " 'push', or 'pop'">,
    InGroup<UnknownPragmas>;
-def warn_pragma_diagnostic_cannot_pop :
-   ExtWarn<"pragma diagnostic pop could not pop, no matching push">,
+def warn_pragma_cannot_pop :
+   ExtWarn<"pragma %select{diagnostic|scope}0 pop could not pop, no matching push">,
    InGroup<UnknownPragmas>;
 def warn_pragma_diagnostic_invalid_option :
    ExtWarn<"pragma diagnostic expected option name (e.g. \"-Wundef\")">,
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 3d223c345ea156..96240533deff5e 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -1059,6 +1059,10 @@ class Preprocessor {
   /// Warning information for macro annotations.
   llvm::DenseMap<const IdentifierInfo *, MacroAnnotations> AnnotationInfos;
 
+  using MacroScopeVec = llvm::SmallVector<std::pair<IdentifierInfo *, MacroDirective *> >;
+  MacroScopeVec *CurScope = nullptr;
+  llvm::SmallVector<MacroScopeVec> MacroScopeStack;
+
   /// A "freelist" of MacroArg objects that can be
   /// reused for quick allocation.
   MacroArgs *MacroArgCache = nullptr;
@@ -2896,6 +2900,9 @@ class Preprocessor {
     AnnotationInfos[II].FinalAnnotationLoc = AnnotationLoc;
   }
 
+  void pushMacroScope();
+  void popMacroScope(SourceLocation Loc);
+
   const MacroAnnotations &getMacroAnnotations(const IdentifierInfo *II) const {
     return AnnotationInfos.find(II)->second;
   }
diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp
index 347c13da0ad215..f47a2eb1a37caf 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -66,7 +66,35 @@ Preprocessor::getLocalMacroDirectiveHistory(const IdentifierInfo *II) const {
                                                 : Pos->second.getLatest();
 }
 
-void Preprocessor::appendMacroDirective(IdentifierInfo *II, MacroDirective *MD){
+void Preprocessor::pushMacroScope() {
+  MacroScopeStack.emplace_back(MacroScopeVec());
+  CurScope = &MacroScopeStack.back();
+}
+
+void Preprocessor::popMacroScope(SourceLocation Loc) {
+  if (!CurScope) {
+    Diag(Loc, diag::warn_pragma_cannot_pop) << /*scope*/ 1;
+    return;
+  }
+
+  for (auto It = CurScope->rbegin(); It != CurScope->rend(); ++It) {
+    MacroDirective *Prev = It->second->getPrevious();
+    if (Prev && Prev->getKind() == MacroDirective::MD_Define) {
+        DefMacroDirective *MD =
+            AllocateDefMacroDirective(Prev->getMacroInfo(), Loc);
+        appendMacroDirective(It->first, MD);
+    } else {
+      UndefMacroDirective *Undef = AllocateUndefMacroDirective(Loc);
+      appendMacroDirective(It->first, Undef);
+    }
+  }
+  // Unwind macro stack...
+  MacroScopeStack.pop_back();
+  CurScope = MacroScopeStack.empty() ? nullptr : &MacroScopeStack.back();
+}
+
+void Preprocessor::appendMacroDirective(IdentifierInfo *II,
+                                        MacroDirective *MD) {
   assert(MD && "MacroDirective should be non-zero!");
   assert(!MD->getPrevious() && "Already attached to a MacroDirective history.");
 
@@ -76,6 +104,9 @@ void Preprocessor::appendMacroDirective(IdentifierInfo *II, MacroDirective *MD){
   StoredMD.setLatest(MD);
   StoredMD.overrideActiveModuleMacros(*this, II);
 
+  if (CurScope)
+    CurScope->push_back(std::make_pair(II,MD));
+
   if (needModuleMacros()) {
     // Track that we created a new macro directive, so we know we should
     // consider building a ModuleMacro for it when we get to the end of
@@ -254,7 +285,7 @@ void Preprocessor::updateModuleMacroInfo(const IdentifierInfo *II,
 }
 
 void Preprocessor::dumpMacroInfo(const IdentifierInfo *II) {
-  ArrayRef<ModuleMacro*> Leaf;
+  ArrayRef<ModuleMacro *> Leaf;
   auto LeafIt = LeafModuleMacros.find(II);
   if (LeafIt != LeafModuleMacros.end())
     Leaf = LeafIt->second;
@@ -281,11 +312,11 @@ void Preprocessor::dumpMacroInfo(const IdentifierInfo *II) {
   }
 
   // Dump module macros.
-  llvm::DenseSet<ModuleMacro*> Active;
+  llvm::DenseSet<ModuleMacro *> Active;
   for (auto *MM : State ? State->getActiveModuleMacros(*this, II)
                         : ArrayRef<ModuleMacro *>())
     Active.insert(MM);
-  llvm::DenseSet<ModuleMacro*> Visited;
+  llvm::DenseSet<ModuleMacro *> Visited;
   llvm::SmallVector<ModuleMacro *, 16> Worklist(Leaf);
   while (!Worklist.empty()) {
     auto *MM = Worklist.pop_back_val();
@@ -394,7 +425,8 @@ static bool isTrivialSingleTokenExpansion(const MacroInfo *MI,
   IdentifierInfo *II = MI->getReplacementToken(0).getIdentifierInfo();
 
   // If the token isn't an identifier, it's always literally expanded.
-  if (!II) return true;
+  if (!II)
+    return true;
 
   // If the information about this identifier is out of date, update it from
   // the external source.
@@ -411,7 +443,8 @@ static bool isTrivialSingleTokenExpansion(const MacroInfo *MI,
 
   // If this is an object-like macro invocation, it is safe to trivially expand
   // it.
-  if (MI->isObjectLike()) return true;
+  if (MI->isObjectLike())
+    return true;
 
   // If this is a function-like macro invocation, it's safe to trivially expand
   // as long as the identifier is not a macro argument.
@@ -467,7 +500,8 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier,
   // If this is a macro expansion in the "#if !defined(x)" line for the file,
   // then the macro could expand to different things in other contexts, we need
   // to disable the optimization in this case.
-  if (CurPPLexer) CurPPLexer->MIOpt.ExpandedMacro();
+  if (CurPPLexer)
+    CurPPLexer->MIOpt.ExpandedMacro();
 
   // If this is a builtin macro, like __LINE__ or _Pragma, handle it specially.
   if (MI->isBuiltinMacro()) {
@@ -502,7 +536,8 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier,
     ArgMacro = nullptr;
 
     // If there was an error parsing the arguments, bail out.
-    if (!Args) return true;
+    if (!Args)
+      return true;
 
     ++NumFnMacroExpanded;
   } else {
@@ -540,13 +575,13 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier,
   // If the macro definition is ambiguous, complain.
   if (M.isAmbiguous()) {
     Diag(Identifier, diag::warn_pp_ambiguous_macro)
-      << Identifier.getIdentifierInfo();
+        << Identifier.getIdentifierInfo();
     Diag(MI->getDefinitionLoc(), diag::note_pp_ambiguous_macro_chosen)
-      << Identifier.getIdentifierInfo();
+        << Identifier.getIdentifierInfo();
     M.forAllDefinitions([&](const MacroInfo *OtherMI) {
       if (OtherMI != MI)
         Diag(OtherMI->getDefinitionLoc(), diag::note_pp_ambiguous_macro_other)
-          << Identifier.getIdentifierInfo();
+            << Identifier.getIdentifierInfo();
     });
   }
 
@@ -556,7 +591,8 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier,
   // expansion stack, only to take it right back off.
   if (MI->getNumTokens() == 0) {
     // No need for arg info.
-    if (Args) Args->destroy(*this);
+    if (Args)
+      Args->destroy(*this);
 
     // Propagate whitespace info as if we had pushed, then popped,
     // a macro context.
@@ -572,7 +608,8 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier,
     // "#define VAL 42".
 
     // No need for arg info.
-    if (Args) Args->destroy(*this);
+    if (Args)
+      Args->destroy(*this);
 
     // Propagate the isAtStartOfLine/hasLeadingSpace markers of the macro
     // identifier to the expanded token.
@@ -583,14 +620,14 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier,
     Identifier = MI->getReplacementToken(0);
 
     // Restore the StartOfLine/LeadingSpace markers.
-    Identifier.setFlagValue(Token::StartOfLine , isAtStartOfLine);
+    Identifier.setFlagValue(Token::StartOfLine, isAtStartOfLine);
     Identifier.setFlagValue(Token::LeadingSpace, hasLeadingSpace);
 
     // Update the tokens location to include both its expansion and physical
     // locations.
     SourceLocation Loc =
-      SourceMgr.createExpansionLoc(Identifier.getLocation(), ExpandLoc,
-                                   ExpansionEnd,Identifier.getLength());
+        SourceMgr.createExpansionLoc(Identifier.getLocation(), ExpandLoc,
+                                     ExpansionEnd, Identifier.getLength());
     Identifier.setLocation(Loc);
 
     // If this is a disabled macro or #define X X, we must mark the result as
@@ -617,10 +654,7 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier,
   return false;
 }
 
-enum Bracket {
-  Brace,
-  Paren
-};
+enum Bracket { Brace, Paren };
 
 /// CheckMatchedBrackets - Returns true if the braces and parentheses in the
 /// token vector are properly nested.
@@ -728,8 +762,8 @@ static bool GenerateNewArgTokens(Preprocessor &PP,
           TempToken.setLocation(Loc);
           TempToken.setLength(0);
           NewTokens.push_back(TempToken);
-          ParenHints.push_back(SourceRange(ArgStartIterator->getLocation(),
-                                           Loc));
+          ParenHints.push_back(
+              SourceRange(ArgStartIterator->getLocation(), Loc));
         }
 
         // Copy separator token
@@ -797,7 +831,7 @@ MacroArgs *Preprocessor::ReadMacroCallArgumentList(Token &MacroName,
         if (!ContainsCodeCompletionTok) {
           Diag(MacroName, diag::err_unterm_macro_invoc);
           Diag(MI->getDefinitionLoc(), diag::note_macro_here)
-            << MacroName.getIdentifierInfo();
+              << MacroName.getIdentifierInfo();
           // Do not lose the EOF/EOD.  Return it to the client.
           MacroName = Tok;
           return nullptr;
@@ -811,8 +845,7 @@ MacroArgs *Preprocessor::ReadMacroCallArgumentList(Token &MacroName,
         // If we found the ) token, the macro arg list is done.
         if (NumParens-- == 0) {
           MacroEnd = Tok.getLocation();
-          if (!ArgTokens.empty() &&
-              ArgTokens.back().commaAfterElided()) {
+          if (!ArgTokens.empty() && ArgTokens.back().commaAfterElided()) {
             FoundElidedComma = true;
           }
           break;
@@ -911,7 +944,7 @@ MacroArgs *Preprocessor::ReadMacroCallArgumentList(Token &MacroName,
     // Emitting it at the , could be far away from the macro name.
     Diag(TooManyArgsLoc, diag::err_too_many_args_in_macro_invoc);
     Diag(MI->getDefinitionLoc(), diag::note_macro_here)
-      << MacroName.getIdentifierInfo();
+        << MacroName.getIdentifierInfo();
 
     // Commas from braced initializer lists will be treated as argument
     // separators inside macros.  Attempt to correct for this with parentheses.
@@ -924,9 +957,8 @@ MacroArgs *Preprocessor::ReadMacroCallArgumentList(Token &MacroName,
     if (!GenerateNewArgTokens(*this, ArgTokens, FixedArgTokens, FixedNumArgs,
                               ParenHints, InitLists)) {
       if (!InitLists.empty()) {
-        DiagnosticBuilder DB =
-            Diag(MacroName,
-                 diag::note_init_list_at_beginning_of_macro_argument);
+        DiagnosticBuilder DB = Diag(
+            MacroName, diag::note_init_list_at_beginning_of_macro_argument);
         for (SourceRange Range : InitLists)
           DB << Range;
       }
@@ -968,8 +1000,8 @@ MacroArgs *Preprocessor::ReadMacroCallArgumentList(Token &MacroName,
       // the macro expects one argument (the argument is just empty).
       isVarargsElided = MI->isVariadic();
     } else if ((FoundElidedComma || MI->isVariadic()) &&
-               (NumActuals+1 == MinArgsExpected ||  // A(x, ...) -> A(X)
-                (NumActuals == 0 && MinArgsExpected == 2))) {// A(x,...) -> A()
+               (NumActuals + 1 == MinArgsExpected || // A(x, ...) -> A(X)
+                (NumActuals == 0 && MinArgsExpected == 2))) { // A(x,...) -> A()
       // Varargs where the named vararg parameter is missing: OK as extension.
       //   #define A(x, ...)
       //   A("blah")
@@ -992,7 +1024,7 @@ MacroArgs *Preprocessor::ReadMacroCallArgumentList(Token &MacroName,
           ID = diag::ext_c_missing_varargs_arg;
         Diag(Tok, ID);
         Diag(MI->getDefinitionLoc(), diag::note_macro_here)
-          << MacroName.getIdentifierInfo();
+            << MacroName.getIdentifierInfo();
       }
 
       // Remember this occurred, allowing us to elide the comma when used for
@@ -1006,7 +1038,7 @@ MacroArgs *Preprocessor::ReadMacroCallArgumentList(Token &MacroName,
       // Otherwise, emit the error.
       Diag(Tok, diag::err_too_few_args_in_macro_invoc);
       Diag(MI->getDefinitionLoc(), diag::note_macro_here)
-        << MacroName.getIdentifierInfo();
+          << MacroName.getIdentifierInfo();
       return nullptr;
     }
 
@@ -1028,7 +1060,7 @@ MacroArgs *Preprocessor::ReadMacroCallArgumentList(Token &MacroName,
     // Emitting it at the , could be far away from the macro name.
     Diag(MacroName, diag::err_too_many_args_in_macro_invoc);
     Diag(MI->getDefinitionLoc(), diag::note_macro_here)
-      << MacroName.getIdentifierInfo();
+        << MacroName.getIdentifierInfo();
     return nullptr;
   }
 
@@ -1047,8 +1079,8 @@ Token *Preprocessor::cacheMacroExpandedTokens(TokenLexer *tokLexer,
     return nullptr;
 
   size_t newIndex = MacroExpandedTokens.size();
-  bool cacheNeedsToGrow = tokens.size() >
-                      MacroExpandedTokens.capacity()-MacroExpandedTokens.size();
+  bool cacheNeedsToGrow = tokens.size() > MacroExpandedTokens.capacity() -
+                                              MacroExpandedTokens.size();
   MacroExpandedTokens.append(tokens.begin(), tokens.end());
 
   if (cacheNeedsToGrow) {
@@ -1090,9 +1122,9 @@ static void ComputeDATE_TIME(SourceLocation &DATELoc, SourceLocation &TIMELoc,
     TM = std::localtime(&TT);
   }
 
-  static const char * const Months[] = {
-    "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
-  };
+  static const char *const Months[] = {"Jan", "Feb", "Mar", "Apr",
+                                       "May", "Jun", "Jul", "Aug",
+                                       "Sep", "Oct", "Nov", "Dec"};
 
   {
     SmallString<32> TmpBuffer;
@@ -1160,8 +1192,8 @@ static bool HasExtension(const Preprocessor &PP, StringRef Extension) {
       Extension.size() >= 4)
     Extension = Extension.substr(2, Extension.size() - 4);
 
-    // Because we inherit the feature list from HasFeature, this string switch
-    // must be less restrictive than HasFeature's.
+  // Because we inherit the feature list from HasFeature, this string switch
+  // must be less restrictive than HasFeature's.
 #define EXTENSION(Name, Predicate) .Case(#Name, Predicate)
   return llvm::StringSwitch<bool>(Extension)
 #include "clang/Basic/Features.def"
@@ -1376,17 +1408,15 @@ bool Preprocessor::EvaluateHasIncludeNext(Token &Tok, IdentifierInfo *II) {
 
 /// Process single-argument builtin feature-like macros that return
 /// integer values.
-static void EvaluateFeatureLikeBuiltinMacro(llvm::raw_svector_ostream& OS,
-                                            Token &Tok, IdentifierInfo *II,
-                                            Preprocessor &PP, bool ExpandArgs,
-                                            llvm::function_ref<
-                                              int(Token &Tok,
-                                                  bool &HasLexedNextTok)> Op) {
+static void EvaluateFeatureLikeBuiltinMacro(
+    llvm::raw_svector_ostream &OS, Token &Tok, IdentifierInfo *II,
+    Preprocessor &PP, bool ExpandArgs,
+    llvm::function_ref<int(Token &Tok, bool &HasLexedNextTok)> Op) {
   // Parse the initial '('.
   PP.LexUnexpandedToken(Tok);
   if (Tok.isNot(tok::l_paren)) {
-    PP.Diag(Tok.getLocation(), diag::err_pp_expected_after) << II
-                                                            << tok::l_paren;
+    PP.Diag(Tok.getLocation(), diag::err_pp_expected_after)
+        << II << tok::l_paren;
 
     // Provide a dummy '0' value on output stream to elide further errors.
     if (!Tok.isOneOf(tok::eof, tok::eod)) {
@@ -1409,64 +1439,64 @@ static void EvaluateFeatureLikeBuiltinMacro(llvm::raw_svector_ostream& OS,
     else
       PP.LexUnexpandedToken(Tok);
 
-already_lexed:
+  already_lexed:
     switch (Tok.getKind()) {
-      case tok::eof:
-      case tok::eod:
-        // Don't provide even a dummy value if the eod or eof marker is
-        // reached.  Simply provide a diagnostic.
-        PP.Diag(Tok.getLocation(), diag::err_unterm_macro_invoc);
-        return;
+    case tok::eof:
+    case tok::eod:
+      // Don't provide even a dummy value if the eod or eof marker is
+      // reached.  Simply provide a diagnostic.
+      PP.Diag(Tok.getLocation(), diag::err_unterm_macro_invoc);
+      return;
 
-      case tok::comma:
-        if (!SuppressDiagnostic) {
-          PP.Diag(Tok.getLocation(), diag::err_too_many_args_in_macro_invoc);
-          SuppressDiagnostic = true;
-        }
-        continue;
+    case tok::comma:
+      if (!SuppressDiagnostic) {
+        PP.Diag(Tok.getLocation(), diag::err_too_many_args_in_macro_invoc);
+        SuppressDiagnostic = true;
+      }
+      continue;
 
-      case tok::l_paren:
-        ++ParenDepth;
-        if (Result)
-          break;
-        if (!SuppressDiagnostic) {
-          PP.Diag(Tok.getLocation(), diag::err_pp_nested_paren) << II;
-          SuppressDiagnostic = true;
-        }
+    case tok::l_paren:
+      ++ParenDepth;
+      if (Result)
+        break;
+      if (!SuppressDiagnostic) {
+        PP.Diag(Tok.getLocation(), diag::err_pp_nested_paren) << II;
+        SuppressDiagnostic = true;
+      }
+      continue;
+
+    case tok::r_paren:
+      if (--ParenDepth > 0)
         continue;
 
-      case tok::r_paren:
-        if (--ParenDepth > 0)
-          continue;
-
-        // The last ')' has been reached; return the value if one found or
-        // a diagnostic and a dummy value.
-        if (Result) {
-          OS << *Result;
-          // For strict conformance to __has_cpp_attribute rules, use 'L'
-          // suffix for dated literals.
-          if (*Result > 1)
-            OS << 'L';
-        } else {
-          OS << 0;
-          if (!SuppressDiagnostic)
-            PP.Diag(Tok.getLocation(), diag::err_too_few_args_in_macro_invoc);
-        }
-        Tok.setKind(tok::numeric_constant);
-        return;
+      // The last ')' has been reached; return the value if one found or
+      // a diagnostic and a dummy value.
+      if (Result) {
+        OS << *Result;
+        // For strict conformance to __has_cpp_attribute rules, use 'L'
+        // suffix for dated literals.
+        if (*Result > 1)
+          OS << 'L';
+      } else {
+        OS << 0;
+        if (!SuppressDiagnostic)
+          PP.Diag(Tok.getLocation(), diag::err_too_few_args_in_macro_invoc);
+      }
+      Tok.setKind(tok::numeric_constant);
+      return;
 
-      default: {
-        // Parse the macro argument, if one not found so far.
-        if (Result)
-          break;
+    default: {
+      // Parse the macro argument, if one not found so far.
+      if (Result)
+        break;
 
-        bool HasLexedNextToken = false;
-        Result = Op(Tok, HasLexedNextToken);
-        ResultTok = Tok;
-        if (HasLexedNextToken)
-          goto already_lexed;
-        continue;
-      }
+      bool HasLexedNextToken = false;
+      Result = Op(Tok, HasLexedNextToken);
+      ResultTok = Tok;
+      if (HasLexedNextToken)
+        goto already_lexed;
+      continue;
+    }
     }
 
     // Diagnose missing ')'.
@@ -1486,8 +1516,7 @@ static void EvaluateFeatureLikeBuiltinMacro(llvm::raw_svector_ostream& OS,
 
 /// Helper function to return the IdentifierInfo structure of a Token
 /// or generate a diagnostic if none available.
-static IdentifierInfo *ExpectFeatureIdentifierInfo(Token &Tok,
-                                                   Preprocessor &PP,
+static IdentifierInfo *ExpectFeatureIdentifierInfo(Token &Tok, Preprocessor &PP,
                                                    signed DiagID) {
   IdentifierInfo *II;
   if (!Tok.isAnnotation() && (II = Tok.getIdentifierInfo()))
@@ -1573,7 +1602,7 @@ static bool isTargetVariantOS(const TargetInfo &TI, const IdentifierInfo *II) {
 
 /// Implements the __is_target_variant_environment builtin macro.
 static bool isTargetVariantEnvironment(const TargetInfo &TI,
-                                const IdentifierInfo *II) {
+                                       const IdentifierInfo *II) {
   if (TI.getTriple().isOSDarwin()) {
     const llvm::Triple *VariantTriple = TI.getDarwinTargetVariantTriple();
     if (!VariantTriple)
@@ -1669,7 +1698,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
     PresumedLoc PLoc = SourceMgr.getPresumedLoc(Loc);
 
     // __LINE__ expands to a simple numeric value.
-    OS << (PLoc.isValid()? PLoc.getLine() : 1);
+    OS << (PLoc.isValid() ? PLoc.getLine() : 1);
     Tok.setKind(tok::numeric_constant);
   } else if (II == Ident__FILE__ || II == Ident__BASE_FILE__ ||
              II == Ident__FILE_NAME__) {
@@ -1711,9 +1740,8 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
       ComputeDATE_TIME(DATELoc, TIMELoc, *this);
     Tok.setKind(tok::string_literal);
     Tok.setLength(strlen("\"Mmm dd yyyy\""));
-    Tok.setLocation(SourceMgr.createExpansionLoc(DATELoc, Tok.getLocation(),
-                                                 Tok.getLocation(),
-                                                 Tok.getLength()));
+    Tok.setLocation(SourceMgr.createExpansionLoc(
+        DATELoc, Tok.getLocation(), Tok.getLocation(), Tok.getLength()));
     return;
   } else if (II == Ident__TIME__) {
     Diag(Tok.getLocation(), diag::warn_pp_date_time);
@@ -1721,9 +1749,8 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
       ComputeDATE_TIME(DATELoc, TIMELoc, *this);
     Tok.setKind(tok::string_literal);
     Tok.setLength(strlen("\"hh:mm:ss\""));
-    Tok.setLocation(SourceMgr.createExpansionLoc(TIMELoc, Tok.getLocation(),
-                                                 Tok.getLocation(),
-                                                 Tok.getLength()));
+    Tok.setLocation(SourceMgr.createExpansionLoc(
+        TIMELoc, Tok.getLocation(), Tok.getLocation(), Tok.getLength()));
     return;
   } else if (II == Ident__INCLUDE_LEVEL__) {
     // Compute the presumed include depth of this token.  This can be affected
@@ -1784,68 +1811,71 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
     OS << CounterValue++;
     Tok.setKind(tok::numeric_constant);
   } else if (II == Ident__has_feature) {
-    EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false,
-      [this](Token &Tok, bool &HasLexedNextToken) -> int {
-        IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
-                                           diag::err_feature_check_malformed);
-        return II && HasFeature(*this, II->getName());
-      });
+    EvaluateFeatureLikeBuiltinMacro(
+        OS, Tok, II, *this, false,
+        [this](Token &Tok, bool &HasLexedNextToken) -> int {
+          IdentifierInfo *II = ExpectFeatureIdentifierInfo(
+              Tok, *this, diag::err_feature_check_malformed);
+          return II && HasFeature(*this, II->getName());
+        });
   } else if (II == Ident__has_extension) {
-    EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false,
-      [this](Token &Tok, bool &HasLexedNextToken) -> int {
-        IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
-                                           diag::err_feature_check_malformed);
-        return II && HasExtension(*this, II->getName());
-      });
+    EvaluateFeatureLikeBuiltinMacro(
+        OS, Tok, II, *this, false,
+        [this](Token &Tok, bool &HasLexedNextToken) -> int {
+          IdentifierInfo *II = ExpectFeatureIdentifierInfo(
+              Tok, *this, diag::err_feature_check_malformed);
+          return II && HasExtension(*this, II->getName());
+        });
   } else if (II == Ident__has_builtin) {
-    EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false,
-      [this](Token &Tok, bool &HasLexedNextToken) -> int {
-        IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
-                                           diag::err_feature_check_malformed);
-        if (!II)
-          return false;
-        else if (II->getBuiltinID() != 0) {
-          switch (II->getBuiltinID()) {
-          case Builtin::BI__builtin_cpu_is:
-            return getTargetInfo().supportsCpuIs();
-          case Builtin::BI__builtin_cpu_init:
-            return getTargetInfo().supportsCpuInit();
-          case Builtin::BI__builtin_cpu_supports:
-            return getTargetInfo().supportsCpuSupports();
-          case Builtin::BI__builtin_operator_new:
-          case Builtin::BI__builtin_operator_delete:
-            // denotes date of behavior change to support calling arbitrary
-            // usual allocation and deallocation functions. Required by libc++
-            return 201802;
-          default:
-            return Builtin::evaluateRequiredTargetFeatures(
-                getBuiltinInfo().getRequiredFeatures(II->getBuiltinID()),
-                getTargetInfo().getTargetOpts().FeatureMap);
+    EvaluateFeatureLikeBuiltinMacro(
+        OS, Tok, II, *this, false,
+        [this](Token &Tok, bool &HasLexedNextToken) -> int {
+          IdentifierInfo *II = ExpectFeatureIdentifierInfo(
+              Tok, *this, diag::err_feature_check_malformed);
+          if (!II)
+            return false;
+          else if (II->getBuiltinID() != 0) {
+            switch (II->getBuiltinID()) {
+            case Builtin::BI__builtin_cpu_is:
+              return getTargetInfo().supportsCpuIs();
+            case Builtin::BI__builtin_cpu_init:
+              return getTargetInfo().supportsCpuInit();
+            case Builtin::BI__builtin_cpu_supports:
+              return getTargetInfo().supportsCpuSupports();
+            case Builtin::BI__builtin_operator_new:
+            case Builtin::BI__builtin_operator_delete:
+              // denotes date of behavior change to support calling arbitrary
+              // usual allocation and deallocation functions. Required by libc++
+              return 201802;
+            default:
+              return Builtin::evaluateRequiredTargetFeatures(
+                  getBuiltinInfo().getRequiredFeatures(II->getBuiltinID()),
+                  getTargetInfo().getTargetOpts().FeatureMap);
+            }
+            return true;
+          } else if (IsBuiltinTrait(Tok)) {
+            return true;
+          } else if (II->getTokenID() != tok::identifier &&
+                     II->getName().starts_with("__builtin_")) {
+            return true;
+          } else {
+            return llvm::StringSwitch<bool>(II->getName())
+                // Report builtin templates as being builtins.
+                .Case("__make_integer_seq", getLangOpts().CPlusPlus)
+                .Case("__type_pack_element", getLangOpts().CPlusPlus)
+                .Case("__builtin_common_type", getLangOpts().CPlusPlus)
+                // Likewise for some builtin preprocessor macros.
+                // FIXME: This is inconsistent; we usually suggest detecting
+                // builtin macros via #ifdef. Don't add more cases here.
+                .Case("__is_target_arch", true)
+                .Case("__is_target_vendor", true)
+                .Case("__is_target_os", true)
+                .Case("__is_target_environment", true)
+                .Case("__is_target_variant_os", true)
+                .Case("__is_target_variant_environment", true)
+                .Default(false);
           }
-          return true;
-        } else if (IsBuiltinTrait(Tok)) {
-          return true;
-        } else if (II->getTokenID() != tok::identifier &&
-                   II->getName().starts_with("__builtin_")) {
-          return true;
-        } else {
-          return llvm::StringSwitch<bool>(II->getName())
-              // Report builtin templates as being builtins.
-              .Case("__make_integer_seq", getLangOpts().CPlusPlus)
-              .Case("__type_pack_element", getLangOpts().CPlusPlus)
-              .Case("__builtin_common_type", getLangOpts().CPlusPlus)
-              // Likewise for some builtin preprocessor macros.
-              // FIXME: This is inconsistent; we usually suggest detecting
-              // builtin macros via #ifdef. Don't add more cases here.
-              .Case("__is_target_arch", true)
-              .Case("__is_target_vendor", true)
-              .Case("__is_target_os", true)
-              .Case("__is_target_environment", true)
-              .Case("__is_target_variant_os", true)
-              .Case("__is_target_variant_environment", true)
-              .Default(false);
-        }
-      });
+        });
   } else if (II == Ident__has_constexpr_builtin) {
     EvaluateFeatureLikeBuiltinMacro(
         OS, Tok, II, *this, false,
@@ -1859,37 +1889,40 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
                  this->getBuiltinInfo().isConstantEvaluated(BuiltinOp);
         });
   } else if (II == Ident__is_identifier) {
-    EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false,
-      [](Token &Tok, bool &HasLexedNextToken) -> int {
-        return Tok.is(tok::identifier);
-      });
+    EvaluateFeatureLikeBuiltinMacro(
+        OS, Tok, II, *this, false,
+        [](Token &Tok, bool &HasLexedNextToken) -> int {
+          return Tok.is(tok::identifier);
+        });
   } else if (II == Ident__has_attribute) {
-    EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, true,
-      [this](Token &Tok, bool &HasLexedNextToken) -> int {
-        IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
-                                           diag::err_feature_check_malformed);
-        return II ? hasAttribute(AttributeCommonInfo::Syntax::AS_GNU, nullptr,
-                                 II, getTargetInfo(), getLangOpts())
-                  : 0;
-      });
+    EvaluateFeatureLikeBuiltinMacro(
+        OS, Tok, II, *this, true,
+        [this](Token &Tok, bool &HasLexedNextToken) -> int {
+          IdentifierInfo *II = ExpectFeatureIdentifierInfo(
+              Tok, *this, diag::err_feature_check_malformed);
+          return II ? hasAttribute(AttributeCommonInfo::Syntax::AS_GNU, nullptr,
+                                   II, getTargetInfo(), getLangOpts())
+                    : 0;
+        });
   } else if (II == Ident__has_declspec) {
-    EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, true,
-      [this](Token &Tok, bool &HasLexedNextToken) -> int {
-        IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
-                                           diag::err_feature_check_malformed);
-        if (II) {
-          const LangOptions &LangOpts = getLangOpts();
-          return LangOpts.DeclSpecKeyword &&
-                 hasAttribute(AttributeCommonInfo::Syntax::AS_Declspec, nullptr,
-                              II, getTargetInfo(), LangOpts);
-        }
+    EvaluateFeatureLikeBuiltinMacro(
+        OS, Tok, II, *this, true,
+        [this](Token &Tok, bool &HasLexedNextToken) -> int {
+          IdentifierInfo *II = ExpectFeatureIdentifierInfo(
+              Tok, *this, diag::err_feature_check_malformed);
+          if (II) {
+            const LangOptions &LangOpts = getLangOpts();
+            return LangOpts.DeclSpecKeyword &&
+                   hasAttribute(AttributeCommonInfo::Syntax::AS_Declspec,
+                                nullptr, II, getTargetInfo(), LangOpts);
+          }
 
-        return false;
-      });
-  } else if (II == Ident__has_cpp_attribute ||
-             II == Ident__has_c_attribute) {
+          return false;
+        });
+  } else if (II == Ident__has_cpp_attribute || II == Ident__has_c_attribute) {
     bool IsCXX = II == Ident__has_cpp_attribute;
-    EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, true,
+    EvaluateFeatureLikeBuiltinMacro(
+        OS, Tok, II, *this, true,
         [&](Token &Tok, bool &HasLexedNextToken) -> int {
           IdentifierInfo *ScopeII = nullptr;
           IdentifierInfo *II = ExpectFeatureIdentifierInfo(
@@ -1917,8 +1950,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
                                    getLangOpts())
                     : 0;
         });
-  } else if (II == Ident__has_include ||
-             II == Ident__has_include_next) {
+  } else if (II == Ident__has_include || II == Ident__has_include_next) {
     // The argument to these two builtins should be a parenthesized
     // file name string literal using angle brackets (<>) or
     // double-quotes ("").
@@ -1945,44 +1977,45 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
     OS << static_cast<int>(Value);
   } else if (II == Ident__has_warning) {
     // The argument should be a parenthesized string literal.
-    EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false,
-      [this](Token &Tok, bool &HasLexedNextToken) -> int {
-        std::string WarningName;
-        SourceLocation StrStartLoc = Tok.getLocation();
-
-        HasLexedNextToken = Tok.is(tok::string_literal);
-        if (!FinishLexStringLiteral(Tok, WarningName, "'__has_warning'",
-                                    /*AllowMacroExpansion=*/false))
-          return false;
+    EvaluateFeatureLikeBuiltinMacro(
+        OS, Tok, II, *this, false,
+        [this](Token &Tok, bool &HasLexedNextToken) -> int {
+          std::string WarningName;
+          SourceLocation StrStartLoc = Tok.getLocation();
 
-        // FIXME: Should we accept "-R..." flags here, or should that be
-        // handled by a separate __has_remark?
-        if (WarningName.size() < 3 || WarningName[0] != '-' ||
-            WarningName[1] != 'W') {
-          Diag(StrStartLoc, diag::warn_has_warning_invalid_option);
-          return false;
-        }
+          HasLexedNextToken = Tok.is(tok::string_literal);
+          if (!FinishLexStringLiteral(Tok, WarningName, "'__has_warning'",
+                                      /*AllowMacroExpansion=*/false))
+            return false;
+
+          // FIXME: Should we accept "-R..." flags here, or should that be
+          // handled by a separate __has_remark?
+          if (WarningName.size() < 3 || WarningName[0] != '-' ||
+              WarningName[1] != 'W') {
+            Diag(StrStartLoc, diag::warn_has_warning_invalid_option);
+            return false;
+          }
 
-        // Finally, check if the warning flags maps to a diagnostic group.
-        // We construct a SmallVector here to talk to getDiagnosticIDs().
-        // Although we don't use the result, this isn't a hot path, and not
-        // worth special casing.
-        SmallVector<diag::kind, 10> Diags;
-        return !getDiagnostics().getDiagnosticIDs()->
-                getDiagnosticsInGroup(diag::Flavor::WarningOrError,
-                                      WarningName.substr(2), Diags);
-      });
+          // Finally, check if the warning flags maps to a diagnostic group.
+          // We construct a SmallVector here to talk to getDiagnosticIDs().
+          // Although we don't use the result, this isn't a hot path, and not
+          // worth special casing.
+          SmallVector<diag::kind, 10> Diags;
+          return !getDiagnostics().getDiagnosticIDs()->getDiagnosticsInGroup(
+              diag::Flavor::WarningOrError, WarningName.substr(2), Diags);
+        });
   } else if (II == Ident__building_module) {
     // The argument to this builtin should be an identifier. The
     // builtin evaluates to 1 when that identifier names the module we are
     // currently building.
-    EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false,
-      [this](Token &Tok, bool &HasLexedNextToken) -> int {
-        IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
-                                       diag::err_expected_id_building_module);
-        return getLangOpts().isCompilingModule() && II &&
-               (II->getName() == getLangOpts().CurrentModule);
-      });
+    EvaluateFeatureLikeBuiltinMacro(
+        OS, Tok, II, *this, false,
+        [this](Token &Tok, bool &HasLexedNextToken) -> int {
+          IdentifierInfo *II = ExpectFeatureIdentifierInfo(
+              Tok, *this, diag::err_expected_id_building_module);
+          return getLangOpts().isCompilingModule() && II &&
+                 (II->getName() == getLangOpts().CurrentModule);
+        });
   } else if (II == Ident__MODULE__) {
     // The current module as an identifier.
     OS << getLangOpts().CurrentModule;
@@ -1998,7 +2031,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
     if (Tok.isNot(tok::l_paren)) {
       // No '(', use end of last token.
       Diag(getLocForEndOfToken(Loc), diag::err_pp_expected_after)
-        << II << tok::l_paren;
+          << II << tok::l_paren;
       // If the next token isn't valid as our argument, we can't recover.
       if (!Tok.isAnnotation() && Tok.getIdentifierInfo())
         Tok.setKind(tok::identifier);
@@ -2020,7 +2053,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
       Tok.setKind(tok::identifier);
     } else {
       Diag(Tok.getLocation(), diag::err_pp_identifier_arg_not_identifier)
-        << Tok.getKind();
+          << Tok.getKind();
       // Don't walk past anything that's not a real token.
       if (Tok.isOneOf(tok::eof, tok::eod) || Tok.isAnnotation())
         return;
@@ -2031,7 +2064,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
     LexNonComment(RParen);
     if (RParen.isNot(tok::r_paren)) {
       Diag(getLocForEndOfToken(Tok.getLocation()), diag::err_pp_expected_after)
-        << Tok.getKind() << tok::r_paren;
+          << Tok.getKind() << tok::r_paren;
       Diag(LParenLoc, diag::note_matching) << tok::l_paren;
     }
     return;
diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp
index e339ca84222784..c4dec81e109f28 100644
--- a/clang/lib/Lex/Pragma.cpp
+++ b/clang/lib/Lex/Pragma.cpp
@@ -1294,7 +1294,7 @@ struct PragmaDiagnosticHandler : public PragmaHandler {
 
     if (II->isStr("pop")) {
       if (!PP.getDiagnostics().popMappings(DiagLoc))
-        PP.Diag(Tok, diag::warn_pragma_diagnostic_cannot_pop);
+        PP.Diag(Tok, diag::warn_pragma_cannot_pop) << /*diagnostic*/ 0;
       else if (Callbacks)
         Callbacks->PragmaDiagnosticPop(DiagLoc, Namespace);
 
@@ -1416,7 +1416,7 @@ struct PragmaWarningHandler : public PragmaHandler {
       // #pragma warning( pop )
       PP.Lex(Tok);
       if (!PP.getDiagnostics().popMappings(DiagLoc))
-        PP.Diag(Tok, diag::warn_pragma_diagnostic_cannot_pop);
+        PP.Diag(Tok, diag::warn_pragma_cannot_pop) << /*diagnostic*/ 0;
       else if (Callbacks)
         Callbacks->PragmaWarningPop(DiagLoc);
     } else {
@@ -2122,6 +2122,26 @@ struct PragmaFinalHandler : public PragmaHandler {
   }
 };
 
+struct PragmaScopeHandler : public PragmaHandler {
+  PragmaScopeHandler() : PragmaHandler("scope") {}
+
+  void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+                    Token &Tok) override {
+    // Lex the 'push' or 'pop'.
+    PP.LexUnexpandedToken(Tok);
+    const IdentifierInfo *PushPop = Tok.getIdentifierInfo();
+    if (PushPop && PushPop->isStr("push"))
+      PP.pushMacroScope();
+    else if (PushPop && PushPop->isStr("pop"))
+      PP.popMacroScope(Tok.getLocation());
+    else
+      PP.Diag(Tok, diag::err_expected) << "'push' or 'pop'";
+    PP.Lex(Tok);
+    if (Tok.isNot(tok::eod))
+      PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
+  }
+};
+
 } // namespace
 
 /// RegisterBuiltinPragmas - Install the standard preprocessor pragmas:
@@ -2153,6 +2173,7 @@ void Preprocessor::RegisterBuiltinPragmas() {
   AddPragmaHandler("clang", new PragmaDeprecatedHandler());
   AddPragmaHandler("clang", new PragmaRestrictExpansionHandler());
   AddPragmaHandler("clang", new PragmaFinalHandler());
+  AddPragmaHandler("clang", new PragmaScopeHandler());
 
   // #pragma clang module ...
   auto *ModuleHandler = new PragmaNamespace("module");
diff --git a/clang/test/Lexer/Inputs/SomeHeaderThatDefinesAwfulThings.h b/clang/test/Lexer/Inputs/SomeHeaderThatDefinesAwfulThings.h
new file mode 100644
index 00000000000000..ac100af24a6868
--- /dev/null
+++ b/clang/test/Lexer/Inputs/SomeHeaderThatDefinesAwfulThings.h
@@ -0,0 +1 @@
+#define max(l, r) l > r ? l : r
diff --git a/clang/test/Lexer/pragma-scope.c b/clang/test/Lexer/pragma-scope.c
new file mode 100644
index 00000000000000..5304f206178e90
--- /dev/null
+++ b/clang/test/Lexer/pragma-scope.c
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 %s -fsyntax-only -isystem %S/Inputs -verify
+
+#define Foo 1
+
+#pragma clang scope push
+#undef Foo
+#pragma clang scope pop
+
+#ifndef Foo
+#error "Foo is still defined!"
+#endif
+
+#define Bar 1 // expected-note{{previous definition is here}}
+#pragma clang scope push
+#define Bar 2 // expected-warning{{'Bar' macro redefined}}
+#pragma clang scope pop
+
+#if Bar != 1
+#error "Bar is set back to 1"
+#endif
+
+#pragma clang scope push
+#include <SomeHeaderThatDefinesAwfulThings.h>
+#pragma clang scope pop
+
+#ifdef max
+#error "Nobody should ever define max as a macro!"
+#endif
+
+#pragma clang scope pop // expected-warning{{pragma scope pop could not pop, no matching push}}
+
+#pragma clang scope enter // expected-error{{expected 'push' or 'pop'}}
+
+#pragma clang scope push pop // expected-warning{{extra tokens at end of #pragma directive}}
+
+#pragma clang scope () // expected-warning{{expected 'push' or 'pop'}} expected-warning{{extra tokens at end of #pragma directive}}



More information about the cfe-commits mailing list