Lex and ignore Microsoft's #pragma warning(...)
Summary:
This fixes PR17145 and avoids unknown pragma warnings.
This change does not attempt to map MSVC warning numbers to clang
warning flags. Perhaps in the future we will implement a mapping for
some common subset of Microsoft warnings, but for now we don't.
Reviewers: rsmith
CC: cfe-commits
Differential Revision: http://llvm-reviews.chandlerc.com/D1652
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@190726 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td
index f1c8a09..3abbb85 100644
--- a/include/clang/Basic/DiagnosticLexKinds.td
+++ b/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', 'suppress', 1, 2, 3, or 4">,
+ 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<
diff --git a/include/clang/Lex/PPCallbacks.h b/include/clang/Lex/PPCallbacks.h
index cb635bc..73cfb0a 100644
--- a/include/clang/Lex/PPCallbacks.h
+++ b/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);
diff --git a/lib/Frontend/PrintPreprocessedOutput.cpp b/lib/Frontend/PrintPreprocessedOutput.cpp
index e0ec08f..3e45fc7 100644
--- a/lib/Frontend/PrintPreprocessedOutput.cpp
+++ b/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 (ArrayRef<int>::iterator I = Ids.begin(), E = Ids.end(); I != E; ++I)
+ OS << ' ' << *I;
+ OS << ')';
+ setEmittedDirectiveOnThisLine();
+}
+
+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.
diff --git a/lib/Lex/Pragma.cpp b/lib/Lex/Pragma.cpp
index 324bbd2..8d3dedc 100644
--- a/lib/Lex/Pragma.cpp
+++ b/lib/Lex/Pragma.cpp
@@ -20,11 +20,15 @@
#include "clang/Lex/LiteralSupport.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringSwitch.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 +1007,137 @@
}
};
+// Returns 0 on failure.
+static unsigned LexSimpleUint(Preprocessor &PP, Token &Tok) {
+ assert(Tok.is(tok::numeric_constant));
+ SmallString<8> IntegerBuffer;
+ bool NumberInvalid = false;
+ StringRef Spelling = PP.getSpelling(Tok, IntegerBuffer, &NumberInvalid);
+ if (NumberInvalid)
+ return 0;
+ NumericLiteralParser Literal(Spelling, Tok.getLocation(), PP);
+ if (Literal.hadError || !Literal.isIntegerLiteral() || Literal.hasUDSuffix())
+ return 0;
+ llvm::APInt APVal(32, 0);
+ if (Literal.GetIntegerValue(APVal))
+ return 0;
+ PP.Lex(Tok);
+ return unsigned(APVal.getLimitedValue(UINT_MAX));
+}
+
+/// "\#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();
+ PPCallbacks *Callbacks = PP.getPPCallbacks();
+
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(Tok, diag::warn_pragma_warning_expected) << "(";
+ return;
+ }
+
+ 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.
+ StringRef Specifier = II->getName();
+ bool SpecifierValid =
+ llvm::StringSwitch<bool>(Specifier)
+ .Cases("1", "2", "3", "4", true)
+ .Cases("default", "disable", "error", "once", "suppress", true)
+ .Default(false);
+ if (!SpecifierValid) {
+ 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, 4> 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;
+ }
+
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::eod))
+ PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma warning";
+ }
+};
+
/// 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 +1395,7 @@
// MS extensions.
if (LangOpts.MicrosoftExt) {
+ AddPragmaHandler(new PragmaWarningHandler());
AddPragmaHandler(new PragmaIncludeAliasHandler());
AddPragmaHandler(new PragmaRegionHandler("region"));
AddPragmaHandler(new PragmaRegionHandler("endregion"));
diff --git a/test/Lexer/pragma-operators.cpp b/test/Lexer/pragma-operators.cpp
index 6a5a498..7270f1e 100644
--- a/test/Lexer/pragma-operators.cpp
+++ b/test/Lexer/pragma-operators.cpp
@@ -35,3 +35,25 @@
// 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: #line [[@LINE-2]]
+// CHECK: #pragma warning(error: 4 5 6)
+// CHECK: #line [[@LINE-4]]
+// CHECK: #pragma warning(suppress: 7 8 9)
+
+#pragma warning(push)
+#pragma warning(push, 1L)
+#pragma warning(push, 4U)
+#pragma warning(push, 0x1)
+#pragma warning(push, 03)
+#pragma warning(push, 0b10)
+#pragma warning(push, 1i8)
+// CHECK: #pragma warning(push)
+// CHECK: #pragma warning(push, 1)
+// CHECK: #pragma warning(push, 4)
+// CHECK: #pragma warning(push, 1)
+// CHECK: #pragma warning(push, 3)
+// CHECK: #pragma warning(push, 2)
+// CHECK: #pragma warning(push, 1)
diff --git a/test/Preprocessor/pragma_microsoft.c b/test/Preprocessor/pragma_microsoft.c
index 26f0a1d..c7bf3c4 100644
--- a/test/Preprocessor/pragma_microsoft.c
+++ b/test/Preprocessor/pragma_microsoft.c
@@ -87,3 +87,26 @@
// 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', 'suppress', 1, 2, 3, or 4}}
+#pragma warning() // expected-warning {{expected 'push', 'pop', 'default', 'disable', 'error', 'once', 'suppress', 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(push, 1) asdf // expected-warning {{extra tokens at end of #pragma warning directive}}
+#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'}}
diff --git a/test/Preprocessor/pragma_microsoft.cpp b/test/Preprocessor/pragma_microsoft.cpp
new file mode 100644
index 0000000..5bc1ccc
--- /dev/null
+++ b/test/Preprocessor/pragma_microsoft.cpp
@@ -0,0 +1,3 @@
+// RUN: %clang_cc1 %s -fsyntax-only -std=c++11 -verify -fms-extensions
+
+#pragma warning(push, 4_D) // expected-warning {{requires a level between 1 and 4}}