clang 22.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/StringRef.h"
40#include "llvm/ADT/Twine.h"
41#include "llvm/Option/ArgList.h"
42#include "llvm/Option/OptTable.h"
43#include "llvm/Option/Option.h"
44#include "llvm/Support/CommandLine.h"
45#include "llvm/Support/Debug.h"
46#include "llvm/Support/ErrorHandling.h"
47#include "llvm/Support/MemoryBuffer.h"
48#include "llvm/Support/Path.h"
49#include "llvm/Support/VirtualFileSystem.h"
50#include "llvm/Support/raw_ostream.h"
51#include "llvm/TargetParser/Host.h"
52#include <cassert>
53#include <cstring>
54#include <memory>
55#include <string>
56#include <system_error>
57#include <utility>
58#include <vector>
59
60#define DEBUG_TYPE "clang-tooling"
61
62using namespace clang;
63using namespace tooling;
64
65ToolAction::~ToolAction() = default;
66
68
69// FIXME: This file contains structural duplication with other parts of the
70// code that sets up a compiler to run tools on it, and we should refactor
71// it to be based on the same framework.
72
73/// Builds a clang driver initialized for running clang tools.
74static driver::Driver *
75newDriver(DiagnosticsEngine *Diagnostics, const char *BinaryName,
77 driver::Driver *CompilerDriver =
78 new driver::Driver(BinaryName, llvm::sys::getDefaultTargetTriple(),
79 *Diagnostics, "clang LLVM compiler", std::move(VFS));
80 CompilerDriver->setTitle("clang_based_tool");
81 return CompilerDriver;
82}
83
84/// Decide whether extra compiler frontend commands can be ignored.
85static bool ignoreExtraCC1Commands(const driver::Compilation *Compilation) {
86 const driver::JobList &Jobs = Compilation->getJobs();
87 const driver::ActionList &Actions = Compilation->getActions();
88
89 bool OffloadCompilation = false;
90
91 // Jobs and Actions look very different depending on whether the Clang tool
92 // injected -fsyntax-only or not. Try to handle both cases here.
93
94 for (const auto &Job : Jobs)
95 if (StringRef(Job.getExecutable()) == "clang-offload-bundler")
96 OffloadCompilation = true;
97
98 if (Jobs.size() > 1) {
99 for (auto *A : Actions){
100 // On MacOSX real actions may end up being wrapped in BindArchAction
101 if (isa<driver::BindArchAction>(A))
102 A = *A->input_begin();
103 if (isa<driver::OffloadAction>(A)) {
104 // Offload compilation has 2 top-level actions, one (at the front) is
105 // the original host compilation and the other is offload action
106 // composed of at least one device compilation. For such case, general
107 // tooling will consider host-compilation only. For tooling on device
108 // compilation, device compilation only option, such as
109 // `--cuda-device-only`, needs specifying.
110 assert(Actions.size() > 1);
111 assert(
112 isa<driver::CompileJobAction>(Actions.front()) ||
113 // On MacOSX real actions may end up being wrapped in
114 // BindArchAction.
115 (isa<driver::BindArchAction>(Actions.front()) &&
116 isa<driver::CompileJobAction>(*Actions.front()->input_begin())));
117 OffloadCompilation = true;
118 break;
119 }
120 }
121 }
122
123 return OffloadCompilation;
124}
125
126namespace clang {
127namespace tooling {
128
129const llvm::opt::ArgStringList *
131 driver::Compilation *Compilation) {
132 const driver::JobList &Jobs = Compilation->getJobs();
133
134 auto IsCC1Command = [](const driver::Command &Cmd) {
135 return StringRef(Cmd.getCreator().getName()) == "clang";
136 };
137
138 auto IsSrcFile = [](const driver::InputInfo &II) {
139 return isSrcFile(II.getType());
140 };
141
143 for (const driver::Command &Job : Jobs)
144 if (IsCC1Command(Job) && llvm::all_of(Job.getInputInfos(), IsSrcFile))
145 CC1Jobs.push_back(&Job);
146
147 // If there are no jobs for source files, try checking again for a single job
148 // with any file type. This accepts a preprocessed file as input.
149 if (CC1Jobs.empty())
150 for (const driver::Command &Job : Jobs)
151 if (IsCC1Command(Job))
152 CC1Jobs.push_back(&Job);
153
154 if (CC1Jobs.empty() ||
155 (CC1Jobs.size() > 1 && !ignoreExtraCC1Commands(Compilation))) {
156 SmallString<256> error_msg;
157 llvm::raw_svector_ostream error_stream(error_msg);
158 Jobs.Print(error_stream, "; ", true);
159 Diagnostics->Report(diag::err_fe_expected_compiler_job)
160 << error_stream.str();
161 return nullptr;
162 }
163
164 return &CC1Jobs[0]->getArguments();
165}
166
167/// Returns a clang build invocation initialized from the CC1 flags.
170 const char *const BinaryName) {
171 assert(!CC1Args.empty() && "Must at least contain the program name!");
172 CompilerInvocation *Invocation = new CompilerInvocation;
173 CompilerInvocation::CreateFromArgs(*Invocation, CC1Args, *Diagnostics,
174 BinaryName);
175 Invocation->getFrontendOpts().DisableFree = false;
176 Invocation->getCodeGenOpts().DisableFree = false;
177 return Invocation;
178}
179
180bool runToolOnCode(std::unique_ptr<FrontendAction> ToolAction,
181 const Twine &Code, const Twine &FileName,
182 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
183 return runToolOnCodeWithArgs(std::move(ToolAction), Code,
184 std::vector<std::string>(), FileName,
185 "clang-tool", std::move(PCHContainerOps));
186}
187
188} // namespace tooling
189} // namespace clang
190
191static std::vector<std::string>
192getSyntaxOnlyToolArgs(const Twine &ToolName,
193 const std::vector<std::string> &ExtraArgs,
194 StringRef FileName) {
195 std::vector<std::string> Args;
196 Args.push_back(ToolName.str());
197 Args.push_back("-fsyntax-only");
198 llvm::append_range(Args, ExtraArgs);
199 Args.push_back(FileName.str());
200 return Args;
201}
202
203namespace clang {
204namespace tooling {
205
207 std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,
209 const std::vector<std::string> &Args, const Twine &FileName,
210 const Twine &ToolName,
211 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
212 SmallString<16> FileNameStorage;
213 StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
214
216 llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOptions(), VFS);
218 ToolInvocation Invocation(
219 getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileNameRef), FileNameRef),
220 std::move(ToolAction), Files.get(), std::move(PCHContainerOps));
221 return Invocation.run();
222}
223
225 std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,
226 const std::vector<std::string> &Args, const Twine &FileName,
227 const Twine &ToolName,
228 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
229 const FileContentMappings &VirtualMappedFiles) {
230 auto OverlayFileSystem =
231 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
232 llvm::vfs::getRealFileSystem());
233 auto InMemoryFileSystem =
234 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
235 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
236
237 SmallString<1024> CodeStorage;
238 InMemoryFileSystem->addFile(FileName, 0,
239 llvm::MemoryBuffer::getMemBuffer(
240 Code.toNullTerminatedStringRef(CodeStorage)));
241
242 for (auto &FilenameWithContent : VirtualMappedFiles) {
243 InMemoryFileSystem->addFile(
244 FilenameWithContent.first, 0,
245 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
246 }
247
248 return runToolOnCodeWithArgs(std::move(ToolAction), Code, OverlayFileSystem,
249 Args, FileName, ToolName);
250}
251
253 StringRef File) {
254 StringRef RelativePath(File);
255 // FIXME: Should '.\\' be accepted on Win32?
256 RelativePath.consume_front("./");
257
258 SmallString<1024> AbsolutePath = RelativePath;
259 if (auto EC = FS.makeAbsolute(AbsolutePath))
260 return llvm::errorCodeToError(EC);
261 llvm::sys::path::native(AbsolutePath);
262 return std::string(AbsolutePath);
263}
264
265std::string getAbsolutePath(StringRef File) {
266 return llvm::cantFail(getAbsolutePath(*llvm::vfs::getRealFileSystem(), File));
267}
268
269void addTargetAndModeForProgramName(std::vector<std::string> &CommandLine,
270 StringRef InvokedAs) {
271 if (CommandLine.empty() || InvokedAs.empty())
272 return;
273 const auto &Table = driver::getDriverOptTable();
274 // --target=X
275 StringRef TargetOPT =
276 Table.getOption(driver::options::OPT_target).getPrefixedName();
277 // -target X
278 StringRef TargetOPTLegacy =
279 Table.getOption(driver::options::OPT_target_legacy_spelling)
280 .getPrefixedName();
281 // --driver-mode=X
282 StringRef DriverModeOPT =
283 Table.getOption(driver::options::OPT_driver_mode).getPrefixedName();
284 auto TargetMode =
286 // No need to search for target args if we don't have a target/mode to insert.
287 bool ShouldAddTarget = TargetMode.TargetIsValid;
288 bool ShouldAddMode = TargetMode.DriverMode != nullptr;
289 // Skip CommandLine[0].
290 for (auto Token = ++CommandLine.begin(); Token != CommandLine.end();
291 ++Token) {
292 StringRef TokenRef(*Token);
293 ShouldAddTarget = ShouldAddTarget && !TokenRef.starts_with(TargetOPT) &&
294 TokenRef != TargetOPTLegacy;
295 ShouldAddMode = ShouldAddMode && !TokenRef.starts_with(DriverModeOPT);
296 }
297 if (ShouldAddMode) {
298 CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
299 }
300 if (ShouldAddTarget) {
301 CommandLine.insert(++CommandLine.begin(),
302 (TargetOPT + TargetMode.TargetPrefix).str());
303 }
304}
305
306void addExpandedResponseFiles(std::vector<std::string> &CommandLine,
307 llvm::StringRef WorkingDir,
308 llvm::cl::TokenizerCallback Tokenizer,
309 llvm::vfs::FileSystem &FS) {
310 bool SeenRSPFile = false;
312 Argv.reserve(CommandLine.size());
313 for (auto &Arg : CommandLine) {
314 Argv.push_back(Arg.c_str());
315 if (!Arg.empty())
316 SeenRSPFile |= Arg.front() == '@';
317 }
318 if (!SeenRSPFile)
319 return;
320 llvm::BumpPtrAllocator Alloc;
321 llvm::cl::ExpansionContext ECtx(Alloc, Tokenizer);
322 llvm::Error Err =
323 ECtx.setVFS(&FS).setCurrentDir(WorkingDir).expandResponseFiles(Argv);
324 if (Err)
325 llvm::errs() << Err;
326 // Don't assign directly, Argv aliases CommandLine.
327 std::vector<std::string> ExpandedArgv(Argv.begin(), Argv.end());
328 CommandLine = std::move(ExpandedArgv);
329}
330
331} // namespace tooling
332} // namespace clang
333
334namespace {
335
336class SingleFrontendActionFactory : public FrontendActionFactory {
337 std::unique_ptr<FrontendAction> Action;
338
339public:
340 SingleFrontendActionFactory(std::unique_ptr<FrontendAction> Action)
341 : Action(std::move(Action)) {}
342
343 std::unique_ptr<FrontendAction> create() override {
344 return std::move(Action);
345 }
346};
347
348} // namespace
349
351 std::vector<std::string> CommandLine, ToolAction *Action,
352 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
353 : CommandLine(std::move(CommandLine)), Action(Action), OwnsAction(false),
354 Files(Files), PCHContainerOps(std::move(PCHContainerOps)) {}
355
357 std::vector<std::string> CommandLine,
358 std::unique_ptr<FrontendAction> FAction, FileManager *Files,
359 std::shared_ptr<PCHContainerOperations> PCHContainerOps)
360 : CommandLine(std::move(CommandLine)),
361 Action(new SingleFrontendActionFactory(std::move(FAction))),
362 OwnsAction(true), Files(Files),
363 PCHContainerOps(std::move(PCHContainerOps)) {}
364
366 if (OwnsAction)
367 delete Action;
368}
369
371 llvm::opt::ArgStringList Argv;
372 for (const std::string &Str : CommandLine)
373 Argv.push_back(Str.c_str());
374 const char *const BinaryName = Argv[0];
375
376 // Parse diagnostic options from the driver command-line only if none were
377 // explicitly set.
378 std::unique_ptr<DiagnosticOptions> ParsedDiagOpts;
379 DiagnosticOptions *DiagOpts = this->DiagOpts;
380 if (!DiagOpts) {
381 ParsedDiagOpts = CreateAndPopulateDiagOpts(Argv);
382 DiagOpts = &*ParsedDiagOpts;
383 }
384
385 TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), *DiagOpts);
388 Files->getVirtualFileSystem(), *DiagOpts,
389 DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);
390 // Although `Diagnostics` are used only for command-line parsing, the custom
391 // `DiagConsumer` might expect a `SourceManager` to be present.
392 SourceManager SrcMgr(*Diagnostics, *Files);
393 Diagnostics->setSourceManager(&SrcMgr);
394
395 // We already have a cc1, just create an invocation.
396 if (CommandLine.size() >= 2 && CommandLine[1] == "-cc1") {
397 ArrayRef<const char *> CC1Args = ArrayRef(Argv).drop_front();
398 std::unique_ptr<CompilerInvocation> Invocation(
399 newInvocation(&*Diagnostics, CC1Args, BinaryName));
400 if (Diagnostics->hasErrorOccurred())
401 return false;
402 return Action->runInvocation(std::move(Invocation), Files,
403 std::move(PCHContainerOps), DiagConsumer);
404 }
405
406 const std::unique_ptr<driver::Driver> Driver(
407 newDriver(&*Diagnostics, BinaryName, Files->getVirtualFileSystemPtr()));
408 // The "input file not found" diagnostics from the driver are useful.
409 // The driver is only aware of the VFS working directory, but some clients
410 // change this at the FileManager level instead.
411 // In this case the checks have false positives, so skip them.
412 if (!Files->getFileSystemOpts().WorkingDir.empty())
413 Driver->setCheckInputsExist(false);
414 const std::unique_ptr<driver::Compilation> Compilation(
415 Driver->BuildCompilation(llvm::ArrayRef(Argv)));
416 if (!Compilation)
417 return false;
418 const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
419 &*Diagnostics, Compilation.get());
420 if (!CC1Args)
421 return false;
422 std::unique_ptr<CompilerInvocation> Invocation(
423 newInvocation(&*Diagnostics, *CC1Args, BinaryName));
424 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
425 std::move(PCHContainerOps));
426}
427
428bool ToolInvocation::runInvocation(
429 const char *BinaryName, driver::Compilation *Compilation,
430 std::shared_ptr<CompilerInvocation> Invocation,
431 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
432 // Show the invocation, with -v.
433 if (Invocation->getHeaderSearchOpts().Verbose) {
434 llvm::errs() << "clang Invocation:\n";
435 Compilation->getJobs().Print(llvm::errs(), "\n", true);
436 llvm::errs() << "\n";
437 }
438
439 return Action->runInvocation(std::move(Invocation), Files,
440 std::move(PCHContainerOps), DiagConsumer);
441}
442
444 std::shared_ptr<CompilerInvocation> Invocation, FileManager *Files,
445 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
446 DiagnosticConsumer *DiagConsumer) {
447 // Create a compiler instance to handle the actual work.
448 CompilerInstance Compiler(std::move(Invocation), std::move(PCHContainerOps));
449 Compiler.setFileManager(Files);
450
451 // The FrontendAction can have lifetime requirements for Compiler or its
452 // members, and we need to ensure it's deleted earlier than Compiler. So we
453 // pass it to an std::unique_ptr declared after the Compiler variable.
454 std::unique_ptr<FrontendAction> ScopedToolAction(create());
455
456 // Create the compiler's actual diagnostics engine.
457 Compiler.createDiagnostics(Files->getVirtualFileSystem(), DiagConsumer,
458 /*ShouldOwnClient=*/false);
459 if (!Compiler.hasDiagnostics())
460 return false;
461
462 Compiler.createSourceManager(*Files);
463
464 const bool Success = Compiler.ExecuteAction(*ScopedToolAction);
465
466 Files->clearStatCache();
467 return Success;
468}
469
471 ArrayRef<std::string> SourcePaths,
472 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
475 : Compilations(Compilations), SourcePaths(SourcePaths),
476 PCHContainerOps(std::move(PCHContainerOps)),
477 OverlayFileSystem(llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
478 std::move(BaseFS))),
479 InMemoryFileSystem(
480 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>()),
481 Files(Files ? Files
482 : llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOptions(),
483 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.starts_with("-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 size_t NumOfTotalFiles = AbsolutePaths.size();
560 unsigned ProcessedFileCounter = 0;
561 for (llvm::StringRef File : AbsolutePaths) {
562 // Currently implementations of CompilationDatabase::getCompileCommands can
563 // change the state of the file system (e.g. prepare generated headers), so
564 // this method needs to run right before we invoke the tool, as the next
565 // file may require a different (incompatible) state of the file system.
566 //
567 // FIXME: Make the compilation database interface more explicit about the
568 // requirements to the order of invocation of its members.
569 std::vector<CompileCommand> CompileCommandsForFile =
570 Compilations.getCompileCommands(File);
571 if (CompileCommandsForFile.empty()) {
572 llvm::errs() << "Skipping " << File << ". Compile command not found.\n";
573 FileSkipped = true;
574 continue;
575 }
576 for (CompileCommand &CompileCommand : CompileCommandsForFile) {
577 // FIXME: chdir is thread hostile; on the other hand, creating the same
578 // behavior as chdir is complex: chdir resolves the path once, thus
579 // guaranteeing that all subsequent relative path operations work
580 // on the same path the original chdir resulted in. This makes a
581 // difference for example on network filesystems, where symlinks might be
582 // switched during runtime of the tool. Fixing this depends on having a
583 // file system abstraction that allows openat() style interactions.
584 if (OverlayFileSystem->setCurrentWorkingDirectory(
586 llvm::report_fatal_error("Cannot chdir into \"" +
587 Twine(CompileCommand.Directory) + "\"!");
588
589 // Now fill the in-memory VFS with the relative file mappings so it will
590 // have the correct relative paths. We never remove mappings but that
591 // should be fine.
592 if (SeenWorkingDirectories.insert(CompileCommand.Directory).second)
593 for (const auto &MappedFile : MappedFileContents)
594 if (!llvm::sys::path::is_absolute(MappedFile.first))
595 InMemoryFileSystem->addFile(
596 MappedFile.first, 0,
597 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
598
599 std::vector<std::string> CommandLine = CompileCommand.CommandLine;
600 if (ArgsAdjuster)
601 CommandLine = ArgsAdjuster(CommandLine, CompileCommand.Filename);
602 assert(!CommandLine.empty());
603
604 // Add the resource dir based on the binary of this tool. argv[0] in the
605 // compilation database may refer to a different compiler and we want to
606 // pick up the very same standard library that compiler is using. The
607 // builtin headers in the resource dir need to match the exact clang
608 // version the tool is using.
609 // FIXME: On linux, GetMainExecutable is independent of the value of the
610 // first argument, thus allowing ClangTool and runToolOnCode to just
611 // pass in made-up names here. Make sure this works on other platforms.
612 injectResourceDir(CommandLine, "clang_tool", &StaticSymbol);
613
614 // FIXME: We need a callback mechanism for the tool writer to output a
615 // customized message for each file.
616 if (NumOfTotalFiles > 1)
617 llvm::errs() << "[" + std::to_string(++ProcessedFileCounter) + "/" +
618 std::to_string(NumOfTotalFiles) +
619 "] Processing file " + File
620 << ".\n";
621 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
622 PCHContainerOps);
623 Invocation.setDiagnosticConsumer(DiagConsumer);
624
625 if (!Invocation.run()) {
626 // FIXME: Diagnostics should be used instead.
627 if (PrintErrorMessage)
628 llvm::errs() << "Error while processing " << File << ".\n";
629 ProcessingFailed = true;
630 }
631 }
632 }
633
634 if (!InitialWorkingDir.empty()) {
635 if (auto EC =
636 OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
637 llvm::errs() << "Error when trying to restore working dir: "
638 << EC.message() << "\n";
639 }
640 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
641}
642
643namespace {
644
645class ASTBuilderAction : public ToolAction {
646 std::vector<std::unique_ptr<ASTUnit>> &ASTs;
647 CaptureDiagsKind CaptureKind;
648
649public:
650 ASTBuilderAction(
651 std::vector<std::unique_ptr<ASTUnit>> &ASTs,
652 CaptureDiagsKind CaptureDiagnosticsKind = CaptureDiagsKind::None)
653 : ASTs(ASTs), CaptureKind(CaptureDiagnosticsKind) {}
654
655 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
656 FileManager *Files,
657 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
658 DiagnosticConsumer *DiagConsumer) override {
659 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
660 Invocation, std::move(PCHContainerOps), nullptr,
662 Invocation->getDiagnosticOpts(),
663 DiagConsumer,
664 /*ShouldOwnClient=*/false),
665 Files, false, CaptureKind);
666 if (!AST)
667 return false;
668
669 ASTs.push_back(std::move(AST));
670 return true;
671 }
672};
673
674} // namespace
675
676int ClangTool::buildASTs(std::vector<std::unique_ptr<ASTUnit>> &ASTs) {
677 ASTBuilderAction Action(ASTs);
678 return run(&Action);
679}
680
681void ClangTool::setPrintErrorMessage(bool PrintErrorMessage) {
682 this->PrintErrorMessage = PrintErrorMessage;
683}
684
685namespace clang {
686namespace tooling {
687
688std::unique_ptr<ASTUnit>
689buildASTFromCode(StringRef Code, StringRef FileName,
690 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
691 return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName,
692 "clang-tool", std::move(PCHContainerOps));
693}
694
695std::unique_ptr<ASTUnit> buildASTFromCodeWithArgs(
696 StringRef Code, const std::vector<std::string> &Args, StringRef FileName,
697 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
698 ArgumentsAdjuster Adjuster, const FileContentMappings &VirtualMappedFiles,
699 DiagnosticConsumer *DiagConsumer,
701 CaptureDiagsKind CaptureKind) {
702 std::vector<std::unique_ptr<ASTUnit>> ASTs;
703
704 ASTBuilderAction Action(ASTs, CaptureKind);
705
706 auto OverlayFileSystem =
707 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
708 std::move(BaseFS));
709 auto InMemoryFileSystem =
710 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
711 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
713 llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOptions(),
714 OverlayFileSystem);
715
716 ToolInvocation Invocation(
717 getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileName), FileName),
718 &Action, Files.get(), std::move(PCHContainerOps));
719 Invocation.setDiagnosticConsumer(DiagConsumer);
720
721 InMemoryFileSystem->addFile(FileName, 0,
722 llvm::MemoryBuffer::getMemBufferCopy(Code));
723 for (auto &FilenameWithContent : VirtualMappedFiles) {
724 InMemoryFileSystem->addFile(
725 FilenameWithContent.first, 0,
726 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
727 }
728
729 if (!Invocation.run())
730 return nullptr;
731
732 assert(ASTs.size() == 1);
733 return std::move(ASTs[0]);
734}
735
736} // namespace tooling
737} // 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:192
static bool ignoreExtraCC1Commands(const driver::Compilation *Compilation)
Decide whether extra compiler frontend commands can be ignored.
Definition: Tooling.cpp:85
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:75
CompilerInstance - Helper class for managing a single instance of the Clang compiler.
void createSourceManager(FileManager &FileMgr)
Create the source manager and replace any existing one with it.
void createDiagnostics(llvm::vfs::FileSystem &VFS, DiagnosticConsumer *Client=nullptr, bool ShouldOwnClient=true)
Create the diagnostics engine using the invocation's diagnostic options and replace any existing one ...
void setFileManager(IntrusiveRefCntPtr< FileManager > Value)
Replace the current file manager and virtual file system.
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:1722
Options for controlling the compiler diagnostics engine.
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:231
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1529
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:61
llvm::vfs::FileSystem & getVirtualFileSystem() const
Definition: FileManager.h:219
FileSystemOptions & getFileSystemOpts()
Returns the current file system options.
Definition: FileManager.h:216
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > getVirtualFileSystemPtr() const
Definition: FileManager.h:221
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:36
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:199
Driver - Encapsulate logic for constructing compilation processes from a set of gcc-driver-like comma...
Definition: Driver.h:99
void setTitle(std::string Value)
Definition: Driver.h:447
InputInfo - Wrapper for information about an input source.
Definition: InputInfo.h:22
JobList - A sequence of jobs to perform.
Definition: Job.h:260
size_type size() const
Definition: Job.h:283
void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo=nullptr) const
Definition: Job.cpp:449
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:504
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:681
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:470
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:676
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:99
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:443
Interface to process a clang::CompilerInvocation.
Definition: Tooling.h:81
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:247
void setDiagnosticConsumer(DiagnosticConsumer *DiagConsumer)
Set a DiagnosticConsumer to use during driver command-line parsing and the action invocation itself.
Definition: Tooling.h:283
bool run()
Run the clang invocation.
Definition: Tooling.cpp:370
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:356
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:172
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:130
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:689
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:306
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:269
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:224
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, IntrusiveRefCntPtr< llvm::vfs::FileSystem > BaseFS=llvm::vfs::getRealFileSystem(), CaptureDiagsKind CaptureKind=CaptureDiagsKind::None)
Builds an AST for 'Code' with additional flags.
Definition: Tooling.cpp:695
CompilerInvocation * newInvocation(DiagnosticsEngine *Diagnostics, ArrayRef< const char * > CC1Args, const char *const BinaryName)
Creates a CompilerInvocation.
Definition: Tooling.cpp:168
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:265
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:180
The JSON file list parser is used to communicate input to InstallAPI.
std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)
@ Success
Annotation was successful.
CaptureDiagsKind
Enumerates the available kinds for capturing diagnostics.
Definition: ASTUnit.h:87
Diagnostic wrappers for TextAPI types for error reporting.
Definition: Dominators.h:30
#define true
Definition: stdbool.h:25
#define false
Definition: stdbool.h:26
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.