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