clang: lib/Tooling/Tooling.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

13

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

56#include

57#include

58#include

59#include <system_error>

60#include

61#include

62

63#define DEBUG_TYPE "clang-tooling"

64

65using namespace clang;

66using namespace tooling;

67

69

71

72

73

74

75

76

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

91

92 bool OffloadCompilation = false;

93

94

95

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

104 if (isadriver::BindArchAction(A))

105 A = *A->input_begin();

106 if (isadriver::OffloadAction(A)) {

107

108

109

110

111

112

113 assert(Actions.size() > 1);

114 assert(

115 isadriver::CompileJobAction(Actions.front()) ||

116

117

118 (isadriver::BindArchAction(Actions.front()) &&

119 isadriver::CompileJobAction(*Actions.front()->input_begin())));

120 OffloadCompilation = true;

121 break;

122 }

123 }

124 }

125

126 return OffloadCompilation;

127}

128

130namespace tooling {

131

132const llvm::opt::ArgStringList *

136

138 return StringRef(Cmd.getCreator().getName()) == "clang";

139 };

140

142 return isSrcFile(II.getType());

143 };

144

147 if (IsCC1Command(Job) && llvm::all_of(Job.getInputInfos(), IsSrcFile))

148 CC1Jobs.push_back(&Job);

149

150

151

152 if (CC1Jobs.empty())

154 if (IsCC1Command(Job))

155 CC1Jobs.push_back(&Job);

156

157 if (CC1Jobs.empty() ||

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

173 const char *const BinaryName) {

174 assert(!CC1Args.empty() && "Must at least contain the program name!");

177 BinaryName);

180 return Invocation;

181}

182

184 const Twine &Code, const Twine &FileName,

185 std::shared_ptr PCHContainerOps) {

187 std::vectorstd::string(), FileName,

188 "clang-tool", std::move(PCHContainerOps));

189}

190

191}

192}

193

194static std::vectorstd::string

196 const std::vectorstd::string &ExtraArgs,

198 std::vectorstd::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

207namespace tooling {

208

210 std::unique_ptr ToolAction, const Twine &Code,

212 const std::vectorstd::string &Args, const Twine &FileName,

213 const Twine &ToolName,

214 std::shared_ptr PCHContainerOps) {

216 StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);

217

223 std::move(ToolAction), Files.get(), std::move(PCHContainerOps));

224 return Invocation.run();

225}

226

228 std::unique_ptr ToolAction, const Twine &Code,

229 const std::vectorstd::string &Args, const Twine &FileName,

230 const Twine &ToolName,

231 std::shared_ptr PCHContainerOps,

234 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));

236 new llvm::vfs::InMemoryFileSystem);

237 OverlayFileSystem->pushOverlay(InMemoryFileSystem);

238

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

252}

253

255 StringRef File) {

256 StringRef RelativePath(File);

257

258 RelativePath.consume_front("./");

259

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

268 return llvm::cantFail(getAbsolutePath(*llvm::vfs::getRealFileSystem(), File));

269}

270

272 StringRef InvokedAs) {

273 if (CommandLine.empty() || InvokedAs.empty())

274 return;

276

277 StringRef TargetOPT =

278 Table.getOption(driver::options::OPT_target).getPrefixedName();

279

280 StringRef TargetOPTLegacy =

281 Table.getOption(driver::options::OPT_target_legacy_spelling)

282 .getPrefixedName();

283

284 StringRef DriverModeOPT =

285 Table.getOption(driver::options::OPT_driver_mode).getPrefixedName();

286 auto TargetMode =

288

289 bool ShouldAddTarget = TargetMode.TargetIsValid;

290 bool ShouldAddMode = TargetMode.DriverMode != nullptr;

291

292 for (auto Token = ++CommandLine.begin(); Token != CommandLine.end();

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

309 llvm::StringRef WorkingDir,

310 llvm:🆑: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:🆑:ExpansionContext ECtx(Alloc, Tokenizer);

324 llvm::Error Err =

325 ECtx.setVFS(&FS).setCurrentDir(WorkingDir).expandResponseFiles(Argv);

326 if (Err)

327 llvm::errs() << Err;

328

329 std::vectorstd::string ExpandedArgv(Argv.begin(), Argv.end());

330 CommandLine = std::move(ExpandedArgv);

331}

332

333}

334}

335

336namespace {

337

339 std::unique_ptr Action;

340

341public:

342 SingleFrontendActionFactory(std::unique_ptr Action)

343 : Action(std::move(Action)) {}

344

345 std::unique_ptr create() override {

346 return std::move(Action);

347 }

348};

349

350}

351

353 std::vectorstd::string CommandLine, ToolAction *Action,

354 FileManager *Files, std::shared_ptr PCHContainerOps)

355 : CommandLine(std::move(CommandLine)), Action(Action), OwnsAction(false),

356 Files(Files), PCHContainerOps(std::move(PCHContainerOps)) {}

357

359 std::vectorstd::string CommandLine,

360 std::unique_ptr FAction, FileManager *Files,

361 std::shared_ptr 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

379

