Path: blob/main/contrib/llvm-project/clang/lib/Tooling/Tooling.cpp
35233 views
//===- Tooling.cpp - Running clang standalone tools -----------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements functions to run clang tools standalone instead9// of running them as a plugin.10//11//===----------------------------------------------------------------------===//1213#include "clang/Tooling/Tooling.h"14#include "clang/Basic/Diagnostic.h"15#include "clang/Basic/DiagnosticIDs.h"16#include "clang/Basic/DiagnosticOptions.h"17#include "clang/Basic/FileManager.h"18#include "clang/Basic/FileSystemOptions.h"19#include "clang/Basic/LLVM.h"20#include "clang/Driver/Compilation.h"21#include "clang/Driver/Driver.h"22#include "clang/Driver/Job.h"23#include "clang/Driver/Options.h"24#include "clang/Driver/Tool.h"25#include "clang/Driver/ToolChain.h"26#include "clang/Frontend/ASTUnit.h"27#include "clang/Frontend/CompilerInstance.h"28#include "clang/Frontend/CompilerInvocation.h"29#include "clang/Frontend/FrontendDiagnostic.h"30#include "clang/Frontend/FrontendOptions.h"31#include "clang/Frontend/TextDiagnosticPrinter.h"32#include "clang/Lex/HeaderSearchOptions.h"33#include "clang/Lex/PreprocessorOptions.h"34#include "clang/Tooling/ArgumentsAdjusters.h"35#include "clang/Tooling/CompilationDatabase.h"36#include "llvm/ADT/ArrayRef.h"37#include "llvm/ADT/IntrusiveRefCntPtr.h"38#include "llvm/ADT/SmallString.h"39#include "llvm/ADT/StringRef.h"40#include "llvm/ADT/Twine.h"41#include "llvm/Option/ArgList.h"42#include "llvm/Option/OptTable.h"43#include "llvm/Option/Option.h"44#include "llvm/Support/Casting.h"45#include "llvm/Support/CommandLine.h"46#include "llvm/Support/Debug.h"47#include "llvm/Support/ErrorHandling.h"48#include "llvm/Support/FileSystem.h"49#include "llvm/Support/MemoryBuffer.h"50#include "llvm/Support/Path.h"51#include "llvm/Support/VirtualFileSystem.h"52#include "llvm/Support/raw_ostream.h"53#include "llvm/TargetParser/Host.h"54#include <cassert>55#include <cstring>56#include <memory>57#include <string>58#include <system_error>59#include <utility>60#include <vector>6162#define DEBUG_TYPE "clang-tooling"6364using namespace clang;65using namespace tooling;6667ToolAction::~ToolAction() = default;6869FrontendActionFactory::~FrontendActionFactory() = default;7071// FIXME: This file contains structural duplication with other parts of the72// code that sets up a compiler to run tools on it, and we should refactor73// it to be based on the same framework.7475/// Builds a clang driver initialized for running clang tools.76static driver::Driver *77newDriver(DiagnosticsEngine *Diagnostics, const char *BinaryName,78IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {79driver::Driver *CompilerDriver =80new driver::Driver(BinaryName, llvm::sys::getDefaultTargetTriple(),81*Diagnostics, "clang LLVM compiler", std::move(VFS));82CompilerDriver->setTitle("clang_based_tool");83return CompilerDriver;84}8586/// Decide whether extra compiler frontend commands can be ignored.87static bool ignoreExtraCC1Commands(const driver::Compilation *Compilation) {88const driver::JobList &Jobs = Compilation->getJobs();89const driver::ActionList &Actions = Compilation->getActions();9091bool OffloadCompilation = false;9293// Jobs and Actions look very different depending on whether the Clang tool94// injected -fsyntax-only or not. Try to handle both cases here.9596for (const auto &Job : Jobs)97if (StringRef(Job.getExecutable()) == "clang-offload-bundler")98OffloadCompilation = true;99100if (Jobs.size() > 1) {101for (auto *A : Actions){102// On MacOSX real actions may end up being wrapped in BindArchAction103if (isa<driver::BindArchAction>(A))104A = *A->input_begin();105if (isa<driver::OffloadAction>(A)) {106// Offload compilation has 2 top-level actions, one (at the front) is107// the original host compilation and the other is offload action108// composed of at least one device compilation. For such case, general109// tooling will consider host-compilation only. For tooling on device110// compilation, device compilation only option, such as111// `--cuda-device-only`, needs specifying.112assert(Actions.size() > 1);113assert(114isa<driver::CompileJobAction>(Actions.front()) ||115// On MacOSX real actions may end up being wrapped in116// BindArchAction.117(isa<driver::BindArchAction>(Actions.front()) &&118isa<driver::CompileJobAction>(*Actions.front()->input_begin())));119OffloadCompilation = true;120break;121}122}123}124125return OffloadCompilation;126}127128namespace clang {129namespace tooling {130131const llvm::opt::ArgStringList *132getCC1Arguments(DiagnosticsEngine *Diagnostics,133driver::Compilation *Compilation) {134const driver::JobList &Jobs = Compilation->getJobs();135136auto IsCC1Command = [](const driver::Command &Cmd) {137return StringRef(Cmd.getCreator().getName()) == "clang";138};139140auto IsSrcFile = [](const driver::InputInfo &II) {141return isSrcFile(II.getType());142};143144llvm::SmallVector<const driver::Command *, 1> CC1Jobs;145for (const driver::Command &Job : Jobs)146if (IsCC1Command(Job) && llvm::all_of(Job.getInputInfos(), IsSrcFile))147CC1Jobs.push_back(&Job);148149// If there are no jobs for source files, try checking again for a single job150// with any file type. This accepts a preprocessed file as input.151if (CC1Jobs.empty())152for (const driver::Command &Job : Jobs)153if (IsCC1Command(Job))154CC1Jobs.push_back(&Job);155156if (CC1Jobs.empty() ||157(CC1Jobs.size() > 1 && !ignoreExtraCC1Commands(Compilation))) {158SmallString<256> error_msg;159llvm::raw_svector_ostream error_stream(error_msg);160Jobs.Print(error_stream, "; ", true);161Diagnostics->Report(diag::err_fe_expected_compiler_job)162<< error_stream.str();163return nullptr;164}165166return &CC1Jobs[0]->getArguments();167}168169/// Returns a clang build invocation initialized from the CC1 flags.170CompilerInvocation *newInvocation(DiagnosticsEngine *Diagnostics,171ArrayRef<const char *> CC1Args,172const char *const BinaryName) {173assert(!CC1Args.empty() && "Must at least contain the program name!");174CompilerInvocation *Invocation = new CompilerInvocation;175CompilerInvocation::CreateFromArgs(*Invocation, CC1Args, *Diagnostics,176BinaryName);177Invocation->getFrontendOpts().DisableFree = false;178Invocation->getCodeGenOpts().DisableFree = false;179return Invocation;180}181182bool runToolOnCode(std::unique_ptr<FrontendAction> ToolAction,183const Twine &Code, const Twine &FileName,184std::shared_ptr<PCHContainerOperations> PCHContainerOps) {185return runToolOnCodeWithArgs(std::move(ToolAction), Code,186std::vector<std::string>(), FileName,187"clang-tool", std::move(PCHContainerOps));188}189190} // namespace tooling191} // namespace clang192193static std::vector<std::string>194getSyntaxOnlyToolArgs(const Twine &ToolName,195const std::vector<std::string> &ExtraArgs,196StringRef FileName) {197std::vector<std::string> Args;198Args.push_back(ToolName.str());199Args.push_back("-fsyntax-only");200Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());201Args.push_back(FileName.str());202return Args;203}204205namespace clang {206namespace tooling {207208bool runToolOnCodeWithArgs(209std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,210llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,211const std::vector<std::string> &Args, const Twine &FileName,212const Twine &ToolName,213std::shared_ptr<PCHContainerOperations> PCHContainerOps) {214SmallString<16> FileNameStorage;215StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);216217llvm::IntrusiveRefCntPtr<FileManager> Files(218new FileManager(FileSystemOptions(), VFS));219ArgumentsAdjuster Adjuster = getClangStripDependencyFileAdjuster();220ToolInvocation Invocation(221getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileNameRef), FileNameRef),222std::move(ToolAction), Files.get(), std::move(PCHContainerOps));223return Invocation.run();224}225226bool runToolOnCodeWithArgs(227std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,228const std::vector<std::string> &Args, const Twine &FileName,229const Twine &ToolName,230std::shared_ptr<PCHContainerOperations> PCHContainerOps,231const FileContentMappings &VirtualMappedFiles) {232llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(233new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));234llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(235new llvm::vfs::InMemoryFileSystem);236OverlayFileSystem->pushOverlay(InMemoryFileSystem);237238SmallString<1024> CodeStorage;239InMemoryFileSystem->addFile(FileName, 0,240llvm::MemoryBuffer::getMemBuffer(241Code.toNullTerminatedStringRef(CodeStorage)));242243for (auto &FilenameWithContent : VirtualMappedFiles) {244InMemoryFileSystem->addFile(245FilenameWithContent.first, 0,246llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));247}248249return runToolOnCodeWithArgs(std::move(ToolAction), Code, OverlayFileSystem,250Args, FileName, ToolName);251}252253llvm::Expected<std::string> getAbsolutePath(llvm::vfs::FileSystem &FS,254StringRef File) {255StringRef RelativePath(File);256// FIXME: Should '.\\' be accepted on Win32?257RelativePath.consume_front("./");258259SmallString<1024> AbsolutePath = RelativePath;260if (auto EC = FS.makeAbsolute(AbsolutePath))261return llvm::errorCodeToError(EC);262llvm::sys::path::native(AbsolutePath);263return std::string(AbsolutePath);264}265266std::string getAbsolutePath(StringRef File) {267return llvm::cantFail(getAbsolutePath(*llvm::vfs::getRealFileSystem(), File));268}269270void addTargetAndModeForProgramName(std::vector<std::string> &CommandLine,271StringRef InvokedAs) {272if (CommandLine.empty() || InvokedAs.empty())273return;274const auto &Table = driver::getDriverOptTable();275// --target=X276StringRef TargetOPT =277Table.getOption(driver::options::OPT_target).getPrefixedName();278// -target X279StringRef TargetOPTLegacy =280Table.getOption(driver::options::OPT_target_legacy_spelling)281.getPrefixedName();282// --driver-mode=X283StringRef DriverModeOPT =284Table.getOption(driver::options::OPT_driver_mode).getPrefixedName();285auto TargetMode =286driver::ToolChain::getTargetAndModeFromProgramName(InvokedAs);287// No need to search for target args if we don't have a target/mode to insert.288bool ShouldAddTarget = TargetMode.TargetIsValid;289bool ShouldAddMode = TargetMode.DriverMode != nullptr;290// Skip CommandLine[0].291for (auto Token = ++CommandLine.begin(); Token != CommandLine.end();292++Token) {293StringRef TokenRef(*Token);294ShouldAddTarget = ShouldAddTarget && !TokenRef.starts_with(TargetOPT) &&295TokenRef != TargetOPTLegacy;296ShouldAddMode = ShouldAddMode && !TokenRef.starts_with(DriverModeOPT);297}298if (ShouldAddMode) {299CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);300}301if (ShouldAddTarget) {302CommandLine.insert(++CommandLine.begin(),303(TargetOPT + TargetMode.TargetPrefix).str());304}305}306307void addExpandedResponseFiles(std::vector<std::string> &CommandLine,308llvm::StringRef WorkingDir,309llvm::cl::TokenizerCallback Tokenizer,310llvm::vfs::FileSystem &FS) {311bool SeenRSPFile = false;312llvm::SmallVector<const char *, 20> Argv;313Argv.reserve(CommandLine.size());314for (auto &Arg : CommandLine) {315Argv.push_back(Arg.c_str());316if (!Arg.empty())317SeenRSPFile |= Arg.front() == '@';318}319if (!SeenRSPFile)320return;321llvm::BumpPtrAllocator Alloc;322llvm::cl::ExpansionContext ECtx(Alloc, Tokenizer);323llvm::Error Err =324ECtx.setVFS(&FS).setCurrentDir(WorkingDir).expandResponseFiles(Argv);325if (Err)326llvm::errs() << Err;327// Don't assign directly, Argv aliases CommandLine.328std::vector<std::string> ExpandedArgv(Argv.begin(), Argv.end());329CommandLine = std::move(ExpandedArgv);330}331332} // namespace tooling333} // namespace clang334335namespace {336337class SingleFrontendActionFactory : public FrontendActionFactory {338std::unique_ptr<FrontendAction> Action;339340public:341SingleFrontendActionFactory(std::unique_ptr<FrontendAction> Action)342: Action(std::move(Action)) {}343344std::unique_ptr<FrontendAction> create() override {345return std::move(Action);346}347};348349} // namespace350351ToolInvocation::ToolInvocation(352std::vector<std::string> CommandLine, ToolAction *Action,353FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)354: CommandLine(std::move(CommandLine)), Action(Action), OwnsAction(false),355Files(Files), PCHContainerOps(std::move(PCHContainerOps)) {}356357ToolInvocation::ToolInvocation(358std::vector<std::string> CommandLine,359std::unique_ptr<FrontendAction> FAction, FileManager *Files,360std::shared_ptr<PCHContainerOperations> PCHContainerOps)361: CommandLine(std::move(CommandLine)),362Action(new SingleFrontendActionFactory(std::move(FAction))),363OwnsAction(true), Files(Files),364PCHContainerOps(std::move(PCHContainerOps)) {}365366ToolInvocation::~ToolInvocation() {367if (OwnsAction)368delete Action;369}370371bool ToolInvocation::run() {372llvm::opt::ArgStringList Argv;373for (const std::string &Str : CommandLine)374Argv.push_back(Str.c_str());375const char *const BinaryName = Argv[0];376377// Parse diagnostic options from the driver command-line only if none were378// explicitly set.379IntrusiveRefCntPtr<DiagnosticOptions> ParsedDiagOpts;380DiagnosticOptions *DiagOpts = this->DiagOpts;381if (!DiagOpts) {382ParsedDiagOpts = CreateAndPopulateDiagOpts(Argv);383DiagOpts = &*ParsedDiagOpts;384}385386TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), DiagOpts);387IntrusiveRefCntPtr<DiagnosticsEngine> Diagnostics =388CompilerInstance::createDiagnostics(389&*DiagOpts, DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);390// Although `Diagnostics` are used only for command-line parsing, the custom391// `DiagConsumer` might expect a `SourceManager` to be present.392SourceManager SrcMgr(*Diagnostics, *Files);393Diagnostics->setSourceManager(&SrcMgr);394395// We already have a cc1, just create an invocation.396if (CommandLine.size() >= 2 && CommandLine[1] == "-cc1") {397ArrayRef<const char *> CC1Args = ArrayRef(Argv).drop_front();398std::unique_ptr<CompilerInvocation> Invocation(399newInvocation(&*Diagnostics, CC1Args, BinaryName));400if (Diagnostics->hasErrorOccurred())401return false;402return Action->runInvocation(std::move(Invocation), Files,403std::move(PCHContainerOps), DiagConsumer);404}405406const std::unique_ptr<driver::Driver> Driver(407newDriver(&*Diagnostics, BinaryName, &Files->getVirtualFileSystem()));408// The "input file not found" diagnostics from the driver are useful.409// The driver is only aware of the VFS working directory, but some clients410// change this at the FileManager level instead.411// In this case the checks have false positives, so skip them.412if (!Files->getFileSystemOpts().WorkingDir.empty())413Driver->setCheckInputsExist(false);414const std::unique_ptr<driver::Compilation> Compilation(415Driver->BuildCompilation(llvm::ArrayRef(Argv)));416if (!Compilation)417return false;418const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(419&*Diagnostics, Compilation.get());420if (!CC1Args)421return false;422std::unique_ptr<CompilerInvocation> Invocation(423newInvocation(&*Diagnostics, *CC1Args, BinaryName));424return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),425std::move(PCHContainerOps));426}427428bool ToolInvocation::runInvocation(429const char *BinaryName, driver::Compilation *Compilation,430std::shared_ptr<CompilerInvocation> Invocation,431std::shared_ptr<PCHContainerOperations> PCHContainerOps) {432// Show the invocation, with -v.433if (Invocation->getHeaderSearchOpts().Verbose) {434llvm::errs() << "clang Invocation:\n";435Compilation->getJobs().Print(llvm::errs(), "\n", true);436llvm::errs() << "\n";437}438439return Action->runInvocation(std::move(Invocation), Files,440std::move(PCHContainerOps), DiagConsumer);441}442443bool FrontendActionFactory::runInvocation(444std::shared_ptr<CompilerInvocation> Invocation, FileManager *Files,445std::shared_ptr<PCHContainerOperations> PCHContainerOps,446DiagnosticConsumer *DiagConsumer) {447// Create a compiler instance to handle the actual work.448CompilerInstance Compiler(std::move(PCHContainerOps));449Compiler.setInvocation(std::move(Invocation));450Compiler.setFileManager(Files);451452// The FrontendAction can have lifetime requirements for Compiler or its453// members, and we need to ensure it's deleted earlier than Compiler. So we454// pass it to an std::unique_ptr declared after the Compiler variable.455std::unique_ptr<FrontendAction> ScopedToolAction(create());456457// Create the compiler's actual diagnostics engine.458Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);459if (!Compiler.hasDiagnostics())460return false;461462Compiler.createSourceManager(*Files);463464const bool Success = Compiler.ExecuteAction(*ScopedToolAction);465466Files->clearStatCache();467return Success;468}469470ClangTool::ClangTool(const CompilationDatabase &Compilations,471ArrayRef<std::string> SourcePaths,472std::shared_ptr<PCHContainerOperations> PCHContainerOps,473IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,474IntrusiveRefCntPtr<FileManager> Files)475: Compilations(Compilations), SourcePaths(SourcePaths),476PCHContainerOps(std::move(PCHContainerOps)),477OverlayFileSystem(new llvm::vfs::OverlayFileSystem(std::move(BaseFS))),478InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),479Files(Files ? Files480: new FileManager(FileSystemOptions(), OverlayFileSystem)) {481OverlayFileSystem->pushOverlay(InMemoryFileSystem);482appendArgumentsAdjuster(getClangStripOutputAdjuster());483appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());484appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());485if (Files)486Files->setVirtualFileSystem(OverlayFileSystem);487}488489ClangTool::~ClangTool() = default;490491void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {492MappedFileContents.push_back(std::make_pair(FilePath, Content));493}494495void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) {496ArgsAdjuster = combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));497}498499void ClangTool::clearArgumentsAdjusters() {500ArgsAdjuster = nullptr;501}502503static void injectResourceDir(CommandLineArguments &Args, const char *Argv0,504void *MainAddr) {505// Allow users to override the resource dir.506for (StringRef Arg : Args)507if (Arg.starts_with("-resource-dir"))508return;509510// If there's no override in place add our resource dir.511Args = getInsertArgumentAdjuster(512("-resource-dir=" + CompilerInvocation::GetResourcesPath(Argv0, MainAddr))513.c_str())(Args, "");514}515516int ClangTool::run(ToolAction *Action) {517// Exists solely for the purpose of lookup of the resource path.518// This just needs to be some symbol in the binary.519static int StaticSymbol;520521// First insert all absolute paths into the in-memory VFS. These are global522// for all compile commands.523if (SeenWorkingDirectories.insert("/").second)524for (const auto &MappedFile : MappedFileContents)525if (llvm::sys::path::is_absolute(MappedFile.first))526InMemoryFileSystem->addFile(527MappedFile.first, 0,528llvm::MemoryBuffer::getMemBuffer(MappedFile.second));529530bool ProcessingFailed = false;531bool FileSkipped = false;532// Compute all absolute paths before we run any actions, as those will change533// the working directory.534std::vector<std::string> AbsolutePaths;535AbsolutePaths.reserve(SourcePaths.size());536for (const auto &SourcePath : SourcePaths) {537auto AbsPath = getAbsolutePath(*OverlayFileSystem, SourcePath);538if (!AbsPath) {539llvm::errs() << "Skipping " << SourcePath540<< ". Error while getting an absolute path: "541<< llvm::toString(AbsPath.takeError()) << "\n";542continue;543}544AbsolutePaths.push_back(std::move(*AbsPath));545}546547// Remember the working directory in case we need to restore it.548std::string InitialWorkingDir;549if (auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {550InitialWorkingDir = std::move(*CWD);551} else {552llvm::errs() << "Could not get working directory: "553<< CWD.getError().message() << "\n";554}555556size_t NumOfTotalFiles = AbsolutePaths.size();557unsigned ProcessedFileCounter = 0;558for (llvm::StringRef File : AbsolutePaths) {559// Currently implementations of CompilationDatabase::getCompileCommands can560// change the state of the file system (e.g. prepare generated headers), so561// this method needs to run right before we invoke the tool, as the next562// file may require a different (incompatible) state of the file system.563//564// FIXME: Make the compilation database interface more explicit about the565// requirements to the order of invocation of its members.566std::vector<CompileCommand> CompileCommandsForFile =567Compilations.getCompileCommands(File);568if (CompileCommandsForFile.empty()) {569llvm::errs() << "Skipping " << File << ". Compile command not found.\n";570FileSkipped = true;571continue;572}573for (CompileCommand &CompileCommand : CompileCommandsForFile) {574// FIXME: chdir is thread hostile; on the other hand, creating the same575// behavior as chdir is complex: chdir resolves the path once, thus576// guaranteeing that all subsequent relative path operations work577// on the same path the original chdir resulted in. This makes a578// difference for example on network filesystems, where symlinks might be579// switched during runtime of the tool. Fixing this depends on having a580// file system abstraction that allows openat() style interactions.581if (OverlayFileSystem->setCurrentWorkingDirectory(582CompileCommand.Directory))583llvm::report_fatal_error("Cannot chdir into \"" +584Twine(CompileCommand.Directory) + "\"!");585586// Now fill the in-memory VFS with the relative file mappings so it will587// have the correct relative paths. We never remove mappings but that588// should be fine.589if (SeenWorkingDirectories.insert(CompileCommand.Directory).second)590for (const auto &MappedFile : MappedFileContents)591if (!llvm::sys::path::is_absolute(MappedFile.first))592InMemoryFileSystem->addFile(593MappedFile.first, 0,594llvm::MemoryBuffer::getMemBuffer(MappedFile.second));595596std::vector<std::string> CommandLine = CompileCommand.CommandLine;597if (ArgsAdjuster)598CommandLine = ArgsAdjuster(CommandLine, CompileCommand.Filename);599assert(!CommandLine.empty());600601// Add the resource dir based on the binary of this tool. argv[0] in the602// compilation database may refer to a different compiler and we want to603// pick up the very same standard library that compiler is using. The604// builtin headers in the resource dir need to match the exact clang605// version the tool is using.606// FIXME: On linux, GetMainExecutable is independent of the value of the607// first argument, thus allowing ClangTool and runToolOnCode to just608// pass in made-up names here. Make sure this works on other platforms.609injectResourceDir(CommandLine, "clang_tool", &StaticSymbol);610611// FIXME: We need a callback mechanism for the tool writer to output a612// customized message for each file.613if (NumOfTotalFiles > 1)614llvm::errs() << "[" + std::to_string(++ProcessedFileCounter) + "/" +615std::to_string(NumOfTotalFiles) +616"] Processing file " + File617<< ".\n";618ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),619PCHContainerOps);620Invocation.setDiagnosticConsumer(DiagConsumer);621622if (!Invocation.run()) {623// FIXME: Diagnostics should be used instead.624if (PrintErrorMessage)625llvm::errs() << "Error while processing " << File << ".\n";626ProcessingFailed = true;627}628}629}630631if (!InitialWorkingDir.empty()) {632if (auto EC =633OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))634llvm::errs() << "Error when trying to restore working dir: "635<< EC.message() << "\n";636}637return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);638}639640namespace {641642class ASTBuilderAction : public ToolAction {643std::vector<std::unique_ptr<ASTUnit>> &ASTs;644645public:646ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}647648bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,649FileManager *Files,650std::shared_ptr<PCHContainerOperations> PCHContainerOps,651DiagnosticConsumer *DiagConsumer) override {652std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(653Invocation, std::move(PCHContainerOps),654CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts(),655DiagConsumer,656/*ShouldOwnClient=*/false),657Files);658if (!AST)659return false;660661ASTs.push_back(std::move(AST));662return true;663}664};665666} // namespace667668int ClangTool::buildASTs(std::vector<std::unique_ptr<ASTUnit>> &ASTs) {669ASTBuilderAction Action(ASTs);670return run(&Action);671}672673void ClangTool::setPrintErrorMessage(bool PrintErrorMessage) {674this->PrintErrorMessage = PrintErrorMessage;675}676677namespace clang {678namespace tooling {679680std::unique_ptr<ASTUnit>681buildASTFromCode(StringRef Code, StringRef FileName,682std::shared_ptr<PCHContainerOperations> PCHContainerOps) {683return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName,684"clang-tool", std::move(PCHContainerOps));685}686687std::unique_ptr<ASTUnit> buildASTFromCodeWithArgs(688StringRef Code, const std::vector<std::string> &Args, StringRef FileName,689StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,690ArgumentsAdjuster Adjuster, const FileContentMappings &VirtualMappedFiles,691DiagnosticConsumer *DiagConsumer) {692std::vector<std::unique_ptr<ASTUnit>> ASTs;693ASTBuilderAction Action(ASTs);694llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(695new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));696llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(697new llvm::vfs::InMemoryFileSystem);698OverlayFileSystem->pushOverlay(InMemoryFileSystem);699llvm::IntrusiveRefCntPtr<FileManager> Files(700new FileManager(FileSystemOptions(), OverlayFileSystem));701702ToolInvocation Invocation(703getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileName), FileName),704&Action, Files.get(), std::move(PCHContainerOps));705Invocation.setDiagnosticConsumer(DiagConsumer);706707InMemoryFileSystem->addFile(FileName, 0,708llvm::MemoryBuffer::getMemBufferCopy(Code));709for (auto &FilenameWithContent : VirtualMappedFiles) {710InMemoryFileSystem->addFile(711FilenameWithContent.first, 0,712llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));713}714715if (!Invocation.run())716return nullptr;717718assert(ASTs.size() == 1);719return std::move(ASTs[0]);720}721722} // namespace tooling723} // namespace clang724725726