diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h index b930165ba144390d0e2581cfc3a4e8d003c2c55e..0b2934947b405a52b95bc781ece5922b5dc82d91 100644 --- a/clang/include/clang/Basic/Diagnostic.h +++ b/clang/include/clang/Basic/Diagnostic.h @@ -57,15 +57,14 @@ namespace clang { /// Enum values that allow the client to map NOTEs, WARNINGs, and EXTENSIONs /// to either MAP_IGNORE (nothing), MAP_WARNING (emit a warning), MAP_ERROR - /// (emit as an error), or MAP_DEFAULT (handle the default way). It allows - /// clients to map errors to MAP_ERROR/MAP_DEFAULT or MAP_FATAL (stop - /// emitting diagnostics after this one). + /// (emit as an error). It allows clients to map errors to + /// MAP_ERROR/MAP_DEFAULT or MAP_FATAL (stop emitting diagnostics after this + /// one). enum Mapping { - MAP_DEFAULT = 0, //< Do not map this diagnostic. - MAP_IGNORE = 1, //< Map this diagnostic to nothing, ignore it. - MAP_WARNING = 2, //< Map this diagnostic to a warning. - MAP_ERROR = 3, //< Map this diagnostic to an error. - MAP_FATAL = 4 //< Map this diagnostic to a fatal error. + MAP_IGNORE = 0, //< Map this diagnostic to nothing, ignore it. + MAP_WARNING = 1, //< Map this diagnostic to a warning. + MAP_ERROR = 2, //< Map this diagnostic to an error. + MAP_FATAL = 3 //< Map this diagnostic to a fatal error. }; } @@ -147,17 +146,16 @@ public: ak_nameddecl // NamedDecl * }; -private: +private: + unsigned char AllExtensionsSilenced; // Used by __extension__ bool IgnoreAllWarnings; // Ignore all warnings: -w bool WarningsAsErrors; // Treat warnings like errors: - bool WarnOnExtensions; // Enables warnings for gcc extensions: -pedantic. - bool ErrorOnExtensions; // Error on extensions: -pedantic-errors. bool SuppressSystemWarnings;// Suppress warnings in system headers. DiagnosticClient *Client; /// DiagMappings - Mapping information for diagnostics. Mapping info is - /// packed into four bits per diagnostic. - unsigned char DiagMappings[diag::DIAG_UPPER_LIMIT/2]; + /// packed into two bits per diagnostic. + unsigned char DiagMappings[diag::DIAG_UPPER_LIMIT/4]; /// ErrorOccurred / FatalErrorOccurred - This is set to true when an error or /// fatal error is emitted, and is sticky. @@ -209,21 +207,17 @@ public: void setWarningsAsErrors(bool Val) { WarningsAsErrors = Val; } bool getWarningsAsErrors() const { return WarningsAsErrors; } - /// setWarnOnExtensions - When set to true, issue warnings on GCC extensions, - /// the equivalent of GCC's -pedantic. - void setWarnOnExtensions(bool Val) { WarnOnExtensions = Val; } - bool getWarnOnExtensions() const { return WarnOnExtensions; } - - /// setErrorOnExtensions - When set to true issue errors for GCC extensions - /// instead of warnings. This is the equivalent to GCC's -pedantic-errors. - void setErrorOnExtensions(bool Val) { ErrorOnExtensions = Val; } - bool getErrorOnExtensions() const { return ErrorOnExtensions; } - /// setSuppressSystemWarnings - When set to true mask warnings that /// come from system headers. void setSuppressSystemWarnings(bool Val) { SuppressSystemWarnings = Val; } bool getSuppressSystemWarnings() const { return SuppressSystemWarnings; } + /// AllExtensionsSilenced - This is a counter bumped when an __extension__ + /// block is encountered. When non-zero, all extension diagnostics are + /// entirely silenced, no matter how they are mapped. + void IncrementAllExtensionsSilenced() { ++AllExtensionsSilenced; } + void DecrementAllExtensionsSilenced() { --AllExtensionsSilenced; } + /// setDiagnosticMapping - This allows the client to specify that certain /// warnings are ignored. Only WARNINGs and EXTENSIONs can be mapped. void setDiagnosticMapping(diag::kind Diag, diag::Mapping Map) { @@ -231,16 +225,19 @@ public: "Can only map builtin diagnostics"); assert((isBuiltinWarningOrExtension(Diag) || Map == diag::MAP_FATAL) && "Cannot map errors!"); - unsigned char &Slot = DiagMappings[Diag/2]; - unsigned Bits = (Diag & 1)*4; - Slot &= ~(7 << Bits); + setDiagnosticMappingInternal(Diag, Map); + } + void setDiagnosticMappingInternal(unsigned Diag, unsigned Map) { + unsigned char &Slot = DiagMappings[Diag/4]; + unsigned Bits = (Diag & 3)*2; + Slot &= ~(3 << Bits); Slot |= Map << Bits; } /// getDiagnosticMapping - Return the mapping currently set for the specified /// diagnostic. diag::Mapping getDiagnosticMapping(diag::kind Diag) const { - return (diag::Mapping)((DiagMappings[Diag/2] >> (Diag & 1)*4) & 7); + return (diag::Mapping)((DiagMappings[Diag/4] >> (Diag & 3)*2) & 3); } bool hasErrorOccurred() const { return ErrorOccurred; } @@ -287,6 +284,12 @@ public: /// \brief Determine whether the given built-in diagnostic ID is a /// Note. static bool isBuiltinNote(unsigned DiagID); + + /// isBuiltinExtensionDiag - Determine whether the given built-in diagnostic + /// ID is for an extension of some sort. + /// + static bool isBuiltinExtensionDiag(unsigned DiagID); + /// getDiagnosticLevel - Based on the way the client configured the Diagnostic /// object, classify the specified diagnostic ID into a Level, consumable by diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp index 8c393c525eb76395448e39021cf51d289250a891..4dcd7c3608797fedc8a7bd0192f2b27c348008c9 100644 --- a/clang/lib/Basic/Diagnostic.cpp +++ b/clang/lib/Basic/Diagnostic.cpp @@ -12,6 +12,15 @@ //===----------------------------------------------------------------------===// #include "clang/Basic/Diagnostic.h" + +#include "clang/Lex/LexDiagnostic.h" +#include "clang/Parse/ParseDiagnostic.h" +#include "clang/AST/ASTDiagnostic.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Analysis/AnalysisDiagnostic.h" +#include "clang/Driver/DriverDiagnostic.h" + #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/SmallVector.h" @@ -25,50 +34,85 @@ using namespace clang; // Builtin Diagnostic information //===----------------------------------------------------------------------===// -/// Flag values for diagnostics. +// DefaultDiagnosticMappings - This specifies the default mapping for each diag, +// based on its kind. Yay for macros? + +struct DefaultMappingInfo { + unsigned DiagID : 14; + unsigned Mapping : 2; +}; + +#define NOTE diag::MAP_IGNORE +#define WARNING diag::MAP_WARNING +#define EXTENSION diag::MAP_IGNORE +#define EXTWARN diag::MAP_WARNING +#define ERROR diag::MAP_ERROR +#define FATAL diag::MAP_FATAL + +static const DefaultMappingInfo DefaultMappings[] = { +#define DIAG(ENUM,CLASS,DESC) { diag::ENUM, CLASS }, +#include "clang/Basic/DiagnosticCommonKinds.inc" +#include "clang/Basic/DiagnosticDriverKinds.inc" +#include "clang/Basic/DiagnosticFrontendKinds.inc" +#include "clang/Basic/DiagnosticLexKinds.inc" +#include "clang/Basic/DiagnosticParseKinds.inc" +#include "clang/Basic/DiagnosticASTKinds.inc" +#include "clang/Basic/DiagnosticSemaKinds.inc" +#include "clang/Basic/DiagnosticAnalysisKinds.inc" +{ 0, 0 } +}; + +#undef DIAG +#undef NOTE +#undef WARNING +#undef EXTENSION +#undef EXTWARN +#undef ERROR +#undef FATAL + + + +// Diagnostic classes. enum { - // Diagnostic classes. NOTE = 0x01, WARNING = 0x02, EXTENSION = 0x03, EXTWARN = 0x04, ERROR = 0x05, - FATAL = 0x06, - class_mask = 0x07 + FATAL = 0x06 }; -/// DiagnosticFlags - A set of flags, or'd together, that describe the -/// diagnostic. -#define DIAG(ENUM,FLAGS,DESC) FLAGS, -static unsigned char DiagnosticFlagsCommon[] = { +/// DiagnosticClasses - The class for each diagnostic. +#define DIAG(ENUM,CLASS,DESC) CLASS, +static unsigned char DiagnosticClassesCommon[] = { #include "clang/Basic/DiagnosticCommonKinds.inc" 0 }; -static unsigned char DiagnosticFlagsDriver[] = { +static unsigned char DiagnosticClassesDriver[] = { #include "clang/Basic/DiagnosticDriverKinds.inc" 0 }; -static unsigned char DiagnosticFlagsFrontend[] = { +static unsigned char DiagnosticClassesFrontend[] = { #include "clang/Basic/DiagnosticFrontendKinds.inc" 0 }; -static unsigned char DiagnosticFlagsLex[] = { +static unsigned char DiagnosticClassesLex[] = { #include "clang/Basic/DiagnosticLexKinds.inc" 0 }; -static unsigned char DiagnosticFlagsParse[] = { +static unsigned char DiagnosticClassesParse[] = { #include "clang/Basic/DiagnosticParseKinds.inc" 0 }; -static unsigned char DiagnosticFlagsAST[] = { +static unsigned char DiagnosticClassesAST[] = { #include "clang/Basic/DiagnosticASTKinds.inc" 0 }; -static unsigned char DiagnosticFlagsSema[] = { +static unsigned char DiagnosticClassesSema[] = { #include "clang/Basic/DiagnosticSemaKinds.inc" 0 }; -static unsigned char DiagnosticFlagsAnalysis[] = { +static unsigned char DiagnosticClassesAnalysis[] = { #include "clang/Basic/DiagnosticAnalysisKinds.inc" 0 }; @@ -81,27 +125,27 @@ static unsigned getBuiltinDiagClass(unsigned DiagID) { "Diagnostic ID out of range!"); unsigned res; if (DiagID < diag::DIAG_START_DRIVER) - res = DiagnosticFlagsCommon[DiagID]; + res = DiagnosticClassesCommon[DiagID]; else if (DiagID < diag::DIAG_START_FRONTEND) - res = DiagnosticFlagsDriver[DiagID - diag::DIAG_START_DRIVER - 1]; + res = DiagnosticClassesDriver[DiagID - diag::DIAG_START_DRIVER - 1]; else if (DiagID < diag::DIAG_START_LEX) - res = DiagnosticFlagsFrontend[DiagID - diag::DIAG_START_FRONTEND - 1]; + res = DiagnosticClassesFrontend[DiagID - diag::DIAG_START_FRONTEND - 1]; else if (DiagID < diag::DIAG_START_PARSE) - res = DiagnosticFlagsLex[DiagID - diag::DIAG_START_LEX - 1]; + res = DiagnosticClassesLex[DiagID - diag::DIAG_START_LEX - 1]; else if (DiagID < diag::DIAG_START_AST) - res = DiagnosticFlagsParse[DiagID - diag::DIAG_START_PARSE - 1]; + res = DiagnosticClassesParse[DiagID - diag::DIAG_START_PARSE - 1]; else if (DiagID < diag::DIAG_START_SEMA) - res = DiagnosticFlagsAST[DiagID - diag::DIAG_START_AST - 1]; + res = DiagnosticClassesAST[DiagID - diag::DIAG_START_AST - 1]; else if (DiagID < diag::DIAG_START_ANALYSIS) - res = DiagnosticFlagsSema[DiagID - diag::DIAG_START_SEMA - 1]; + res = DiagnosticClassesSema[DiagID - diag::DIAG_START_SEMA - 1]; else - res = DiagnosticFlagsAnalysis[DiagID - diag::DIAG_START_ANALYSIS - 1]; - return res & class_mask; + res = DiagnosticClassesAnalysis[DiagID - diag::DIAG_START_ANALYSIS - 1]; + return res; } /// DiagnosticText - An english message to print for the diagnostic. These /// should be localized. -#define DIAG(ENUM,FLAGS,DESC) DESC, +#define DIAG(ENUM,CLASS,DESC) DESC, static const char * const DiagnosticTextCommon[] = { #include "clang/Basic/DiagnosticCommonKinds.inc" 0 @@ -203,13 +247,10 @@ static void DummyArgToStringFn(Diagnostic::ArgumentKind AK, intptr_t QT, Diagnostic::Diagnostic(DiagnosticClient *client) : Client(client) { + AllExtensionsSilenced = 0; IgnoreAllWarnings = false; WarningsAsErrors = false; - WarnOnExtensions = false; - ErrorOnExtensions = false; SuppressSystemWarnings = false; - // Clear all mappings, setting them to MAP_DEFAULT. - memset(DiagMappings, 0, sizeof(DiagMappings)); ErrorOccurred = false; FatalErrorOccurred = false; @@ -221,6 +262,12 @@ Diagnostic::Diagnostic(DiagnosticClient *client) : Client(client) { ArgToStringFn = DummyArgToStringFn; ArgToStringCookie = 0; + + // Set all mappings to their default. + for (unsigned i = 0, e = sizeof(DefaultMappings)/sizeof(DefaultMappings[0]); + i != e; ++i) + setDiagnosticMappingInternal(DefaultMappings[i].DiagID, + DefaultMappings[i].Mapping); } Diagnostic::~Diagnostic() { @@ -251,6 +298,17 @@ bool Diagnostic::isBuiltinNote(unsigned DiagID) { return DiagID < diag::DIAG_UPPER_LIMIT && getBuiltinDiagClass(DiagID) == NOTE; } +/// isBuiltinExtensionDiag - Determine whether the given built-in diagnostic +/// ID is for an extension of some sort. +/// +bool Diagnostic::isBuiltinExtensionDiag(unsigned DiagID) { + if (DiagID < diag::DIAG_UPPER_LIMIT) { + unsigned Class = getBuiltinDiagClass(DiagID); + return Class == EXTENSION || Class == EXTWARN; + } + return false; +} + /// getDescription - Given a diagnostic ID, return a description of the /// issue. @@ -294,40 +352,31 @@ Diagnostic::Level Diagnostic::getDiagnosticLevel(unsigned DiagID, unsigned DiagClass) const { // Specific non-error diagnostics may be mapped to various levels from ignored // to error. Errors can only be mapped to fatal. + Diagnostic::Level Result = Diagnostic::Fatal; switch (getDiagnosticMapping((diag::kind)DiagID)) { - case diag::MAP_DEFAULT: break; - case diag::MAP_IGNORE: return Diagnostic::Ignored; - case diag::MAP_WARNING: DiagClass = WARNING; break; - case diag::MAP_ERROR: DiagClass = ERROR; break; - case diag::MAP_FATAL: DiagClass = FATAL; break; - } - - // Map diagnostic classes based on command line argument settings. - if (DiagClass == EXTENSION) { - if (ErrorOnExtensions) - DiagClass = ERROR; - else if (WarnOnExtensions) - DiagClass = WARNING; - else - return Ignored; - } else if (DiagClass == EXTWARN) { - DiagClass = ErrorOnExtensions ? ERROR : WARNING; - } - - // If warnings are globally mapped to ignore or error, do it. - if (DiagClass == WARNING) { + case diag::MAP_IGNORE: + return Diagnostic::Ignored; + case diag::MAP_ERROR: + Result = Diagnostic::Error; + break; + case diag::MAP_FATAL: + Result = Diagnostic::Fatal; + break; + case diag::MAP_WARNING: + // If warnings are globally mapped to ignore or error, do it. if (IgnoreAllWarnings) return Diagnostic::Ignored; - if (WarningsAsErrors) - DiagClass = ERROR; + Result = WarningsAsErrors ? Diagnostic::Error : Diagnostic::Warning; + break; } + + // Okay, we're about to return this as a "diagnostic to emit" one last check: + // if this is any sort of extension warning, and if we're in an __extension__ + // block, silence it. + if (AllExtensionsSilenced && isBuiltinExtensionDiag(DiagID)) + return Diagnostic::Ignored; - switch (DiagClass) { - default: assert(0 && "Unknown diagnostic class!"); - case WARNING: return Diagnostic::Warning; - case ERROR: return Diagnostic::Error; - case FATAL: return Diagnostic::Fatal; - } + return Result; } /// ProcessDiag - This is the method used to report a diagnostic that is diff --git a/clang/lib/Parse/ExtensionRAIIObject.h b/clang/lib/Parse/ExtensionRAIIObject.h index 928d443b74cad39ee3ac48ed7a986084e2697596..2b2bd3b21648e66285fb8ceb3ff186915df712db 100644 --- a/clang/lib/Parse/ExtensionRAIIObject.h +++ b/clang/lib/Parse/ExtensionRAIIObject.h @@ -26,15 +26,13 @@ namespace clang { void operator=(const ExtensionRAIIObject &); // DO NOT IMPLEMENT ExtensionRAIIObject(const ExtensionRAIIObject&); // DO NOT IMPLEMENT Diagnostic &Diags; - bool OldState; public: ExtensionRAIIObject(Diagnostic &diags) : Diags(diags) { - OldState = Diags.getWarnOnExtensions(); - Diags.setWarnOnExtensions(false); + Diags.IncrementAllExtensionsSilenced(); } ~ExtensionRAIIObject() { - Diags.setWarnOnExtensions(OldState); + Diags.DecrementAllExtensionsSilenced(); } }; } diff --git a/clang/test/Misc/diag-mapping.c b/clang/test/Misc/diag-mapping.c new file mode 100644 index 0000000000000000000000000000000000000000..f6ace02d077b55748319e2141dbc5773930ff9c5 --- /dev/null +++ b/clang/test/Misc/diag-mapping.c @@ -0,0 +1,27 @@ +// This should warn by default. +// RUN: clang-cc %s 2>&1 | grep "warning:" && +// This should not emit anything. +// RUN: clang-cc %s -Wno-extra-tokens 2>&1 | not grep diagnostic + +// -Werror can map all warnings to error. +// RUN: clang-cc %s -Werror 2>&1 | grep "error:" && + +// -Werror can map this one warning to error. +// RUN: clang-cc %s -Werror=extra-tokens 2>&1 | grep "error:" && + +// This should stay a warning with -pedantic. +// RUN: clang-cc %s -pedantic 2>&1 | grep "warning:" && + +// This should emit an error with -pedantic-errors. +// RUN: clang-cc %s -pedantic-errors 2>&1 | grep "error:" && + +// This should emit a warning, because -Wfoo overrides -pedantic*. +// RUN: clang-cc %s -pedantic-errors -Wextra_tokens 2>&1 | grep "error:" && + +// This should emit nothing, because -Wno-extra-tokens overrides -pedantic* +// RUN: clang-cc %s -pedantic-errors -Wno-extra-tokens 2>&1 | not grep diagnostic + +#ifdef foo +#endif bad // extension! + +int x; diff --git a/clang/tools/clang-cc/Warnings.cpp b/clang/tools/clang-cc/Warnings.cpp index e43da0a4eabe295de49aed194b64c4c467491b94..95983e2c5a9a615d28c9bc8e93c715958876e77a 100644 --- a/clang/tools/clang-cc/Warnings.cpp +++ b/clang/tools/clang-cc/Warnings.cpp @@ -101,11 +101,10 @@ static bool WarningOptionCompare(const WarningOption &LHS, } bool clang::ProcessWarningOptions(Diagnostic &Diags) { + Diags.setSuppressSystemWarnings(true); // Default to -Wno-system-headers + // FIXME: These should be mapped to group options. Diags.setIgnoreAllWarnings(OptNoWarnings); - Diags.setWarnOnExtensions(OptPedantic); - Diags.setErrorOnExtensions(OptPedanticErrors); - Diags.setSuppressSystemWarnings(true); // Default to -Wno-system-headers // Set some defaults that are currently set manually. This, too, should // be in the tablegen stuff later. @@ -125,6 +124,10 @@ bool clang::ProcessWarningOptions(Diagnostic &Diags) { // FIXME: -fdiagnostics-show-option // FIXME: -Wfatal-errors / -Wfatal-errors=foo + /// ControlledOptions - Keep track of the options that the user explicitly + /// poked with -Wfoo, -Wno-foo, or -Werror=foo. + llvm::SmallVector ControlledDiags; + for (unsigned i = 0, e = OptWarnings.size(); i != e; ++i) { const std::string &Opt = OptWarnings[i]; const char *OptStart = &Opt[0]; @@ -183,8 +186,53 @@ bool clang::ProcessWarningOptions(Diagnostic &Diags) { } // Option exists, poke all the members of its diagnostic set. - for (unsigned i = 0, e = Found->NumMembers; i != e; ++i) - Diags.setDiagnosticMapping(Found->Members[i], Mapping); + for (const diag::kind *Member = Found->Members, + *E = Found->Members+Found->NumMembers; Member != E; ++Member) { + Diags.setDiagnosticMapping(*Member, Mapping); + assert(*Member < 65536 && "ControlledOptions element too small"); + ControlledDiags.push_back(*Member); + } + } + + // If -pedantic or -pedantic-errors was specified, then we want to map all + // extension diagnostics onto WARNING or ERROR unless the user has futz'd + // around with them explicitly. + if (OptPedantic || OptPedanticErrors) { + // Sort the array of options that has been poked at directly so we can do + // efficient queries. + std::sort(ControlledDiags.begin(), ControlledDiags.end()); + + // Don't worry about iteration off the end down below. + ControlledDiags.push_back(diag::DIAG_UPPER_LIMIT); + + diag::Mapping Mapping = + OptPedanticErrors ? diag::MAP_ERROR : diag::MAP_WARNING; + + // Loop over all of the extension diagnostics. Unless they were explicitly + // controlled, reset their mapping to Mapping. We walk through the + // ControlledOptions in parallel with this walk, which is faster than + // repeatedly binary searching it. + // + llvm::SmallVectorImpl::iterator ControlledDiagsIt = + ControlledDiags.begin(); + + // TODO: if it matters, we could make tblgen produce a list of just the + // extension diags to avoid skipping ones that don't matter. + for (unsigned short i = 0; i != diag::DIAG_UPPER_LIMIT; ++i) { + // If this diagnostic was controlled, ignore it. + if (i == *ControlledDiagsIt) { + ++ControlledDiagsIt; + while (i == *ControlledDiagsIt) // ControlledDiags can have dupes. + ++ControlledDiagsIt; + // Do not map this diagnostic ID#. + continue; + } + + // Okay, the user didn't control this ID. If it is an example, map it. + if (Diagnostic::isBuiltinExtensionDiag(i)) + Diags.setDiagnosticMapping(i, Mapping); + } } + return false; }