Clang Project

clang_source_code/lib/Tooling/Tooling.cpp
1//===- Tooling.cpp - Running clang standalone tools -----------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9//  This file implements functions to run clang tools standalone instead
10//  of running them as a plugin.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/Tooling/Tooling.h"
15#include "clang/Basic/Diagnostic.h"
16#include "clang/Basic/DiagnosticIDs.h"
17#include "clang/Basic/DiagnosticOptions.h"
18#include "clang/Basic/FileManager.h"
19#include "clang/Basic/FileSystemOptions.h"
20#include "clang/Basic/LLVM.h"
21#include "clang/Driver/Compilation.h"
22#include "clang/Driver/Driver.h"
23#include "clang/Driver/Job.h"
24#include "clang/Driver/Options.h"
25#include "clang/Driver/Tool.h"
26#include "clang/Driver/ToolChain.h"
27#include "clang/Frontend/ASTUnit.h"
28#include "clang/Frontend/CompilerInstance.h"
29#include "clang/Frontend/CompilerInvocation.h"
30#include "clang/Frontend/FrontendDiagnostic.h"
31#include "clang/Frontend/FrontendOptions.h"
32#include "clang/Frontend/TextDiagnosticPrinter.h"
33#include "clang/Lex/HeaderSearchOptions.h"
34#include "clang/Lex/PreprocessorOptions.h"
35#include "clang/Tooling/ArgumentsAdjusters.h"
36#include "clang/Tooling/CompilationDatabase.h"
37#include "llvm/ADT/ArrayRef.h"
38#include "llvm/ADT/IntrusiveRefCntPtr.h"
39#include "llvm/ADT/SmallString.h"
40#include "llvm/ADT/StringRef.h"
41#include "llvm/ADT/Twine.h"
42#include "llvm/Option/ArgList.h"
43#include "llvm/Option/OptTable.h"
44#include "llvm/Option/Option.h"
45#include "llvm/Support/Casting.h"
46#include "llvm/Support/Debug.h"
47#include "llvm/Support/ErrorHandling.h"
48#include "llvm/Support/FileSystem.h"
49#include "llvm/Support/Host.h"
50#include "llvm/Support/MemoryBuffer.h"
51#include "llvm/Support/Path.h"
52#include "llvm/Support/VirtualFileSystem.h"
53#include "llvm/Support/raw_ostream.h"
54#include <cassert>
55#include <cstring>
56#include <memory>
57#include <string>
58#include <system_error>
59#include <utility>
60#include <vector>
61
62#define DEBUG_TYPE "clang-tooling"
63
64using namespace clang;
65using namespace tooling;
66
67ToolAction::~ToolAction() = default;
68
69FrontendActionFactory::~FrontendActionFactory() = default;
70
71// FIXME: This file contains structural duplication with other parts of the
72// code that sets up a compiler to run tools on it, and we should refactor
73// it to be based on the same framework.
74
75/// Builds a clang driver initialized for running clang tools.
76static driver::Driver *
77newDriver(DiagnosticsEngine *Diagnosticsconst char *BinaryName,
78          IntrusiveRefCntPtr<llvm::vfs::FileSystemVFS) {
79  driver::Driver *CompilerDriver =
80      new driver::Driver(BinaryName, llvm::sys::getDefaultTargetTriple(),
81                         *Diagnostics, std::move(VFS));
82  CompilerDriver->setTitle("clang_based_tool");
83  return CompilerDriver;
84}
85
86/// Retrieves the clang CC1 specific flags out of the compilation's jobs.
87///
88/// Returns nullptr on error.
89static const llvm::opt::ArgStringList *getCC1Arguments(
90    DiagnosticsEngine *Diagnostics, driver::Compilation *Compilation) {
91  // We expect to get back exactly one Command job, if we didn't something
92  // failed. Extract that job from the Compilation.
93  const driver::JobList &Jobs = Compilation->getJobs();
94  if (Jobs.size() != 1 || !isa<driver::Command>(*Jobs.begin())) {
95    SmallString<256error_msg;
96    llvm::raw_svector_ostream error_stream(error_msg);
97    Jobs.Print(error_stream, "; "true);
98    Diagnostics->Report(diag::err_fe_expected_compiler_job)
99        << error_stream.str();
100    return nullptr;
101  }
102
103  // The one job we find should be to invoke clang again.
104  const auto &Cmd = cast<driver::Command>(*Jobs.begin());
105  if (StringRef(Cmd.getCreator().getName()) != "clang") {
106    Diagnostics->Report(diag::err_fe_expected_clang_command);
107    return nullptr;
108  }
109
110  return &Cmd.getArguments();
111}
112
113namespace clang {
114namespace tooling {
115
116/// Returns a clang build invocation initialized from the CC1 flags.
117CompilerInvocation *newInvocation(
118    DiagnosticsEngine *Diagnosticsconst llvm::opt::ArgStringList &CC1Args) {
119   (0) . __assert_fail ("!CC1Args.empty() && \"Must at least contain the program name!\"", "/home/seafit/code_projects/clang_source/clang/lib/Tooling/Tooling.cpp", 119, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(!CC1Args.empty() && "Must at least contain the program name!");
120  CompilerInvocation *Invocation = new CompilerInvocation;
121  CompilerInvocation::CreateFromArgs(
122      *Invocation, CC1Args.data() + 1, CC1Args.data() + CC1Args.size(),
123      *Diagnostics);
124  Invocation->getFrontendOpts().DisableFree = false;
125  Invocation->getCodeGenOpts().DisableFree = false;
126  return Invocation;
127}
128
129bool runToolOnCode(FrontendAction *ToolActionconst Twine &Code,
130                   const Twine &FileName,
131                   std::shared_ptr<PCHContainerOperationsPCHContainerOps) {
132  return runToolOnCodeWithArgs(ToolAction, Code, std::vector<std::string>(),
133                               FileName, "clang-tool",
134                               std::move(PCHContainerOps));
135}
136
137// namespace tooling
138// namespace clang
139
140static std::vector<std::string>
141getSyntaxOnlyToolArgs(const Twine &ToolName,
142                      const std::vector<std::string> &ExtraArgs,
143                      StringRef FileName) {
144  std::vector<std::stringArgs;
145  Args.push_back(ToolName.str());
146  Args.push_back("-fsyntax-only");
147  Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
148  Args.push_back(FileName.str());
149  return Args;
150}
151
152namespace clang {
153namespace tooling {
154
155bool runToolOnCodeWithArgs(
156    FrontendAction *ToolActionconst Twine &Code,
157    llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystemVFS,
158    const std::vector<std::string> &Argsconst Twine &FileName,
159    const Twine &ToolName,
160    std::shared_ptr<PCHContainerOperationsPCHContainerOps) {
161  SmallString<16FileNameStorage;
162  StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
163
164  llvm::IntrusiveRefCntPtr<FileManagerFiles(
165      new FileManager(FileSystemOptions(), VFS));
166  ArgumentsAdjuster Adjuster = getClangStripDependencyFileAdjuster();
167  ToolInvocation Invocation(
168      getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileNameRef), FileNameRef),
169      ToolAction, Files.get(),
170      std::move(PCHContainerOps));
171  return Invocation.run();
172}
173
174bool runToolOnCodeWithArgs(
175    FrontendAction *ToolActionconst Twine &Code,
176    const std::vector<std::string> &Argsconst Twine &FileName,
177    const Twine &ToolName,
178    std::shared_ptr<PCHContainerOperationsPCHContainerOps,
179    const FileContentMappings &VirtualMappedFiles) {
180  llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
181      new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
182  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
183      new llvm::vfs::InMemoryFileSystem);
184  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
185
186  SmallString<1024> CodeStorage;
187  InMemoryFileSystem->addFile(FileName, 0,
188                              llvm::MemoryBuffer::getMemBuffer(
189                                  Code.toNullTerminatedStringRef(CodeStorage)));
190
191  for (auto &FilenameWithContent : VirtualMappedFiles) {
192    InMemoryFileSystem->addFile(
193        FilenameWithContent.first, 0,
194        llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
195  }
196
197  return runToolOnCodeWithArgs(ToolAction, Code, OverlayFileSystem, Args,
198                               FileName, ToolName);
199}
200
201llvm::Expected<std::string> getAbsolutePath(llvm::vfs::FileSystem &FS,
202                                            StringRef File) {
203  StringRef RelativePath(File);
204  // FIXME: Should '.\\' be accepted on Win32?
205  if (RelativePath.startswith("./")) {
206    RelativePath = RelativePath.substr(strlen("./"));
207  }
208
209  SmallString<1024> AbsolutePath = RelativePath;
210  if (auto EC = FS.makeAbsolute(AbsolutePath))
211    return llvm::errorCodeToError(EC);
212  llvm::sys::path::native(AbsolutePath);
213  return AbsolutePath.str();
214}
215
216std::string getAbsolutePath(StringRef File) {
217  return llvm::cantFail(getAbsolutePath(*llvm::vfs::getRealFileSystem(), File));
218}
219
220void addTargetAndModeForProgramName(std::vector<std::string> &CommandLine,
221                                    StringRef InvokedAs) {
222  if (!CommandLine.empty() && !InvokedAs.empty()) {
223    bool AlreadyHasTarget = false;
224    bool AlreadyHasMode = false;
225    // Skip CommandLine[0].
226    for (auto Token = ++CommandLine.begin(); Token != CommandLine.end();
227         ++Token) {
228      StringRef TokenRef(*Token);
229      AlreadyHasTarget |=
230          (TokenRef == "-target" || TokenRef.startswith("-target="));
231      AlreadyHasMode |= (TokenRef == "--driver-mode" ||
232                         TokenRef.startswith("--driver-mode="));
233    }
234    auto TargetMode =
235        driver::ToolChain::getTargetAndModeFromProgramName(InvokedAs);
236    if (!AlreadyHasMode && TargetMode.DriverMode) {
237      CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
238    }
239    if (!AlreadyHasTarget && TargetMode.TargetIsValid) {
240      CommandLine.insert(++CommandLine.begin(), {"-target",
241                                                 TargetMode.TargetPrefix});
242    }
243  }
244}
245
246// namespace tooling
247// namespace clang
248
249namespace {
250
251class SingleFrontendActionFactory : public FrontendActionFactory {
252  FrontendAction *Action;
253
254public:
255  SingleFrontendActionFactory(FrontendAction *Action) : Action(Action) {}
256
257  FrontendAction *create() override { return Action; }
258};
259
260// namespace
261
262ToolInvocation::ToolInvocation(
263    std::vector<std::stringCommandLineToolAction *Action,
264    FileManager *Filesstd::shared_ptr<PCHContainerOperationsPCHContainerOps)
265    : CommandLine(std::move(CommandLine)), Action(Action), OwnsAction(false),
266      Files(Files), PCHContainerOps(std::move(PCHContainerOps)) {}
267
268ToolInvocation::ToolInvocation(
269    std::vector<std::stringCommandLineFrontendAction *FAction,
270    FileManager *Filesstd::shared_ptr<PCHContainerOperationsPCHContainerOps)
271    : CommandLine(std::move(CommandLine)),
272      Action(new SingleFrontendActionFactory(FAction)), OwnsAction(true),
273      Files(Files), PCHContainerOps(std::move(PCHContainerOps)) {}
274
275ToolInvocation::~ToolInvocation() {
276  if (OwnsAction)
277    delete Action;
278}
279
280void ToolInvocation::mapVirtualFile(StringRef FilePathStringRef Content) {
281  SmallString<1024PathStorage;
282  llvm::sys::path::native(FilePath, PathStorage);
283  MappedFileContents[PathStorage] = Content;
284}
285
286bool ToolInvocation::run() {
287  std::vector<const char*> Argv;
288  for (const std::string &Str : CommandLine)
289    Argv.push_back(Str.c_str());
290  const char *const BinaryName = Argv[0];
291  IntrusiveRefCntPtr<DiagnosticOptionsDiagOpts = new DiagnosticOptions();
292  unsigned MissingArgIndexMissingArgCount;
293  std::unique_ptr<llvm::opt::OptTableOpts = driver::createDriverOptTable();
294  llvm::opt::InputArgList ParsedArgs = Opts->ParseArgs(
295      ArrayRef<const char *>(Argv).slice(1), MissingArgIndex, MissingArgCount);
296  ParseDiagnosticArgs(*DiagOpts, ParsedArgs);
297  TextDiagnosticPrinter DiagnosticPrinter(
298      llvm::errs(), &*DiagOpts);
299  DiagnosticsEngine Diagnostics(
300      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
301      DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);
302
303  const std::unique_ptr<driver::DriverDriver(
304      newDriver(&DiagnosticsBinaryName, &Files->getVirtualFileSystem()));
305  // The "input file not found" diagnostics from the driver are useful.
306  // The driver is only aware of the VFS working directory, but some clients
307  // change this at the FileManager level instead.
308  // In this case the checks have false positives, so skip them.
309  if (!Files->getFileSystemOpts().WorkingDir.empty())
310    Driver->setCheckInputsExist(false);
311  const std::unique_ptr<driver::CompilationCompilation(
312      Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
313  if (!Compilation)
314    return false;
315  const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
316      &Diagnostics, Compilation.get());
317  if (!CC1Args)
318    return false;
319  std::unique_ptr<CompilerInvocationInvocation(
320      newInvocation(&Diagnostics, *CC1Args));
321  // FIXME: remove this when all users have migrated!
322  for (const auto &It : MappedFileContents) {
323    // Inject the code as the given file name into the preprocessor options.
324    std::unique_ptr<llvm::MemoryBuffer> Input =
325        llvm::MemoryBuffer::getMemBuffer(It.getValue());
326    Invocation->getPreprocessorOpts().addRemappedFile(It.getKey(),
327                                                      Input.release());
328  }
329  return runInvocation(BinaryNameCompilation.get(), std::move(Invocation),
330                       std::move(PCHContainerOps));
331}
332
333bool ToolInvocation::runInvocation(
334    const char *BinaryNamedriver::Compilation *Compilation,
335    std::shared_ptr<CompilerInvocationInvocation,
336    std::shared_ptr<PCHContainerOperationsPCHContainerOps) {
337  // Show the invocation, with -v.
338  if (Invocation->getHeaderSearchOpts().Verbose) {
339    llvm::errs() << "clang Invocation:\n";
340    Compilation->getJobs().Print(llvm::errs(), "\n"true);
341    llvm::errs() << "\n";
342  }
343
344  return Action->runInvocation(std::move(Invocation), Files,
345                               std::move(PCHContainerOps), DiagConsumer);
346}
347
348bool FrontendActionFactory::runInvocation(
349    std::shared_ptr<CompilerInvocationInvocationFileManager *Files,
350    std::shared_ptr<PCHContainerOperationsPCHContainerOps,
351    DiagnosticConsumer *DiagConsumer) {
352  // Create a compiler instance to handle the actual work.
353  CompilerInstance Compiler(std::move(PCHContainerOps));
354  Compiler.setInvocation(std::move(Invocation));
355  Compiler.setFileManager(Files);
356
357  // The FrontendAction can have lifetime requirements for Compiler or its
358  // members, and we need to ensure it's deleted earlier than Compiler. So we
359  // pass it to an std::unique_ptr declared after the Compiler variable.
360  std::unique_ptr<FrontendActionScopedToolAction(create());
361
362  // Create the compiler's actual diagnostics engine.
363  Compiler.createDiagnostics(DiagConsumer/*ShouldOwnClient=*/false);
364  if (!Compiler.hasDiagnostics())
365    return false;
366
367  Compiler.createSourceManager(*Files);
368
369  const bool Success = Compiler.ExecuteAction(*ScopedToolAction);
370
371  Files->clearStatCache();
372  return Success;
373}
374
375ClangTool::ClangTool(const CompilationDatabase &Compilations,
376                     ArrayRef<std::stringSourcePaths,
377                     std::shared_ptr<PCHContainerOperationsPCHContainerOps,
378                     IntrusiveRefCntPtr<llvm::vfs::FileSystemBaseFS)
379    : Compilations(Compilations), SourcePaths(SourcePaths),
380      PCHContainerOps(std::move(PCHContainerOps)),
381      OverlayFileSystem(new llvm::vfs::OverlayFileSystem(std::move(BaseFS))),
382      InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
383      Files(new FileManager(FileSystemOptions(), OverlayFileSystem)) {
384  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
385  appendArgumentsAdjuster(getClangStripOutputAdjuster());
386  appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
387  appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
388}
389
390ClangTool::~ClangTool() = default;
391
392void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {
393  MappedFileContents.push_back(std::make_pair(FilePath, Content));
394}
395
396void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) {
397  ArgsAdjuster = combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
398}
399
400void ClangTool::clearArgumentsAdjusters() {
401  ArgsAdjuster = nullptr;
402}
403
404static void injectResourceDir(CommandLineArguments &Args, const char *Argv0,
405                              void *MainAddr) {
406  // Allow users to override the resource dir.
407  for (StringRef Arg : Args)
408    if (Arg.startswith("-resource-dir"))
409      return;
410
411  // If there's no override in place add our resource dir.
412  Args.push_back("-resource-dir=" +
413                 CompilerInvocation::GetResourcesPath(Argv0, MainAddr));
414}
415
416int ClangTool::run(ToolAction *Action) {
417  // Exists solely for the purpose of lookup of the resource path.
418  // This just needs to be some symbol in the binary.
419  static int StaticSymbol;
420
421  // First insert all absolute paths into the in-memory VFS. These are global
422  // for all compile commands.
423  if (SeenWorkingDirectories.insert("/").second)
424    for (const auto &MappedFile : MappedFileContents)
425      if (llvm::sys::path::is_absolute(MappedFile.first))
426        InMemoryFileSystem->addFile(
427            MappedFile.first, 0,
428            llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
429
430  bool ProcessingFailed = false;
431  bool FileSkipped = false;
432  // Compute all absolute paths before we run any actions, as those will change
433  // the working directory.
434  std::vector<std::string> AbsolutePaths;
435  AbsolutePaths.reserve(SourcePaths.size());
436  for (const auto &SourcePath : SourcePaths) {
437    auto AbsPath = getAbsolutePath(*OverlayFileSystem, SourcePath);
438    if (!AbsPath) {
439      llvm::errs() << "Skipping " << SourcePath
440                   << ". Error while getting an absolute path: "
441                   << llvm::toString(AbsPath.takeError()) << "\n";
442      continue;
443    }
444    AbsolutePaths.push_back(std::move(*AbsPath));
445  }
446
447  // Remember the working directory in case we need to restore it.
448  std::string InitialWorkingDir;
449  if (RestoreCWD) {
450    if (auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
451      InitialWorkingDir = std::move(*CWD);
452    } else {
453      llvm::errs() << "Could not get working directory: "
454                   << CWD.getError().message() << "\n";
455    }
456  }
457
458  for (llvm::StringRef File : AbsolutePaths) {
459    // Currently implementations of CompilationDatabase::getCompileCommands can
460    // change the state of the file system (e.g.  prepare generated headers), so
461    // this method needs to run right before we invoke the tool, as the next
462    // file may require a different (incompatible) state of the file system.
463    //
464    // FIXME: Make the compilation database interface more explicit about the
465    // requirements to the order of invocation of its members.
466    std::vector<CompileCommand> CompileCommandsForFile =
467        Compilations.getCompileCommands(File);
468    if (CompileCommandsForFile.empty()) {
469      llvm::errs() << "Skipping " << File << ". Compile command not found.\n";
470      FileSkipped = true;
471      continue;
472    }
473    for (CompileCommand &CompileCommand : CompileCommandsForFile) {
474      // FIXME: chdir is thread hostile; on the other hand, creating the same
475      // behavior as chdir is complex: chdir resolves the path once, thus
476      // guaranteeing that all subsequent relative path operations work
477      // on the same path the original chdir resulted in. This makes a
478      // difference for example on network filesystems, where symlinks might be
479      // switched during runtime of the tool. Fixing this depends on having a
480      // file system abstraction that allows openat() style interactions.
481      if (OverlayFileSystem->setCurrentWorkingDirectory(
482              CompileCommand.Directory))
483        llvm::report_fatal_error("Cannot chdir into \"" +
484                                 Twine(CompileCommand.Directory) + "\n!");
485
486      // Now fill the in-memory VFS with the relative file mappings so it will
487      // have the correct relative paths. We never remove mappings but that
488      // should be fine.
489      if (SeenWorkingDirectories.insert(CompileCommand.Directory).second)
490        for (const auto &MappedFile : MappedFileContents)
491          if (!llvm::sys::path::is_absolute(MappedFile.first))
492            InMemoryFileSystem->addFile(
493                MappedFile.first, 0,
494                llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
495
496      std::vector<std::string> CommandLine = CompileCommand.CommandLine;
497      if (ArgsAdjuster)
498        CommandLine = ArgsAdjuster(CommandLine, CompileCommand.Filename);
499      assert(!CommandLine.empty());
500
501      // Add the resource dir based on the binary of this tool. argv[0] in the
502      // compilation database may refer to a different compiler and we want to
503      // pick up the very same standard library that compiler is using. The
504      // builtin headers in the resource dir need to match the exact clang
505      // version the tool is using.
506      // FIXME: On linux, GetMainExecutable is independent of the value of the
507      // first argument, thus allowing ClangTool and runToolOnCode to just
508      // pass in made-up names here. Make sure this works on other platforms.
509      injectResourceDir(CommandLine, "clang_tool", &StaticSymbol);
510
511      // FIXME: We need a callback mechanism for the tool writer to output a
512      // customized message for each file.
513      LLVM_DEBUG({ llvm::dbgs() << "Processing: " << File << ".\n"; });
514      ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
515                                PCHContainerOps);
516      Invocation.setDiagnosticConsumer(DiagConsumer);
517
518      if (!Invocation.run()) {
519        // FIXME: Diagnostics should be used instead.
520        llvm::errs() << "Error while processing " << File << ".\n";
521        ProcessingFailed = true;
522      }
523    }
524  }
525
526  if (!InitialWorkingDir.empty()) {
527    if (auto EC =
528            OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
529      llvm::errs() << "Error when trying to restore working dir: "
530                   << EC.message() << "\n";
531  }
532  return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
533}
534
535namespace {
536
537class ASTBuilderAction : public ToolAction {
538  std::vector<std::unique_ptr<ASTUnit>> &ASTs;
539
540public:
541  ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
542
543  bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
544                     FileManager *Files,
545                     std::shared_ptr<PCHContainerOperations> PCHContainerOps,
546                     DiagnosticConsumer *DiagConsumer) override {
547    std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
548        Invocation, std::move(PCHContainerOps),
549        CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts(),
550                                            DiagConsumer,
551                                            /*ShouldOwnClient=*/false),
552        Files);
553    if (!AST)
554      return false;
555
556    ASTs.push_back(std::move(AST));
557    return true;
558  }
559};
560
561// namespace
562
563int ClangTool::buildASTs(std::vector<std::unique_ptr<ASTUnit>> &ASTs) {
564  ASTBuilderAction Action(ASTs);
565  return run(&Action);
566}
567
568void ClangTool::setRestoreWorkingDir(bool RestoreCWD) {
569  this->RestoreCWD = RestoreCWD;
570}
571
572namespace clang {
573namespace tooling {
574
575std::unique_ptr<ASTUnit>
576buildASTFromCode(StringRef Code, StringRef FileName,
577                 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
578  return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName,
579                                  "clang-tool", std::move(PCHContainerOps));
580}
581
582std::unique_ptr<ASTUnit> buildASTFromCodeWithArgs(
583    StringRef Code, const std::vector<std::string> &Args, StringRef FileName,
584    StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
585    ArgumentsAdjuster Adjuster) {
586  std::vector<std::unique_ptr<ASTUnit>> ASTs;
587  ASTBuilderAction Action(ASTs);
588  llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
589      new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
590  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
591      new llvm::vfs::InMemoryFileSystem);
592  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
593  llvm::IntrusiveRefCntPtr<FileManager> Files(
594      new FileManager(FileSystemOptions(), OverlayFileSystem));
595
596  ToolInvocation Invocation(
597      getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileName), FileName),
598      &Action, Files.get(), std::move(PCHContainerOps));
599
600  InMemoryFileSystem->addFile(FileName, 0,
601                              llvm::MemoryBuffer::getMemBufferCopy(Code));
602  if (!Invocation.run())
603    return nullptr;
604
605  assert(ASTs.size() == 1);
606  return std::move(ASTs[0]);
607}
608
609// namespace tooling
610// namespace clang
611
clang::tooling::ToolInvocation::mapVirtualFile
clang::tooling::ToolInvocation::run
clang::tooling::ToolInvocation::runInvocation
clang::tooling::FrontendActionFactory::runInvocation