[PATCH] Lex and ignore Microsoft's #pragma warning(...)
Aaron Ballman
aaron at aaronballman.com
Wed Sep 11 16:12:12 PDT 2013
> Index: include/clang/Basic/DiagnosticLexKinds.td
> ===================================================================
> --- include/clang/Basic/DiagnosticLexKinds.td
> +++ include/clang/Basic/DiagnosticLexKinds.td
> @@ -408,6 +408,21 @@
> ExtWarn<"pragma include_alias expected include filename">,
> InGroup<UnknownPragmas>;
>
> +// - #pragma warning(...)
> +def warn_pragma_warning_expected :
> + ExtWarn<"#pragma warning expected '%0'">,
> + InGroup<UnknownPragmas>;
> +def warn_pragma_warning_spec_invalid :
> + ExtWarn<"#pragma warning expected 'push', 'pop', 'default', 'disable',"
> + " 'error', 'once', 'supress', 1, 2, 3, or 4">,
Typo with suppress
> + InGroup<UnknownPragmas>;
> +def warn_pragma_warning_push_level :
> + ExtWarn<"#pragma warning(push, level) requires a level between 1 and 4">,
> + InGroup<UnknownPragmas>;
> +def warn_pragma_warning_expected_number :
> + ExtWarn<"#pragma warning expected a warning number">,
> + InGroup<UnknownPragmas>;
> +
> def err__Pragma_malformed : Error<
> "_Pragma takes a parenthesized string literal">;
> def err_pragma_message_malformed : Error<
> Index: include/clang/Lex/PPCallbacks.h
> ===================================================================
> --- include/clang/Lex/PPCallbacks.h
> +++ include/clang/Lex/PPCallbacks.h
> @@ -217,6 +217,19 @@
> diag::Mapping mapping, StringRef Str) {
> }
>
> + /// \brief Callback invoked when a \#pragma warning directive is read.
> + virtual void PragmaWarning(SourceLocation Loc, StringRef WarningSpec,
> + ArrayRef<int> Ids) {
> + }
> +
> + /// \brief Callback invoked when a \#pragma warning(push) directive is read.
> + virtual void PragmaWarningPush(SourceLocation Loc, int Level) {
> + }
> +
> + /// \brief Callback invoked when a \#pragma warning(pop) directive is read.
> + virtual void PragmaWarningPop(SourceLocation Loc) {
> + }
> +
> /// \brief Called by Preprocessor::HandleMacroExpandedIdentifier when a
> /// macro invocation is found.
> virtual void MacroExpands(const Token &MacroNameTok, const MacroDirective *MD,
> @@ -400,6 +413,22 @@
> Second->PragmaDiagnostic(Loc, Namespace, mapping, Str);
> }
>
> + virtual void PragmaWarning(SourceLocation Loc, StringRef WarningSpec,
> + ArrayRef<int> Ids) {
> + First->PragmaWarning(Loc, WarningSpec, Ids);
> + Second->PragmaWarning(Loc, WarningSpec, Ids);
> + }
> +
> + virtual void PragmaWarningPush(SourceLocation Loc, int Level) {
> + First->PragmaWarningPush(Loc, Level);
> + Second->PragmaWarningPush(Loc, Level);
> + }
> +
> + virtual void PragmaWarningPop(SourceLocation Loc) {
> + First->PragmaWarningPop(Loc);
> + Second->PragmaWarningPop(Loc);
> + }
> +
> virtual void MacroExpands(const Token &MacroNameTok, const MacroDirective *MD,
> SourceRange Range, const MacroArgs *Args) {
> First->MacroExpands(MacroNameTok, MD, Range, Args);
> Index: lib/Frontend/PrintPreprocessedOutput.cpp
> ===================================================================
> --- lib/Frontend/PrintPreprocessedOutput.cpp
> +++ lib/Frontend/PrintPreprocessedOutput.cpp
> @@ -152,6 +152,10 @@
> StringRef Namespace);
> virtual void PragmaDiagnostic(SourceLocation Loc, StringRef Namespace,
> diag::Mapping Map, StringRef Str);
> + virtual void PragmaWarning(SourceLocation Loc, StringRef WarningSpec,
> + ArrayRef<int> Ids);
> + virtual void PragmaWarningPush(SourceLocation Loc, int Level);
> + virtual void PragmaWarningPop(SourceLocation Loc);
>
> bool HandleFirstTokOnLine(Token &Tok);
>
> @@ -507,6 +511,36 @@
> setEmittedDirectiveOnThisLine();
> }
>
> +void PrintPPOutputPPCallbacks::PragmaWarning(SourceLocation Loc,
> + StringRef WarningSpec,
> + ArrayRef<int> Ids) {
> + startNewLineIfNeeded();
> + MoveToLine(Loc);
> + OS << "#pragma warning(" << WarningSpec << ':';
> + for (int I = 0, E = Ids.size(); I != E; ++I)
> + OS << ' ' << Ids[I];
Use an iterator instead of indexes?
> + OS << ')';
> + setEmittedDirectiveOnThisLine();
This doesn't seem to support the syntax where there are multiple
specifiers. Eg)
#pragma warning(disable:1; once:2)
> +}
> +
> +void PrintPPOutputPPCallbacks::PragmaWarningPush(SourceLocation Loc,
> + int Level) {
> + startNewLineIfNeeded();
> + MoveToLine(Loc);
> + OS << "#pragma warning(push";
> + if (Level)
> + OS << ", " << Level;
> + OS << ')';
> + setEmittedDirectiveOnThisLine();
> +}
> +
> +void PrintPPOutputPPCallbacks::PragmaWarningPop(SourceLocation Loc) {
> + startNewLineIfNeeded();
> + MoveToLine(Loc);
> + OS << "#pragma warning(pop)";
> + setEmittedDirectiveOnThisLine();
> +}
> +
> /// HandleFirstTokOnLine - When emitting a preprocessed file in -E mode, this
> /// is called for the first token on each new line. If this really is the start
> /// of a new logical line, handle it and return true, otherwise return false.
> Index: lib/Lex/Pragma.cpp
> ===================================================================
> --- lib/Lex/Pragma.cpp
> +++ lib/Lex/Pragma.cpp
> @@ -20,11 +20,14 @@
> #include "clang/Lex/LiteralSupport.h"
> #include "clang/Lex/MacroInfo.h"
> #include "clang/Lex/Preprocessor.h"
> +#include "llvm/ADT/STLExtras.h"
> #include "llvm/Support/CrashRecoveryContext.h"
> #include "llvm/Support/ErrorHandling.h"
> #include <algorithm>
> using namespace clang;
>
> +#include "llvm/Support/raw_ostream.h"
> +
> // Out-of-line destructor to provide a home for the class.
> PragmaHandler::~PragmaHandler() {
> }
> @@ -1003,12 +1006,136 @@
> }
> };
>
> +// Returns 0 on failure.
> +static unsigned LexSimpleUint(Preprocessor &PP, Token &Tok) {
> + assert(Tok.is(tok::numeric_constant));
> + SmallString<64> IntegerBuffer;
Seems a bit huge for the numbers we'd be lexing.
> + bool NumberInvalid = false;
> + StringRef Spelling = PP.getSpelling(Tok, IntegerBuffer, &NumberInvalid);
> + if (NumberInvalid)
> + return 0;
> + NumericLiteralParser Literal(Spelling, Tok.getLocation(), PP);
> + if (Literal.hadError)
> + return 0;
> + llvm::APInt APVal(32, 0);
> + if (Literal.GetIntegerValue(APVal))
> + return 0;
> + PP.Lex(Tok);
> + return unsigned(APVal.getLimitedValue(UINT_MAX));
In the error case, do we want to diagnose what's wrong?
> +}
> +
> +/// "\#pragma warning(...)". MSVC's diagnostics do not map cleanly to clang's
> +/// diagnostics, so we don't really implement this pragma. We parse it and
> +/// ignore it to avoid -Wunknown-pragma warnings.
> +struct PragmaWarningHandler : public PragmaHandler {
> + PragmaWarningHandler() : PragmaHandler("warning") {}
> +
> + virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
> + Token &Tok) {
> + // Parse things like:
> + // warning(push, 1)
> + // warning(pop)
> + // warning(disable : 1 2 3 ; error 4 5 6 ; suppress 7 8 9)
> + SourceLocation DiagLoc = Tok.getLocation();
> + PP.Lex(Tok);
> + if (Tok.isNot(tok::l_paren)) {
> + PP.Diag(Tok, diag::warn_pragma_warning_expected) << "(";
> + return;
> + }
> + PPCallbacks *Callbacks = PP.getPPCallbacks();
> +
> + PP.Lex(Tok);
> + IdentifierInfo *II = Tok.getIdentifierInfo();
> + if (!II) {
> + PP.Diag(Tok, diag::warn_pragma_warning_spec_invalid);
> + return;
> + }
> +
> + if (II->isStr("push")) {
> + // #pragma warning( push[ ,n ] )
> + unsigned Level = 0;
> + PP.Lex(Tok);
> + if (Tok.is(tok::comma)) {
> + PP.Lex(Tok);
> + if (Tok.is(tok::numeric_constant))
> + Level = LexSimpleUint(PP, Tok);
> + if (Level < 1 || Level > 4) {
> + PP.Diag(Tok, diag::warn_pragma_warning_push_level);
> + return;
> + }
> + }
> + if (Callbacks)
> + Callbacks->PragmaWarningPush(DiagLoc, Level);
> + } else if (II->isStr("pop")) {
> + // #pragma warning( pop )
> + PP.Lex(Tok);
> + if (Callbacks)
> + Callbacks->PragmaWarningPop(DiagLoc);
> + } else {
> + // #pragma warning( warning-specifier : warning-number-list
> + // [; warning-specifier : warning-number-list...] )
> + while (true) {
> + II = Tok.getIdentifierInfo();
> + if (!II) {
> + PP.Diag(Tok, diag::warn_pragma_warning_spec_invalid);
> + return;
> + }
> +
> + // Figure out which warning specifier this is.
> + const char *Specifiers[] = { "1", "2", "3",
> + "4", "default", "disable",
> + "error", "once", "suppress" };
> + StringRef Specifier;
> + for (int I = 0, E = llvm::array_lengthof(Specifiers); I != E; ++I) {
> + if (II->getName().equals(Specifiers[I])) {
> + Specifier = Specifiers[I];
> + break;
> + }
> + }
> + if (Specifier.empty()) {
> + PP.Diag(Tok, diag::warn_pragma_warning_spec_invalid);
> + return;
> + }
> + PP.Lex(Tok);
> + if (Tok.isNot(tok::colon)) {
> + PP.Diag(Tok, diag::warn_pragma_warning_expected) << ":";
> + return;
> + }
> +
> + // Collect the warning ids.
> + SmallVector<int, 1> Ids;
> + PP.Lex(Tok);
> + while (Tok.is(tok::numeric_constant)) {
> + unsigned Id = LexSimpleUint(PP, Tok);
> + if (Id == 0 || Id >= INT_MAX) {
> + PP.Diag(Tok, diag::warn_pragma_warning_expected_number);
> + return;
> + }
> + Ids.push_back(Id);
> + }
> + if (Callbacks)
> + Callbacks->PragmaWarning(DiagLoc, Specifier, Ids);
> +
> + // Parse the next specifier if there is a semicolon.
> + if (Tok.isNot(tok::semi))
> + break;
> + PP.Lex(Tok);
> + }
> + }
> +
> + if (Tok.isNot(tok::r_paren)) {
> + PP.Diag(Tok, diag::warn_pragma_warning_expected) << ")";
> + return;
> + }
> + }
In all of the early returns, I think you'll want to skip until you get
to the end of the pragma or else you'll get a cascade of errors.
> +};
> +
> /// PragmaIncludeAliasHandler - "\#pragma include_alias("...")".
> struct PragmaIncludeAliasHandler : public PragmaHandler {
> PragmaIncludeAliasHandler() : PragmaHandler("include_alias") {}
> virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
> Token &IncludeAliasTok) {
> - PP.HandlePragmaIncludeAlias(IncludeAliasTok);
> + PP.HandlePragmaIncludeAlias(IncludeAliasTok);
> }
> };
>
> @@ -1266,6 +1393,7 @@
>
> // MS extensions.
> if (LangOpts.MicrosoftExt) {
> + AddPragmaHandler(new PragmaWarningHandler());
> AddPragmaHandler(new PragmaIncludeAliasHandler());
> AddPragmaHandler(new PragmaRegionHandler("region"));
> AddPragmaHandler(new PragmaRegionHandler("endregion"));
> Index: test/Lexer/pragma-operators.cpp
> ===================================================================
> --- test/Lexer/pragma-operators.cpp
> +++ test/Lexer/pragma-operators.cpp
> @@ -35,3 +35,15 @@
> // CHECK: #pragma message("\042Hello\042, world!")
> // CHECK: 0;
> int n = pragma_L pragma_u8 pragma_u pragma_U pragma_R pragma_UR pragma_hello 0;
> +
> +#pragma warning(disable : 1 2L 3U ; error : 4 5 6 ; suppress : 7 8 9)
> +// CHECK: #pragma warning(disable: 1 2 3)
> +// CHECK: #pragma warning(error: 4 5 6)
> +// CHECK: #pragma warning(suppress: 7 8 9)
> +
> +#pragma warning(push)
> +#pragma warning(push, 1L)
> +#pragma warning(push, 4U)
> +// CHECK: #pragma warning(push)
> +// CHECK: #pragma warning(push, 1)
> +// CHECK: #pragma warning(push, 4)
> Index: test/Preprocessor/pragma_microsoft.c
> ===================================================================
> --- test/Preprocessor/pragma_microsoft.c
> +++ test/Preprocessor/pragma_microsoft.c
> @@ -87,3 +87,25 @@
> // Make sure that empty includes don't work
> #pragma include_alias("", "foo.h") // expected-error {{empty filename}}
> #pragma include_alias(<foo.h>, <>) // expected-error {{empty filename}}
> +
> +// Test that we ignore pragma warning.
> +#pragma warning(push)
> +#pragma warning(push, 1)
> +#pragma warning(disable : 4705)
> +#pragma warning(disable : 123 456 789 ; error : 321)
> +#pragma warning(once : 321)
> +#pragma warning(suppress : 321)
> +#pragma warning(default : 321)
> +#pragma warning(pop)
> +
> +#pragma warning // expected-warning {{expected '('}}
> +#pragma warning( // expected-warning {{expected 'push', 'pop', 'default', 'disable', 'error', 'once', 'supress', 1, 2, 3, or 4}}
> +#pragma warning() // expected-warning {{expected 'push', 'pop', 'default', 'disable', 'error', 'once', 'supress', 1, 2, 3, or 4}}
> +#pragma warning(push 4) // expected-warning {{expected ')'}}
> +#pragma warning(push // expected-warning {{expected ')'}}
> +#pragma warning(push, 5) // expected-warning {{requires a level between 1 and 4}}
> +#pragma warning(pop, 1) // expected-warning {{expected ')'}}
> +#pragma warning(disable 4705) // expected-warning {{expected ':'}}
> +#pragma warning(disable : 0) // expected-warning {{expected a warning number}}
> +#pragma warning(default 321) // expected-warning {{expected ':'}}
> +#pragma warning(asdf : 321) // expected-warning {{expected 'push', 'pop'}}
>
On Wed, Sep 11, 2013 at 7:06 PM, Richard Smith <richard at metafoo.co.uk> wrote:
>
>
> ================
> Comment at: include/clang/Basic/DiagnosticLexKinds.td:417
> @@ +416,3 @@
> + ExtWarn<"#pragma warning expected 'push', 'pop', 'default', 'disable',"
> + " 'error', 'once', 'supress', 1, 2, 3, or 4">,
> + InGroup<UnknownPragmas>;
> ----------------
> Typo 'supress'... I hope?
>
> ================
> Comment at: lib/Lex/Pragma.cpp:1017-1019
> @@ +1016,5 @@
> + return 0;
> + NumericLiteralParser Literal(Spelling, Tok.getLocation(), PP);
> + if (Literal.hadError)
> + return 0;
> + llvm::APInt APVal(32, 0);
> ----------------
> Does MSVC support octal/hex/binary literals?
>
> I think you should also check for !Literal.hasUDSuffix() and Literal.isIntegerLiteral() here.
>
> ================
> Comment at: lib/Lex/Pragma.cpp:1045
> @@ +1044,3 @@
> + }
> + PPCallbacks *Callbacks = PP.getPPCallbacks();
> +
> ----------------
> Move this earlier or later? It's a bit strange for this to be between lexing the '(' and the next token.
>
> ================
> Comment at: lib/Lex/Pragma.cpp:1085-1094
> @@ +1084,12 @@
> + // Figure out which warning specifier this is.
> + const char *Specifiers[] = { "1", "2", "3",
> + "4", "default", "disable",
> + "error", "once", "suppress" };
> + StringRef Specifier;
> + for (int I = 0, E = llvm::array_lengthof(Specifiers); I != E; ++I) {
> + if (II->getName().equals(Specifiers[I])) {
> + Specifier = Specifiers[I];
> + break;
> + }
> + }
> + if (Specifier.empty()) {
> ----------------
> Maybe use a StringSwitch for this?
>
> ================
> Comment at: lib/Lex/Pragma.cpp:1106
> @@ +1105,3 @@
> + // Collect the warning ids.
> + SmallVector<int, 1> Ids;
> + PP.Lex(Tok);
> ----------------
> Only 1 inline ID? Cheapskate! =) Maybe 10? 16?
>
> ================
> Comment at: lib/Lex/Pragma.cpp:1126-1129
> @@ +1125,6 @@
> +
> + if (Tok.isNot(tok::r_paren)) {
> + PP.Diag(Tok, diag::warn_pragma_warning_expected) << ")";
> + return;
> + }
> + }
> ----------------
> Do you need to also check for extra junk after the ')' here?
>
>
> http://llvm-reviews.chandlerc.com/D1652
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
More information about the cfe-commits
mailing list