implement "#pragma GCC diagnostic".  Besides being a nice feature, this
will let us test for multiple different warning modes in the same
file in regression tests.

This implements rdar://2362963, a 10-year old feature request :)


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@69560 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td
index bd10642..32890c7 100644
--- a/include/clang/Basic/DiagnosticLexKinds.td
+++ b/include/clang/Basic/DiagnosticLexKinds.td
@@ -217,6 +217,19 @@
 def warn_stdc_fenv_access_not_supported :
    ExtWarn<"pragma STDC FENV_ACCESS ON is not supported, ignoring pragma">,
    InGroup<UnknownPragmas>;
+def warn_pragma_diagnostic_invalid :
+   ExtWarn<"pragma diagnostic expected 'error', 'warning', 'ignored', or"
+            " 'fatal'">,
+   InGroup<UnknownPragmas>;
+def warn_pragma_diagnostic_invalid_option :
+   ExtWarn<"pragma diagnostic expected option name (e.g. \"-Wundef\")">,
+   InGroup<UnknownPragmas>;
+def warn_pragma_diagnostic_invalid_token :
+   ExtWarn<"unexpected token in pragma diagnostic">,
+   InGroup<UnknownPragmas>;
+def warn_pragma_diagnostic_unknown_warning :
+   ExtWarn<"unknown warning group '%0', ignored">,
+   InGroup<UnknownPragmas>;
 
 def err_pragma_comment_unknown_kind : Error<"unknown kind of pragma comment">;
 def err_defined_macro_name : Error<"'defined' cannot be used as a macro name">;
diff --git a/lib/Lex/Pragma.cpp b/lib/Lex/Pragma.cpp
index 0dc093f..bde3fbc 100644
--- a/lib/Lex/Pragma.cpp
+++ b/lib/Lex/Pragma.cpp
@@ -348,7 +348,7 @@
     Lex(Tok); // eat the comma.
 
     // We need at least one string.
-    if (Tok.getKind() != tok::string_literal) {
+    if (Tok.isNot(tok::string_literal)) {
       Diag(Tok.getLocation(), diag::err_pragma_comment_malformed);
       return;
     }
@@ -357,7 +357,7 @@
     // macro expansion.
     // "foo " "bar" "Baz"
     llvm::SmallVector<Token, 4> StrToks;
-    while (Tok.getKind() == tok::string_literal) {
+    while (Tok.is(tok::string_literal)) {
       StrToks.push_back(Tok);
       Lex(Tok);
     }
@@ -502,6 +502,81 @@
   }
 };
   
+/// PragmaDiagnosticHandler - e.g. '#pragma GCC diagnostic ignored "-Wformat"'
+struct PragmaDiagnosticHandler : public PragmaHandler {
+  PragmaDiagnosticHandler(const IdentifierInfo *ID) : PragmaHandler(ID) {}
+  virtual void HandlePragma(Preprocessor &PP, Token &DiagToken) {
+    Token Tok;
+    PP.LexUnexpandedToken(Tok);
+    if (Tok.isNot(tok::identifier)) {
+      PP.Diag(Tok, diag::warn_pragma_diagnostic_invalid);
+      return;
+    }
+    IdentifierInfo *II = Tok.getIdentifierInfo();
+    
+    diag::Mapping Map;
+    if (II->isStr("warning"))
+      Map = diag::MAP_WARNING;
+    else if (II->isStr("error"))
+      Map = diag::MAP_ERROR;
+    else if (II->isStr("ignored"))
+      Map = diag::MAP_IGNORE;
+    else if (II->isStr("fatal"))
+      Map = diag::MAP_FATAL;
+    else {
+      PP.Diag(Tok, diag::warn_pragma_diagnostic_invalid);
+      return;
+    }
+    
+    PP.LexUnexpandedToken(Tok);
+
+    // We need at least one string.
+    if (Tok.isNot(tok::string_literal)) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_diagnostic_invalid_token);
+      return;
+    }
+    
+    // String concatenation allows multiple strings, which can even come from
+    // macro expansion.
+    // "foo " "bar" "Baz"
+    llvm::SmallVector<Token, 4> StrToks;
+    while (Tok.is(tok::string_literal)) {
+      StrToks.push_back(Tok);
+      PP.LexUnexpandedToken(Tok);
+    }
+    
+    if (Tok.isNot(tok::eom)) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_diagnostic_invalid_token);
+      return;
+    }
+    
+    // Concatenate and parse the strings.
+    StringLiteralParser Literal(&StrToks[0], StrToks.size(), PP);
+    assert(!Literal.AnyWide && "Didn't allow wide strings in");
+    if (Literal.hadError)
+      return;
+    if (Literal.Pascal) {
+      PP.Diag(StrToks[0].getLocation(), diag::warn_pragma_diagnostic_invalid);
+      return;
+    }
+    
+    std::string WarningName(Literal.GetString(),
+                            Literal.GetString()+Literal.GetStringLength());
+
+    if (WarningName.size() < 3 || WarningName[0] != '-' ||
+        WarningName[1] != 'W') {
+      PP.Diag(StrToks[0].getLocation(),
+              diag::warn_pragma_diagnostic_invalid_option);
+      return;
+    }
+    
+    if (PP.getDiagnostics().setDiagnosticGroupMapping(WarningName.c_str()+2,
+                                                      Map))
+      PP.Diag(StrToks[0].getLocation(),
+              diag::warn_pragma_diagnostic_unknown_warning) << WarningName;
+  }
+};
+  
 /// PragmaCommentHandler - "#pragma comment ...".
 struct PragmaCommentHandler : public PragmaHandler {
   PragmaCommentHandler(const IdentifierInfo *ID) : PragmaHandler(ID) {}
@@ -596,6 +671,8 @@
                                           getIdentifierInfo("system_header")));
   AddPragmaHandler("GCC", new PragmaDependencyHandler(
                                           getIdentifierInfo("dependency")));
+  AddPragmaHandler("GCC", new PragmaDiagnosticHandler(
+                                              getIdentifierInfo("diagnostic")));
   
   AddPragmaHandler("STDC", new PragmaSTDC_FP_CONTRACTHandler(
                                              getIdentifierInfo("FP_CONTRACT")));