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 *Diagnostics, const char *BinaryName,
78          IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
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  const driver::ActionList &Actions = Compilation->getActions();
95  bool OffloadCompilation = false;
96  if (Jobs.size() > 1) {
97    for (auto A : Actions){
98      // On MacOSX real actions may end up being wrapped in BindArchAction
99      if (isa<driver::BindArchAction>(A))
100        A = *A->input_begin();
101      if (isa<driver::OffloadAction>(A)) {
102        // Offload compilation has 2 top-level actions, one (at the front) is
103        // the original host compilation and the other is offload action
104        // composed of at least one device compilation. For such case, general
105        // tooling will consider host-compilation only. For tooling on device
106        // compilation, device compilation only option, such as
107        // `--cuda-device-only`, needs specifying.
108        assert(Actions.size() > 1);
109        assert(
110            isa<driver::CompileJobAction>(Actions.front()) ||
111            // On MacOSX real actions may end up being wrapped in
112            // BindArchAction.
113            (isa<driver::BindArchAction>(Actions.front()) &&
114             isa<driver::CompileJobAction>(*Actions.front()->input_begin())));
115        OffloadCompilation = true;
116        break;
117      }
118    }
119  }
120  if (Jobs.size() == 0 || !isa<driver::Command>(*Jobs.begin()) ||
121      (Jobs.size() > 1 && !OffloadCompilation)) {
122    SmallString<256> error_msg;
123    llvm::raw_svector_ostream error_stream(error_msg);
124    Jobs.Print(error_stream, "; ", true);
125    Diagnostics->Report(diag::err_fe_expected_compiler_job)
126        << error_stream.str();
127    return nullptr;
128  }
129
130  // The one job we find should be to invoke clang again.
131  const auto &Cmd = cast<driver::Command>(*Jobs.begin());
132  if (StringRef(Cmd.getCreator().getName()) != "clang") {
133    Diagnostics->Report(diag::err_fe_expected_clang_command);
134    return nullptr;
135  }
136
137  return &Cmd.getArguments();
138}
139
140namespace clang {
141namespace tooling {
142
143/// Returns a clang build invocation initialized from the CC1 flags.
144CompilerInvocation *newInvocation(
145    DiagnosticsEngine *Diagnostics, const llvm::opt::ArgStringList &CC1Args) {
146  assert(!CC1Args.empty() && "Must at least contain the program name!");
147  CompilerInvocation *Invocation = new CompilerInvocation;
148  CompilerInvocation::CreateFromArgs(*Invocation, CC1Args, *Diagnostics);
149  Invocation->getFrontendOpts().DisableFree = false;
150  Invocation->getCodeGenOpts().DisableFree = false;
151  return Invocation;
152}
153
154bool runToolOnCode(std::unique_ptr<FrontendAction> ToolAction,
155                   const Twine &Code, const Twine &FileName,
156                   std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
157  return runToolOnCodeWithArgs(std::move(ToolAction), Code,
158                               std::vector<std::string>(), FileName,
159                               "clang-tool", std::move(PCHContainerOps));
160}
161
162} // namespace tooling
163} // namespace clang
164
165static std::vector<std::string>
166getSyntaxOnlyToolArgs(const Twine &ToolName,
167                      const std::vector<std::string> &ExtraArgs,
168                      StringRef FileName) {
169  std::vector<std::string> Args;
170  Args.push_back(ToolName.str());
171  Args.push_back("-fsyntax-only");
172  Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
173  Args.push_back(FileName.str());
174  return Args;
175}
176
177namespace clang {
178namespace tooling {
179
180bool runToolOnCodeWithArgs(
181    std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,
182    llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
183    const std::vector<std::string> &Args, const Twine &FileName,
184    const Twine &ToolName,
185    std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
186  SmallString<16> FileNameStorage;
187  StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
188
189  llvm::IntrusiveRefCntPtr<FileManager> Files(
190      new FileManager(FileSystemOptions(), VFS));
191  ArgumentsAdjuster Adjuster = getClangStripDependencyFileAdjuster();
192  ToolInvocation Invocation(
193      getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileNameRef), FileNameRef),
194      std::move(ToolAction), Files.get(), std::move(PCHContainerOps));
195  return Invocation.run();
196}
197
198bool runToolOnCodeWithArgs(
199    std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,
200    const std::vector<std::string> &Args, const Twine &FileName,
201    const Twine &ToolName,
202    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
203    const FileContentMappings &VirtualMappedFiles) {
204  llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
205      new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
206  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
207      new llvm::vfs::InMemoryFileSystem);
208  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
209
210  SmallString<1024> CodeStorage;
211  InMemoryFileSystem->addFile(FileName, 0,
212                              llvm::MemoryBuffer::getMemBuffer(
213                                  Code.toNullTerminatedStringRef(CodeStorage)));
214
215  for (auto &FilenameWithContent : VirtualMappedFiles) {
216    InMemoryFileSystem->addFile(
217        FilenameWithContent.first, 0,
218        llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
219  }
220
221  return runToolOnCodeWithArgs(std::move(ToolAction), Code, OverlayFileSystem,
222                               Args, FileName, ToolName);
223}
224
225llvm::Expected<std::string> getAbsolutePath(llvm::vfs::FileSystem &FS,
226                                            StringRef File) {
227  StringRef RelativePath(File);
228  // FIXME: Should '.\\' be accepted on Win32?
229  if (RelativePath.startswith("./")) {
230    RelativePath = RelativePath.substr(strlen("./"));
231  }
232
233  SmallString<1024> AbsolutePath = RelativePath;
234  if (auto EC = FS.makeAbsolute(AbsolutePath))
235    return llvm::errorCodeToError(EC);
236  llvm::sys::path::native(AbsolutePath);
237  return AbsolutePath.str();
238}
239
240std::string getAbsolutePath(StringRef File) {
241  return llvm::cantFail(getAbsolutePath(*llvm::vfs::getRealFileSystem(), File));
242}
243
244void addTargetAndModeForProgramName(std::vector<std::string> &CommandLine,
245                                    StringRef InvokedAs) {
246  if (!CommandLine.empty() && !InvokedAs.empty()) {
247    bool AlreadyHasTarget = false;
248    bool AlreadyHasMode = false;
249    // Skip CommandLine[0].
250    for (auto Token = ++CommandLine.begin(); Token != CommandLine.end();
251         ++Token) {
252      StringRef TokenRef(*Token);
253      AlreadyHasTarget |=
254          (TokenRef == "-target" || TokenRef.startswith("-target="));
255      AlreadyHasMode |= (TokenRef == "--driver-mode" ||
256                         TokenRef.startswith("--driver-mode="));
257    }
258    auto TargetMode =
259        driver::ToolChain::getTargetAndModeFromProgramName(InvokedAs);
260    if (!AlreadyHasMode && TargetMode.DriverMode) {
261      CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
262    }
263    if (!AlreadyHasTarget && TargetMode.TargetIsValid) {
264      CommandLine.insert(++CommandLine.begin(), {"-target",
265                                                 TargetMode.TargetPrefix});
266    }
267  }
268}
269
270} // namespace tooling
271} // namespace clang
272
273namespace {
274
275class SingleFrontendActionFactory : public FrontendActionFactory {
276  std::unique_ptr<FrontendAction> Action;
277
278public:
279  SingleFrontendActionFactory(std::unique_ptr<FrontendAction> Action)
280      : Action(std::move(Action)) {}
281
282  std::unique_ptr<FrontendAction> create() override {
283    return std::move(Action);
284  }
285};
286
287} // namespace
288
289ToolInvocation::ToolInvocation(
290    std::vector<std::string> CommandLine, ToolAction *Action,
291    FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
292    : CommandLine(std::move(CommandLine)), Action(Action), OwnsAction(false),
293      Files(Files), PCHContainerOps(std::move(PCHContainerOps)) {}
294
295ToolInvocation::ToolInvocation(
296    std::vector<std::string> CommandLine,
297    std::unique_ptr<FrontendAction> FAction, FileManager *Files,
298    std::shared_ptr<PCHContainerOperations> PCHContainerOps)
299    : CommandLine(std::move(CommandLine)),
300      Action(new SingleFrontendActionFactory(std::move(FAction))),
301      OwnsAction(true), Files(Files),
302      PCHContainerOps(std::move(PCHContainerOps)) {}
303
304ToolInvocation::~ToolInvocation() {
305  if (OwnsAction)
306    delete Action;
307}
308
309void ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) {
310  SmallString<1024> PathStorage;
311  llvm::sys::path::native(FilePath, PathStorage);
312  MappedFileContents[PathStorage] = Content;
313}
314
315bool ToolInvocation::run() {
316  std::vector<const char*> Argv;
317  for (const std::string &Str : CommandLine)
318    Argv.push_back(Str.c_str());
319  const char *const BinaryName = Argv[0];
320  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
321  unsigned MissingArgIndex, MissingArgCount;
322  llvm::opt::InputArgList ParsedArgs = driver::getDriverOptTable().ParseArgs(
323      ArrayRef<const char *>(Argv).slice(1), MissingArgIndex, MissingArgCount);
324  ParseDiagnosticArgs(*DiagOpts, ParsedArgs);
325  TextDiagnosticPrinter DiagnosticPrinter(
326      llvm::errs(), &*DiagOpts);
327  DiagnosticsEngine Diagnostics(
328      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
329      DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);
330
331  const std::unique_ptr<driver::Driver> Driver(
332      newDriver(&Diagnostics, BinaryName, &Files->getVirtualFileSystem()));
333  // The "input file not found" diagnostics from the driver are useful.
334  // The driver is only aware of the VFS working directory, but some clients
335  // change this at the FileManager level instead.
336  // In this case the checks have false positives, so skip them.
337  if (!Files->getFileSystemOpts().WorkingDir.empty())
338    Driver->setCheckInputsExist(false);
339  const std::unique_ptr<driver::Compilation> Compilation(
340      Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
341  if (!Compilation)
342    return false;
343  const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
344      &Diagnostics, Compilation.get());
345  if (!CC1Args)
346    return false;
347  std::unique_ptr<CompilerInvocation> Invocation(
348      newInvocation(&Diagnostics, *CC1Args));
349  // FIXME: remove this when all users have migrated!
350  for (const auto &It : MappedFileContents) {
351    // Inject the code as the given file name into the preprocessor options.
352    std::unique_ptr<llvm::MemoryBuffer> Input =
353        llvm::MemoryBuffer::getMemBuffer(It.getValue());
354    Invocation->getPreprocessorOpts().addRemappedFile(It.getKey(),
355                                                      Input.release());
356  }
357  return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
358                       std::move(PCHContainerOps));
359}
360
361bool ToolInvocation::runInvocation(
362    const char *BinaryName, driver::Compilation *Compilation,
363    std::shared_ptr<CompilerInvocation> Invocation,
364    std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
365  // Show the invocation, with -v.
366  if (Invocation->getHeaderSearchOpts().Verbose) {
367    llvm::errs() << "clang Invocation:\n";
368    Compilation->getJobs().Print(llvm::errs(), "\n", true);
369    llvm::errs() << "\n";
370  }
371
372  return Action->runInvocation(std::move(Invocation), Files,
373                               std::move(PCHContainerOps), DiagConsumer);
374}
375
376bool FrontendActionFactory::runInvocation(
377    std::shared_ptr<CompilerInvocation> Invocation, FileManager *Files,
378    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
379    DiagnosticConsumer *DiagConsumer) {
380  // Create a compiler instance to handle the actual work.
381  CompilerInstance Compiler(std::move(PCHContainerOps));
382  Compiler.setInvocation(std::move(Invocation));
383  Compiler.setFileManager(Files);
384
385  // The FrontendAction can have lifetime requirements for Compiler or its
386  // members, and we need to ensure it's deleted earlier than Compiler. So we
387  // pass it to an std::unique_ptr declared after the Compiler variable.
388  std::unique_ptr<FrontendAction> ScopedToolAction(create());
389
390  // Create the compiler's actual diagnostics engine.
391  Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
392  if (!Compiler.hasDiagnostics())
393    return false;
394
395  Compiler.createSourceManager(*Files);
396
397  const bool Success = Compiler.ExecuteAction(*ScopedToolAction);
398
399  Files->clearStatCache();
400  return Success;
401}
402
403ClangTool::ClangTool(const CompilationDatabase &Compilations,
404                     ArrayRef<std::string> SourcePaths,
405                     std::shared_ptr<PCHContainerOperations> PCHContainerOps,
406                     IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
407                     IntrusiveRefCntPtr<FileManager> Files)
408    : Compilations(Compilations), SourcePaths(SourcePaths),
409      PCHContainerOps(std::move(PCHContainerOps)),
410      OverlayFileSystem(new llvm::vfs::OverlayFileSystem(std::move(BaseFS))),
411      InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
412      Files(Files ? Files
413                  : new FileManager(FileSystemOptions(), OverlayFileSystem)) {
414  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
415  appendArgumentsAdjuster(getClangStripOutputAdjuster());
416  appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
417  appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
418  if (Files)
419    Files->setVirtualFileSystem(OverlayFileSystem);
420}
421
422ClangTool::~ClangTool() = default;
423
424void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {
425  MappedFileContents.push_back(std::make_pair(FilePath, Content));
426}
427
428void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) {
429  ArgsAdjuster = combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
430}
431
432void ClangTool::clearArgumentsAdjusters() {
433  ArgsAdjuster = nullptr;
434}
435
436static void injectResourceDir(CommandLineArguments &Args, const char *Argv0,
437                              void *MainAddr) {
438  // Allow users to override the resource dir.
439  for (StringRef Arg : Args)
440    if (Arg.startswith("-resource-dir"))
441      return;
442
443  // If there's no override in place add our resource dir.
444  Args.push_back("-resource-dir=" +
445                 CompilerInvocation::GetResourcesPath(Argv0, MainAddr));
446}
447
448int ClangTool::run(ToolAction *Action) {
449  // Exists solely for the purpose of lookup of the resource path.
450  // This just needs to be some symbol in the binary.
451  static int StaticSymbol;
452
453  // First insert all absolute paths into the in-memory VFS. These are global
454  // for all compile commands.
455  if (SeenWorkingDirectories.insert("/").second)
456    for (const auto &MappedFile : MappedFileContents)
457      if (llvm::sys::path::is_absolute(MappedFile.first))
458        InMemoryFileSystem->addFile(
459            MappedFile.first, 0,
460            llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
461
462  bool ProcessingFailed = false;
463  bool FileSkipped = false;
464  // Compute all absolute paths before we run any actions, as those will change
465  // the working directory.
466  std::vector<std::string> AbsolutePaths;
467  AbsolutePaths.reserve(SourcePaths.size());
468  for (const auto &SourcePath : SourcePaths) {
469    auto AbsPath = getAbsolutePath(*OverlayFileSystem, SourcePath);
470    if (!AbsPath) {
471      llvm::errs() << "Skipping " << SourcePath
472                   << ". Error while getting an absolute path: "
473                   << llvm::toString(AbsPath.takeError()) << "\n";
474      continue;
475    }
476    AbsolutePaths.push_back(std::move(*AbsPath));
477  }
478
479  // Remember the working directory in case we need to restore it.
480  std::string InitialWorkingDir;
481  if (RestoreCWD) {
482    if (auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
483      InitialWorkingDir = std::move(*CWD);
484    } else {
485      llvm::errs() << "Could not get working directory: "
486                   << CWD.getError().message() << "\n";
487    }
488  }
489
490  for (llvm::StringRef File : AbsolutePaths) {
491    // Currently implementations of CompilationDatabase::getCompileCommands can
492    // change the state of the file system (e.g.  prepare generated headers), so
493    // this method needs to run right before we invoke the tool, as the next
494    // file may require a different (incompatible) state of the file system.
495    //
496    // FIXME: Make the compilation database interface more explicit about the
497    // requirements to the order of invocation of its members.
498    std::vector<CompileCommand> CompileCommandsForFile =
499        Compilations.getCompileCommands(File);
500    if (CompileCommandsForFile.empty()) {
501      llvm::errs() << "Skipping " << File << ". Compile command not found.\n";
502      FileSkipped = true;
503      continue;
504    }
505    for (CompileCommand &CompileCommand : CompileCommandsForFile) {
506      // FIXME: chdir is thread hostile; on the other hand, creating the same
507      // behavior as chdir is complex: chdir resolves the path once, thus
508      // guaranteeing that all subsequent relative path operations work
509      // on the same path the original chdir resulted in. This makes a
510      // difference for example on network filesystems, where symlinks might be
511      // switched during runtime of the tool. Fixing this depends on having a
512      // file system abstraction that allows openat() style interactions.
513      if (OverlayFileSystem->setCurrentWorkingDirectory(
514              CompileCommand.Directory))
515        llvm::report_fatal_error("Cannot chdir into \"" +
516                                 Twine(CompileCommand.Directory) + "\"!");
517
518      // Now fill the in-memory VFS with the relative file mappings so it will
519      // have the correct relative paths. We never remove mappings but that
520      // should be fine.
521      if (SeenWorkingDirectories.insert(CompileCommand.Directory).second)
522        for (const auto &MappedFile : MappedFileContents)
523          if (!llvm::sys::path::is_absolute(MappedFile.first))
524            InMemoryFileSystem->addFile(
525                MappedFile.first, 0,
526                llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
527
528      std::vector<std::string> CommandLine = CompileCommand.CommandLine;
529      if (ArgsAdjuster)
530        CommandLine = ArgsAdjuster(CommandLine, CompileCommand.Filename);
531      assert(!CommandLine.empty());
532
533      // Add the resource dir based on the binary of this tool. argv[0] in the
534      // compilation database may refer to a different compiler and we want to
535      // pick up the very same standard library that compiler is using. The
536      // builtin headers in the resource dir need to match the exact clang
537      // version the tool is using.
538      // FIXME: On linux, GetMainExecutable is independent of the value of the
539      // first argument, thus allowing ClangTool and runToolOnCode to just
540      // pass in made-up names here. Make sure this works on other platforms.
541      injectResourceDir(CommandLine, "clang_tool", &StaticSymbol);
542
543      // FIXME: We need a callback mechanism for the tool writer to output a
544      // customized message for each file.
545      LLVM_DEBUG({ llvm::dbgs() << "Processing: " << File << ".\n"; });
546      ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
547                                PCHContainerOps);
548      Invocation.setDiagnosticConsumer(DiagConsumer);
549
550      if (!Invocation.run()) {
551        // FIXME: Diagnostics should be used instead.
552        if (PrintErrorMessage)
553          llvm::errs() << "Error while processing " << File << ".\n";
554        ProcessingFailed = true;
555      }
556    }
557  }
558
559  if (!InitialWorkingDir.empty()) {
560    if (auto EC =
561            OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
562      llvm::errs() << "Error when trying to restore working dir: "
563                   << EC.message() << "\n";
564  }
565  return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
566}
567
568namespace {
569
570class ASTBuilderAction : public ToolAction {
571  std::vector<std::unique_ptr<ASTUnit>> &ASTs;
572
573public:
574  ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
575
576  bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
577                     FileManager *Files,
578                     std::shared_ptr<PCHContainerOperations> PCHContainerOps,
579                     DiagnosticConsumer *DiagConsumer) override {
580    std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
581        Invocation, std::move(PCHContainerOps),
582        CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts(),
583                                            DiagConsumer,
584                                            /*ShouldOwnClient=*/false),
585        Files);
586    if (!AST)
587      return false;
588
589    ASTs.push_back(std::move(AST));
590    return true;
591  }
592};
593
594} // namespace
595
596int ClangTool::buildASTs(std::vector<std::unique_ptr<ASTUnit>> &ASTs) {
597  ASTBuilderAction Action(ASTs);
598  return run(&Action);
599}
600
601void ClangTool::setRestoreWorkingDir(bool RestoreCWD) {
602  this->RestoreCWD = RestoreCWD;
603}
604
605void ClangTool::setPrintErrorMessage(bool PrintErrorMessage) {
606  this->PrintErrorMessage = PrintErrorMessage;
607}
608
609namespace clang {
610namespace tooling {
611
612std::unique_ptr<ASTUnit>
613buildASTFromCode(StringRef Code, StringRef FileName,
614                 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
615  return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName,
616                                  "clang-tool", std::move(PCHContainerOps));
617}
618
619std::unique_ptr<ASTUnit> buildASTFromCodeWithArgs(
620    StringRef Code, const std::vector<std::string> &Args, StringRef FileName,
621    StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
622    ArgumentsAdjuster Adjuster, const FileContentMappings &VirtualMappedFiles) {
623  std::vector<std::unique_ptr<ASTUnit>> ASTs;
624  ASTBuilderAction Action(ASTs);
625  llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
626      new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
627  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
628      new llvm::vfs::InMemoryFileSystem);
629  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
630  llvm::IntrusiveRefCntPtr<FileManager> Files(
631      new FileManager(FileSystemOptions(), OverlayFileSystem));
632
633  ToolInvocation Invocation(
634      getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileName), FileName),
635      &Action, Files.get(), std::move(PCHContainerOps));
636
637  InMemoryFileSystem->addFile(FileName, 0,
638                              llvm::MemoryBuffer::getMemBufferCopy(Code));
639  for (auto &FilenameWithContent : VirtualMappedFiles) {
640    InMemoryFileSystem->addFile(
641        FilenameWithContent.first, 0,
642        llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
643  }
644
645  if (!Invocation.run())
646    return nullptr;
647
648  assert(ASTs.size() == 1);
649  return std::move(ASTs[0]);
650}
651
652} // namespace tooling
653} // namespace clang
654