clang 18.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 there are no jobs for source files, try checking again for a single job
151 // with any file type. This accepts a preprocessed file as input.
152 if (CC1Jobs.empty())
153 for (const driver::Command &Job : Jobs)
154 if (IsCC1Command(Job))
155 CC1Jobs.push_back(&Job);
156
157 if (CC1Jobs.empty() ||
158 (CC1Jobs.size() > 1 && !ignoreExtraCC1Commands(Compilation))) {
159 SmallString<256> error_msg;
160 llvm::raw_svector_ostream error_stream(error_msg);
161 Jobs.Print(error_stream, "; ", true);
162 Diagnostics->Report(diag::err_fe_expected_compiler_job)
163 << error_stream.str();
164 return nullptr;
165 }
166
167 return &CC1Jobs[0]->getArguments();
168}
169
170/// Returns a clang build invocation initialized from the CC1 flags.
173 const char *const BinaryName) {
174 assert(!CC1Args.empty() && "Must at least contain the program name!");
175 CompilerInvocation *Invocation = new CompilerInvocation;
176 CompilerInvocation::CreateFromArgs(*Invocation, CC1Args, *Diagnostics,
177 BinaryName);
178 Invocation->getFrontendOpts().DisableFree = false;
179 Invocation->getCodeGenOpts().DisableFree = false;
180 return Invocation;
181}
182
183bool runToolOnCode(std::unique_ptr<FrontendAction> ToolAction,
184 const Twine &Code, const Twine &FileName,
185 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
186 return runToolOnCodeWithArgs(std::move(ToolAction), Code,
187 std::vector<std::string>(), FileName,
188 "clang-tool", std::move(PCHContainerOps));
189}
190
191} // namespace tooling
192} // namespace clang
193
194static std::vector<std::string>
195getSyntaxOnlyToolArgs(const Twine &ToolName,
196 const std::vector<std::string> &ExtraArgs,
197 StringRef FileName) {
198 std::vector<std::string> Args;
199 Args.push_back(ToolName.str());
200 Args.push_back("-fsyntax-only");
201 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
202 Args.push_back(FileName.str());
203 return Args;
204}
205
206namespace clang {
207namespace tooling {
208
210 std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,
212 const std::vector<std::string> &Args, const Twine &FileName,
213 const Twine &ToolName,
214 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
215 SmallString<16> FileNameStorage;
216 StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
217
219 new FileManager(FileSystemOptions(), VFS));
221 ToolInvocation Invocation(
222 getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileNameRef), FileNameRef),
223 std::move(ToolAction), Files.get(), std::move(PCHContainerOps));
224 return Invocation.run();
225}
226
228 std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,
229 const std::vector<std::string> &Args, const Twine &FileName,
230 const Twine &ToolName,
231 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
232 const FileContentMappings &VirtualMappedFiles) {
234 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
236 new llvm::vfs::InMemoryFileSystem);
237 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
238
239 SmallString<1024> CodeStorage;
240 InMemoryFileSystem->addFile(FileName, 0,
241 llvm::MemoryBuffer::getMemBuffer(
242 Code.toNullTerminatedStringRef(CodeStorage)));
243
244 for (auto &FilenameWithContent : VirtualMappedFiles) {
245 InMemoryFileSystem->addFile(
246 FilenameWithContent.first, 0,
247 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
248 }
249
250 return runToolOnCodeWithArgs(std::move(ToolAction), Code, OverlayFileSystem,
251 Args, FileName, ToolName);
252}
253
255 StringRef File) {
256 StringRef RelativePath(File);
257 // FIXME: Should '.\\' be accepted on Win32?
258 if (RelativePath.startswith("./")) {
259 RelativePath = RelativePath.substr(strlen("./"));
260 }
261
262 SmallString<1024> AbsolutePath = RelativePath;
263 if (auto EC = FS.makeAbsolute(AbsolutePath))
264 return llvm::errorCodeToError(EC);
265 llvm::sys::path::native(AbsolutePath);
266 return std::string(AbsolutePath.str());
267}
268
269std::string getAbsolutePath(StringRef File) {
270 return llvm::cantFail(getAbsolutePath(*llvm::vfs::getRealFileSystem(), File));
271}
272
273void addTargetAndModeForProgramName(std::vector<std::string> &CommandLine,
274 StringRef InvokedAs) {
275 if (CommandLine.empty() || InvokedAs.empty())
276 return;
277 const auto &Table = driver::getDriverOptTable();
278 // --target=X
279 StringRef TargetOPT =
280 Table.getOption(driver::options::OPT_target).getPrefixedName();
281 // -target X
282 StringRef TargetOPTLegacy =
283 Table.getOption(driver::options::OPT_target_legacy_spelling)
284 .getPrefixedName();
285 // --driver-mode=X
286 StringRef DriverModeOPT =
287 Table.getOption(driver::options::OPT_driver_mode).getPrefixedName();
288 auto TargetMode =
290 // No need to search for target args if we don't have a target/mode to insert.
291 bool ShouldAddTarget = TargetMode.TargetIsValid;
292 bool ShouldAddMode = TargetMode.DriverMode != nullptr;
293 // Skip CommandLine[0].
294 for (auto Token = ++CommandLine.begin(); Token != CommandLine.end();
295 ++Token) {
296 StringRef TokenRef(*Token);
297 ShouldAddTarget = ShouldAddTarget && !TokenRef.startswith(TargetOPT) &&
298 !TokenRef.equals(TargetOPTLegacy);
299 ShouldAddMode = ShouldAddMode && !TokenRef.startswith(DriverModeOPT);
300 }
301 if (ShouldAddMode) {
302 CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
303 }
304 if (ShouldAddTarget) {
305 CommandLine.insert(++CommandLine.begin(),
306 (TargetOPT + TargetMode.TargetPrefix).str());
307 }
308}
309
310void addExpandedResponseFiles(std::vector<std::string> &CommandLine,
311 llvm::StringRef WorkingDir,
312 llvm::cl::TokenizerCallback Tokenizer,
313 llvm::vfs::FileSystem &FS) {
314 bool SeenRSPFile = false;
316 Argv.reserve(CommandLine.size());
317 for (auto &Arg : CommandLine) {
318 Argv.push_back(Arg.c_str());
319 if (!Arg.empty())
320 SeenRSPFile |= Arg.front() == '@';
321 }
322 if (!SeenRSPFile)
323 return;
324 llvm::BumpPtrAllocator Alloc;
325 llvm::cl::ExpansionContext ECtx(Alloc, Tokenizer);
326 llvm::Error Err =
327 ECtx.setVFS(&FS).setCurrentDir(WorkingDir).expandResponseFiles(Argv);
328 if (Err)
329 llvm::errs() << Err;
330 // Don't assign directly, Argv aliases CommandLine.
331 std::vector<std::string> ExpandedArgv(Argv.begin(), Argv.end());
332 CommandLine = std::move(ExpandedArgv);
333}
334
335} // namespace tooling
336} // namespace clang
337
338namespace {
339
340class SingleFrontendActionFactory : public FrontendActionFactory {
341 std::unique_ptr<FrontendAction> Action;
342
343public:
344 SingleFrontendActionFactory(std::unique_ptr<FrontendAction> Action)
345 : Action(std::move(Action)) {}
346
347 std::unique_ptr<FrontendAction> create() override {
348 return std::move(Action);
349 }
350};
351
352} // namespace
353
355 std::vector<std::string> CommandLine, ToolAction *Action,
356 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
357 : CommandLine(std::move(CommandLine)), Action(Action), OwnsAction(false),
358 Files(Files), PCHContainerOps(std::move(PCHContainerOps)) {}
359
361 std::vector<std::string> CommandLine,
362 std::unique_ptr<FrontendAction> FAction, FileManager *Files,
363 std::shared_ptr<PCHContainerOperations> PCHContainerOps)
364 : CommandLine(std::move(CommandLine)),
365 Action(new SingleFrontendActionFactory(std::move(FAction))),
366 OwnsAction(true), Files(Files),
367 PCHContainerOps(std::move(PCHContainerOps)) {}
368
370 if (OwnsAction)
371 delete Action;
372}
373
375 llvm::opt::ArgStringList Argv;
376 for (const std::string &Str : CommandLine)
377 Argv.push_back(Str.c_str());
378 const char *const BinaryName = Argv[0];
379
380 // Parse diagnostic options from the driver command-line only if none were
381 // explicitly set.
383 DiagnosticOptions *DiagOpts = this->DiagOpts;
384 if (!DiagOpts) {
385 ParsedDiagOpts = CreateAndPopulateDiagOpts(Argv);
386 DiagOpts = &*ParsedDiagOpts;
387 }
388
389 TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), DiagOpts);
392 &*DiagOpts, DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);
393 // Although `Diagnostics` are used only for command-line parsing, the custom
394 // `DiagConsumer` might expect a `SourceManager` to be present.
395 SourceManager SrcMgr(*Diagnostics, *Files);
396 Diagnostics->setSourceManager(&SrcMgr);
397
398 // We already have a cc1, just create an invocation.
399 if (CommandLine.size() >= 2 && CommandLine[1] == "-cc1") {
400 ArrayRef<const char *> CC1Args = ArrayRef(Argv).drop_front();
401 std::unique_ptr<CompilerInvocation> Invocation(
402 newInvocation(&*Diagnostics, CC1Args, BinaryName));
403 if (Diagnostics->hasErrorOccurred())
404 return false;
405 return Action->runInvocation(std::move(Invocation), Files,
406 std::move(PCHContainerOps), DiagConsumer);
407 }
408
409 const std::unique_ptr<driver::Driver> Driver(
410 newDriver(&*Diagnostics, BinaryName, &Files->getVirtualFileSystem()));
411 // The "input file not found" diagnostics from the driver are useful.
412 // The driver is only aware of the VFS working directory, but some clients
413 // change this at the FileManager level instead.
414 // In this case the checks have false positives, so skip them.
415 if (!Files->getFileSystemOpts().WorkingDir.empty())
416 Driver->setCheckInputsExist(false);
417 const std::unique_ptr<driver::Compilation> Compilation(
418 Driver->BuildCompilation(llvm::ArrayRef(Argv)));
419 if (!Compilation)
420 return false;
421 const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
422 &*Diagnostics, Compilation.get());
423 if (!CC1Args)
424 return false;
425 std::unique_ptr<CompilerInvocation> Invocation(
426 newInvocation(&*Diagnostics, *CC1Args, BinaryName));
427 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
428 std::move(PCHContainerOps));
429}
430
431bool ToolInvocation::runInvocation(
432 const char *BinaryName, driver::Compilation *Compilation,
433 std::shared_ptr<CompilerInvocation> Invocation,
434 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
435 // Show the invocation, with -v.
436 if (Invocation->getHeaderSearchOpts().Verbose) {
437 llvm::errs() << "clang Invocation:\n";
438 Compilation->getJobs().Print(llvm::errs(), "\n", true);
439 llvm::errs() << "\n";
440 }
441
442 return Action->runInvocation(std::move(Invocation), Files,
443 std::move(PCHContainerOps), DiagConsumer);
444}
445
447 std::shared_ptr<CompilerInvocation> Invocation, FileManager *Files,
448 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
449 DiagnosticConsumer *DiagConsumer) {
450 // Create a compiler instance to handle the actual work.
451 CompilerInstance Compiler(std::move(PCHContainerOps));
452 Compiler.setInvocation(std::move(Invocation));
453 Compiler.setFileManager(Files);
454
455 // The FrontendAction can have lifetime requirements for Compiler or its
456 // members, and we need to ensure it's deleted earlier than Compiler. So we
457 // pass it to an std::unique_ptr declared after the Compiler variable.
458 std::unique_ptr<FrontendAction> ScopedToolAction(create());
459
460 // Create the compiler's actual diagnostics engine.
461 Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
462 if (!Compiler.hasDiagnostics())
463 return false;
464
465 Compiler.createSourceManager(*Files);
466
467 const bool Success = Compiler.ExecuteAction(*ScopedToolAction);
468
469 Files->clearStatCache();
470 return Success;
471}
472
474 ArrayRef<std::string> SourcePaths,
475 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
478 : Compilations(Compilations), SourcePaths(SourcePaths),
479 PCHContainerOps(std::move(PCHContainerOps)),
480 OverlayFileSystem(new llvm::vfs::OverlayFileSystem(std::move(BaseFS))),
481 InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
482 Files(Files ? Files
483 : new FileManager(FileSystemOptions(), OverlayFileSystem)) {
484 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
488 if (Files)
489 Files->setVirtualFileSystem(OverlayFileSystem);
490}
491
492ClangTool::~ClangTool() = default;
493
494void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {
495 MappedFileContents.push_back(std::make_pair(FilePath, Content));
496}
497
499 ArgsAdjuster = combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
500}
501
503 ArgsAdjuster = nullptr;
504}
505
506static void injectResourceDir(CommandLineArguments &Args, const char *Argv0,
507 void *MainAddr) {
508 // Allow users to override the resource dir.
509 for (StringRef Arg : Args)
510 if (Arg.startswith("-resource-dir"))
511 return;
512
513 // If there's no override in place add our resource dir.
515 ("-resource-dir=" + CompilerInvocation::GetResourcesPath(Argv0, MainAddr))
516 .c_str())(Args, "");
517}
518
520 // Exists solely for the purpose of lookup of the resource path.
521 // This just needs to be some symbol in the binary.
522 static int StaticSymbol;
523
524 // First insert all absolute paths into the in-memory VFS. These are global
525 // for all compile commands.
526 if (SeenWorkingDirectories.insert("/").second)
527 for (const auto &MappedFile : MappedFileContents)
528 if (llvm::sys::path::is_absolute(MappedFile.first))
529 InMemoryFileSystem->addFile(
530 MappedFile.first, 0,
531 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
532
533 bool ProcessingFailed = false;
534 bool FileSkipped = false;
535 // Compute all absolute paths before we run any actions, as those will change
536 // the working directory.
537 std::vector<std::string> AbsolutePaths;
538 AbsolutePaths.reserve(SourcePaths.size());
539 for (const auto &SourcePath : SourcePaths) {
540 auto AbsPath = getAbsolutePath(*OverlayFileSystem, SourcePath);
541 if (!AbsPath) {
542 llvm::errs() << "Skipping " << SourcePath
543 << ". Error while getting an absolute path: "
544 << llvm::toString(AbsPath.takeError()) << "\n";
545 continue;
546 }
547 AbsolutePaths.push_back(std::move(*AbsPath));
548 }
549
550 // Remember the working directory in case we need to restore it.
551 std::string InitialWorkingDir;
552 if (auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
553 InitialWorkingDir = std::move(*CWD);
554 } else {
555 llvm::errs() << "Could not get working directory: "
556 << CWD.getError().message() << "\n";
557 }
558
559 for (llvm::StringRef File : AbsolutePaths) {
560 // Currently implementations of CompilationDatabase::getCompileCommands can
561 // change the state of the file system (e.g. prepare generated headers), so
562 // this method needs to run right before we invoke the tool, as the next
563 // file may require a different (incompatible) state of the file system.
564 //
565 // FIXME: Make the compilation database interface more explicit about the
566 // requirements to the order of invocation of its members.
567 std::vector<CompileCommand> CompileCommandsForFile =
568 Compilations.getCompileCommands(File);
569 if (CompileCommandsForFile.empty()) {
570 llvm::errs() << "Skipping " << File << ". Compile command not found.\n";
571 FileSkipped = true;
572 continue;
573 }
574 for (CompileCommand &CompileCommand : CompileCommandsForFile) {
575 // FIXME: chdir is thread hostile; on the other hand, creating the same
576 // behavior as chdir is complex: chdir resolves the path once, thus
577 // guaranteeing that all subsequent relative path operations work
578 // on the same path the original chdir resulted in. This makes a
579 // difference for example on network filesystems, where symlinks might be
580 // switched during runtime of the tool. Fixing this depends on having a
581 // file system abstraction that allows openat() style interactions.
582 if (OverlayFileSystem->setCurrentWorkingDirectory(
584 llvm::report_fatal_error("Cannot chdir into \"" +
585 Twine(CompileCommand.Directory) + "\"!");
586
587 // Now fill the in-memory VFS with the relative file mappings so it will
588 // have the correct relative paths. We never remove mappings but that
589 // should be fine.
590 if (SeenWorkingDirectories.insert(CompileCommand.Directory).second)
591 for (const auto &MappedFile : MappedFileContents)
592 if (!llvm::sys::path::is_absolute(MappedFile.first))
593 InMemoryFileSystem->addFile(
594 MappedFile.first, 0,
595 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
596
597 std::vector<std::string> CommandLine = CompileCommand.CommandLine;
598 if (ArgsAdjuster)
599 CommandLine = ArgsAdjuster(CommandLine, CompileCommand.Filename);
600 assert(!CommandLine.empty());
601
602 // Add the resource dir based on the binary of this tool. argv[0] in the
603 // compilation database may refer to a different compiler and we want to
604 // pick up the very same standard library that compiler is using. The
605 // builtin headers in the resource dir need to match the exact clang
606 // version the tool is using.
607 // FIXME: On linux, GetMainExecutable is independent of the value of the
608 // first argument, thus allowing ClangTool and runToolOnCode to just
609 // pass in made-up names here. Make sure this works on other platforms.
610 injectResourceDir(CommandLine, "clang_tool", &StaticSymbol);
611
612 // FIXME: We need a callback mechanism for the tool writer to output a
613 // customized message for each file.
614 LLVM_DEBUG({ llvm::dbgs() << "Processing: " << File << ".\n"; });
615 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
616 PCHContainerOps);
617 Invocation.setDiagnosticConsumer(DiagConsumer);
618
619 if (!Invocation.run()) {
620 // FIXME: Diagnostics should be used instead.
621 if (PrintErrorMessage)
622 llvm::errs() << "Error while processing " << File << ".\n";
623 ProcessingFailed = true;
624 }
625 }
626 }
627
628 if (!InitialWorkingDir.empty()) {
629 if (auto EC =
630 OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
631 llvm::errs() << "Error when trying to restore working dir: "
632 << EC.message() << "\n";
633 }
634 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
635}
636
637namespace {
638
639class ASTBuilderAction : public ToolAction {
640 std::vector<std::unique_ptr<ASTUnit>> &ASTs;
641
642public:
643 ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
644
645 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
646 FileManager *Files,
647 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
648 DiagnosticConsumer *DiagConsumer) override {
649 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
650 Invocation, std::move(PCHContainerOps),
651 CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts(),
652 DiagConsumer,
653 /*ShouldOwnClient=*/false),
654 Files);
655 if (!AST)
656 return false;
657
658 ASTs.push_back(std::move(AST));
659 return true;
660 }
661};
662
663} // namespace
664
665int ClangTool::buildASTs(std::vector<std::unique_ptr<ASTUnit>> &ASTs) {
666 ASTBuilderAction Action(ASTs);
667 return run(&Action);
668}
669
670void ClangTool::setPrintErrorMessage(bool PrintErrorMessage) {
671 this->PrintErrorMessage = PrintErrorMessage;
672}
673
674namespace clang {
675namespace tooling {
676
677std::unique_ptr<ASTUnit>
678buildASTFromCode(StringRef Code, StringRef FileName,
679 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
680 return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName,
681 "clang-tool", std::move(PCHContainerOps));
682}
683
684std::unique_ptr<ASTUnit> buildASTFromCodeWithArgs(
685 StringRef Code, const std::vector<std::string> &Args, StringRef FileName,
686 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
687 ArgumentsAdjuster Adjuster, const FileContentMappings &VirtualMappedFiles,
688 DiagnosticConsumer *DiagConsumer) {
689 std::vector<std::unique_ptr<ASTUnit>> ASTs;
690 ASTBuilderAction Action(ASTs);
692 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
694 new llvm::vfs::InMemoryFileSystem);
695 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
697 new FileManager(FileSystemOptions(), OverlayFileSystem));
698
699 ToolInvocation Invocation(
700 getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileName), FileName),
701 &Action, Files.get(), std::move(PCHContainerOps));
702 Invocation.setDiagnosticConsumer(DiagConsumer);
703
704 InMemoryFileSystem->addFile(FileName, 0,
705 llvm::MemoryBuffer::getMemBufferCopy(Code));
706 for (auto &FilenameWithContent : VirtualMappedFiles) {
707 InMemoryFileSystem->addFile(
708 FilenameWithContent.first, 0,
709 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
710 }
711
712 if (!Invocation.run())
713 return nullptr;
714
715 assert(ASTs.size() == 1);
716 return std::move(ASTs[0]);
717}
718
719} // namespace tooling
720} // 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:506
static std::vector< std::string > getSyntaxOnlyToolArgs(const Twine &ToolName, const std::vector< std::string > &ExtraArgs, StringRef FileName)
Definition: Tooling.cpp:195
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.
FrontendOptions & getFrontendOpts()
CodeGenOptions & getCodeGenOpts()
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
Definition: Diagnostic.h:1745
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:1547
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:453
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:372
int run(ToolAction *Action)
Runs an action over all files specified in the command line.
Definition: Tooling.cpp:519
void setPrintErrorMessage(bool PrintErrorMessage)
Sets whether an error message should be printed out if an action fails.
Definition: Tooling.cpp:670
void appendArgumentsAdjuster(ArgumentsAdjuster Adjuster)
Append a command line arguments adjuster to the adjuster chain.
Definition: Tooling.cpp:498
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:473
void clearArgumentsAdjusters()
Clear the command line arguments adjuster chain.
Definition: Tooling.cpp:502
void mapVirtualFile(StringRef FilePath, StringRef Content)
Map a virtual file to be used while running the tool.
Definition: Tooling.cpp:494
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:665
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:446
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:275
bool run()
Run the clang invocation.
Definition: Tooling.cpp:374
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:360
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:678
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:310
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:273
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:227
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:684
CompilerInvocation * newInvocation(DiagnosticsEngine *Diagnostics, ArrayRef< const char * > CC1Args, const char *const BinaryName)
Creates a CompilerInvocation.
Definition: Tooling.cpp:171
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:269
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:183
std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)
YAML serialization mapping.
Definition: Dominators.h:30
Definition: Format.h:5226
#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.