diff options
author | Andy Gibbs <andyg1001@hotmail.co.uk> | 2013-04-17 16:16:16 +0000 |
---|---|---|
committer | Andy Gibbs <andyg1001@hotmail.co.uk> | 2013-04-17 16:16:16 +0000 |
commit | 076eea20b80024fc63bbd71fb019375983680ea6 (patch) | |
tree | 8d42ef66e2730c8cc45f696cc2aa95494c46617d | |
parent | 141f1d24d5aa139ebb91f7ad0252e97a25895fa4 (diff) |
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
-rw-r--r-- | docs/UsersManual.rst | 25 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticLexKinds.td | 3 | ||||
-rw-r--r-- | include/clang/Lex/PPCallbacks.h | 24 | ||||
-rw-r--r-- | include/clang/Lex/Preprocessor.h | 1 | ||||
-rw-r--r-- | lib/Frontend/PrintPreprocessedOutput.cpp | 22 | ||||
-rw-r--r-- | lib/Lex/Pragma.cpp | 146 | ||||
-rw-r--r-- | test/Lexer/pragma-message.c | 16 |
7 files changed, 166 insertions, 71 deletions
diff --git a/docs/UsersManual.rst b/docs/UsersManual.rst index 97784f1b1b..3dc07aba90 100644 --- a/docs/UsersManual.rst +++ b/docs/UsersManual.rst @@ -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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index 422ce866fb..a84ce09f99 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -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">, diff --git a/include/clang/Lex/PPCallbacks.h b/include/clang/Lex/PPCallbacks.h index 661a3a79ec..45deb1756d 100644 --- a/include/clang/Lex/PPCallbacks.h +++ b/include/clang/Lex/PPCallbacks.h @@ -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, diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 7a912ec0f8..47d274220a 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -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); diff --git a/lib/Frontend/PrintPreprocessedOutput.cpp b/lib/Frontend/PrintPreprocessedOutput.cpp index 7e1cae95d7..180c177444 100644 --- a/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/lib/Frontend/PrintPreprocessedOutput.cpp @@ -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]; diff --git a/lib/Lex/Pragma.cpp b/lib/Lex/Pragma.cpp index af58c50f05..bc77c5adcf 100644 --- a/lib/Lex/Pragma.cpp +++ b/lib/Lex/Pragma.cpp @@ -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()); diff --git a/test/Lexer/pragma-message.c b/test/Lexer/pragma-message.c index b67886fa33..d0bbe9ea3a 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}} |