Index: clang-tidy/ClangTidy.h =================================================================== --- clang-tidy/ClangTidy.h +++ clang-tidy/ClangTidy.h @@ -214,7 +214,8 @@ // /// \brief Displays the found \p Errors to the users. If \p Fix is true, \p /// Errors containing fixes are automatically applied. -void handleErrors(const std::vector &Errors, bool Fix); +void handleErrors(const std::vector &Errors, bool Fix, + unsigned &WarningsAsErrorsCount); /// \brief Serializes replacements into YAML and writes them to the specified /// output stream. Index: clang-tidy/ClangTidy.cpp =================================================================== --- clang-tidy/ClangTidy.cpp +++ clang-tidy/ClangTidy.cpp @@ -101,7 +101,7 @@ Diags(IntrusiveRefCntPtr(new DiagnosticIDs), &*DiagOpts, DiagPrinter), SourceMgr(Diags, Files), Rewrite(SourceMgr, LangOpts), - ApplyFixes(ApplyFixes), TotalFixes(0), AppliedFixes(0) { + ApplyFixes(ApplyFixes), TotalFixes(0), AppliedFixes(0), WarningsAsErrors(0) { DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors(); DiagPrinter->BeginSourceFile(LangOpts); } @@ -114,8 +114,14 @@ SmallVector, 4> FixLocations; { auto Level = static_cast(Error.DiagLevel); + std::string Name = Error.CheckName; + if (Error.IsWarningAsError) { + Name += ",-warnings-as-errors"; + Level = DiagnosticsEngine::Error; + WarningsAsErrors++; + } auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0 [%1]")) - << Message.Message << Error.CheckName; + << Message.Message << Name; for (const tooling::Replacement &Fix : Error.Fix) { SourceLocation FixLoc = getLocation(Fix.getFilePath(), Fix.getOffset()); SourceLocation FixEndLoc = FixLoc.getLocWithOffset(Fix.getLength()); @@ -147,6 +153,8 @@ } } + unsigned WarningsAsErrorsCount() const { return WarningsAsErrors; } + private: SourceLocation getLocation(StringRef FilePath, unsigned Offset) { if (FilePath.empty()) @@ -174,6 +182,7 @@ bool ApplyFixes; unsigned TotalFixes; unsigned AppliedFixes; + unsigned WarningsAsErrors; }; class ClangTidyASTConsumer : public MultiplexConsumer { @@ -421,11 +430,13 @@ return Context.getStats(); } -void handleErrors(const std::vector &Errors, bool Fix) { +void handleErrors(const std::vector &Errors, bool Fix, + unsigned &WarningsAsErrorsCount) { ErrorReporter Reporter(Fix); for (const ClangTidyError &Error : Errors) Reporter.reportDiagnostic(Error); Reporter.Finish(); + WarningsAsErrorsCount += Reporter.WarningsAsErrorsCount(); } void exportReplacements(const std::vector &Errors, Index: clang-tidy/ClangTidyDiagnosticConsumer.h =================================================================== --- clang-tidy/ClangTidyDiagnosticConsumer.h +++ clang-tidy/ClangTidyDiagnosticConsumer.h @@ -57,7 +57,7 @@ Error = DiagnosticsEngine::Error }; - ClangTidyError(StringRef CheckName, Level DiagLevel); + ClangTidyError(StringRef CheckName, Level DiagLevel, bool IsWarningAsError); std::string CheckName; ClangTidyMessage Message; @@ -65,6 +65,7 @@ SmallVector Notes; Level DiagLevel; + bool IsWarningAsError; }; /// \brief Read-only set of strings represented as a list of positive and @@ -164,6 +165,10 @@ /// \brief Returns true if the check name is enabled for the \c CurrentFile. bool isCheckEnabled(StringRef CheckName) const; + /// \brief Returns check filter for the \c CurrentFile which + /// selects checks for upgrade to error. + GlobList &getWarningAsErrorFilter(); + /// \brief Returns global options. const ClangTidyGlobalOptions &getGlobalOptions() const; @@ -211,6 +216,7 @@ std::string CurrentFile; ClangTidyOptions CurrentOptions; std::unique_ptr CheckFilter; + std::unique_ptr WarningAsErrorFilter; LangOptions LangOpts; Index: clang-tidy/ClangTidyDiagnosticConsumer.cpp =================================================================== --- clang-tidy/ClangTidyDiagnosticConsumer.cpp +++ clang-tidy/ClangTidyDiagnosticConsumer.cpp @@ -115,8 +115,9 @@ } ClangTidyError::ClangTidyError(StringRef CheckName, - ClangTidyError::Level DiagLevel) - : CheckName(CheckName), DiagLevel(DiagLevel) {} + ClangTidyError::Level DiagLevel, + bool IsWarningAsError) + : CheckName(CheckName), DiagLevel(DiagLevel), IsWarningAsError(IsWarningAsError) {} // Returns true if GlobList starts with the negative indicator ('-'), removes it // from the GlobList. @@ -204,6 +205,7 @@ CurrentFile = File; CurrentOptions = getOptionsForFile(CurrentFile); CheckFilter.reset(new GlobList(*getOptions().Checks)); + WarningAsErrorFilter.reset(new GlobList(*getOptions().WarningsAsErrors)); } void ClangTidyContext::setASTContext(ASTContext *Context) { @@ -237,6 +239,11 @@ return CheckFilter->contains(CheckName); } +GlobList &ClangTidyContext::getWarningAsErrorFilter() { + assert(WarningAsErrorFilter != nullptr); + return *WarningAsErrorFilter; +} + /// \brief Store a \c ClangTidyError. void ClangTidyContext::storeError(const ClangTidyError &Error) { Errors.push_back(Error); @@ -324,7 +331,9 @@ LastErrorRelatesToUserCode = true; LastErrorPassesLineFilter = true; } - Errors.push_back(ClangTidyError(CheckName, Level)); + bool IsWarningAsError = DiagLevel == DiagnosticsEngine::Warning && + Context.getWarningAsErrorFilter().contains(CheckName); + Errors.push_back(ClangTidyError(CheckName, Level, IsWarningAsError)); } // FIXME: Provide correct LangOptions for each file. Index: clang-tidy/ClangTidyOptions.h =================================================================== --- clang-tidy/ClangTidyOptions.h +++ clang-tidy/ClangTidyOptions.h @@ -62,6 +62,9 @@ /// \brief Checks filter. llvm::Optional Checks; + /// \brief WarningsAsErrors filter. + llvm::Optional WarningsAsErrors; + /// \brief Output warnings from headers matching this filter. Warnings from /// main files will always be displayed. llvm::Optional HeaderFilterRegex; Index: clang-tidy/ClangTidyOptions.cpp =================================================================== --- clang-tidy/ClangTidyOptions.cpp +++ clang-tidy/ClangTidyOptions.cpp @@ -85,6 +85,7 @@ MappingNormalization NOpts( IO, Options.CheckOptions); IO.mapOptional("Checks", Options.Checks); + IO.mapOptional("WarningsAsErrors", Options.WarningsAsErrors); IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex); IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors); IO.mapOptional("User", Options.User); @@ -103,6 +104,7 @@ ClangTidyOptions ClangTidyOptions::getDefaults() { ClangTidyOptions Options; Options.Checks = ""; + Options.WarningsAsErrors = ""; Options.HeaderFilterRegex = ""; Options.SystemHeaders = false; Options.AnalyzeTemporaryDtors = false; @@ -123,6 +125,10 @@ Result.Checks = (Result.Checks && !Result.Checks->empty() ? *Result.Checks + "," : "") + *Other.Checks; + if (Other.WarningsAsErrors) + Result.WarningsAsErrors = + (Result.WarningsAsErrors && !Result.WarningsAsErrors->empty() ? *Result.WarningsAsErrors + "," : "") + + *Other.WarningsAsErrors; if (Other.HeaderFilterRegex) Result.HeaderFilterRegex = Other.HeaderFilterRegex; Index: clang-tidy/tool/ClangTidyMain.cpp =================================================================== --- clang-tidy/tool/ClangTidyMain.cpp +++ clang-tidy/tool/ClangTidyMain.cpp @@ -64,6 +64,11 @@ cl::init(""), cl::cat(ClangTidyCategory)); static cl::opt +WarningsAsErrors("warnings-as-errors", cl::desc("Upgrades warnings to errors. " + "Same format as '-checks'."), + cl::init(""), cl::cat(ClangTidyCategory)); + +static cl::opt HeaderFilter("header-filter", cl::desc("Regular expression matching the names of the\n" "headers to output diagnostics from. Diagnostics\n" @@ -227,6 +232,7 @@ ClangTidyOptions DefaultOptions; DefaultOptions.Checks = DefaultChecks; + DefaultOptions.WarningsAsErrors = ""; DefaultOptions.HeaderFilterRegex = HeaderFilter; DefaultOptions.SystemHeaders = SystemHeaders; DefaultOptions.AnalyzeTemporaryDtors = AnalyzeTemporaryDtors; @@ -238,6 +244,8 @@ ClangTidyOptions OverrideOptions; if (Checks.getNumOccurrences() > 0) OverrideOptions.Checks = Checks; + if (WarningsAsErrors.getNumOccurrences() > 0) + OverrideOptions.WarningsAsErrors = WarningsAsErrors; if (HeaderFilter.getNumOccurrences() > 0) OverrideOptions.HeaderFilterRegex = HeaderFilter; if (SystemHeaders.getNumOccurrences() > 0) @@ -322,8 +330,10 @@ const bool DisableFixes = Fix && FoundErrors && !FixErrors; + unsigned WErrorCount = 0; + // -fix-errors implies -fix. - handleErrors(Errors, (FixErrors || Fix) && !DisableFixes); + handleErrors(Errors, (FixErrors || Fix) && !DisableFixes, WErrorCount); if (!ExportFixes.empty() && !Errors.empty()) { std::error_code EC; @@ -344,6 +354,13 @@ if (EnableCheckProfile) printProfileData(Profile, llvm::errs()); + if (WErrorCount) { + StringRef Plural = WErrorCount == 1 ? "" : "s"; + llvm::errs() << WErrorCount << " warning" << Plural + << " treated as error" << Plural << "\n"; + return WErrorCount; + } + return 0; } Index: docs/clang-tidy/index.rst =================================================================== --- docs/clang-tidy/index.rst +++ docs/clang-tidy/index.rst @@ -68,7 +68,9 @@ diagnostics are displayed by clang-tidy and can be filtered out using ``-checks=`` option. However, the ``-checks=`` option does not affect compilation arguments, so it can not turn on Clang warnings which are not -already turned on in build configuration. +already turned on in build configuration. The ``-warnings-as-errors=`` option +upgrades any warnings emitted under the ``-checks=`` flag to errors (but it +does not enable any checks itself). Clang diagnostics have check names starting with ``clang-diagnostic-``. Diagnostics which have a corresponding warning option, are named @@ -150,6 +152,7 @@ -checks=* to list all available checks. -p= - Build path -system-headers - Display the errors from system headers. + -warnings-as-errors= - Upgrades warnings to errors. Same format as '-checks'. -p is used to read a compile command database. Index: test/clang-tidy/werrors-plural.cpp =================================================================== --- test/clang-tidy/werrors-plural.cpp +++ test/clang-tidy/werrors-plural.cpp @@ -0,0 +1,15 @@ +// RUN: clang-tidy %s -checks='-*,llvm-namespace-comment' -- 2>&1 | FileCheck %s --check-prefix=CHECK-WARN +// RUN: not clang-tidy %s -checks='-*,llvm-namespace-comment' -warnings-as-errors='llvm-namespace-comment' -- 2>&1 | FileCheck %s --check-prefix=CHECK-WERR + +namespace j { +} +// CHECK-WARN: warning: namespace 'j' not terminated with a closing comment [llvm-namespace-comment] +// CHECK-WERR: error: namespace 'j' not terminated with a closing comment [llvm-namespace-comment,-warnings-as-errors] + +namespace k { +} +// CHECK-WARN: warning: namespace 'k' not terminated with a closing comment [llvm-namespace-comment] +// CHECK-WERR: error: namespace 'k' not terminated with a closing comment [llvm-namespace-comment,-warnings-as-errors] + +// CHECK-WARN-NOT: treated as +// CHECK-WERR: 2 warnings treated as errors