Coverage Report

Created: 2021-01-23 06:44

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Tooling.cpp
Line
Count
Source (jump to first uncovered line)
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
14
#include "clang/Tooling/Tooling.h"
15
#include "clang/Basic/Diagnostic.h"
16
#include "clang/Basic/DiagnosticIDs.h"
17
#include "clang/Basic/DiagnosticOptions.h"
18
#include "clang/Basic/FileManager.h"
19
#include "clang/Basic/FileSystemOptions.h"
20
#include "clang/Basic/LLVM.h"
21
#include "clang/Driver/Compilation.h"
22
#include "clang/Driver/Driver.h"
23
#include "clang/Driver/Job.h"
24
#include "clang/Driver/Options.h"
25
#include "clang/Driver/Tool.h"
26
#include "clang/Driver/ToolChain.h"
27
#include "clang/Frontend/ASTUnit.h"
28
#include "clang/Frontend/CompilerInstance.h"
29
#include "clang/Frontend/CompilerInvocation.h"
30
#include "clang/Frontend/FrontendDiagnostic.h"
31
#include "clang/Frontend/FrontendOptions.h"
32
#include "clang/Frontend/TextDiagnosticPrinter.h"
33
#include "clang/Lex/HeaderSearchOptions.h"
34
#include "clang/Lex/PreprocessorOptions.h"
35
#include "clang/Tooling/ArgumentsAdjusters.h"
36
#include "clang/Tooling/CompilationDatabase.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/Debug.h"
47
#include "llvm/Support/ErrorHandling.h"
48
#include "llvm/Support/FileSystem.h"
49
#include "llvm/Support/Host.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 <cassert>
55
#include <cstring>
56
#include <memory>
57
#include <string>
58
#include <system_error>
59
#include <utility>
60
#include <vector>
61
62
#define DEBUG_TYPE "clang-tooling"
63
64
using namespace clang;
65
using namespace tooling;
66
67
38.4k
ToolAction::~ToolAction() = default;
68
69
32.3k
FrontendActionFactory::~FrontendActionFactory() = default;
70
71
// FIXME: This file contains structural duplication with other parts of the
72
// code that sets up a compiler to run tools on it, and we should refactor
73
// it to be based on the same framework.
74
75
/// Builds a clang driver initialized for running clang tools.
76
static driver::Driver *
77
newDriver(DiagnosticsEngine *Diagnostics, const char *BinaryName,
78
22.7k
          IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
79
22.7k
  driver::Driver *CompilerDriver =
80
22.7k
      new driver::Driver(BinaryName, llvm::sys::getDefaultTargetTriple(),
81
22.7k
                         *Diagnostics, "clang LLVM compiler", std::move(VFS));
82
22.7k
  CompilerDriver->setTitle("clang_based_tool");
83
22.7k
  return CompilerDriver;
84
22.7k
}
85
86
/// Retrieves the clang CC1 specific flags out of the compilation's jobs.
87
///
88
/// Returns nullptr on error.
89
static const llvm::opt::ArgStringList *getCC1Arguments(
90
22.7k
    DiagnosticsEngine *Diagnostics, driver::Compilation *Compilation) {
91
  // We expect to get back exactly one Command job, if we didn't something
92
  // failed. Extract that job from the Compilation.
93
22.7k
  const driver::JobList &Jobs = Compilation->getJobs();
94
22.7k
  const driver::ActionList &Actions = Compilation->getActions();
95
22.7k
  bool OffloadCompilation = false;
96
22.7k
  if (Jobs.size() > 1) {
97
4
    for (auto A : Actions){
98
      // On MacOSX real actions may end up being wrapped in BindArchAction
99
4
      if (isa<driver::BindArchAction>(A))
100
3
        A = *A->input_begin();
101
4
      if (isa<driver::OffloadAction>(A)) {
102
        // Offload compilation has 2 top-level actions, one (at the front) is
103
        // the original host compilation and the other is offload action
104
        // composed of at least one device compilation. For such case, general
105
        // tooling will consider host-compilation only. For tooling on device
106
        // compilation, device compilation only option, such as
107
        // `--cuda-device-only`, needs specifying.
108
1
        assert(Actions.size() > 1);
109
1
        assert(
110
1
            isa<driver::CompileJobAction>(Actions.front()) ||
111
            // On MacOSX real actions may end up being wrapped in
112
            // BindArchAction.
113
1
            (isa<driver::BindArchAction>(Actions.front()) &&
114
1
             isa<driver::CompileJobAction>(*Actions.front()->input_begin())));
115
1
        OffloadCompilation = true;
116
1
        break;
117
1
      }
118
22.7k
    }
119
3
  }
120
22.7k
  if (Jobs.size() == 0 || !isa<driver::Command>(*Jobs.begin()) ||
121
22.7k
      (Jobs.size() > 1 && 
!OffloadCompilation3
)) {
122
3
    SmallString<256> error_msg;
123
3
    llvm::raw_svector_ostream error_stream(error_msg);
124
3
    Jobs.Print(error_stream, "; ", true);
125
3
    Diagnostics->Report(diag::err_fe_expected_compiler_job)
126
3
        << error_stream.str();
127
3
    return nullptr;
128
3
  }
129
130
  // The one job we find should be to invoke clang again.
131
22.7k
  const auto &Cmd = cast<driver::Command>(*Jobs.begin());
132
22.7k
  if (StringRef(Cmd.getCreator().getName()) != "clang") {
133
0
    Diagnostics->Report(diag::err_fe_expected_clang_command);
134
0
    return nullptr;
135
0
  }
136
137
22.7k
  return &Cmd.getArguments();
138
22.7k
}
139
140
namespace clang {
141
namespace tooling {
142
143
/// Returns a clang build invocation initialized from the CC1 flags.
144
CompilerInvocation *newInvocation(DiagnosticsEngine *Diagnostics,
145
                                  const llvm::opt::ArgStringList &CC1Args,
146
22.7k
                                  const char *const BinaryName) {
147
22.7k
  assert(!CC1Args.empty() && "Must at least contain the program name!");
148
22.7k
  CompilerInvocation *Invocation = new CompilerInvocation;
149
22.7k
  CompilerInvocation::CreateFromArgs(*Invocation, CC1Args, *Diagnostics,
150
22.7k
                                     BinaryName);
151
22.7k
  Invocation->getFrontendOpts().DisableFree = false;
152
22.7k
  Invocation->getCodeGenOpts().DisableFree = false;
153
22.7k
  return Invocation;
154
22.7k
}
155
156
bool runToolOnCode(std::unique_ptr<FrontendAction> ToolAction,
157
                   const Twine &Code, const Twine &FileName,
158
118
                   std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
159
118
  return runToolOnCodeWithArgs(std::move(ToolAction), Code,
160
118
                               std::vector<std::string>(), FileName,
161
118
                               "clang-tool", std::move(PCHContainerOps));
162
118
}
163
164
} // namespace tooling
165
} // namespace clang
166
167
static std::vector<std::string>
168
getSyntaxOnlyToolArgs(const Twine &ToolName,
169
                      const std::vector<std::string> &ExtraArgs,
170
22.3k
                      StringRef FileName) {
171
22.3k
  std::vector<std::string> Args;
172
22.3k
  Args.push_back(ToolName.str());
173
22.3k
  Args.push_back("-fsyntax-only");
174
22.3k
  Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
175
22.3k
  Args.push_back(FileName.str());
176
22.3k
  return Args;
177
22.3k
}
178
179
namespace clang {
180
namespace tooling {
181
182
bool runToolOnCodeWithArgs(
183
    std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,
184
    llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
185
    const std::vector<std::string> &Args, const Twine &FileName,
186
    const Twine &ToolName,
187
16.3k
    std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
188
16.3k
  SmallString<16> FileNameStorage;
189
16.3k
  StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
190
191
16.3k
  llvm::IntrusiveRefCntPtr<FileManager> Files(
192
16.3k
      new FileManager(FileSystemOptions(), VFS));
193
16.3k
  ArgumentsAdjuster Adjuster = getClangStripDependencyFileAdjuster();
194
16.3k
  ToolInvocation Invocation(
195
16.3k
      getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileNameRef), FileNameRef),
196
16.3k
      std::move(ToolAction), Files.get(), std::move(PCHContainerOps));
197
16.3k
  return Invocation.run();
198
16.3k
}
199
200
bool runToolOnCodeWithArgs(
201
    std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,
202
    const std::vector<std::string> &Args, const Twine &FileName,
203
    const Twine &ToolName,
204
    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
205
16.3k
    const FileContentMappings &VirtualMappedFiles) {
206
16.3k
  llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
207
16.3k
      new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
208
16.3k
  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
209
16.3k
      new llvm::vfs::InMemoryFileSystem);
