Index: tools/llvm-project/extra/clang-tidy/ClangTidy.h =================================================================== --- tools/llvm-project/extra/clang-tidy/ClangTidy.h +++ tools/llvm-project/extra/clang-tidy/ClangTidy.h @@ -214,7 +214,7 @@ // /// \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 &WErrorCount); /// \brief Serializes replacements into YAML and writes them to the specified /// output stream. Index: tools/llvm-project/extra/clang-tidy/ClangTidy.cpp =================================================================== --- tools/llvm-project/extra/clang-tidy/ClangTidy.cpp +++ tools/llvm-project/extra/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), WErrors(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.IsWError) { + Name += ", -Werrors="; + Level = DiagnosticsEngine::Error; + WErrors++; + } 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 WErrorCount() { return WErrors; } + private: SourceLocation getLocation(StringRef FilePath, unsigned Offset) { if (FilePath.empty()) @@ -174,6 +182,7 @@ bool ApplyFixes; unsigned TotalFixes; unsigned AppliedFixes; + unsigned WErrors; }; class ClangTidyASTConsumer : public MultiplexConsumer { @@ -421,11 +430,12 @@ return Context.getStats(); } -void handleErrors(const std::vector &Errors, bool Fix) { +void handleErrors(const std::vector &Errors, bool Fix, unsigned &WErrorCount) { ErrorReporter Reporter(Fix); for (const ClangTidyError &Error : Errors) Reporter.reportDiagnostic(Error); Reporter.Finish(); + WErrorCount += Reporter.WErrorCount(); } void exportReplacements(const std::vector &Errors, Index: tools/llvm-project/extra/clang-tidy/ClangTidyDiagnosticConsumer.h =================================================================== --- tools/llvm-project/extra/clang-tidy/ClangTidyDiagnosticConsumer.h +++ tools/llvm-project/extra/clang-tidy/ClangTidyDiagnosticConsumer.h @@ -57,7 +57,7 @@ Error = DiagnosticsEngine::Error }; - ClangTidyError(StringRef CheckName, Level DiagLevel); + ClangTidyError(StringRef CheckName, Level DiagLevel, bool IsWError); std::string CheckName; ClangTidyMessage Message; @@ -65,6 +65,7 @@ SmallVector Notes; Level DiagLevel; + bool IsWError; }; /// \brief Read-only set of strings represented as a list of positive and @@ -161,6 +162,10 @@ /// The \c CurrentFile can be changed using \c setCurrentFile. GlobList &getChecksFilter(); + /// \brief Returns check filter for the \c CurrentFile which + /// selects checks for upgrade to error. + GlobList &getWErrorFilter(); + /// \brief Returns global options. const ClangTidyGlobalOptions &getGlobalOptions() const; @@ -208,6 +213,7 @@ std::string CurrentFile; ClangTidyOptions CurrentOptions; std::unique_ptr CheckFilter; + std::unique_ptr WErrorFilter; LangOptions LangOpts; Index: tools/llvm-project/extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp =================================================================== --- tools/llvm-project/extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp +++ tools/llvm-project/extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp @@ -115,8 +115,9 @@ } ClangTidyError::ClangTidyError(StringRef CheckName, - ClangTidyError::Level DiagLevel) - : CheckName(CheckName), DiagLevel(DiagLevel) {} + ClangTidyError::Level DiagLevel, + bool IsWError) + : CheckName(CheckName), DiagLevel(DiagLevel), IsWError(IsWError) {} // 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)); + WErrorFilter.reset(new GlobList(*getOptions().WErrors)); } void ClangTidyContext::setASTContext(ASTContext *Context) { @@ -233,6 +235,11 @@ return *CheckFilter; } +GlobList &ClangTidyContext::getWErrorFilter() { + assert(WErrorFilter != nullptr); + return *WErrorFilter; +} + /// \brief Store a \c ClangTidyError. void ClangTidyContext::storeError(const ClangTidyError &Error) { Errors.push_back(Error); @@ -320,7 +327,9 @@ LastErrorRelatesToUserCode = true; LastErrorPassesLineFilter = true; } - Errors.push_back(ClangTidyError(CheckName, Level)); + bool IsWError = DiagLevel == DiagnosticsEngine::Warning && + Context.getWErrorFilter().contains(CheckName); + Errors.push_back(ClangTidyError(CheckName, Level, IsWError)); } // FIXME: Provide correct LangOptions for each file. Index: tools/llvm-project/extra/clang-tidy/ClangTidyOptions.h =================================================================== --- tools/llvm-project/extra/clang-tidy/ClangTidyOptions.h +++ tools/llvm-project/extra/clang-tidy/ClangTidyOptions.h @@ -62,6 +62,9 @@ /// \brief Checks filter. llvm::Optional Checks; + /// \brief WErrors filter. + llvm::Optional WErrors; + /// \brief Output warnings from headers matching this filter. Warnings from /// main files will always be displayed. llvm::Optional HeaderFilterRegex; Index: tools/llvm-project/extra/clang-tidy/ClangTidyOptions.cpp =================================================================== --- tools/llvm-project/extra/clang-tidy/ClangTidyOptions.cpp +++ tools/llvm-project/extra/clang-tidy/ClangTidyOptions.cpp @@ -85,6 +85,7 @@ MappingNormalization NOpts( IO, Options.CheckOptions); IO.mapOptional("Checks", Options.Checks); + IO.mapOptional("WErrors", Options.WErrors); 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.WErrors = ""; 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.WErrors) + Result.WErrors = + (Result.WErrors && !Result.WErrors->empty() ? *Result.WErrors + "," : "") + + *Other.WErrors; if (Other.HeaderFilterRegex) Result.HeaderFilterRegex = Other.HeaderFilterRegex; Index: tools/llvm-project/extra/clang-tidy/tool/ClangTidyMain.cpp =================================================================== --- tools/llvm-project/extra/clang-tidy/tool/ClangTidyMain.cpp +++ tools/llvm-project/extra/clang-tidy/tool/ClangTidyMain.cpp @@ -64,6 +64,10 @@ cl::init(""), cl::cat(ClangTidyCategory)); static cl::opt +WErrors("Werrors", 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 +231,7 @@ ClangTidyOptions DefaultOptions; DefaultOptions.Checks = DefaultChecks; + DefaultOptions.WErrors = ""; DefaultOptions.HeaderFilterRegex = HeaderFilter; DefaultOptions.SystemHeaders = SystemHeaders; DefaultOptions.AnalyzeTemporaryDtors = AnalyzeTemporaryDtors; @@ -238,6 +243,8 @@ ClangTidyOptions OverrideOptions; if (Checks.getNumOccurrences() > 0) OverrideOptions.Checks = Checks; + if (WErrors.getNumOccurrences() > 0) + OverrideOptions.WErrors = WErrors; if (HeaderFilter.getNumOccurrences() > 0) OverrideOptions.HeaderFilterRegex = HeaderFilter; if (SystemHeaders.getNumOccurrences() > 0) @@ -322,8 +329,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 +353,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: tools/llvm-project/extra/test/clang-tidy/werrors-plural.cpp =================================================================== --- /dev/null +++ tools/llvm-project/extra/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' -Werrors='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, -Werrors=] + +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, -Werrors=] + +// CHECK-WARN-NOT: treated as +// CHECK-WERR: 2 warnings treated as errors Index: tools/llvm-project/extra/test/clang-tidy/werrors.cpp =================================================================== --- /dev/null +++ tools/llvm-project/extra/test/clang-tidy/werrors.cpp @@ -0,0 +1,10 @@ +// 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' -Werrors='llvm-namespace-comment' -- 2>&1 | FileCheck %s --check-prefix=CHECK-WERR + +namespace i { +} +// CHECK-WARN: warning: namespace 'i' not terminated with a closing comment [llvm-namespace-comment] +// CHECK-WERR: error: namespace 'i' not terminated with a closing comment [llvm-namespace-comment, -Werrors=] + +// CHECK-WARN-NOT: treated as +// CHECK-WERR: 1 warning treated as error