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

llvm-svn: 179687
This commit is contained in:
Andy Gibbs 2013-04-17 16:16:16 +00:00
parent 2d5c341cee
commit 9c2ccd622f
7 changed files with 166 additions and 71 deletions

View File

@ -652,6 +652,31 @@ supports the GCC pragma, Clang and GCC do not support the exact same set
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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -406,13 +406,14 @@ def err__Pragma_malformed : Error<
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">,

View File

@ -165,10 +165,25 @@ public:
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 @@ public:
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,

View File

@ -1445,7 +1445,6 @@ public:
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);

View File

@ -140,7 +140,8 @@ public:
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::PragmaComment(SourceLocation Loc,
}
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];

View File

@ -554,62 +554,6 @@ void Preprocessor::HandlePragmaComment(Token &Tok) {
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 @@ struct PragmaIncludeAliasHandler : public PragmaHandler {
}
};
/// 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 @@ void Preprocessor::RegisterBuiltinPragmas() {
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());

View File

@ -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}}