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