382 if (!DiagOpts) {

384 DiagOpts = &*ParsedDiagOpts;

385 }

386

391 DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);

392

393

395 Diagnostics->setSourceManager(&SrcMgr);

396

397

398 if (CommandLine.size() >= 2 && CommandLine[1] == "-cc1") {

400 std::unique_ptr 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_ptrdriver::Driver Driver(

410

411

412

413

415 Driver->setCheckInputsExist(false);

416 const std::unique_ptrdriver::Compilation Compilation(

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 Invocation(

425 newInvocation(&*Diagnostics, *CC1Args, BinaryName));

426 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),

427 std::move(PCHContainerOps));

428}

429

430bool ToolInvocation::runInvocation(

432 std::shared_ptr Invocation,

433 std::shared_ptr PCHContainerOps) {

434

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 Invocation, FileManager *Files,

447 std::shared_ptr PCHContainerOps,

449

453

454

455

456

457 std::unique_ptr ScopedToolAction(create());

458

459

461 false);

463 return false;

464

466

468

471}

472

475 std::shared_ptr 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

484 OverlayFileSystem->pushOverlay(InMemoryFileSystem);

488 if (Files)

489 Files->setVirtualFileSystem(OverlayFileSystem);

490}

491

493

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

507 void *MainAddr) {

508

509 for (StringRef Arg : Args)

510 if (Arg.starts_with("-resource-dir"))

511 return;

512

513

516 .c_str())(Args, "");

517}

518

520

521

522 static int StaticSymbol;

523

524

525

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

536

537 std::vectorstd::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

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

563

564

565

566

567

568

569 std::vector CompileCommandsForFile =

571 if (CompileCommandsForFile.empty()) {

572 llvm::errs() << "Skipping " << File << ". Compile command not found.\n";

573 FileSkipped = true;

574 continue;

575 }

577

578

579

580

581

582

583

584 if (OverlayFileSystem->setCurrentWorkingDirectory(

586 llvm::report_fatal_error("Cannot chdir into \"" +

588

589

590

591

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

600 if (ArgsAdjuster)

602 assert(!CommandLine.empty());

603

604

605

606

607

608

609

610

611

613

614

615

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);

624

625 if (!Invocation.run()) {

626

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> &ASTs;

647

648public:

649 ASTBuilderAction(std::vector<std::unique_ptr> &ASTs) : ASTs(ASTs) {}

650

651 bool runInvocation(std::shared_ptr Invocation,

653 std::shared_ptr PCHContainerOps,

655 std::unique_ptr AST = ASTUnit::LoadFromCompilerInvocation(

656 Invocation, std::move(PCHContainerOps),

658 &Invocation->getDiagnosticOpts(),

659 DiagConsumer,

660 false),

661 Files);

662 if (!AST)

663 return false;

664

665 ASTs.push_back(std::move(AST));

666 return true;

667 }

668};

669

670}

671

673 ASTBuilderAction Action(ASTs);

674 return run(&Action);

675}

676

678 this->PrintErrorMessage = PrintErrorMessage;

679}

680

682namespace tooling {

683

684std::unique_ptr

686 std::shared_ptr PCHContainerOps) {

688 "clang-tool", std::move(PCHContainerOps));

689}

690

692 StringRef Code, const std::vectorstd::string &Args, StringRef FileName,

693 StringRef ToolName, std::shared_ptr PCHContainerOps,

697 std::vector<std::unique_ptr> ASTs;

698 ASTBuilderAction Action(ASTs);

700 new llvm::vfs::OverlayFileSystem(std::move(BaseFS)));

702 new llvm::vfs::InMemoryFileSystem);

703 OverlayFileSystem->pushOverlay(InMemoryFileSystem);

706

709 &Action, Files.get(), std::move(PCHContainerOps));

711

712 InMemoryFileSystem->addFile(FileName, 0,

713 llvm::MemoryBuffer::getMemBufferCopy(Code));

714 for (auto &FilenameWithContent : VirtualMappedFiles) {

715 InMemoryFileSystem->addFile(

716 FilenameWithContent.first, 0,

717 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));

718 }

719

720 if (!Invocation.run())

721 return nullptr;

722

723 assert(ASTs.size() == 1);

724 return std::move(ASTs[0]);

725}

726

727}

728}

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.

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 hasDiagnostics() const

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...

Options for controlling the compiler diagnostics engine.

Concrete class used by the front-end to report problems and issues.

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.

void clearStatCache()

Removes the FileSystemStatCache object from the manager.

llvm::vfs::FileSystem & getVirtualFileSystem() const

FileSystemOptions & getFileSystemOpts()

Returns the current file system options.

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.

Command - An executable path/name and argument vector to execute.

Compilation - A set of tasks to perform for a single driver invocation.

ActionList & getActions()

Driver - Encapsulate logic for constructing compilation processes from a set of gcc-driver-like comma...

void setTitle(std::string Value)

InputInfo - Wrapper for information about an input source.

JobList - A sequence of jobs to perform.

void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo=nullptr) const

const llvm::opt::OptTable & getDriverOptTable()

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.