Implemented #pragma GCC warning/error in the same mould as #pragma message.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@179687 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/docs/UsersManual.rst b/docs/UsersManual.rst
index 97784f1..3dc07ab 100644
--- a/docs/UsersManual.rst
+++ b/docs/UsersManual.rst
@@ -652,6 +652,31 @@
 of warnings, so even when using GCC compatible #pragmas there is no
 guarantee that they will have identical behaviour on both compilers.
 
+In addition to controlling warnings and errors generated by the compiler, it is
+possible to generate custom warning and error messages through the following
+pragmas:
+
+.. code-block:: c
+
+  // The following will produce warning messages
+  #pragma message "some diagnostic message"
+  #pragma GCC warning "TODO: replace deprecated feature"
+
+  // The following will produce an error message
+  #pragma GCC error "Not supported"
+
+These pragmas operate similarly to the ``#warning`` and ``#error`` preprocessor
+directives, except that they may also be embedded into preprocessor macros via
+the C99 ``_Pragma`` operator, for example:
+
+.. code-block:: c
+
+  #define STR(X) #X
+  #define DEFER(M,...) M(__VA_ARGS__)
+  #define CUSTOM_ERROR(X) _Pragma(STR(GCC error(X " at line " DEFER(STR,__LINE__))))
+
+  CUSTOM_ERROR("Feature not available");
+
 Controlling Diagnostics in System Headers
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td
index 422ce86..a84ce09 100644
--- a/include/clang/Basic/DiagnosticLexKinds.td
+++ b/include/clang/Basic/DiagnosticLexKinds.td
@@ -406,13 +406,14 @@
 def err_pragma_comment_malformed : Error<
   "pragma comment requires parenthesized identifier and optional string">;
 def err_pragma_message_malformed : Error<
-  "pragma message requires parenthesized string">;
+  "pragma %select{message|warning|error}0 requires parenthesized string">;
 def err_pragma_push_pop_macro_malformed : Error<
    "pragma %0 requires a parenthesized string">;
 def warn_pragma_pop_macro_no_push : Warning<
    "pragma pop_macro could not pop '%0', no matching push_macro">;
 def warn_pragma_message : Warning<"%0">,
    InGroup<PoundPragmaMessage>, DefaultWarnNoWerror;
+def err_pragma_message : Error<"%0">;
 def warn_pragma_ignored : Warning<"unknown pragma ignored">,
    InGroup<UnknownPragmas>, DefaultIgnore;
 def ext_stdc_pragma_ignored : ExtWarn<"unknown pragma in STDC namespace">,
diff --git a/include/clang/Lex/PPCallbacks.h b/include/clang/Lex/PPCallbacks.h
index 661a3a7..45deb17 100644
--- a/include/clang/Lex/PPCallbacks.h
+++ b/include/clang/Lex/PPCallbacks.h
@@ -165,10 +165,25 @@
   virtual void PragmaDebug(SourceLocation Loc, StringRef DebugType) {
   }
 
+  /// \brief Determines the kind of \#pragma invoking a call to PragmaMessage.
+  enum PragmaMessageKind {
+    /// \brief \#pragma message has been invoked.
+    PMK_Message,
+
+    /// \brief \#pragma GCC warning has been invoked.
+    PMK_Warning,
+
+    /// \brief \#pragma GCC error has been invoked.
+    PMK_Error
+  };
+
   /// \brief Callback invoked when a \#pragma message directive is read.
   /// \param Loc The location of the message directive.
+  /// \param Namespace The namespace of the message directive.
+  /// \param Kind The type of the message directive.
   /// \param Str The text of the message directive.
-  virtual void PragmaMessage(SourceLocation Loc, StringRef Str) {
+  virtual void PragmaMessage(SourceLocation Loc, StringRef Namespace,
+                             PragmaMessageKind Kind, StringRef Str) {
   }
 
   /// \brief Callback invoked when a \#pragma gcc dianostic push directive
@@ -336,9 +351,10 @@
     Second->PragmaComment(Loc, Kind, Str);
   }
 
