[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