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