diff options
author | vitalyisaev <vitalyisaev@yandex-team.com> | 2023-06-29 10:00:50 +0300 |
---|---|---|
committer | vitalyisaev <vitalyisaev@yandex-team.com> | 2023-06-29 10:00:50 +0300 |
commit | 6ffe9e53658409f212834330e13564e4952558f6 (patch) | |
tree | 85b1e00183517648b228aafa7c8fb07f5276f419 /contrib/libs/clang14/tools/extra/clang-tidy/ClangTidy.cpp | |
parent | 726057070f9c5a91fc10fde0d5024913d10f1ab9 (diff) | |
download | ydb-6ffe9e53658409f212834330e13564e4952558f6.tar.gz |
YQ Connector: support managed ClickHouse
Со стороны dqrun можно обратиться к инстансу коннектора, который работает на streaming стенде, и извлечь данные из облачного CH.
Diffstat (limited to 'contrib/libs/clang14/tools/extra/clang-tidy/ClangTidy.cpp')
-rw-r--r-- | contrib/libs/clang14/tools/extra/clang-tidy/ClangTidy.cpp | 633 |
1 files changed, 633 insertions, 0 deletions
diff --git a/contrib/libs/clang14/tools/extra/clang-tidy/ClangTidy.cpp b/contrib/libs/clang14/tools/extra/clang-tidy/ClangTidy.cpp new file mode 100644 index 0000000000..7de313ad4d --- /dev/null +++ b/contrib/libs/clang14/tools/extra/clang-tidy/ClangTidy.cpp @@ -0,0 +1,633 @@ +//===--- tools/extra/clang-tidy/ClangTidy.cpp - Clang tidy tool -----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file This file implements a clang-tidy tool. +/// +/// This tool uses the Clang Tooling infrastructure, see +/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html +/// for details on setting it up with LLVM source tree. +/// +//===----------------------------------------------------------------------===// + +#include "ClangTidy.h" +#include "ClangTidyCheck.h" +#include "ClangTidyDiagnosticConsumer.h" +#include "ClangTidyModuleRegistry.h" +#include "ClangTidyProfiling.h" +#include "ExpandModularHeadersPPCallbacks.h" +#include "clang-tidy-config.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Format/Format.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Rewrite/Frontend/FixItRewriter.h" +#include "clang/Rewrite/Frontend/FrontendActions.h" +#include "clang/Tooling/Core/Diagnostic.h" +#include "clang/Tooling/DiagnosticsYaml.h" +#include "clang/Tooling/Refactoring.h" +#include "clang/Tooling/ReplacementsYaml.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Support/Process.h" +#include <algorithm> +#include <utility> + +#if CLANG_TIDY_ENABLE_STATIC_ANALYZER +#include "clang/Analysis/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" +#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER + +using namespace clang::ast_matchers; +using namespace clang::driver; +using namespace clang::tooling; +using namespace llvm; + +LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry) + +namespace clang { +namespace tidy { + +namespace { +#if CLANG_TIDY_ENABLE_STATIC_ANALYZER +static const char *AnalyzerCheckNamePrefix = "clang-analyzer-"; + +class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer { +public: + AnalyzerDiagnosticConsumer(ClangTidyContext &Context) : Context(Context) {} + + void FlushDiagnosticsImpl(std::vector<const ento::PathDiagnostic *> &Diags, + FilesMade *FilesMade) override { + for (const ento::PathDiagnostic *PD : Diags) { + SmallString<64> CheckName(AnalyzerCheckNamePrefix); + CheckName += PD->getCheckerName(); + Context.diag(CheckName, PD->getLocation().asLocation(), + PD->getShortDescription()) + << PD->path.back()->getRanges(); + + for (const auto &DiagPiece : + PD->path.flatten(/*ShouldFlattenMacros=*/true)) { + Context.diag(CheckName, DiagPiece->getLocation().asLocation(), + DiagPiece->getString(), DiagnosticIDs::Note) + << DiagPiece->getRanges(); + } + } + } + + StringRef getName() const override { return "ClangTidyDiags"; } + bool supportsLogicalOpControlFlow() const override { return true; } + bool supportsCrossFileDiagnostics() const override { return true; } + +private: + ClangTidyContext &Context; +}; +#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER + +class ErrorReporter { +public: + ErrorReporter(ClangTidyContext &Context, FixBehaviour ApplyFixes, + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS) + : Files(FileSystemOptions(), std::move(BaseFS)), + DiagOpts(new DiagnosticOptions()), + DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)), + Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, + DiagPrinter), + SourceMgr(Diags, Files), Context(Context), ApplyFixes(ApplyFixes), + TotalFixes(0), AppliedFixes(0), WarningsAsErrors(0) { + DiagOpts->ShowColors = Context.getOptions().UseColor.getValueOr( + llvm::sys::Process::StandardOutHasColors()); + DiagPrinter->BeginSourceFile(LangOpts); + if (DiagOpts->ShowColors && !llvm::sys::Process::StandardOutIsDisplayed()) { + llvm::sys::Process::UseANSIEscapeCodes(true); + } + } + + SourceManager &getSourceManager() { return SourceMgr; } + + void reportDiagnostic(const ClangTidyError &Error) { + const tooling::DiagnosticMessage &Message = Error.Message; + SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset); + // Contains a pair for each attempted fix: location and whether the fix was + // applied successfully. + SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations; + { + auto Level = static_cast<DiagnosticsEngine::Level>(Error.DiagLevel); + std::string Name = Error.DiagnosticName; + if (!Error.EnabledDiagnosticAliases.empty()) + Name += "," + llvm::join(Error.EnabledDiagnosticAliases, ","); + if (Error.IsWarningAsError) { + Name += ",-warnings-as-errors"; + Level = DiagnosticsEngine::Error; + WarningsAsErrors++; + } + auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0 [%1]")) + << Message.Message << Name; + for (const FileByteRange &FBR : Error.Message.Ranges) + Diag << getRange(FBR); + // FIXME: explore options to support interactive fix selection. + const llvm::StringMap<Replacements> *ChosenFix; + if (ApplyFixes != FB_NoFix && + (ChosenFix = getFixIt(Error, ApplyFixes == FB_FixNotes))) { + for (const auto &FileAndReplacements : *ChosenFix) { + for (const auto &Repl : FileAndReplacements.second) { + ++TotalFixes; + bool CanBeApplied = false; + if (!Repl.isApplicable()) + continue; + SourceLocation FixLoc; + SmallString<128> FixAbsoluteFilePath = Repl.getFilePath(); + Files.makeAbsolutePath(FixAbsoluteFilePath); + tooling::Replacement R(FixAbsoluteFilePath, Repl.getOffset(), + Repl.getLength(), Repl.getReplacementText()); + Replacements &Replacements = FileReplacements[R.getFilePath()]; + llvm::Error Err = Replacements.add(R); + if (Err) { + // FIXME: Implement better conflict handling. + llvm::errs() << "Trying to resolve conflict: " + << llvm::toString(std::move(Err)) << "\n"; + unsigned NewOffset = + Replacements.getShiftedCodePosition(R.getOffset()); + unsigned NewLength = Replacements.getShiftedCodePosition( + R.getOffset() + R.getLength()) - + NewOffset; + if (NewLength == R.getLength()) { + R = Replacement(R.getFilePath(), NewOffset, NewLength, + R.getReplacementText()); + Replacements = Replacements.merge(tooling::Replacements(R)); + CanBeApplied = true; + ++AppliedFixes; + } else { + llvm::errs() + << "Can't resolve conflict, skipping the replacement.\n"; + } + } else { + CanBeApplied = true; + ++AppliedFixes; + } + FixLoc = getLocation(FixAbsoluteFilePath, Repl.getOffset()); + FixLocations.push_back(std::make_pair(FixLoc, CanBeApplied)); + } + } + } + reportFix(Diag, Error.Message.Fix); + } + for (auto Fix : FixLocations) { + Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied + : diag::note_fixit_failed); + } + for (const auto &Note : Error.Notes) + reportNote(Note); + } + + void finish() { + if (TotalFixes > 0) { + Rewriter Rewrite(SourceMgr, LangOpts); + for (const auto &FileAndReplacements : FileReplacements) { + StringRef File = FileAndReplacements.first(); + llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer = + SourceMgr.getFileManager().getBufferForFile(File); + if (!Buffer) { + llvm::errs() << "Can't get buffer for file " << File << ": " + << Buffer.getError().message() << "\n"; + // FIXME: Maybe don't apply fixes for other files as well. + continue; + } + StringRef Code = Buffer.get()->getBuffer(); + auto Style = format::getStyle( + *Context.getOptionsForFile(File).FormatStyle, File, "none"); + if (!Style) { + llvm::errs() << llvm::toString(Style.takeError()) << "\n"; + continue; + } + llvm::Expected<tooling::Replacements> Replacements = + format::cleanupAroundReplacements(Code, FileAndReplacements.second, + *Style); + if (!Replacements) { + llvm::errs() << llvm::toString(Replacements.takeError()) << "\n"; + continue; + } + if (llvm::Expected<tooling::Replacements> FormattedReplacements = + format::formatReplacements(Code, *Replacements, *Style)) { + Replacements = std::move(FormattedReplacements); + if (!Replacements) + llvm_unreachable("!Replacements"); + } else { + llvm::errs() << llvm::toString(FormattedReplacements.takeError()) + << ". Skipping formatting.\n"; + } + if (!tooling::applyAllReplacements(Replacements.get(), Rewrite)) { + llvm::errs() << "Can't apply replacements for file " << File << "\n"; + } + } + if (Rewrite.overwriteChangedFiles()) { + llvm::errs() << "clang-tidy failed to apply suggested fixes.\n"; + } else { + llvm::errs() << "clang-tidy applied " << AppliedFixes << " of " + << TotalFixes << " suggested fixes.\n"; + } + } + } + + unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors; } + +private: + SourceLocation getLocation(StringRef FilePath, unsigned Offset) { + if (FilePath.empty()) + return SourceLocation(); + + auto File = SourceMgr.getFileManager().getFile(FilePath); + if (!File) + return SourceLocation(); + + FileID ID = SourceMgr.getOrCreateFileID(*File, SrcMgr::C_User); + return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset); + } + + void reportFix(const DiagnosticBuilder &Diag, + const llvm::StringMap<Replacements> &Fix) { + for (const auto &FileAndReplacements : Fix) { + for (const auto &Repl : FileAndReplacements.second) { + if (!Repl.isApplicable()) + continue; + FileByteRange FBR; + FBR.FilePath = Repl.getFilePath().str(); + FBR.FileOffset = Repl.getOffset(); + FBR.Length = Repl.getLength(); + + Diag << FixItHint::CreateReplacement(getRange(FBR), + Repl.getReplacementText()); + } + } + } + + void reportNote(const tooling::DiagnosticMessage &Message) { + SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset); + auto Diag = + Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0")) + << Message.Message; + for (const FileByteRange &FBR : Message.Ranges) + Diag << getRange(FBR); + reportFix(Diag, Message.Fix); + } + + CharSourceRange getRange(const FileByteRange &Range) { + SmallString<128> AbsoluteFilePath{Range.FilePath}; + Files.makeAbsolutePath(AbsoluteFilePath); + SourceLocation BeginLoc = getLocation(AbsoluteFilePath, Range.FileOffset); + SourceLocation EndLoc = BeginLoc.getLocWithOffset(Range.Length); + // Retrieve the source range for applicable highlights and fixes. Macro + // definition on the command line have locations in a virtual buffer and + // don't have valid file paths and are therefore not applicable. + return CharSourceRange::getCharRange(BeginLoc, EndLoc); + } + + FileManager Files; + LangOptions LangOpts; // FIXME: use langopts from each original file + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; + DiagnosticConsumer *DiagPrinter; + DiagnosticsEngine Diags; + SourceManager SourceMgr; + llvm::StringMap<Replacements> FileReplacements; + ClangTidyContext &Context; + FixBehaviour ApplyFixes; + unsigned TotalFixes; + unsigned AppliedFixes; + unsigned WarningsAsErrors; +}; + +class ClangTidyASTConsumer : public MultiplexConsumer { +public: + ClangTidyASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> Consumers, + std::unique_ptr<ClangTidyProfiling> Profiling, + std::unique_ptr<ast_matchers::MatchFinder> Finder, + std::vector<std::unique_ptr<ClangTidyCheck>> Checks) + : MultiplexConsumer(std::move(Consumers)), + Profiling(std::move(Profiling)), Finder(std::move(Finder)), + Checks(std::move(Checks)) {} + +private: + // Destructor order matters! Profiling must be destructed last. + // Or at least after Finder. + std::unique_ptr<ClangTidyProfiling> Profiling; + std::unique_ptr<ast_matchers::MatchFinder> Finder; + std::vector<std::unique_ptr<ClangTidyCheck>> Checks; +}; + +} // namespace + +ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory( + ClangTidyContext &Context, + IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS) + : Context(Context), OverlayFS(std::move(OverlayFS)), + CheckFactories(new ClangTidyCheckFactories) { + for (ClangTidyModuleRegistry::entry E : ClangTidyModuleRegistry::entries()) { + std::unique_ptr<ClangTidyModule> Module = E.instantiate(); + Module->addCheckFactories(*CheckFactories); + } +} + +#if CLANG_TIDY_ENABLE_STATIC_ANALYZER +static void +setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts, + clang::AnalyzerOptions &AnalyzerOptions) { + StringRef AnalyzerPrefix(AnalyzerCheckNamePrefix); + for (const auto &Opt : Opts.CheckOptions) { + StringRef OptName(Opt.getKey()); + if (!OptName.consume_front(AnalyzerPrefix)) + continue; + // Analyzer options are always local options so we can ignore priority. + AnalyzerOptions.Config[OptName] = Opt.getValue().Value; + } +} + +typedef std::vector<std::pair<std::string, bool>> CheckersList; + +static CheckersList getAnalyzerCheckersAndPackages(ClangTidyContext &Context, + bool IncludeExperimental) { + CheckersList List; + + const auto &RegisteredCheckers = + AnalyzerOptions::getRegisteredCheckers(IncludeExperimental); + bool AnalyzerChecksEnabled = false; + for (StringRef CheckName : RegisteredCheckers) { + std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str()); + AnalyzerChecksEnabled |= Context.isCheckEnabled(ClangTidyCheckName); + } + + if (!AnalyzerChecksEnabled) + return List; + + // List all static analyzer checkers that our filter enables. + // + // Always add all core checkers if any other static analyzer check is enabled. + // This is currently necessary, as other path sensitive checks rely on the + // core checkers. + for (StringRef CheckName : RegisteredCheckers) { + std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str()); + + if (CheckName.startswith("core") || + Context.isCheckEnabled(ClangTidyCheckName)) { + List.emplace_back(std::string(CheckName), true); + } + } + return List; +} +#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER + +std::unique_ptr<clang::ASTConsumer> +ClangTidyASTConsumerFactory::createASTConsumer( + clang::CompilerInstance &Compiler, StringRef File) { + // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't + // modify Compiler. + SourceManager *SM = &Compiler.getSourceManager(); + Context.setSourceManager(SM); + Context.setCurrentFile(File); + Context.setASTContext(&Compiler.getASTContext()); + + auto WorkingDir = Compiler.getSourceManager() + .getFileManager() + .getVirtualFileSystem() + .getCurrentWorkingDirectory(); + if (WorkingDir) + Context.setCurrentBuildDirectory(WorkingDir.get()); + + std::vector<std::unique_ptr<ClangTidyCheck>> Checks = + CheckFactories->createChecks(&Context); + + llvm::erase_if(Checks, [&](std::unique_ptr<ClangTidyCheck> &Check) { + return !Check->isLanguageVersionSupported(Context.getLangOpts()); + }); + + ast_matchers::MatchFinder::MatchFinderOptions FinderOptions; + + std::unique_ptr<ClangTidyProfiling> Profiling; + if (Context.getEnableProfiling()) { + Profiling = std::make_unique<ClangTidyProfiling>( + Context.getProfileStorageParams()); + FinderOptions.CheckProfiling.emplace(Profiling->Records); + } + + std::unique_ptr<ast_matchers::MatchFinder> Finder( + new ast_matchers::MatchFinder(std::move(FinderOptions))); + + Preprocessor *PP = &Compiler.getPreprocessor(); + Preprocessor *ModuleExpanderPP = PP; + + if (Context.getLangOpts().Modules && OverlayFS != nullptr) { + auto ModuleExpander = std::make_unique<ExpandModularHeadersPPCallbacks>( + &Compiler, OverlayFS); + ModuleExpanderPP = ModuleExpander->getPreprocessor(); + PP->addPPCallbacks(std::move(ModuleExpander)); + } + + for (auto &Check : Checks) { + Check->registerMatchers(&*Finder); + Check->registerPPCallbacks(*SM, PP, ModuleExpanderPP); + } + + std::vector<std::unique_ptr<ASTConsumer>> Consumers; + if (!Checks.empty()) + Consumers.push_back(Finder->newASTConsumer()); + +#if CLANG_TIDY_ENABLE_STATIC_ANALYZER + AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts(); + AnalyzerOptions->CheckersAndPackages = getAnalyzerCheckersAndPackages( + Context, Context.canEnableAnalyzerAlphaCheckers()); + if (!AnalyzerOptions->CheckersAndPackages.empty()) { + setStaticAnalyzerCheckerOpts(Context.getOptions(), *AnalyzerOptions); + AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel; + AnalyzerOptions->AnalysisDiagOpt = PD_NONE; + AnalyzerOptions->AnalyzeNestedBlocks = true; + AnalyzerOptions->eagerlyAssumeBinOpBifurcation = true; + std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer = + ento::CreateAnalysisConsumer(Compiler); + AnalysisConsumer->AddDiagnosticConsumer( + new AnalyzerDiagnosticConsumer(Context)); + Consumers.push_back(std::move(AnalysisConsumer)); + } +#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER + return std::make_unique<ClangTidyASTConsumer>( + std::move(Consumers), std::move(Profiling), std::move(Finder), + std::move(Checks)); +} + +std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() { + std::vector<std::string> CheckNames; + for (const auto &CheckFactory : *CheckFactories) { + if (Context.isCheckEnabled(CheckFactory.getKey())) + CheckNames.emplace_back(CheckFactory.getKey()); + } + +#if CLANG_TIDY_ENABLE_STATIC_ANALYZER + for (const auto &AnalyzerCheck : getAnalyzerCheckersAndPackages( + Context, Context.canEnableAnalyzerAlphaCheckers())) + CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first); +#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER + + llvm::sort(CheckNames); + return CheckNames; +} + +ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() { + ClangTidyOptions::OptionMap Options; + std::vector<std::unique_ptr<ClangTidyCheck>> Checks = + CheckFactories->createChecks(&Context); + for (const auto &Check : Checks) + Check->storeOptions(Options); + return Options; +} + +std::vector<std::string> +getCheckNames(const ClangTidyOptions &Options, + bool AllowEnablingAnalyzerAlphaCheckers) { + clang::tidy::ClangTidyContext Context( + std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(), + Options), + AllowEnablingAnalyzerAlphaCheckers); + ClangTidyASTConsumerFactory Factory(Context); + return Factory.getCheckNames(); +} + +ClangTidyOptions::OptionMap +getCheckOptions(const ClangTidyOptions &Options, + bool AllowEnablingAnalyzerAlphaCheckers) { + clang::tidy::ClangTidyContext Context( + std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(), + Options), + AllowEnablingAnalyzerAlphaCheckers); + ClangTidyASTConsumerFactory Factory(Context); + return Factory.getCheckOptions(); +} + +std::vector<ClangTidyError> +runClangTidy(clang::tidy::ClangTidyContext &Context, + const CompilationDatabase &Compilations, + ArrayRef<std::string> InputFiles, + llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS, + bool ApplyAnyFix, bool EnableCheckProfile, + llvm::StringRef StoreCheckProfile) { + ClangTool Tool(Compilations, InputFiles, + std::make_shared<PCHContainerOperations>(), BaseFS); + + // Add extra arguments passed by the clang-tidy command-line. + ArgumentsAdjuster PerFileExtraArgumentsInserter = + [&Context](const CommandLineArguments &Args, StringRef Filename) { + ClangTidyOptions Opts = Context.getOptionsForFile(Filename); + CommandLineArguments AdjustedArgs = Args; + if (Opts.ExtraArgsBefore) { + auto I = AdjustedArgs.begin(); + if (I != AdjustedArgs.end() && !StringRef(*I).startswith("-")) + ++I; // Skip compiler binary name, if it is there. + AdjustedArgs.insert(I, Opts.ExtraArgsBefore->begin(), + Opts.ExtraArgsBefore->end()); + } + if (Opts.ExtraArgs) + AdjustedArgs.insert(AdjustedArgs.end(), Opts.ExtraArgs->begin(), + Opts.ExtraArgs->end()); + return AdjustedArgs; + }; + + Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter); + Tool.appendArgumentsAdjuster(getStripPluginsAdjuster()); + Context.setEnableProfiling(EnableCheckProfile); + Context.setProfileStoragePrefix(StoreCheckProfile); + + ClangTidyDiagnosticConsumer DiagConsumer(Context, nullptr, true, ApplyAnyFix); + DiagnosticsEngine DE(new DiagnosticIDs(), new DiagnosticOptions(), + &DiagConsumer, /*ShouldOwnClient=*/false); + Context.setDiagnosticsEngine(&DE); + Tool.setDiagnosticConsumer(&DiagConsumer); + + class ActionFactory : public FrontendActionFactory { + public: + ActionFactory(ClangTidyContext &Context, + IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS) + : ConsumerFactory(Context, std::move(BaseFS)) {} + std::unique_ptr<FrontendAction> create() override { + return std::make_unique<Action>(&ConsumerFactory); + } + + bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation, + FileManager *Files, + std::shared_ptr<PCHContainerOperations> PCHContainerOps, + DiagnosticConsumer *DiagConsumer) override { + // Explicitly ask to define __clang_analyzer__ macro. + Invocation->getPreprocessorOpts().SetUpStaticAnalyzer = true; + return FrontendActionFactory::runInvocation( + Invocation, Files, PCHContainerOps, DiagConsumer); + } + + private: + class Action : public ASTFrontendAction { + public: + Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {} + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, + StringRef File) override { + return Factory->createASTConsumer(Compiler, File); + } + + private: + ClangTidyASTConsumerFactory *Factory; + }; + + ClangTidyASTConsumerFactory ConsumerFactory; + }; + + ActionFactory Factory(Context, std::move(BaseFS)); + Tool.run(&Factory); + return DiagConsumer.take(); +} + +void handleErrors(llvm::ArrayRef<ClangTidyError> Errors, + ClangTidyContext &Context, FixBehaviour Fix, + unsigned &WarningsAsErrorsCount, + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS) { + ErrorReporter Reporter(Context, Fix, std::move(BaseFS)); + llvm::vfs::FileSystem &FileSystem = + Reporter.getSourceManager().getFileManager().getVirtualFileSystem(); + auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory(); + if (!InitialWorkingDir) + llvm::report_fatal_error("Cannot get current working path."); + + for (const ClangTidyError &Error : Errors) { + if (!Error.BuildDirectory.empty()) { + // By default, the working directory of file system is the current + // clang-tidy running directory. + // + // Change the directory to the one used during the analysis. + FileSystem.setCurrentWorkingDirectory(Error.BuildDirectory); + } + Reporter.reportDiagnostic(Error); + // Return to the initial directory to correctly resolve next Error. + FileSystem.setCurrentWorkingDirectory(InitialWorkingDir.get()); + } + Reporter.finish(); + WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount(); +} + +void exportReplacements(const llvm::StringRef MainFilePath, + const std::vector<ClangTidyError> &Errors, + raw_ostream &OS) { + TranslationUnitDiagnostics TUD; + TUD.MainSourceFile = std::string(MainFilePath); + for (const auto &Error : Errors) { + tooling::Diagnostic Diag = Error; + TUD.Diagnostics.insert(TUD.Diagnostics.end(), Diag); + } + + yaml::Output YAML(OS); + YAML << TUD; +} + +} // namespace tidy +} // namespace clang |