210
16.3k
  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
211
212
16.3k
  SmallString<1024> CodeStorage;
213
16.3k
  InMemoryFileSystem->addFile(FileName, 0,
214
16.3k
                              llvm::MemoryBuffer::getMemBuffer(
215
16.3k
                                  Code.toNullTerminatedStringRef(CodeStorage)));
216
217
1.11k
  for (auto &FilenameWithContent : VirtualMappedFiles) {
218
1.11k
    InMemoryFileSystem->addFile(
219
1.11k
        FilenameWithContent.first, 0,
220
1.11k
        llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
221
1.11k
  }
222
223
16.3k
  return runToolOnCodeWithArgs(std::move(ToolAction), Code, OverlayFileSystem,
224
16.3k
                               Args, FileName, ToolName);
225
16.3k
}
226
227
llvm::Expected<std::string> getAbsolutePath(llvm::vfs::FileSystem &FS,
228
432
                                            StringRef File) {
229
432
  StringRef RelativePath(File);
230
  // FIXME: Should '.\\' be accepted on Win32?
231
432
  if (RelativePath.startswith("./")) {
232
0
    RelativePath = RelativePath.substr(strlen("./"));
233
0
  }
234
235
432
  SmallString<1024> AbsolutePath = RelativePath;
236
432
  if (auto EC = FS.makeAbsolute(AbsolutePath))
237
0
    return llvm::errorCodeToError(EC);
238
432
  llvm::sys::path::native(AbsolutePath);
239
432
  return std::string(AbsolutePath.str());
240
432
}
241
242
22
std::string getAbsolutePath(StringRef File) {
243
22
  return llvm::cantFail(getAbsolutePath(*llvm::vfs::getRealFileSystem(), File));
244
22
}
245
246
void addTargetAndModeForProgramName(std::vector<std::string> &CommandLine,
247
26
                                    StringRef InvokedAs) {
248
26
  if (CommandLine.empty() || InvokedAs.empty())
249
1
    return;
250
25
  const auto &Table = driver::getDriverOptTable();
251
  // --target=X
252
25
  const std::string TargetOPT =
253
25
      Table.getOption(driver::options::OPT_target).getPrefixedName();
254
  // -target X
255
25
  const std::string TargetOPTLegacy =
256
25
      Table.getOption(driver::options::OPT_target_legacy_spelling)
257
25
          .getPrefixedName();
258
  // --driver-mode=X
259
25
  const std::string DriverModeOPT =
260
25
      Table.getOption(driver::options::OPT_driver_mode).getPrefixedName();
261
25
  auto TargetMode =
262
25
      driver::ToolChain::getTargetAndModeFromProgramName(InvokedAs);
263
  // No need to search for target args if we don't have a target/mode to insert.
264
25
  bool ShouldAddTarget = TargetMode.TargetIsValid;
265
25
  bool ShouldAddMode = TargetMode.DriverMode != nullptr;
266
  // Skip CommandLine[0].
267
96
  for (auto Token = ++CommandLine.begin(); Token != CommandLine.end();
268
71
       ++Token) {
269
71
    StringRef TokenRef(*Token);
270
71
    ShouldAddTarget = ShouldAddTarget && 
!TokenRef.startswith(TargetOPT)8
&&
271
7
                      !TokenRef.equals(TargetOPTLegacy);
272
71
    ShouldAddMode = ShouldAddMode && 
!TokenRef.startswith(DriverModeOPT)62
;
273
71
  }
274
25
  if (ShouldAddMode) {
275
20
    CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
276
20
  }
277
25
  if (ShouldAddTarget) {
278
3
    CommandLine.insert(++CommandLine.begin(),
279
3
                       TargetOPT + TargetMode.TargetPrefix);
280
3
  }
281
25
}
282
283
} // namespace tooling
284
} // namespace clang
285
286
namespace {
287
288
class SingleFrontendActionFactory : public FrontendActionFactory {
289
  std::unique_ptr<FrontendAction> Action;
290
291
public:
292
  SingleFrontendActionFactory(std::unique_ptr<FrontendAction> Action)
293
16.3k
      : Action(std::move(Action)) {}
294
295
16.3k
  std::unique_ptr<FrontendAction> create() override {
296
16.3k
    return std::move(Action);
297
16.3k
  }
298
};
299
300
} // namespace
301
302
ToolInvocation::ToolInvocation(
303
    std::vector<std::string> CommandLine, ToolAction *Action,
304
    FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
305
    : CommandLine(std::move(CommandLine)), Action(Action), OwnsAction(false),
306
6.45k
      Files(Files), PCHContainerOps(std::move(PCHContainerOps)) {}
307
308
ToolInvocation::ToolInvocation(
309
    std::vector<std::string> CommandLine,
310
    std::unique_ptr<FrontendAction> FAction, FileManager *Files,
311
    std::shared_ptr<PCHContainerOperations> PCHContainerOps)
312
    : CommandLine(std::move(CommandLine)),
313
      Action(new SingleFrontendActionFactory(std::move(FAction))),
314
      OwnsAction(true), Files(Files),
315
16.3k
      PCHContainerOps(std::move(PCHContainerOps)) {}
316
317
22.7k
ToolInvocation::~ToolInvocation() {
318
22.7k
  if (OwnsAction)
319
16.3k
    delete Action;
320
22.7k
}
321
322
22.7k
bool ToolInvocation::run() {
323
22.7k
  std::vector<const char*> Argv;
324
22.7k
  for (const std::string &Str : CommandLine)
325
221k
    Argv.push_back(Str.c_str());
326
22.7k
  const char *const BinaryName = Argv[0];
327
22.7k
  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
328
22.7k
  unsigned MissingArgIndex, MissingArgCount;
329
22.7k
  llvm::opt::InputArgList ParsedArgs = driver::getDriverOptTable().ParseArgs(
330
22.7k
      ArrayRef<const char *>(Argv).slice(1), MissingArgIndex, MissingArgCount);
331
22.7k
  ParseDiagnosticArgs(*DiagOpts, ParsedArgs);
332
22.7k
  TextDiagnosticPrinter DiagnosticPrinter(
333
22.7k
      llvm::errs(), &*DiagOpts);
334
22.7k
  DiagnosticsEngine Diagnostics(
335
22.7k
      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
336
22.6k
      DiagConsumer ? 
DiagConsumer78
: &DiagnosticPrinter, false);
337
338
22.7k
  const std::unique_ptr<driver::Driver> Driver(
339
22.7k
      newDriver(&Diagnostics, BinaryName, &Files->getVirtualFileSystem()));
340
  // The "input file not found" diagnostics from the driver are useful.
341
  // The driver is only aware of the VFS working directory, but some clients
342
  // change this at the FileManager level instead.
343
  // In this case the checks have false positives, so skip them.
344
22.7k
  if (!Files->getFileSystemOpts().WorkingDir.empty())
345
28
    Driver->setCheckInputsExist(false);
346
22.7k
  const std::unique_ptr<driver::Compilation> Compilation(
347
22.7k
      Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
348
22.7k
  if (!Compilation)
349
0
    return false;
350
22.7k
  const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
351
22.7k
      &Diagnostics, Compilation.get());
352
22.7k
  if (!CC1Args)
353
3
    return false;
354
22.7k
  std::unique_ptr<CompilerInvocation> Invocation(
355
22.7k
      newInvocation(&Diagnostics, *CC1Args, BinaryName));
356
22.7k
  return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
357
22.7k
                       std::move(PCHContainerOps));
358
22.7k
}
359
360
bool ToolInvocation::runInvocation(
361
    const char *BinaryName, driver::Compilation *Compilation,
362
    std::shared_ptr<CompilerInvocation> Invocation,
363
22.7k
    std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
364
  // Show the invocation, with -v.
365
22.7k
  if (Invocation->getHeaderSearchOpts().Verbose) {
366
1
    llvm::errs() << "clang Invocation:\n";
367
1
    Compilation->getJobs().Print(llvm::errs(), "\n", true);
368
1
    llvm::errs() << "\n";
369
1
  }
370
371
22.7k
  return Action->runInvocation(std::move(Invocation), Files,
372
22.7k
                               std::move(PCHContainerOps), DiagConsumer);
373
22.7k
}
374
375
bool FrontendActionFactory::runInvocation(
376
    std::shared_ptr<CompilerInvocation> Invocation, FileManager *Files,
377
    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
378
16.6k
    DiagnosticConsumer *DiagConsumer) {
379
  // Create a compiler instance to handle the actual work.
380
16.6k
  CompilerInstance Compiler(std::move(PCHContainerOps));
381
16.6k
  Compiler.setInvocation(std::move(Invocation));
382
16.6k
  Compiler.setFileManager(Files);
383
384
  // The FrontendAction can have lifetime requirements for Compiler or its
385
  // members, and we need to ensure it's deleted earlier than Compiler. So we
386
  // pass it to an std::unique_ptr declared after the Compiler variable.
387
16.6k
  std::unique_ptr<FrontendAction> ScopedToolAction(create());
388
389
  // Create the compiler's actual diagnostics engine.
390
16.6k
  Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
391
16.6k
  if (!Compiler.hasDiagnostics())
392
0
    return false;
393
394
16.6k
  Compiler.createSourceManager(*Files);
395
396
16.6k
  const bool Success = Compiler.ExecuteAction(*ScopedToolAction);
397
398
16.6k
  Files->clearStatCache();
399
16.6k
  return Success;
400
16.6k
}
401
402
ClangTool::ClangTool(const CompilationDatabase &Compilations,
403
                     ArrayRef<std::string> SourcePaths,
404
                     std::shared_ptr<PCHContainerOperations> PCHContainerOps,
405
                     IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
406
                     IntrusiveRefCntPtr<FileManager> Files)
