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