clang 20.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 RelativePath.consume_front("./");
259
260 SmallString<1024> AbsolutePath = RelativePath;
261 if (auto EC = FS.makeAbsolute(AbsolutePath))
262 return llvm::errorCodeToError(EC);
263 llvm::sys::path::native(AbsolutePath);
264 return std::string(AbsolutePath);
265}
266
267std::string getAbsolutePath(StringRef File) {
268 return llvm::cantFail(getAbsolutePath(*llvm::vfs::getRealFileSystem(), File));
269}
270
271void addTargetAndModeForProgramName(std::vector<std::string> &CommandLine,
272 StringRef InvokedAs) {
273 if (CommandLine.empty() || InvokedAs.empty())
274 return;
275 const auto &Table = driver::getDriverOptTable();
276 // --target=X
277 StringRef TargetOPT =
278 Table.getOption(driver::options::OPT_target).getPrefixedName();
279 // -target X
280 StringRef TargetOPTLegacy =
281 Table.getOption(driver::options::OPT_target_legacy_spelling)
282 .getPrefixedName();
283 // --driver-mode=X
284 StringRef DriverModeOPT =
285 Table.getOption(driver::options::OPT_driver_mode).getPrefixedName();
286 auto TargetMode =
288 // No need to search for target args if we don't have a target/mode to insert.
289 bool ShouldAddTarget = TargetMode.TargetIsValid;
290 bool ShouldAddMode = TargetMode.DriverMode != nullptr;
291 // Skip CommandLine[0].
292 for (auto Token = ++CommandLine.begin(); Token != CommandLine.end();
293 ++Token) {
294 StringRef TokenRef(*Token);
295 ShouldAddTarget = ShouldAddTarget && !TokenRef.starts_with(TargetOPT) &&
296 TokenRef != TargetOPTLegacy;
297 ShouldAddMode = ShouldAddMode && !TokenRef.starts_with(DriverModeOPT);
298 }
299 if (ShouldAddMode) {
300 CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
301 }
302 if (ShouldAddTarget) {
303 CommandLine.insert(++CommandLine.begin(),
304 (TargetOPT + TargetMode.TargetPrefix).str());
305 }
306}
307
308void addExpandedResponseFiles(std::vector<std::string> &CommandLine,
309 llvm::StringRef WorkingDir,
310 llvm::cl::TokenizerCallback Tokenizer,
311 llvm::vfs::FileSystem &FS) {
312 bool SeenRSPFile = false;
314 Argv.reserve(CommandLine.size());
315 for (auto &Arg : CommandLine) {
316 Argv.push_back(Arg.c_str());
317 if (!Arg.empty())
318 SeenRSPFile |= Arg.front() == '@';
319 }
320 if (!SeenRSPFile)
321 return;
322 llvm::BumpPtrAllocator Alloc;
323 llvm::cl::ExpansionContext ECtx(Alloc, Tokenizer);
324 llvm::Error Err =
325 ECtx.setVFS(&FS).setCurrentDir(WorkingDir).expandResponseFiles(Argv);
326 if (Err)
327 llvm::errs() << Err;
328 // Don't assign directly, Argv aliases CommandLine.
329 std::vector<std::string> ExpandedArgv(Argv.begin(), Argv.end());
330 CommandLine = std::move(ExpandedArgv);
331}
332
333} // namespace tooling
334} // namespace clang
335
336namespace {
337
338class SingleFrontendActionFactory : public FrontendActionFactory {
339 std::unique_ptr<FrontendAction> Action;
340
341public:
342 SingleFrontendActionFactory(std::unique_ptr<FrontendAction> Action)
343 : Action(std::move(Action)) {}
344
345 std::unique_ptr<FrontendAction> create() override {
346 return std::move(Action);
347 }
348};
349
350} // namespace
351
353 std::vector<std::string> CommandLine, ToolAction *Action,
354 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
355 : CommandLine(std::move(CommandLine)), Action(Action), OwnsAction(false),
356 Files(Files), PCHContainerOps(std::move(PCHContainerOps)) {}
357
359 std::vector<std::string> CommandLine,
360 std::unique_ptr<FrontendAction> FAction, FileManager *Files,
361 std::shared_ptr<PCHContainerOperations> PCHContainerOps)
362 : CommandLine(std::move(CommandLine)),
363 Action(new SingleFrontendActionFactory(std::move(FAction))),
364 OwnsAction(true), Files(Files),
365 PCHContainerOps(std::move(PCHContainerOps)) {}
366
368 if (OwnsAction)
369 delete Action;
370}
371
373 llvm::opt::ArgStringList Argv;
374 for (const std::string &Str : CommandLine)
375 Argv.push_back(Str.c_str());
376 const char *const BinaryName = Argv[0];
377
378 // Parse diagnostic options from the driver command-line only if none were
379 // explicitly set.
381 DiagnosticOptions *DiagOpts = this->DiagOpts;
382 if (!DiagOpts) {
383 ParsedDiagOpts = CreateAndPopulateDiagOpts(Argv);
384 DiagOpts = &*ParsedDiagOpts;
385 }
386
387 TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), DiagOpts);
390 Files->getVirtualFileSystem(), &*DiagOpts,
391 DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);
392 // Although `Diagnostics` are used only for command-line parsing, the custom
393 // `DiagConsumer` might expect a `SourceManager` to be present.
394 SourceManager SrcMgr(*Diagnostics, *Files);
395 Diagnostics->setSourceManager(&SrcMgr);
396
397 // We already have a cc1, just create an invocation.
398 if (CommandLine.size() >= 2 && CommandLine[1] == "-cc1") {
399 ArrayRef<const char *> CC1Args = ArrayRef(Argv).drop_front();
400 std::unique_ptr<CompilerInvocation> Invocation(
401 newInvocation(&*Diagnostics, CC1Args, BinaryName));
402 if (Diagnostics->hasErrorOccurred())
403 return false;
404 return Action->runInvocation(std::move(Invocation), Files,
405 std::move(PCHContainerOps), DiagConsumer);
406 }
407
408 const std::unique_ptr<driver::Driver> Driver(
409 newDriver(&*Diagnostics, BinaryName, &Files->getVirtualFileSystem()));
410 // The "input file not found" diagnostics from the driver are useful.
411 // The driver is only aware of the VFS working directory, but some clients
412 // change this at the FileManager level instead.
413 // In this case the checks have false positives, so skip them.
414 if (!Files->getFileSystemOpts().WorkingDir.empty())
415 Driver->setCheckInputsExist(false);
416 const std::unique_ptr<driver::Compilation> Compilation(
417 Driver->BuildCompilation(llvm::ArrayRef(Argv)));
418 if (!Compilation)
419 return false;
420 const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
421 &*Diagnostics, Compilation.get());
422 if (!CC1Args)
423 return false;
424 std::unique_ptr<CompilerInvocation> Invocation(
425 newInvocation(&*Diagnostics, *CC1Args, BinaryName));
426 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
427 std::move(PCHContainerOps));
428}
429
430bool ToolInvocation::runInvocation(
431 const char *BinaryName, driver::Compilation *Compilation,
432 std::shared_ptr<CompilerInvocation> Invocation,
433 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
434 // Show the invocation, with -v.
435 if (Invocation->getHeaderSearchOpts().Verbose) {
436 llvm::errs() << "clang Invocation:\n";
437 Compilation->getJobs().Print(llvm::errs(), "\n", true);
438 llvm::errs() << "\n";
439 }
440
441 return Action->runInvocation(std::move(Invocation), Files,
442 std::move(PCHContainerOps), DiagConsumer);
443}
444
446 std::shared_ptr<CompilerInvocation> Invocation, FileManager *Files,
447 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
448 DiagnosticConsumer *DiagConsumer) {
449 // Create a compiler instance to handle the actual work.
450 CompilerInstance Compiler(std::move(PCHContainerOps));
451 Compiler.setInvocation(std::move(Invocation));
452 Compiler.setFileManager(Files);
453
454 // The FrontendAction can have lifetime requirements for Compiler or its
455 // members, and we need to ensure it's deleted earlier than Compiler. So we
456 // pass it to an std::unique_ptr declared after the Compiler variable.
457 std::unique_ptr<FrontendAction> ScopedToolAction(create());
458
459 // Create the compiler's actual diagnostics engine.
460 Compiler.createDiagnostics(Files->getVirtualFileSystem(), DiagConsumer,
461 /*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.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
648public:
649 ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
650
651 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
652 FileManager *Files,
653 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
654 DiagnosticConsumer *DiagConsumer) override {
655 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
656 Invocation, std::move(PCHContainerOps),
658 &Invocation->getDiagnosticOpts(),
659 DiagConsumer,
660 /*ShouldOwnClient=*/false),
661 Files);
662 if (!AST)
663 return false;
664
665 ASTs.push_back(std::move(AST));
666 return true;
667 }
668};
669
670} // namespace
671
672int ClangTool::buildASTs(std::vector<std::unique_ptr<ASTUnit>> &ASTs) {
673 ASTBuilderAction Action(ASTs);
674 return run(&Action);
675}
676
677void ClangTool::setPrintErrorMessage(bool PrintErrorMessage) {
678 this->PrintErrorMessage = PrintErrorMessage;
679}
680
681namespace clang {
682namespace tooling {
683
684std::unique_ptr<ASTUnit>
685buildASTFromCode(StringRef Code, StringRef FileName,
686 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
687 return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName,
688 "clang-tool", std::move(PCHContainerOps));
689}
690
691std::unique_ptr<ASTUnit> buildASTFromCodeWithArgs(
692 StringRef Code, const std::vector<std::string> &Args, StringRef FileName,
693 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
694 ArgumentsAdjuster Adjuster, const FileContentMappings &VirtualMappedFiles,
695 DiagnosticConsumer *DiagConsumer) {
696 std::vector<std::unique_ptr<ASTUnit>> ASTs;
697 ASTBuilderAction Action(ASTs);
699 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
701 new llvm::vfs::InMemoryFileSystem);
702 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
704 new FileManager(FileSystemOptions(), OverlayFileSystem));
705
706 ToolInvocation Invocation(
707 getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileName), FileName),
708 &Action, Files.get(), std::move(PCHContainerOps));
709 Invocation.setDiagnosticConsumer(DiagConsumer);
710
711 InMemoryFileSystem->addFile(FileName, 0,
712 llvm::MemoryBuffer::getMemBufferCopy(Code));
713 for (auto &FilenameWithContent : VirtualMappedFiles) {
714 InMemoryFileSystem->addFile(
715 FilenameWithContent.first, 0,
716 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
717 }
718
719 if (!Invocation.run())
720 return nullptr;
721
722 assert(ASTs.size() == 1);
723 return std::move(ASTs[0]);
724}
725
726} // namespace tooling
727} // 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(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 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:1684
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:1493
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:63
llvm::vfs::FileSystem & getVirtualFileSystem() const
Definition: FileManager.h:256
FileSystemOptions & getFileSystemOpts()
Returns the current file system options.
Definition: FileManager.h:253
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: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:420
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:450
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:479
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:677
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:672
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:445
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:372
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:358
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:685
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:308
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:271
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:691
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:267
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
The JSON file list parser is used to communicate input to InstallAPI.
std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)
@ Success
Template argument deduction was successful.
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.