clang 19.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.equals(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 &*DiagOpts, DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);
391 // Although `Diagnostics` are used only for command-line parsing, the custom
392 // `DiagConsumer` might expect a `SourceManager` to be present.
393 SourceManager SrcMgr(*Diagnostics, *Files);
394 Diagnostics->setSourceManager(&SrcMgr);
395
396 // We already have a cc1, just create an invocation.
397 if (CommandLine.size() >= 2 && CommandLine[1] == "-cc1") {
398 ArrayRef<const char *> CC1Args = ArrayRef(Argv).drop_front();
399 std::unique_ptr<CompilerInvocation> Invocation(
400 newInvocation(&*Diagnostics, CC1Args, BinaryName));
401 if (Diagnostics->hasErrorOccurred())
402 return false;
403 return Action->runInvocation(std::move(Invocation), Files,
404 std::move(PCHContainerOps), DiagConsumer);
405 }
406
407 const std::unique_ptr<driver::Driver> Driver(
408 newDriver(&*Diagnostics, BinaryName, &Files->getVirtualFileSystem()));
409 // The "input file not found" diagnostics from the driver are useful.
410 // The driver is only aware of the VFS working directory, but some clients
411 // change this at the FileManager level instead.
412 // In this case the checks have false positives, so skip them.
413 if (!Files->getFileSystemOpts().WorkingDir.empty())
414 Driver->setCheckInputsExist(false);
415 const std::unique_ptr<driver::Compilation> Compilation(
416 Driver->BuildCompilation(llvm::ArrayRef(Argv)));
417 if (!Compilation)
418 return false;
419 const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
420 &*Diagnostics, Compilation.get());
421 if (!CC1Args)
422 return false;
423 std::unique_ptr<CompilerInvocation> Invocation(
424 newInvocation(&*Diagnostics, *CC1Args, BinaryName));
425 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
426 std::move(PCHContainerOps));
427}
428
429bool ToolInvocation::runInvocation(
430 const char *BinaryName, driver::Compilation *Compilation,
431 std::shared_ptr<CompilerInvocation> Invocation,
432 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
433 // Show the invocation, with -v.
434 if (Invocation->getHeaderSearchOpts().Verbose) {
435 llvm::errs() << "clang Invocation:\n";
436 Compilation->getJobs().Print(llvm::errs(), "\n", true);
437 llvm::errs() << "\n";
438 }
439
440 return Action->runInvocation(std::move(Invocation), Files,
441 std::move(PCHContainerOps), DiagConsumer);
442}
443
445 std::shared_ptr<CompilerInvocation> Invocation, FileManager *Files,
446 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
447 DiagnosticConsumer *DiagConsumer) {
448 // Create a compiler instance to handle the actual work.
449 CompilerInstance Compiler(std::move(PCHContainerOps));
450 Compiler.setInvocation(std::move(Invocation));
451 Compiler.setFileManager(Files);
452
453 // The FrontendAction can have lifetime requirements for Compiler or its
454 // members, and we need to ensure it's deleted earlier than Compiler. So we
455 // pass it to an std::unique_ptr declared after the Compiler variable.
456 std::unique_ptr<FrontendAction> ScopedToolAction(create());
457
458 // Create the compiler's actual diagnostics engine.
459 Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
460 if (!Compiler.hasDiagnostics())
461 return false;
462
463 Compiler.createSourceManager(*Files);
464
465 const bool Success = Compiler.ExecuteAction(*ScopedToolAction);
466
467 Files->clearStatCache();
468 return Success;
469}
470
472 ArrayRef<std::string> SourcePaths,
473 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
476 : Compilations(Compilations), SourcePaths(SourcePaths),
477 PCHContainerOps(std::move(PCHContainerOps)),
478 OverlayFileSystem(new llvm::vfs::OverlayFileSystem(std::move(BaseFS))),
479 InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
480 Files(Files ? Files
481 : new FileManager(FileSystemOptions(), OverlayFileSystem)) {
482 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
486 if (Files)
487 Files->setVirtualFileSystem(OverlayFileSystem);
488}
489
490ClangTool::~ClangTool() = default;
491
492void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {
493 MappedFileContents.push_back(std::make_pair(FilePath, Content));
494}
495
497 ArgsAdjuster = combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
498}
499
501 ArgsAdjuster = nullptr;
502}
503
504static void injectResourceDir(CommandLineArguments &Args, const char *Argv0,
505 void *MainAddr) {
506 // Allow users to override the resource dir.
507 for (StringRef Arg : Args)
508 if (Arg.starts_with("-resource-dir"))
509 return;
510
511 // If there's no override in place add our resource dir.
513 ("-resource-dir=" + CompilerInvocation::GetResourcesPath(Argv0, MainAddr))
514 .c_str())(Args, "");
515}
516
518 // Exists solely for the purpose of lookup of the resource path.
519 // This just needs to be some symbol in the binary.
520 static int StaticSymbol;
521
522 // First insert all absolute paths into the in-memory VFS. These are global
523 // for all compile commands.
524 if (SeenWorkingDirectories.insert("/").second)
525 for (const auto &MappedFile : MappedFileContents)
526 if (llvm::sys::path::is_absolute(MappedFile.first))
527 InMemoryFileSystem->addFile(
528 MappedFile.first, 0,
529 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
530
531 bool ProcessingFailed = false;
532 bool FileSkipped = false;
533 // Compute all absolute paths before we run any actions, as those will change
534 // the working directory.
535 std::vector<std::string> AbsolutePaths;
536 AbsolutePaths.reserve(SourcePaths.size());
537 for (const auto &SourcePath : SourcePaths) {
538 auto AbsPath = getAbsolutePath(*OverlayFileSystem, SourcePath);
539 if (!AbsPath) {
540 llvm::errs() << "Skipping " << SourcePath
541 << ". Error while getting an absolute path: "
542 << llvm::toString(AbsPath.takeError()) << "\n";
543 continue;
544 }
545 AbsolutePaths.push_back(std::move(*AbsPath));
546 }
547
548 // Remember the working directory in case we need to restore it.
549 std::string InitialWorkingDir;
550 if (auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
551 InitialWorkingDir = std::move(*CWD);
552 } else {
553 llvm::errs() << "Could not get working directory: "
554 << CWD.getError().message() << "\n";
555 }
556
557 size_t NumOfTotalFiles = AbsolutePaths.size();
558 unsigned ProcessedFileCounter = 0;
559 for (llvm::StringRef File : AbsolutePaths) {
560 // Currently implementations of CompilationDatabase::getCompileCommands can
561 // change the state of the file system (e.g. prepare generated headers), so
562 // this method needs to run right before we invoke the tool, as the next
563 // file may require a different (incompatible) state of the file system.
564 //
565 // FIXME: Make the compilation database interface more explicit about the
566 // requirements to the order of invocation of its members.
567 std::vector<CompileCommand> CompileCommandsForFile =
568 Compilations.getCompileCommands(File);
569 if (CompileCommandsForFile.empty()) {
570 llvm::errs() << "Skipping " << File << ". Compile command not found.\n";
571 FileSkipped = true;
572 continue;
573 }
574 for (CompileCommand &CompileCommand : CompileCommandsForFile) {
575 // FIXME: chdir is thread hostile; on the other hand, creating the same
576 // behavior as chdir is complex: chdir resolves the path once, thus
577 // guaranteeing that all subsequent relative path operations work
578 // on the same path the original chdir resulted in. This makes a
579 // difference for example on network filesystems, where symlinks might be
580 // switched during runtime of the tool. Fixing this depends on having a
581 // file system abstraction that allows openat() style interactions.
582 if (OverlayFileSystem->setCurrentWorkingDirectory(
584 llvm::report_fatal_error("Cannot chdir into \"" +
585 Twine(CompileCommand.Directory) + "\"!");
586
587 // Now fill the in-memory VFS with the relative file mappings so it will
588 // have the correct relative paths. We never remove mappings but that
589 // should be fine.
590 if (SeenWorkingDirectories.insert(CompileCommand.Directory).second)
591 for (const auto &MappedFile : MappedFileContents)
592 if (!llvm::sys::path::is_absolute(MappedFile.first))
593 InMemoryFileSystem->addFile(
594 MappedFile.first, 0,
595 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
596
597 std::vector<std::string> CommandLine = CompileCommand.CommandLine;
598 if (ArgsAdjuster)
599 CommandLine = ArgsAdjuster(CommandLine, CompileCommand.Filename);
600 assert(!CommandLine.empty());
601
602 // Add the resource dir based on the binary of this tool. argv[0] in the
603 // compilation database may refer to a different compiler and we want to
604 // pick up the very same standard library that compiler is using. The
605 // builtin headers in the resource dir need to match the exact clang
606 // version the tool is using.
607 // FIXME: On linux, GetMainExecutable is independent of the value of the
608 // first argument, thus allowing ClangTool and runToolOnCode to just
609 // pass in made-up names here. Make sure this works on other platforms.
610 injectResourceDir(CommandLine, "clang_tool", &StaticSymbol);
611
612 // FIXME: We need a callback mechanism for the tool writer to output a
613 // customized message for each file.
614 if (NumOfTotalFiles > 1)
615 llvm::errs() << "[" + std::to_string(++ProcessedFileCounter) + "/" +
616 std::to_string(NumOfTotalFiles) +
617 "] Processing file " + File
618 << ".\n";
619 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
620 PCHContainerOps);
621 Invocation.setDiagnosticConsumer(DiagConsumer);
622
623 if (!Invocation.run()) {
624 // FIXME: Diagnostics should be used instead.
625 if (PrintErrorMessage)
626 llvm::errs() << "Error while processing " << File << ".\n";
627 ProcessingFailed = true;
628 }
629 }
630 }
631
632 if (!InitialWorkingDir.empty()) {
633 if (auto EC =
634 OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
635 llvm::errs() << "Error when trying to restore working dir: "
636 << EC.message() << "\n";
637 }
638 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
639}
640
641namespace {
642
643class ASTBuilderAction : public ToolAction {
644 std::vector<std::unique_ptr<ASTUnit>> &ASTs;
645
646public:
647 ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
648
649 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
650 FileManager *Files,
651 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
652 DiagnosticConsumer *DiagConsumer) override {
653 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
654 Invocation, std::move(PCHContainerOps),
655 CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts(),
656 DiagConsumer,
657 /*ShouldOwnClient=*/false),
658 Files);
659 if (!AST)
660 return false;
661
662 ASTs.push_back(std::move(AST));
663 return true;
664 }
665};
666
667} // namespace
668
669int ClangTool::buildASTs(std::vector<std::unique_ptr<ASTUnit>> &ASTs) {
670 ASTBuilderAction Action(ASTs);
671 return run(&Action);
672}
673
674void ClangTool::setPrintErrorMessage(bool PrintErrorMessage) {
675 this->PrintErrorMessage = PrintErrorMessage;
676}
677
678namespace clang {
679namespace tooling {
680
681std::unique_ptr<ASTUnit>
682buildASTFromCode(StringRef Code, StringRef FileName,
683 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
684 return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName,
685 "clang-tool", std::move(PCHContainerOps));
686}
687
688std::unique_ptr<ASTUnit> buildASTFromCodeWithArgs(
689 StringRef Code, const std::vector<std::string> &Args, StringRef FileName,
690 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
691 ArgumentsAdjuster Adjuster, const FileContentMappings &VirtualMappedFiles,
692 DiagnosticConsumer *DiagConsumer) {
693 std::vector<std::unique_ptr<ASTUnit>> ASTs;
694 ASTBuilderAction Action(ASTs);
696 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
698 new llvm::vfs::InMemoryFileSystem);
699 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
701 new FileManager(FileSystemOptions(), OverlayFileSystem));
702
703 ToolInvocation Invocation(
704 getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileName), FileName),
705 &Action, Files.get(), std::move(PCHContainerOps));
706 Invocation.setDiagnosticConsumer(DiagConsumer);
707
708 InMemoryFileSystem->addFile(FileName, 0,
709 llvm::MemoryBuffer::getMemBufferCopy(Code));
710 for (auto &FilenameWithContent : VirtualMappedFiles) {
711 InMemoryFileSystem->addFile(
712 FilenameWithContent.first, 0,
713 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
714 }
715
716 if (!Invocation.run())
717 return nullptr;
718
719 assert(ASTs.size() == 1);
720 return std::move(ASTs[0]);
721}
722
723} // namespace tooling
724} // 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:504
static std::vector< std::string > getSyntaxOnlyToolArgs(const Twine &ToolName, const std::vector< std::string > &ExtraArgs, StringRef FileName)
Definition: Tooling.cpp:195
static bool ignoreExtraCC1Commands(const driver::Compilation *Compilation)
Decide whether extra compiler frontend commands can be ignored.
Definition: Tooling.cpp:88
static driver::Driver * newDriver(DiagnosticsEngine *Diagnostics, const char *BinaryName, IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS)
Builds a clang driver initialized for running clang tools.
Definition: Tooling.cpp:78
CompilerInstance - Helper class for managing a single instance of the Clang compiler.
void setFileManager(FileManager *Value)
Replace the current file manager and virtual file system.
void createSourceManager(FileManager &FileMgr)
Create the source manager and replace any existing one with it.
void createDiagnostics(DiagnosticConsumer *Client=nullptr, bool ShouldOwnClient=true)
Create the diagnostics engine using the invocation's diagnostic options and replace any existing one ...
void setInvocation(std::shared_ptr< CompilerInvocation > Value)
setInvocation - Replace the current invocation.
bool ExecuteAction(FrontendAction &Act)
ExecuteAction - Execute the provided action against the compiler's CompilerInvocation object.
Helper class for holding the data necessary to invoke the compiler.
static std::string GetResourcesPath(const char *Argv0, void *MainAddr)
Get the directory where the compiler headers reside, relative to the compiler binary (found by the pa...
static bool CreateFromArgs(CompilerInvocation &Res, ArrayRef< const char * > CommandLineArgs, DiagnosticsEngine &Diags, const char *Argv0=nullptr)
Create a compiler invocation from a list of input options.
FrontendOptions & getFrontendOpts()
CodeGenOptions & getCodeGenOpts()
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
Definition: Diagnostic.h:1745
Options for controlling the compiler diagnostics engine.
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:192
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1547
Implements support for file system lookup, file system caching, and directory search management.
Definition: FileManager.h:53
void clearStatCache()
Removes the FileSystemStatCache object from the manager.
Definition: FileManager.cpp:63
llvm::vfs::FileSystem & getVirtualFileSystem() const
Definition: FileManager.h:251
FileSystemOptions & getFileSystemOpts()
Returns the current file system options.
Definition: FileManager.h:248
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:418
InputInfo - Wrapper for information about an input source.
Definition: InputInfo.h:22
JobList - A sequence of jobs to perform.
Definition: Job.h:262
size_type size() const
Definition: Job.h:285
void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo=nullptr) const
Definition: Job.cpp:453
static ParsedClangName getTargetAndModeFromProgramName(StringRef ProgName)
Return any implicit target and/or mode flag for an invocation of the compiler driver as ProgName.
Definition: ToolChain.cpp:398
int run(ToolAction *Action)
Runs an action over all files specified in the command line.
Definition: Tooling.cpp:517
void setPrintErrorMessage(bool PrintErrorMessage)
Sets whether an error message should be printed out if an action fails.
Definition: Tooling.cpp:674
void appendArgumentsAdjuster(ArgumentsAdjuster Adjuster)
Append a command line arguments adjuster to the adjuster chain.
Definition: Tooling.cpp:496
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:471
void clearArgumentsAdjusters()
Clear the command line arguments adjuster chain.
Definition: Tooling.cpp:500
void mapVirtualFile(StringRef FilePath, StringRef Content)
Map a virtual file to be used while running the tool.
Definition: Tooling.cpp:492
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:669
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:444
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:682
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:688
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
Definition: Format.h:5394
#define true
Definition: stdbool.h:21
#define false
Definition: stdbool.h:22
Specifies the working directory and command of a compilation.
std::string Filename
The source file associated with the command.
std::string Directory
The working directory the command was executed from.
std::vector< std::string > CommandLine
The command line that was executed.