clang 17.0.0git
Tooling.cpp
Go to the documentation of this file.
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
20#include "clang/Basic/LLVM.h"
22#include "clang/Driver/Driver.h"
23#include "clang/Driver/Job.h"
25#include "clang/Driver/Tool.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/CommandLine.h"
47#include "llvm/Support/Debug.h"
48#include "llvm/Support/ErrorHandling.h"
49#include "llvm/Support/FileSystem.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 "llvm/TargetParser/Host.h"
55#include <cassert>
56#include <cstring>
57#include <memory>
58#include <string>
59#include <system_error>
60#include <utility>
61#include <vector>
62
63#define DEBUG_TYPE "clang-tooling"
64
65using namespace clang;
66using namespace tooling;
67
68ToolAction::~ToolAction() = default;
69
71
72// FIXME: This file contains structural duplication with other parts of the
73// code that sets up a compiler to run tools on it, and we should refactor
74// it to be based on the same framework.
75
76/// Builds a clang driver initialized for running clang tools.
77static driver::Driver *
78newDriver(DiagnosticsEngine *Diagnostics, const char *BinaryName,
80 driver::Driver *CompilerDriver =
81 new driver::Driver(BinaryName, llvm::sys::getDefaultTargetTriple(),
82 *Diagnostics, "clang LLVM compiler", std::move(VFS));
83 CompilerDriver->setTitle("clang_based_tool");
84 return CompilerDriver;
85}
86
87/// Decide whether extra compiler frontend commands can be ignored.
88static bool ignoreExtraCC1Commands(const driver::Compilation *Compilation) {
89 const driver::JobList &Jobs = Compilation->getJobs();
90 const driver::ActionList &Actions = Compilation->getActions();
91
92 bool OffloadCompilation = false;
93
94 // Jobs and Actions look very different depending on whether the Clang tool
95 // injected -fsyntax-only or not. Try to handle both cases here.
96
97 for (const auto &Job : Jobs)
98 if (StringRef(Job.getExecutable()) == "clang-offload-bundler")
99 OffloadCompilation = true;
100
101 if (Jobs.size() > 1) {
102 for (auto *A : Actions){
103 // On MacOSX real actions may end up being wrapped in BindArchAction
104 if (isa<driver::BindArchAction>(A))
105 A = *A->input_begin();
106 if (isa<driver::OffloadAction>(A)) {
107 // Offload compilation has 2 top-level actions, one (at the front) is
108 // the original host compilation and the other is offload action
109 // composed of at least one device compilation. For such case, general
110 // tooling will consider host-compilation only. For tooling on device
111 // compilation, device compilation only option, such as
112 // `--cuda-device-only`, needs specifying.
113 assert(Actions.size() > 1);
114 assert(
115 isa<driver::CompileJobAction>(Actions.front()) ||
116 // On MacOSX real actions may end up being wrapped in
117 // BindArchAction.
118 (isa<driver::BindArchAction>(Actions.front()) &&
119 isa<driver::CompileJobAction>(*Actions.front()->input_begin())));
120 OffloadCompilation = true;
121 break;
122 }
123 }
124 }
125
126 return OffloadCompilation;
127}
128
129namespace clang {
130namespace tooling {
131
132const llvm::opt::ArgStringList *
134 driver::Compilation *Compilation) {
135 const driver::JobList &Jobs = Compilation->getJobs();
136
137 auto IsCC1Command = [](const driver::Command &Cmd) {
138 return StringRef(Cmd.getCreator().getName()) == "clang";
139 };
140
141 auto IsSrcFile = [](const driver::InputInfo &II) {
142 return isSrcFile(II.getType());
143 };
144
146 for (const driver::Command &Job : Jobs)
147 if (IsCC1Command(Job) && llvm::all_of(Job.getInputInfos(), IsSrcFile))
148 CC1Jobs.push_back(&Job);
149
150 if (CC1Jobs.empty() ||
151 (CC1Jobs.size() > 1 && !ignoreExtraCC1Commands(Compilation))) {
152 SmallString<256> error_msg;
153 llvm::raw_svector_ostream error_stream(error_msg);
154 Jobs.Print(error_stream, "; ", true);
155 Diagnostics->Report(diag::err_fe_expected_compiler_job)
156 << error_stream.str();
157 return nullptr;
158 }
159
160 return &CC1Jobs[0]->getArguments();
161}
162
163/// Returns a clang build invocation initialized from the CC1 flags.
166 const char *const BinaryName) {
167 assert(!CC1Args.empty() && "Must at least contain the program name!");
168 CompilerInvocation *Invocation = new CompilerInvocation;
169 CompilerInvocation::CreateFromArgs(*Invocation, CC1Args, *Diagnostics,
170 BinaryName);
171 Invocation->getFrontendOpts().DisableFree = false;
172 Invocation->getCodeGenOpts().DisableFree = false;
173 return Invocation;
174}
175
176bool runToolOnCode(std::unique_ptr<FrontendAction> ToolAction,
177 const Twine &Code, const Twine &FileName,
178 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
179 return runToolOnCodeWithArgs(std::move(ToolAction), Code,
180 std::vector<std::string>(), FileName,
181 "clang-tool", std::move(PCHContainerOps));
182}
183
184} // namespace tooling
185} // namespace clang
186
187static std::vector<std::string>
188getSyntaxOnlyToolArgs(const Twine &ToolName,
189 const std::vector<std::string> &ExtraArgs,
190 StringRef FileName) {
191 std::vector<std::string> Args;
192 Args.push_back(ToolName.str());
193 Args.push_back("-fsyntax-only");
194 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
195 Args.push_back(FileName.str());
196 return Args;
197}
198
199namespace clang {
200namespace tooling {
201
203 std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,
205 const std::vector<std::string> &Args, const Twine &FileName,
206 const Twine &ToolName,
207 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
208 SmallString<16> FileNameStorage;
209 StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
210
212 new FileManager(FileSystemOptions(), VFS));
214 ToolInvocation Invocation(
215 getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileNameRef), FileNameRef),
216 std::move(ToolAction), Files.get(), std::move(PCHContainerOps));
217 return Invocation.run();
218}
219
221 std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,
222 const std::vector<std::string> &Args, const Twine &FileName,
223 const Twine &ToolName,
224 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
225 const FileContentMappings &VirtualMappedFiles) {
227 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
229 new llvm::vfs::InMemoryFileSystem);
230 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
231
232 SmallString<1024> CodeStorage;
233 InMemoryFileSystem->addFile(FileName, 0,
234 llvm::MemoryBuffer::getMemBuffer(
235 Code.toNullTerminatedStringRef(CodeStorage)));
236
237 for (auto &FilenameWithContent : VirtualMappedFiles) {
238 InMemoryFileSystem->addFile(
239 FilenameWithContent.first, 0,
240 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
241 }
242
243 return runToolOnCodeWithArgs(std::move(ToolAction), Code, OverlayFileSystem,
244 Args, FileName, ToolName);
245}
246
248 StringRef File) {
249 StringRef RelativePath(File);
250 // FIXME: Should '.\\' be accepted on Win32?
251 if (RelativePath.startswith("./")) {
252 RelativePath = RelativePath.substr(strlen("./"));
253 }
254
255 SmallString<1024> AbsolutePath = RelativePath;
256 if (auto EC = FS.makeAbsolute(AbsolutePath))
257 return llvm::errorCodeToError(EC);
258 llvm::sys::path::native(AbsolutePath);
259 return std::string(AbsolutePath.str());
260}
261
262std::string getAbsolutePath(StringRef File) {
263 return llvm::cantFail(getAbsolutePath(*llvm::vfs::getRealFileSystem(), File));
264}
265
266void addTargetAndModeForProgramName(std::vector<std::string> &CommandLine,
267 StringRef InvokedAs) {
268 if (CommandLine.empty() || InvokedAs.empty())
269 return;
270 const auto &Table = driver::getDriverOptTable();
271 // --target=X
272 const std::string TargetOPT =
273 Table.getOption(driver::options::OPT_target).getPrefixedName();
274 // -target X
275 const std::string TargetOPTLegacy =
276 Table.getOption(driver::options::OPT_target_legacy_spelling)
277 .getPrefixedName();
278 // --driver-mode=X
279 const std::string DriverModeOPT =
280 Table.getOption(driver::options::OPT_driver_mode).getPrefixedName();
281 auto TargetMode =
283 // No need to search for target args if we don't have a target/mode to insert.
284 bool ShouldAddTarget = TargetMode.TargetIsValid;
285 bool ShouldAddMode = TargetMode.DriverMode != nullptr;
286 // Skip CommandLine[0].
287 for (auto Token = ++CommandLine.begin(); Token != CommandLine.end();
288 ++Token) {
289 StringRef TokenRef(*Token);
290 ShouldAddTarget = ShouldAddTarget && !TokenRef.startswith(TargetOPT) &&
291 !TokenRef.equals(TargetOPTLegacy);
292 ShouldAddMode = ShouldAddMode && !TokenRef.startswith(DriverModeOPT);
293 }
294 if (ShouldAddMode) {
295 CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
296 }
297 if (ShouldAddTarget) {
298 CommandLine.insert(++CommandLine.begin(),
299 TargetOPT + TargetMode.TargetPrefix);
300 }
301}
302
303void addExpandedResponseFiles(std::vector<std::string> &CommandLine,
304 llvm::StringRef WorkingDir,
305 llvm::cl::TokenizerCallback Tokenizer,
306 llvm::vfs::FileSystem &FS) {
307 bool SeenRSPFile = false;
309 Argv.reserve(CommandLine.size());
310 for (auto &Arg : CommandLine) {
311 Argv.push_back(Arg.c_str());
312 if (!Arg.empty())
313 SeenRSPFile |= Arg.front() == '@';
314 }
315 if (!SeenRSPFile)
316 return;
317 llvm::BumpPtrAllocator Alloc;
318 llvm::cl::ExpansionContext ECtx(Alloc, Tokenizer);
319 llvm::Error Err =
320 ECtx.setVFS(&FS).setCurrentDir(WorkingDir).expandResponseFiles(Argv);
321 if (Err)
322 llvm::errs() << Err;
323 // Don't assign directly, Argv aliases CommandLine.
324 std::vector<std::string> ExpandedArgv(Argv.begin(), Argv.end());
325 CommandLine = std::move(ExpandedArgv);
326}
327
328} // namespace tooling
329} // namespace clang
330
331namespace {
332
333class SingleFrontendActionFactory : public FrontendActionFactory {
334 std::unique_ptr<FrontendAction> Action;
335
336public:
337 SingleFrontendActionFactory(std::unique_ptr<FrontendAction> Action)
338 : Action(std::move(Action)) {}
339
340 std::unique_ptr<FrontendAction> create() override {
341 return std::move(Action);
342 }
343};
344
345} // namespace
346
348 std::vector<std::string> CommandLine, ToolAction *Action,
349 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
350 : CommandLine(std::move(CommandLine)), Action(Action), OwnsAction(false),
351 Files(Files), PCHContainerOps(std::move(PCHContainerOps)) {}
352
354 std::vector<std::string> CommandLine,
355 std::unique_ptr<FrontendAction> FAction, FileManager *Files,
356 std::shared_ptr<PCHContainerOperations> PCHContainerOps)
357 : CommandLine(std::move(CommandLine)),
358 Action(new SingleFrontendActionFactory(std::move(FAction))),
359 OwnsAction(true), Files(Files),
360 PCHContainerOps(std::move(PCHContainerOps)) {}
361
363 if (OwnsAction)
364 delete Action;
365}
366
368 llvm::opt::ArgStringList Argv;
369 for (const std::string &Str : CommandLine)
370 Argv.push_back(Str.c_str());
371 const char *const BinaryName = Argv[0];
372
373 // Parse diagnostic options from the driver command-line only if none were
374 // explicitly set.
376 DiagnosticOptions *DiagOpts = this->DiagOpts;
377 if (!DiagOpts) {
378 ParsedDiagOpts = CreateAndPopulateDiagOpts(Argv);
379 DiagOpts = &*ParsedDiagOpts;
380 }
381
382 TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), DiagOpts);
385 &*DiagOpts, DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);
386 // Although `Diagnostics` are used only for command-line parsing, the custom
387 // `DiagConsumer` might expect a `SourceManager` to be present.
388 SourceManager SrcMgr(*Diagnostics, *Files);
389 Diagnostics->setSourceManager(&SrcMgr);
390
391 // We already have a cc1, just create an invocation.
392 if (CommandLine.size() >= 2 && CommandLine[1] == "-cc1") {
393 ArrayRef<const char *> CC1Args = ArrayRef(Argv).drop_front();
394 std::unique_ptr<CompilerInvocation> Invocation(
395 newInvocation(&*Diagnostics, CC1Args, BinaryName));
396 if (Diagnostics->hasErrorOccurred())
397 return false;
398 return Action->runInvocation(std::move(Invocation), Files,
399 std::move(PCHContainerOps), DiagConsumer);
400 }
401
402 const std::unique_ptr<driver::Driver> Driver(
403 newDriver(&*Diagnostics, BinaryName, &Files->getVirtualFileSystem()));
404 // The "input file not found" diagnostics from the driver are useful.
405 // The driver is only aware of the VFS working directory, but some clients
406 // change this at the FileManager level instead.
407 // In this case the checks have false positives, so skip them.
408 if (!Files->getFileSystemOpts().WorkingDir.empty())
409 Driver->setCheckInputsExist(false);
410 const std::unique_ptr<driver::Compilation> Compilation(
411 Driver->BuildCompilation(llvm::ArrayRef(Argv)));
412 if (!Compilation)
413 return false;
414 const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
415 &*Diagnostics, Compilation.get());
416 if (!CC1Args)
417 return false;
418 std::unique_ptr<CompilerInvocation> Invocation(
419 newInvocation(&*Diagnostics, *CC1Args, BinaryName));
420 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
421 std::move(PCHContainerOps));
422}
423
424bool ToolInvocation::runInvocation(
425 const char *BinaryName, driver::Compilation *Compilation,
426 std::shared_ptr<CompilerInvocation> Invocation,
427 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
428 // Show the invocation, with -v.
429 if (Invocation->getHeaderSearchOpts().Verbose) {
430 llvm::errs() << "clang Invocation:\n";
431 Compilation->getJobs().Print(llvm::errs(), "\n", true);
432 llvm::errs() << "\n";
433 }
434
435 return Action->runInvocation(std::move(Invocation), Files,
436 std::move(PCHContainerOps), DiagConsumer);
437}
438
440 std::shared_ptr<CompilerInvocation> Invocation, FileManager *Files,
441 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
442 DiagnosticConsumer *DiagConsumer) {
443 // Create a compiler instance to handle the actual work.
444 CompilerInstance Compiler(std::move(PCHContainerOps));
445 Compiler.setInvocation(std::move(Invocation));
446 Compiler.setFileManager(Files);
447
448 // The FrontendAction can have lifetime requirements for Compiler or its
449 // members, and we need to ensure it's deleted earlier than Compiler. So we
450 // pass it to an std::unique_ptr declared after the Compiler variable.
451 std::unique_ptr<FrontendAction> ScopedToolAction(create());
452
453 // Create the compiler's actual diagnostics engine.
454 Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
455 if (!Compiler.hasDiagnostics())
456 return false;
457
458 Compiler.createSourceManager(*Files);
459
460 const bool Success = Compiler.ExecuteAction(*ScopedToolAction);
461
462 Files->clearStatCache();
463 return Success;
464}
465
467 ArrayRef<std::string> SourcePaths,
468 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
471 : Compilations(Compilations), SourcePaths(SourcePaths),
472 PCHContainerOps(std::move(PCHContainerOps)),
473 OverlayFileSystem(new llvm::vfs::OverlayFileSystem(std::move(BaseFS))),
474 InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
475 Files(Files ? Files
476 : new FileManager(FileSystemOptions(), OverlayFileSystem)) {
477 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
481 if (Files)
482 Files->setVirtualFileSystem(OverlayFileSystem);
483}
484
485ClangTool::~ClangTool() = default;
486
487void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {
488 MappedFileContents.push_back(std::make_pair(FilePath, Content));
489}
490
492 ArgsAdjuster = combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
493}
494
496 ArgsAdjuster = nullptr;
497}
498
499static void injectResourceDir(CommandLineArguments &Args, const char *Argv0,
500 void *MainAddr) {
501 // Allow users to override the resource dir.
502 for (StringRef Arg : Args)
503 if (Arg.startswith("-resource-dir"))
504 return;
505
506 // If there's no override in place add our resource dir.
508 ("-resource-dir=" + CompilerInvocation::GetResourcesPath(Argv0, MainAddr))
509 .c_str())(Args, "");
510}
511
513 // Exists solely for the purpose of lookup of the resource path.
514 // This just needs to be some symbol in the binary.
515 static int StaticSymbol;
516
517 // First insert all absolute paths into the in-memory VFS. These are global
518 // for all compile commands.
519 if (SeenWorkingDirectories.insert("/").second)
520 for (const auto &MappedFile : MappedFileContents)
521 if (llvm::sys::path::is_absolute(MappedFile.first))
522 InMemoryFileSystem->addFile(
523 MappedFile.first, 0,
524 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
525
526 bool ProcessingFailed = false;
527 bool FileSkipped = false;
528 // Compute all absolute paths before we run any actions, as those will change
529 // the working directory.
530 std::vector<std::string> AbsolutePaths;
531 AbsolutePaths.reserve(SourcePaths.size());
532 for (const auto &SourcePath : SourcePaths) {
533 auto AbsPath = getAbsolutePath(*OverlayFileSystem, SourcePath);
534 if (!AbsPath) {
535 llvm::errs() << "Skipping " << SourcePath
536 << ". Error while getting an absolute path: "
537 << llvm::toString(AbsPath.takeError()) << "\n";
538 continue;
539 }
540 AbsolutePaths.push_back(std::move(*AbsPath));
541 }
542
543 // Remember the working directory in case we need to restore it.
544 std::string InitialWorkingDir;
545 if (RestoreCWD) {
546 if (auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
547 InitialWorkingDir = std::move(*CWD);
548 } else {
549 llvm::errs() << "Could not get working directory: "
550 << CWD.getError().message() << "\n";
551 }
552 }
553
554 for (llvm::StringRef File : AbsolutePaths) {
555 // Currently implementations of CompilationDatabase::getCompileCommands can
556 // change the state of the file system (e.g. prepare generated headers), so
557 // this method needs to run right before we invoke the tool, as the next
558 // file may require a different (incompatible) state of the file system.
559 //
560 // FIXME: Make the compilation database interface more explicit about the
561 // requirements to the order of invocation of its members.
562 std::vector<CompileCommand> CompileCommandsForFile =
563 Compilations.getCompileCommands(File);
564 if (CompileCommandsForFile.empty()) {
565 llvm::errs() << "Skipping " << File << ". Compile command not found.\n";
566 FileSkipped = true;
567 continue;
568 }
569 for (CompileCommand &CompileCommand : CompileCommandsForFile) {
570 // FIXME: chdir is thread hostile; on the other hand, creating the same
571 // behavior as chdir is complex: chdir resolves the path once, thus
572 // guaranteeing that all subsequent relative path operations work
573 // on the same path the original chdir resulted in. This makes a
574 // difference for example on network filesystems, where symlinks might be
575 // switched during runtime of the tool. Fixing this depends on having a
576 // file system abstraction that allows openat() style interactions.
577 if (OverlayFileSystem->setCurrentWorkingDirectory(
579 llvm::report_fatal_error("Cannot chdir into \"" +
580 Twine(CompileCommand.Directory) + "\"!");
581
582 // Now fill the in-memory VFS with the relative file mappings so it will
583 // have the correct relative paths. We never remove mappings but that
584 // should be fine.
585 if (SeenWorkingDirectories.insert(CompileCommand.Directory).second)
586 for (const auto &MappedFile : MappedFileContents)
587 if (!llvm::sys::path::is_absolute(MappedFile.first))
588 InMemoryFileSystem->addFile(
589 MappedFile.first, 0,
590 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
591
592 std::vector<std::string> CommandLine = CompileCommand.CommandLine;
593 if (ArgsAdjuster)
594 CommandLine = ArgsAdjuster(CommandLine, CompileCommand.Filename);
595 assert(!CommandLine.empty());
596
597 // Add the resource dir based on the binary of this tool. argv[0] in the
598 // compilation database may refer to a different compiler and we want to
599 // pick up the very same standard library that compiler is using. The
600 // builtin headers in the resource dir need to match the exact clang
601 // version the tool is using.
602 // FIXME: On linux, GetMainExecutable is independent of the value of the
603 // first argument, thus allowing ClangTool and runToolOnCode to just
604 // pass in made-up names here. Make sure this works on other platforms.
605 injectResourceDir(CommandLine, "clang_tool", &StaticSymbol);
606
607 // FIXME: We need a callback mechanism for the tool writer to output a
608 // customized message for each file.
609 LLVM_DEBUG({ llvm::dbgs() << "Processing: " << File << ".\n"; });
610 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
611 PCHContainerOps);
612 Invocation.setDiagnosticConsumer(DiagConsumer);
613
614 if (!Invocation.run()) {
615 // FIXME: Diagnostics should be used instead.
616 if (PrintErrorMessage)
617 llvm::errs() << "Error while processing " << File << ".\n";
618 ProcessingFailed = true;
619 }
620 }
621 }
622
623 if (!InitialWorkingDir.empty()) {
624 if (auto EC =
625 OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
626 llvm::errs() << "Error when trying to restore working dir: "
627 << EC.message() << "\n";
628 }
629 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
630}
631
632namespace {
633
634class ASTBuilderAction : public ToolAction {
635 std::vector<std::unique_ptr<ASTUnit>> &ASTs;
636
637public:
638 ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
639
640 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
641 FileManager *Files,
642 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
643 DiagnosticConsumer *DiagConsumer) override {
644 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
645 Invocation, std::move(PCHContainerOps),
646 CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts(),
647 DiagConsumer,
648 /*ShouldOwnClient=*/false),
649 Files);
650 if (!AST)
651 return false;
652
653 ASTs.push_back(std::move(AST));
654 return true;
655 }
656};
657
658} // namespace
659
660int ClangTool::buildASTs(std::vector<std::unique_ptr<ASTUnit>> &ASTs) {
661 ASTBuilderAction Action(ASTs);
662 return run(&Action);
663}
664
665void ClangTool::setRestoreWorkingDir(bool RestoreCWD) {
666 this->RestoreCWD = RestoreCWD;
667}
668
669void ClangTool::setPrintErrorMessage(bool PrintErrorMessage) {
670 this->PrintErrorMessage = PrintErrorMessage;
671}
672
673namespace clang {
674namespace tooling {
675
676std::unique_ptr<ASTUnit>
677buildASTFromCode(StringRef Code, StringRef FileName,
678 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
679 return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName,
680 "clang-tool", std::move(PCHContainerOps));
681}
682
683std::unique_ptr<ASTUnit> buildASTFromCodeWithArgs(
684 StringRef Code, const std::vector<std::string> &Args, StringRef FileName,
685 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
686 ArgumentsAdjuster Adjuster, const FileContentMappings &VirtualMappedFiles,
687 DiagnosticConsumer *DiagConsumer) {
688 std::vector<std::unique_ptr<ASTUnit>> ASTs;
689 ASTBuilderAction Action(ASTs);
691 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
693 new llvm::vfs::InMemoryFileSystem);
694 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
696 new FileManager(FileSystemOptions(), OverlayFileSystem));
697
698 ToolInvocation Invocation(
699 getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileName), FileName),
700 &Action, Files.get(), std::move(PCHContainerOps));
701 Invocation.setDiagnosticConsumer(DiagConsumer);
702
703 InMemoryFileSystem->addFile(FileName, 0,
704 llvm::MemoryBuffer::getMemBufferCopy(Code));
705 for (auto &FilenameWithContent : VirtualMappedFiles) {
706 InMemoryFileSystem->addFile(
707 FilenameWithContent.first, 0,
708 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
709 }
710
711 if (!Invocation.run())
712 return nullptr;
713
714 assert(ASTs.size() == 1);
715 return std::move(ASTs[0]);
716}
717
718} // namespace tooling
719} // namespace clang
Defines the Diagnostic-related interfaces.
Defines the Diagnostic IDs-related interfaces.
Defines the clang::FileManager interface and associated types.
Defines the clang::FileSystemOptions interface.
CompileCommand Cmd
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
static void injectResourceDir(CommandLineArguments &Args, const char *Argv0, void *MainAddr)
Definition: Tooling.cpp:499
static std::vector< std::string > getSyntaxOnlyToolArgs(const Twine &ToolName, const std::vector< std::string > &ExtraArgs, StringRef FileName)
Definition: Tooling.cpp:188
static bool ignoreExtraCC1Commands(const driver::Compilation *Compilation)
Decide whether extra compiler frontend commands can be ignored.
Definition: Tooling.cpp:88
static driver::Driver * newDriver(DiagnosticsEngine *Diagnostics, const char *BinaryName, IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS)
Builds a clang driver initialized for running clang tools.
Definition: Tooling.cpp:78
CompilerInstance - Helper class for managing a single instance of the Clang compiler.
void setFileManager(FileManager *Value)
Replace the current file manager and virtual file system.
void createSourceManager(FileManager &FileMgr)
Create the source manager and replace any existing one with it.
void createDiagnostics(DiagnosticConsumer *Client=nullptr, bool ShouldOwnClient=true)
Create the diagnostics engine using the invocation's diagnostic options and replace any existing one ...
void setInvocation(std::shared_ptr< CompilerInvocation > Value)
setInvocation - Replace the current invocation.
bool ExecuteAction(FrontendAction &Act)
ExecuteAction - Execute the provided action against the compiler's CompilerInvocation object.
Helper class for holding the data necessary to invoke the compiler.
static std::string GetResourcesPath(const char *Argv0, void *MainAddr)
Get the directory where the compiler headers reside, relative to the compiler binary (found by the pa...
static bool CreateFromArgs(CompilerInvocation &Res, ArrayRef< const char * > CommandLineArgs, DiagnosticsEngine &Diags, const char *Argv0=nullptr)
Create a compiler invocation from a list of input options.
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
Definition: Diagnostic.h:1740
Options for controlling the compiler diagnostics engine.
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:192
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1542
Implements support for file system lookup, file system caching, and directory search management.
Definition: FileManager.h:53
void clearStatCache()
Removes the FileSystemStatCache object from the manager.
Definition: FileManager.cpp:69
llvm::vfs::FileSystem & getVirtualFileSystem() const
Definition: FileManager.h:245
FileSystemOptions & getFileSystemOpts()
Returns the current file system options.
Definition: FileManager.h:242
Keeps track of options that affect how file operations are performed.
std::string WorkingDir
If set, paths are resolved as if the working directory was set to the value of WorkingDir.
unsigned DisableFree
Disable memory freeing on exit.
This class handles loading and caching of source files into memory.
Token - This structure provides full information about a lexed token.
Definition: Token.h:35
Command - An executable path/name and argument vector to execute.
Definition: Job.h:106
Compilation - A set of tasks to perform for a single driver invocation.
Definition: Compilation.h:45
ActionList & getActions()
Definition: Compilation.h:205
Driver - Encapsulate logic for constructing compilation processes from a set of gcc-driver-like comma...
Definition: Driver.h:77
void setTitle(std::string Value)
Definition: Driver.h:407
InputInfo - Wrapper for information about an input source.
Definition: InputInfo.h:22
JobList - A sequence of jobs to perform.
Definition: Job.h:262
size_type size() const
Definition: Job.h:285
void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo=nullptr) const
Definition: Job.cpp:452
static ParsedClangName getTargetAndModeFromProgramName(StringRef ProgName)
Return any implicit target and/or mode flag for an invocation of the compiler driver as ProgName.
Definition: ToolChain.cpp:273
int run(ToolAction *Action)
Runs an action over all files specified in the command line.
Definition: Tooling.cpp:512
void setPrintErrorMessage(bool PrintErrorMessage)
Sets whether an error message should be printed out if an action fails.
Definition: Tooling.cpp:669
void setRestoreWorkingDir(bool RestoreCWD)
Sets whether working directory should be restored after calling run().
Definition: Tooling.cpp:665
void appendArgumentsAdjuster(ArgumentsAdjuster Adjuster)
Append a command line arguments adjuster to the adjuster chain.
Definition: Tooling.cpp:491
ClangTool(const CompilationDatabase &Compilations, ArrayRef< std::string > SourcePaths, std::shared_ptr< PCHContainerOperations > PCHContainerOps=std::make_shared< PCHContainerOperations >(), IntrusiveRefCntPtr< llvm::vfs::FileSystem > BaseFS=llvm::vfs::getRealFileSystem(), IntrusiveRefCntPtr< FileManager > Files=nullptr)
Constructs a clang tool to run over a list of files.
Definition: Tooling.cpp:466
void clearArgumentsAdjusters()
Clear the command line arguments adjuster chain.
Definition: Tooling.cpp:495
void mapVirtualFile(StringRef FilePath, StringRef Content)
Map a virtual file to be used while running the tool.
Definition: Tooling.cpp:487
int buildASTs(std::vector< std::unique_ptr< ASTUnit > > &ASTs)
Create an AST for each file specified in the command line and append them to ASTs.
Definition: Tooling.cpp:660
Interface for compilation databases.
virtual std::vector< CompileCommand > getCompileCommands(StringRef FilePath) const =0
Returns all compile commands in which the specified file was compiled.
Interface to generate clang::FrontendActions.
Definition: Tooling.h:98
virtual std::unique_ptr< FrontendAction > create()=0
Returns a new clang::FrontendAction.
bool runInvocation(std::shared_ptr< CompilerInvocation > Invocation, FileManager *Files, std::shared_ptr< PCHContainerOperations > PCHContainerOps, DiagnosticConsumer *DiagConsumer) override
Invokes the compiler with a FrontendAction created by create().
Definition: Tooling.cpp:439
Interface to process a clang::CompilerInvocation.
Definition: Tooling.h:80
virtual bool runInvocation(std::shared_ptr< CompilerInvocation > Invocation, FileManager *Files, std::shared_ptr< PCHContainerOperations > PCHContainerOps, DiagnosticConsumer *DiagConsumer)=0
Perform an action for an invocation.
Utility to run a FrontendAction in a single clang invocation.
Definition: Tooling.h:239
void setDiagnosticConsumer(DiagnosticConsumer *DiagConsumer)
Set a DiagnosticConsumer to use during driver command-line parsing and the action invocation itself.
Definition: Tooling.h:272
bool run()
Run the clang invocation.
Definition: Tooling.cpp:367
ToolInvocation(std::vector< std::string > CommandLine, std::unique_ptr< FrontendAction > FAction, FileManager *Files, std::shared_ptr< PCHContainerOperations > PCHContainerOps=std::make_shared< PCHContainerOperations >())
Create a tool invocation.
Definition: Tooling.cpp:353
const llvm::opt::OptTable & getDriverOptTable()
std::vector< std::string > CommandLineArguments
A sequence of command line arguments.
std::vector< std::pair< std::string, std::string > > FileContentMappings
The first part of the pair is the filename, the second part the file-content.
Definition: Tooling.h:171
ArgumentsAdjuster getClangSyntaxOnlyAdjuster()
Gets an argument adjuster that converts input command line arguments to the "syntax check only" varia...
ArgumentsAdjuster combineAdjusters(ArgumentsAdjuster First, ArgumentsAdjuster Second)
Gets an argument adjuster which adjusts the arguments in sequence with the First adjuster and then wi...
const llvm::opt::ArgStringList * getCC1Arguments(DiagnosticsEngine *Diagnostics, driver::Compilation *Compilation)
Retrieves the flags of the -cc1 job in Compilation that has only source files as its inputs.
Definition: Tooling.cpp:133
std::unique_ptr< ASTUnit > buildASTFromCode(StringRef Code, StringRef FileName="input.cc", std::shared_ptr< PCHContainerOperations > PCHContainerOps=std::make_shared< PCHContainerOperations >())
Builds an AST for 'Code'.
Definition: Tooling.cpp:677
ArgumentsAdjuster getClangStripDependencyFileAdjuster()
Gets an argument adjuster which removes dependency-file related command line arguments.
std::function< CommandLineArguments(const CommandLineArguments &, StringRef Filename)> ArgumentsAdjuster
A prototype of a command line adjuster.
void addExpandedResponseFiles(std::vector< std::string > &CommandLine, llvm::StringRef WorkingDir, llvm::cl::TokenizerCallback Tokenizer, llvm::vfs::FileSystem &FS)
Helper function that expands response files in command line.
Definition: Tooling.cpp:303
void addTargetAndModeForProgramName(std::vector< std::string > &CommandLine, StringRef InvokedAs)
Changes CommandLine to contain implicit flags that would have been defined had the compiler driver be...
Definition: Tooling.cpp:266
ArgumentsAdjuster getInsertArgumentAdjuster(const CommandLineArguments &Extra, ArgumentInsertPosition Pos)
Gets an argument adjuster which inserts Extra arguments in the specified position.
bool runToolOnCodeWithArgs(std::unique_ptr< FrontendAction > ToolAction, const Twine &Code, const std::vector< std::string > &Args, const Twine &FileName="input.cc", const Twine &ToolName="clang-tool", std::shared_ptr< PCHContainerOperations > PCHContainerOps=std::make_shared< PCHContainerOperations >(), const FileContentMappings &VirtualMappedFiles=FileContentMappings())
Runs (and deletes) the tool on 'Code' with the -fsyntax-only flag and with additional other flags.
Definition: Tooling.cpp:220
std::unique_ptr< ASTUnit > buildASTFromCodeWithArgs(StringRef Code, const std::vector< std::string > &Args, StringRef FileName="input.cc", StringRef ToolName="clang-tool", std::shared_ptr< PCHContainerOperations > PCHContainerOps=std::make_shared< PCHContainerOperations >(), ArgumentsAdjuster Adjuster=getClangStripDependencyFileAdjuster(), const FileContentMappings &VirtualMappedFiles=FileContentMappings(), DiagnosticConsumer *DiagConsumer=nullptr)
Builds an AST for 'Code' with additional flags.
Definition: Tooling.cpp:683
CompilerInvocation * newInvocation(DiagnosticsEngine *Diagnostics, ArrayRef< const char * > CC1Args, const char *const BinaryName)
Creates a CompilerInvocation.
Definition: Tooling.cpp:164
std::string getAbsolutePath(StringRef File)
Returns the absolute path of File, by prepending it with the current directory if File is not absolut...
Definition: Tooling.cpp:262
ArgumentsAdjuster getClangStripOutputAdjuster()
Gets an argument adjuster which removes output-related command line arguments.
bool runToolOnCode(std::unique_ptr< FrontendAction > ToolAction, const Twine &Code, const Twine &FileName="input.cc", std::shared_ptr< PCHContainerOperations > PCHContainerOps=std::make_shared< PCHContainerOperations >())
Runs (and deletes) the tool on 'Code' with the -fsyntax-only flag.
Definition: Tooling.cpp:176
std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)
YAML serialization mapping.
Definition: Dominators.h:30
Definition: Format.h:4756
#define true
Definition: stdbool.h:21
#define false
Definition: stdbool.h:22
Specifies the working directory and command of a compilation.
std::string Filename
The source file associated with the command.
std::string Directory
The working directory the command was executed from.
std::vector< std::string > CommandLine
The command line that was executed.