-  virtual void PragmaMessage(SourceLocation Loc, StringRef Str) {
-    First->PragmaMessage(Loc, Str);
-    Second->PragmaMessage(Loc, Str);
+  virtual void PragmaMessage(SourceLocation Loc, StringRef Namespace,
+                             PragmaMessageKind Kind, StringRef Str) {
+    First->PragmaMessage(Loc, Namespace, Kind, Str);
+    Second->PragmaMessage(Loc, Namespace, Kind, Str);
   }
 
   virtual void PragmaDiagnosticPush(SourceLocation Loc,
diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h
index 7a912ec..47d2742 100644
--- a/include/clang/Lex/Preprocessor.h
+++ b/include/clang/Lex/Preprocessor.h
@@ -1445,7 +1445,6 @@
   void HandlePragmaSystemHeader(Token &SysHeaderTok);
   void HandlePragmaDependency(Token &DependencyTok);
   void HandlePragmaComment(Token &CommentTok);
-  void HandlePragmaMessage(Token &MessageTok);
   void HandlePragmaPushMacro(Token &Tok);
   void HandlePragmaPopMacro(Token &Tok);
   void HandlePragmaIncludeAlias(Token &Tok);
diff --git a/lib/Frontend/PrintPreprocessedOutput.cpp b/lib/Frontend/PrintPreprocessedOutput.cpp
index 7e1cae9..180c177 100644
--- a/lib/Frontend/PrintPreprocessedOutput.cpp
+++ b/lib/Frontend/PrintPreprocessedOutput.cpp
@@ -140,7 +140,8 @@
   virtual void PragmaCaptured(SourceLocation Loc, StringRef Str);
   virtual void PragmaComment(SourceLocation Loc, const IdentifierInfo *Kind,
                              const std::string &Str);
-  virtual void PragmaMessage(SourceLocation Loc, StringRef Str);
+  virtual void PragmaMessage(SourceLocation Loc, StringRef Namespace,
+                             PragmaMessageKind Kind, StringRef Str);
   virtual void PragmaDebug(SourceLocation Loc, StringRef DebugType);
   virtual void PragmaDiagnosticPush(SourceLocation Loc,
                                     StringRef Namespace);
@@ -407,12 +408,25 @@
 }
 
 void PrintPPOutputPPCallbacks::PragmaMessage(SourceLocation Loc,
+                                             StringRef Namespace,
+                                             PragmaMessageKind Kind,
                                              StringRef Str) {
   startNewLineIfNeeded();
   MoveToLine(Loc);
-  OS << "#pragma message(";
-
-  OS << '"';
+  OS << "#pragma ";
+  if (!Namespace.empty())
+    OS << Namespace << ' ';
+  switch (Kind) {
+    case PMK_Message:
+      OS << "message(\"";
+      break;
+    case PMK_Warning:
+      OS << "warning(\"";
+      break;
+    case PMK_Error:
+      OS << "error(\"";
+      break;
+  }
 
   for (unsigned i = 0, e = Str.size(); i != e; ++i) {
     unsigned char Char = Str[i];
diff --git a/lib/Lex/Pragma.cpp b/lib/Lex/Pragma.cpp
index af58c50..bc77c5a 100644
--- a/lib/Lex/Pragma.cpp
+++ b/lib/Lex/Pragma.cpp
@@ -554,62 +554,6 @@
     Callbacks->PragmaComment(CommentLoc, II, ArgumentString);
 }
 
-/// HandlePragmaMessage - Handle the microsoft and gcc \#pragma message
-/// extension.  The syntax is:
-/// \code
-///   #pragma message(string)
-/// \endcode
-/// OR, in GCC mode:
-/// \code
-///   #pragma message string
-/// \endcode
-/// string is a string, which is fully macro expanded, and permits string
-/// concatenation, embedded escape characters, etc... See MSDN for more details.
-void Preprocessor::HandlePragmaMessage(Token &Tok) {
-  SourceLocation MessageLoc = Tok.getLocation();
-  Lex(Tok);
-  bool ExpectClosingParen = false;
-  switch (Tok.getKind()) {
-  case tok::l_paren:
-    // We have a MSVC style pragma message.
-    ExpectClosingParen = true;
-    // Read the string.
-    Lex(Tok);
-    break;
-  case tok::string_literal:
-    // We have a GCC style pragma message, and we just read the string.
-    break;
-  default:
-    Diag(MessageLoc, diag::err_pragma_message_malformed);
-    return;
-  }
-
-  std::string MessageString;
-  if (!FinishLexStringLiteral(Tok, MessageString, "pragma message",
-                              /*MacroExpansion=*/true))
-    return;
-
-  if (ExpectClosingParen) {
-    if (Tok.isNot(tok::r_paren)) {
-      Diag(Tok.getLocation(), diag::err_pragma_message_malformed);
-      return;
-    }
-    Lex(Tok);  // eat the r_paren.
-  }
-
-  if (Tok.isNot(tok::eod)) {
-    Diag(Tok.getLocation(), diag::err_pragma_message_malformed);
-    return;
-  }
-
-  // Output the message.
-  Diag(MessageLoc, diag::warn_pragma_message) << MessageString;
-
-  // If the pragma is lexically sound, notify any interested PPCallbacks.
-  if (Callbacks)
-    Callbacks->PragmaMessage(MessageLoc, MessageString);
-}
-
 /// ParsePragmaPushOrPopMacro - Handle parsing of pragma push_macro/pop_macro.  
 /// Return the IdentifierInfo* associated with the macro to push or pop.
 IdentifierInfo *Preprocessor::ParsePragmaPushOrPopMacro(Token &Tok) {
@@ -1134,12 +1078,88 @@
   }
 };
 
-/// PragmaMessageHandler - "\#pragma message("...")".
+/// PragmaMessageHandler - Handle the microsoft and gcc \#pragma message
+/// extension.  The syntax is:
+/// \code
+///   #pragma message(string)
+/// \endcode
+/// OR, in GCC mode:
+/// \code
+///   #pragma message string
+/// \endcode
+/// string is a string, which is fully macro expanded, and permits string
+/// concatenation, embedded escape characters, etc... See MSDN for more details.
+/// Also handles \#pragma GCC warning and \#pragma GCC error which take the same
+/// form as \#pragma message.
 struct PragmaMessageHandler : public PragmaHandler {
-  PragmaMessageHandler() : PragmaHandler("message") {}
+private:
+  const PPCallbacks::PragmaMessageKind Kind;
+  const StringRef Namespace;
+
+  static const char* PragmaKind(PPCallbacks::PragmaMessageKind Kind,
+                                bool PragmaNameOnly = false) {
+    switch (Kind) {
+      case PPCallbacks::PMK_Message:
+        return PragmaNameOnly ? "message" : "pragma message";
+      case PPCallbacks::PMK_Warning:
+        return PragmaNameOnly ? "warning" : "pragma warning";
+      case PPCallbacks::PMK_Error:
+        return PragmaNameOnly ? "error" : "pragma error";
+    }
+    llvm_unreachable("Unknown PragmaMessageKind!");
+  }
+
+public:
+  PragmaMessageHandler(PPCallbacks::PragmaMessageKind Kind,
+                       StringRef Namespace = StringRef())
+    : PragmaHandler(PragmaKind(Kind, true)), Kind(Kind), Namespace(Namespace) {}
+
   virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
-                            Token &CommentTok) {
-    PP.HandlePragmaMessage(CommentTok);
+                            Token &Tok) {
+    SourceLocation MessageLoc = Tok.getLocation();
+    PP.Lex(Tok);
+    bool ExpectClosingParen = false;
+    switch (Tok.getKind()) {
+    case tok::l_paren:
+      // We have a MSVC style pragma message.
+      ExpectClosingParen = true;
+      // Read the string.
+      PP.Lex(Tok);
+      break;
+    case tok::string_literal:
+      // We have a GCC style pragma message, and we just read the string.
+      break;
+    default:
+      PP.Diag(MessageLoc, diag::err_pragma_message_malformed) << Kind;
+      return;
+    }
+
+    std::string MessageString;
+    if (!PP.FinishLexStringLiteral(Tok, MessageString, PragmaKind(Kind),
+                                   /*MacroExpansion=*/true))
+      return;
+
+    if (ExpectClosingParen) {
+      if (Tok.isNot(tok::r_paren)) {
+        PP.Diag(Tok.getLocation(), diag::err_pragma_message_malformed) << Kind;
+        return;
+      }
+      PP.Lex(Tok);  // eat the r_paren.
+    }
+
+    if (Tok.isNot(tok::eod)) {
+      PP.Diag(Tok.getLocation(), diag::err_pragma_message_malformed) << Kind;
+      return;
+    }
+
+    // Output the message.
+    PP.Diag(MessageLoc, (Kind == PPCallbacks::PMK_Error)
+                          ? diag::err_pragma_message
+                          : diag::warn_pragma_message) << MessageString;
+
+    // If the pragma is lexically sound, notify any interested PPCallbacks.
+    if (PPCallbacks *Callbacks = PP.getPPCallbacks())
+      Callbacks->PragmaMessage(MessageLoc, Namespace, Kind, MessageString);
   }
 };
 