407
    : Compilations(Compilations), SourcePaths(SourcePaths),
408
      PCHContainerOps(std::move(PCHContainerOps)),
409
      OverlayFileSystem(new llvm::vfs::OverlayFileSystem(std::move(BaseFS))),
410
      InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
411
      Files(Files ? Files
412
341
                  : new FileManager(FileSystemOptions(), OverlayFileSystem)) {
413
341
  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
414
341
  appendArgumentsAdjuster(getClangStripOutputAdjuster());
415
341
  appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
416
341
  appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
417
341
  if (Files)
418
71
    Files->setVirtualFileSystem(OverlayFileSystem);
419
341
}
420
421
341
ClangTool::~ClangTool() = default;
422
423
9.39k
void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {
424
9.39k
  MappedFileContents.push_back(std::make_pair(FilePath, Content));
425
9.39k
}
426
427
1.35k
void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) {
428
1.35k
  ArgsAdjuster = combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
429
1.35k
}
430
431
123
void ClangTool::clearArgumentsAdjusters() {
432
123
  ArgsAdjuster = nullptr;
433
123
}
434
435
static void injectResourceDir(CommandLineArguments &Args, const char *Argv0,
436
415
                              void *MainAddr) {
437
  // Allow users to override the resource dir.
438
415
  for (StringRef Arg : Args)
439
2.46k
    if (Arg.startswith("-resource-dir"))
440
0
      return;
441
442
  // If there's no override in place add our resource dir.
443
415
  Args.push_back("-resource-dir=" +
444
415
                 CompilerInvocation::GetResourcesPath(Argv0, MainAddr));
445
415
}
446
447
406
int ClangTool::run(ToolAction *Action) {
448
  // Exists solely for the purpose of lookup of the resource path.
449
  // This just needs to be some symbol in the binary.
450
406
  static int StaticSymbol;
451
452
  // First insert all absolute paths into the in-memory VFS. These are global
453
  // for all compile commands.
454
406
  if (SeenWorkingDirectories.insert("/").second)
455
339
    for (const auto &MappedFile : MappedFileContents)
456
9.52k
      if (llvm::sys::path::is_absolute(MappedFile.first))
457
13
        InMemoryFileSystem->addFile(
458
13
            MappedFile.first, 0,
459
13
            llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
460
461
406
  bool ProcessingFailed = false;
462
406
  bool FileSkipped = false;
463
  // Compute all absolute paths before we run any actions, as those will change
464
  // the working directory.
465
406
  std::vector<std::string> AbsolutePaths;
466
406
  AbsolutePaths.reserve(SourcePaths.size());
467
414
  for (const auto &SourcePath : SourcePaths) {
468
414
    auto AbsPath = getAbsolutePath(*OverlayFileSystem, SourcePath);
469
414
    if (!AbsPath) {
470
0
      llvm::errs() << "Skipping " << SourcePath
471
0
                   << ". Error while getting an absolute path: "
472
0
                   << llvm::toString(AbsPath.takeError()) << "\n";
473
0
      continue;
474
0
    }
475
414
    AbsolutePaths.push_back(std::move(*AbsPath));
476
414
  }
477
478
  // Remember the working directory in case we need to restore it.
479
406
  std::string InitialWorkingDir;
480
406
  if (RestoreCWD) {
481
335
    if (auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
482
334
      InitialWorkingDir = std::move(*CWD);
483
1
    } else {
484
1
      llvm::errs() << "Could not get working directory: "
485
1
                   << CWD.getError().message() << "\n";
486
1
    }
487
335
  }
488
489
411
  for (llvm::StringRef File : AbsolutePaths) {
490
    // Currently implementations of CompilationDatabase::getCompileCommands can
491
    // change the state of the file system (e.g.  prepare generated headers), so
492
    // this method needs to run right before we invoke the tool, as the next
493
    // file may require a different (incompatible) state of the file system.
494
    //
495
    // FIXME: Make the compilation database interface more explicit about the
496
    // requirements to the order of invocation of its members.
497
411
    std::vector<CompileCommand> CompileCommandsForFile =
498
411
        Compilations.getCompileCommands(File);
499
411
    if (CompileCommandsForFile.empty()) {
500
0
      llvm::errs() << "Skipping " << File << ". Compile command not found.\n";
501
0
      FileSkipped = true;
502
0
      continue;
503
0
    }
504
415
    
for (CompileCommand &CompileCommand : CompileCommandsForFile)411
{
505
      // FIXME: chdir is thread hostile; on the other hand, creating the same
506
      // behavior as chdir is complex: chdir resolves the path once, thus
507
      // guaranteeing that all subsequent relative path operations work
508
      // on the same path the original chdir resulted in. This makes a
509
      // difference for example on network filesystems, where symlinks might be
510
      // switched during runtime of the tool. Fixing this depends on having a
511
      // file system abstraction that allows openat() style interactions.
512
415
      if (OverlayFileSystem->setCurrentWorkingDirectory(
513
415
              CompileCommand.Directory))
514
0
        llvm::report_fatal_error("Cannot chdir into \"" +
515
0
                                 Twine(CompileCommand.Directory) + "\"!");
516
517
      // Now fill the in-memory VFS with the relative file mappings so it will
518
      // have the correct relative paths. We never remove mappings but that
519
      // should be fine.
520
415
      if (SeenWorkingDirectories.insert(CompileCommand.Directory).second)
521
327
        for (const auto &MappedFile : MappedFileContents)
522
9.97k
          if (!llvm::sys::path::is_absolute(MappedFile.first))
523
9.92k
            InMemoryFileSystem->addFile(
524
9.92k
                MappedFile.first, 0,
525
9.92k
                llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
526
527
415
      std::vector<std::string> CommandLine = CompileCommand.CommandLine;
528
415
      if (ArgsAdjuster)
529
334
        CommandLine = ArgsAdjuster(CommandLine, CompileCommand.Filename);
530
415
      assert(!CommandLine.empty());
531
532
      // Add the resource dir based on the binary of this tool. argv[0] in the
533
      // compilation database may refer to a different compiler and we want to
534
      // pick up the very same standard library that compiler is using. The
535
      // builtin headers in the resource dir need to match the exact clang
536
      // version the tool is using.
537
      // FIXME: On linux, GetMainExecutable is independent of the value of the
538
      // first argument, thus allowing ClangTool and runToolOnCode to just
539
      // pass in made-up names here. Make sure this works on other platforms.
540
415
      injectResourceDir(CommandLine, "clang_tool", &StaticSymbol);
541
542
      // FIXME: We need a callback mechanism for the tool writer to output a
543
      // customized message for each file.
544
415
      LLVM_DEBUG({ llvm::dbgs() << "Processing: " << File << ".\n"; });
545
415
      ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
546
415
                                PCHContainerOps);
547
415
      Invocation.setDiagnosticConsumer(DiagConsumer);
548
549
415
      if (!Invocation.run()) {
550
        // FIXME: Diagnostics should be used instead.
551
23
        if (PrintErrorMessage)
552
20
          llvm::errs() << "Error while processing " << File << ".\n";
553
23
        ProcessingFailed = true;
554
23
      }
555
415
    }
556
411
  }
557
558
406
  if (!InitialWorkingDir.empty()) {
559
335
    if (auto EC =
560
0
            OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
561
0
      llvm::errs() << "Error when trying to restore working dir: "
562
0
                   << EC.message() << "\n";
563
335
  }
564
383
  return ProcessingFailed ? 
123
: (FileSkipped ?
20
: 0);
565
406
}
566
567
namespace {
568
569
class ASTBuilderAction : public ToolAction {
570
  std::vector<std::unique_ptr<ASTUnit>> &ASTs;
571
572
public:
573
6.05k
  ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
574
575
  bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
576
                     FileManager *Files,
577
                     std::shared_ptr<PCHContainerOperations> PCHContainerOps,
578
6.05k
                     DiagnosticConsumer *DiagConsumer) override {
579
6.05k
    std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
580
6.05k
        Invocation, std::move(PCHContainerOps),
581
6.05k
        CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts(),
582
6.05k
                                            DiagConsumer,
583
6.05k
                                            /*ShouldOwnClient=*/false),
584
6.05k
        Files);
585
6.05k
    if (!AST)
586
0
      return false;
587
588
6.05k
    ASTs.push_back(std::move(AST));
589
6.05k
    return true;
590
6.05k
  }
591
};
592
593
} // namespace
594
595
19
int ClangTool::buildASTs(std::vector<std::unique_ptr<ASTUnit>> &ASTs) {
596
19
  ASTBuilderAction Action(ASTs);
597
19
  return run(&Action);
598
19
}
599
600
71
void ClangTool::setRestoreWorkingDir(bool RestoreCWD) {
601
71
  this->RestoreCWD = RestoreCWD;
602
71
}
603
604
72
void ClangTool::setPrintErrorMessage(bool PrintErrorMessage) {
605
72
  this->PrintErrorMessage = PrintErrorMessage;
606
72
}
607
608
namespace clang {
609
namespace tooling {
610
611
std::unique_ptr<ASTUnit>
612
buildASTFromCode(StringRef Code, StringRef FileName,
613
116
                 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
614
116
  return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName,
615
116
                                  "clang-tool", std::move(PCHContainerOps));
616
116
}
617
618
std::unique_ptr<ASTUnit> buildASTFromCodeWithArgs(
619
    StringRef Code, const std::vector<std::string> &Args, StringRef FileName,
620
    StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
621
    ArgumentsAdjuster Adjuster, const FileContentMappings &VirtualMappedFiles,
622
6.03k
    DiagnosticConsumer *DiagConsumer) {
623
6.03k
  std::vector<std::unique_ptr<ASTUnit>> ASTs;
624
6.03k
  ASTBuilderAction Action(ASTs);
625
6.03k
  llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
626
6.03k
      new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
627
6.03k
  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
628
6.03k
      new llvm::vfs::InMemoryFileSystem);
629
6.03k
  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
630
6.03k
  llvm::IntrusiveRefCntPtr<FileManager> Files(
631
6.03k
      new FileManager(FileSystemOptions(), OverlayFileSystem));
632
633
6.03k
  ToolInvocation Invocation(
634
6.03k
      getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileName), FileName),
635
6.03k
      &Action, Files.get(), std::move(PCHContainerOps));
636
6.03k
  Invocation.setDiagnosticConsumer(DiagConsumer);
637
638
6.03k
  InMemoryFileSystem->addFile(FileName, 0,
639
6.03k
                              llvm::MemoryBuffer::getMemBufferCopy(Code));
640
0
  for (auto &FilenameWithContent : VirtualMappedFiles) {
641
0
    InMemoryFileSystem->addFile(
642
0
        FilenameWithContent.first, 0,
643
0
        llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
644
0
  }
645
646
6.03k
  if (!Invocation.run())
647
0
    return nullptr;
648
 
649
6.03k
  assert(ASTs.size() == 1);
650
6.03k
  return std::move(ASTs[0]);
651
6.03k
}
652
653
} // namespace tooling
654
} // namespace clang