diff options
Diffstat (limited to 'clang')
-rw-r--r-- | clang/include/clang/Basic/DiagnosticLexKinds.td | 15 | ||||
-rw-r--r-- | clang/include/clang/Lex/PPCallbacks.h | 29 | ||||
-rw-r--r-- | clang/lib/Frontend/PrintPreprocessedOutput.cpp | 34 | ||||
-rw-r--r-- | clang/lib/Lex/Pragma.cpp | 132 | ||||
-rw-r--r-- | clang/test/Lexer/pragma-operators.cpp | 22 | ||||
-rw-r--r-- | clang/test/Preprocessor/pragma_microsoft.c | 23 | ||||
-rw-r--r-- | clang/test/Preprocessor/pragma_microsoft.cpp | 3 |
7 files changed, 257 insertions, 1 deletions
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index f1c8a09dee6..3abbb8574fb 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -408,6 +408,21 @@ def warn_pragma_include_alias_expected_filename : 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/clang/include/clang/Lex/PPCallbacks.h b/clang/include/clang/Lex/PPCallbacks.h index cb635bcb2d7..73cfb0aefc7 100644 --- a/clang/include/clang/Lex/PPCallbacks.h +++ b/clang/include/clang/Lex/PPCallbacks.h @@ -217,6 +217,19 @@ public: 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 @@ public: 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/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp index e0ec08fb90a..3e45fc79f40 100644 --- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp @@ -152,6 +152,10 @@ public: 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 @@ PragmaDiagnostic(SourceLocation Loc, StringRef Namespace, 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/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp index 324bbd29a2b..8d3dedc3752 100644 --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/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 @@ public: } }; +// 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 @@ void Preprocessor::RegisterBuiltinPragmas() { // MS extensions. if (LangOpts.MicrosoftExt) { + AddPragmaHandler(new PragmaWarningHandler()); AddPragmaHandler(new PragmaIncludeAliasHandler()); AddPragmaHandler(new PragmaRegionHandler("region")); AddPragmaHandler(new PragmaRegionHandler("endregion")); diff --git a/clang/test/Lexer/pragma-operators.cpp b/clang/test/Lexer/pragma-operators.cpp index 6a5a498a151..7270f1eed20 100644 --- a/clang/test/Lexer/pragma-operators.cpp +++ b/clang/test/Lexer/pragma-operators.cpp @@ -35,3 +35,25 @@ B(foo) // 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/clang/test/Preprocessor/pragma_microsoft.c b/clang/test/Preprocessor/pragma_microsoft.c index 26f0a1df8c1..c7bf3c47be1 100644 --- a/clang/test/Preprocessor/pragma_microsoft.c +++ b/clang/test/Preprocessor/pragma_microsoft.c @@ -87,3 +87,26 @@ void g() {} // 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/clang/test/Preprocessor/pragma_microsoft.cpp b/clang/test/Preprocessor/pragma_microsoft.cpp new file mode 100644 index 00000000000..5bc1ccc4ce4 --- /dev/null +++ b/clang/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}} |