@@ -1287,13 +1307,17 @@
   AddPragmaHandler(new PragmaMarkHandler());
   AddPragmaHandler(new PragmaPushMacroHandler());
   AddPragmaHandler(new PragmaPopMacroHandler());
-  AddPragmaHandler(new PragmaMessageHandler());
+  AddPragmaHandler(new PragmaMessageHandler(PPCallbacks::PMK_Message));
 
   // #pragma GCC ...
   AddPragmaHandler("GCC", new PragmaPoisonHandler());
   AddPragmaHandler("GCC", new PragmaSystemHeaderHandler());
   AddPragmaHandler("GCC", new PragmaDependencyHandler());
   AddPragmaHandler("GCC", new PragmaDiagnosticHandler("GCC"));
+  AddPragmaHandler("GCC", new PragmaMessageHandler(PPCallbacks::PMK_Warning,
+                                                   "GCC"));
+  AddPragmaHandler("GCC", new PragmaMessageHandler(PPCallbacks::PMK_Error,
+                                                   "GCC"));
   // #pragma clang ...
   AddPragmaHandler("clang", new PragmaPoisonHandler());
   AddPragmaHandler("clang", new PragmaSystemHeaderHandler());
diff --git a/test/Lexer/pragma-message.c b/test/Lexer/pragma-message.c
index b67886f..d0bbe9e 100644
--- a/test/Lexer/pragma-message.c
+++ b/test/Lexer/pragma-message.c
@@ -14,3 +14,19 @@
 #pragma message ":O gcc accepts this! " STRING(__LINE__) // expected-warning {{:O gcc accepts this! 14}}
 
 #pragma message(invalid) // expected-error {{expected string literal in pragma message}}
+
+// GCC supports a similar pragma, #pragma GCC warning (which generates a warning
+// message) and #pragma GCC error (which generates an error message).
+
+#pragma GCC warning(":O I'm a message! " STRING(__LINE__)) // expected-warning {{:O I'm a message! 21}}
+#pragma GCC warning ":O gcc accepts this! " STRING(__LINE__) // expected-warning {{:O gcc accepts this! 22}}
+
+#pragma GCC error(":O I'm a message! " STRING(__LINE__)) // expected-error {{:O I'm a message! 24}}
+#pragma GCC error ":O gcc accepts this! " STRING(__LINE__) // expected-error {{:O gcc accepts this! 25}}
+
+#define COMPILE_ERROR(x) _Pragma(STRING2(GCC error(x)))
+COMPILE_ERROR("Compile error at line " STRING(__LINE__) "!"); // expected-error {{Compile error at line 28!}}
+
+#pragma message // expected-error {{pragma message requires parenthesized string}}
+#pragma GCC warning("" // expected-error {{pragma warning requires parenthesized string}}
+#pragma GCC error(1) // expected-error {{expected string literal in pragma error}}