Coverage Report

Created: 2020-10-24 06:27

/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
37.4k
ToolAction::~ToolAction() = default;
68
69
31.4k
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.1k
          IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
79
22.1k
  driver::Driver *CompilerDriver =
80
22.1k
      new driver::Driver(BinaryName, llvm::sys::getDefaultTargetTriple(),
81
22.1k
                         *Diagnostics, "clang LLVM compiler", std::move(VFS));
82
22.1k
  CompilerDriver->setTitle("clang_based_tool");
83
22.1k
  return CompilerDriver;
84
22.1k
}
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.1k
    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.1k
  const driver::JobList &Jobs = Compilation->getJobs();
94
22.1k
  const driver::ActionList &Actions = Compilation->getActions();
95
22.1k
  bool OffloadCompilation = false;
96
22.1k
  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.1k
    }
119
3
  }
120
22.1k
  if (Jobs.size() == 0 || 
!isa<driver::Command>(*Jobs.begin())22.1k
||
121
22.1k
      (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.1k
  const auto &Cmd = cast<driver::Command>(*Jobs.begin());
132
22.1k
  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.1k
  return &Cmd.getArguments();
138
22.1k
}
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.1k
                                  const char *const BinaryName) {
147
22.1k
  assert(!CC1Args.empty() && "Must at least contain the program name!");
148
22.1k
  CompilerInvocation *Invocation = new CompilerInvocation;
149
22.1k
  CompilerInvocation::CreateFromArgs(*Invocation, CC1Args, *Diagnostics,
150
22.1k
                                     BinaryName);
151
22.1k
  Invocation->getFrontendOpts().DisableFree = false;
152
22.1k
  Invocation->getCodeGenOpts().DisableFree = false;
153
22.1k
  return Invocation;
154
22.1k
}
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
21.7k
                      StringRef FileName) {
171
21.7k
  std::vector<std::string> Args;
172
21.7k
  Args.push_back(ToolName.str());
173
21.7k
  Args.push_back("-fsyntax-only");
174
21.7k
  Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
175
21.7k
  Args.push_back(FileName.str());
176
21.7k
  return Args;
177
21.7k
}
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
15.8k
    std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
188
15.8k
  SmallString<16> FileNameStorage;
189
15.8k
  StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
190
191
15.8k
  llvm::IntrusiveRefCntPtr<FileManager> Files(
192
15.8k
      new FileManager(FileSystemOptions(), VFS));
193
15.8k
  ArgumentsAdjuster Adjuster = getClangStripDependencyFileAdjuster();
194
15.8k
  ToolInvocation Invocation(
195
15.8k
      getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileNameRef), FileNameRef),
196
15.8k
      std::move(ToolAction), Files.get(), std::move(PCHContainerOps));
197
15.8k
  return Invocation.run();
198
15.8k
}
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
15.8k
    const FileContentMappings &VirtualMappedFiles) {
206
15.8k
  llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
207
15.8k
      new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
208
15.8k
  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
209
15.8k
      new llvm::vfs::InMemoryFileSystem);
210
15.8k
  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
211
212
15.8k
  SmallString<1024> CodeStorage;
213
15.8k
  InMemoryFileSystem->addFile(FileName, 0,
214
15.8k
                              llvm::MemoryBuffer::getMemBuffer(
215
15.8k
                                  Code.toNullTerminatedStringRef(CodeStorage)));
216
217
1.10k
  for (auto &FilenameWithContent : VirtualMappedFiles) {
218
1.10k
    InMemoryFileSystem->addFile(
219
1.10k
        FilenameWithContent.first, 0,
220
1.10k
        llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
221
1.10k
  }
222
223
15.8k
  return runToolOnCodeWithArgs(std::move(ToolAction), Code, OverlayFileSystem,
224
15.8k
                               Args, FileName, ToolName);
225
15.8k
}
226
227
llvm::Expected<std::string> getAbsolutePath(llvm::vfs::FileSystem &FS,
228
423
                                            StringRef File) {
229
423
  StringRef RelativePath(File);
230
  // FIXME: Should '.\\' be accepted on Win32?
231
423
  if (RelativePath.startswith("./")) {
232
0
    RelativePath = RelativePath.substr(strlen("./"));
233
0
  }
234
235
423
  SmallString<1024> AbsolutePath = RelativePath;
236
423
  if (auto EC = FS.makeAbsolute(AbsolutePath))
237
0
    return llvm::errorCodeToError(EC);
238
423
  llvm::sys::path::native(AbsolutePath);
239
423
  return std::string(AbsolutePath.str());
240
423
}
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
15.8k
      : Action(std::move(Action)) {}
294
295
15.8k
  std::unique_ptr<FrontendAction> create() override {
296
15.8k
    return std::move(Action);
297
15.8k
  }
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.27k
      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
15.8k
      PCHContainerOps(std::move(PCHContainerOps)) {}
316
317
22.1k
ToolInvocation::~ToolInvocation() {
318
22.1k
  if (OwnsAction)
319
15.8k
    delete Action;
320
22.1k
}
321
322
0
void ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) {
323
0
  SmallString<1024> PathStorage;
324
0
  llvm::sys::path::native(FilePath, PathStorage);
325
0
  MappedFileContents[PathStorage] = Content;
326
0
}
327
328
22.1k
bool ToolInvocation::run() {
329
22.1k
  std::vector<const char*> Argv;
330
22.1k
  for (const std::string &Str : CommandLine)
331
215k
    Argv.push_back(Str.c_str());
332
22.1k
  const char *const BinaryName = Argv[0];
333
22.1k
  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
334
22.1k
  unsigned MissingArgIndex, MissingArgCount;
335
22.1k
  llvm::opt::InputArgList ParsedArgs = driver::getDriverOptTable().ParseArgs(
336
22.1k
      ArrayRef<const char *>(Argv).slice(1), MissingArgIndex, MissingArgCount);
337
22.1k
  ParseDiagnosticArgs(*DiagOpts, ParsedArgs);
338
22.1k
  TextDiagnosticPrinter DiagnosticPrinter(
339
22.1k
      llvm::errs(), &*DiagOpts);
340
22.1k
  DiagnosticsEngine Diagnostics(
341
22.1k
      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
342
22.0k
      DiagConsumer ? 
DiagConsumer68
: &DiagnosticPrinter, false);
343
344
22.1k
  const std::unique_ptr<driver::Driver> Driver(
345
22.1k
      newDriver(&Diagnostics, BinaryName, &Files->getVirtualFileSystem()));
346
  // The "input file not found" diagnostics from the driver are useful.
347
  // The driver is only aware of the VFS working directory, but some clients
348
  // change this at the FileManager level instead.
349
  // In this case the checks have false positives, so skip them.
350
22.1k
  if (!Files->getFileSystemOpts().WorkingDir.empty())
351
24
    Driver->setCheckInputsExist(false);
352
22.1k
  const std::unique_ptr<driver::Compilation> Compilation(
353
22.1k
      Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
354
22.1k
  if (!Compilation)
355
0
    return false;
356
22.1k
  const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
357
22.1k
      &Diagnostics, Compilation.get());
358
22.1k
  if (!CC1Args)
359
3
    return false;
360
22.1k
  std::unique_ptr<CompilerInvocation> Invocation(
361
22.1k
      newInvocation(&Diagnostics, *CC1Args, BinaryName));
362
  // FIXME: remove this when all users have migrated!
363
0
  for (const auto &It : MappedFileContents) {
364
    // Inject the code as the given file name into the preprocessor options.
365
0
    std::unique_ptr<llvm::MemoryBuffer> Input =
366
0
        llvm::MemoryBuffer::getMemBuffer(It.getValue());
367
0
    Invocation->getPreprocessorOpts().addRemappedFile(It.getKey(),
368
0
                                                      Input.release());
369
0
  }
370
22.1k
  return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
371
22.1k
                       std::move(PCHContainerOps));
372
22.1k
}
373
374
bool ToolInvocation::runInvocation(
375
    const char *BinaryName, driver::Compilation *Compilation,
376
    std::shared_ptr<CompilerInvocation> Invocation,
377
22.1k
    std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
378
  // Show the invocation, with -v.
379
22.1k
  if (Invocation->getHeaderSearchOpts().Verbose) {
380
1
    llvm::errs() << "clang Invocation:\n";
381
1
    Compilation->getJobs().Print(llvm::errs(), "\n", true);
382
1
    llvm::errs() << "\n";
383
1
  }
384
385
22.1k
  return Action->runInvocation(std::move(Invocation), Files,
386
22.1k
                               std::move(PCHContainerOps), DiagConsumer);
387
22.1k
}
388
389
bool FrontendActionFactory::runInvocation(
390
    std::shared_ptr<CompilerInvocation> Invocation, FileManager *Files,
391
    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
392
16.1k
    DiagnosticConsumer *DiagConsumer) {
393
  // Create a compiler instance to handle the actual work.
394
16.1k
  CompilerInstance Compiler(std::move(PCHContainerOps));
395
16.1k
  Compiler.setInvocation(std::move(Invocation));
396
16.1k
  Compiler.setFileManager(Files);
397
398
  // The FrontendAction can have lifetime requirements for Compiler or its
399
  // members, and we need to ensure it's deleted earlier than Compiler. So we
400
  // pass it to an std::unique_ptr declared after the Compiler variable.
401
16.1k
  std::unique_ptr<FrontendAction> ScopedToolAction(create());
402
403
  // Create the compiler's actual diagnostics engine.
404
16.1k
  Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
405
16.1k
  if (!Compiler.hasDiagnostics())
406
0
    return false;
407
408
16.1k
  Compiler.createSourceManager(*Files);
409
410
16.1k
  const bool Success = Compiler.ExecuteAction(*ScopedToolAction);
411
412
16.1k
  Files->clearStatCache();
413
16.1k
  return Success;
414
16.1k
}
415
416
ClangTool::ClangTool(const CompilationDatabase &Compilations,
417
                     ArrayRef<std::string> SourcePaths,
418
                     std::shared_ptr<PCHContainerOperations> PCHContainerOps,
419
                     IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
420
                     IntrusiveRefCntPtr<FileManager> Files)
421
    : Compilations(Compilations), SourcePaths(SourcePaths),
422
      PCHContainerOps(std::move(PCHContainerOps)),
423
      OverlayFileSystem(new llvm::vfs::OverlayFileSystem(std::move(BaseFS))),
424
      InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
425
      Files(Files ? Files
426
330
                  : new FileManager(FileSystemOptions(), OverlayFileSystem)) {
427
330
  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
428
330
  appendArgumentsAdjuster(getClangStripOutputAdjuster());
429
330
  appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
430
330
  appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
431
330
  if (Files)
432
61
    Files->setVirtualFileSystem(OverlayFileSystem);
433
330
}
434
435
330
ClangTool::~ClangTool() = default;
436
437
9.75k
void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {
438
9.75k
  MappedFileContents.push_back(std::make_pair(FilePath, Content));
439
9.75k
}
440
441
1.32k
void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) {
442
1.32k
  ArgsAdjuster = combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
443
1.32k
}
444
445
111
void ClangTool::clearArgumentsAdjusters() {
446
111
  ArgsAdjuster = nullptr;
447
111
}
448
449
static void injectResourceDir(CommandLineArguments &Args, const char *Argv0,
450
405
                              void *MainAddr) {
451
  // Allow users to override the resource dir.
452
405
  for (StringRef Arg : Args)
453
2.28k
    if (Arg.startswith("-resource-dir"))
454
0
      return;
455
456
  // If there's no override in place add our resource dir.
457
405
  Args.push_back("-resource-dir=" +
458
405
                 CompilerInvocation::GetResourcesPath(Argv0, MainAddr));
459
405
}
460
461
398
int ClangTool::run(ToolAction *Action) {
462
  // Exists solely for the purpose of lookup of the resource path.
463
  // This just needs to be some symbol in the binary.
464
398
  static int StaticSymbol;
465
466
  // First insert all absolute paths into the in-memory VFS. These are global
467
  // for all compile commands.
468
398
  if (SeenWorkingDirectories.insert("/").second)
469
329
    for (const auto &MappedFile : MappedFileContents)
470
9.65k
      if (llvm::sys::path::is_absolute(MappedFile.first))
471
13
        InMemoryFileSystem->addFile(
472
13
            MappedFile.first, 0,
473
13
            llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
474
475
398
  bool ProcessingFailed = false;
476
398
  bool FileSkipped = false;
477
  // Compute all absolute paths before we run any actions, as those will change
478
  // the working directory.
479
398
  std::vector<std::string> AbsolutePaths;
480
398
  AbsolutePaths.reserve(SourcePaths.size());
481
402
  for (const auto &SourcePath : SourcePaths) {
482
402
    auto AbsPath = getAbsolutePath(*OverlayFileSystem, SourcePath);
483
402
    if (!AbsPath) {
484
0
      llvm::errs() << "Skipping " << SourcePath
485
0
                   << ". Error while getting an absolute path: "
486
0
                   << llvm::toString(AbsPath.takeError()) << "\n";
487
0
      continue;
488
0
    }
489
402
    AbsolutePaths.push_back(std::move(*AbsPath));
490
402
  }
491
492
  // Remember the working directory in case we need to restore it.
493
398
  std::string InitialWorkingDir;
494
398
  if (RestoreCWD) {
495
335
    if (auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
496
334
      InitialWorkingDir = std::move(*CWD);
497
1
    } else {
498
1
      llvm::errs() << "Could not get working directory: "
499
1
                   << CWD.getError().message() << "\n";
500
1
    }
501
335
  }
502
503
400
  for (llvm::StringRef File : AbsolutePaths) {
504
    // Currently implementations of CompilationDatabase::getCompileCommands can
505
    // change the state of the file system (e.g.  prepare generated headers), so
506
    // this method needs to run right before we invoke the tool, as the next
507
    // file may require a different (incompatible) state of the file system.
508
    //
509
    // FIXME: Make the compilation database interface more explicit about the
510
    // requirements to the order of invocation of its members.
511
400
    std::vector<CompileCommand> CompileCommandsForFile =
512
400
        Compilations.getCompileCommands(File);
513
400
    if (CompileCommandsForFile.empty()) {
514
0
      llvm::errs() << "Skipping " << File << ". Compile command not found.\n";
515
0
      FileSkipped = true;
516
0
      continue;
517
0
    }
518
405
    
for (CompileCommand &CompileCommand : CompileCommandsForFile)400
{
519
      // FIXME: chdir is thread hostile; on the other hand, creating the same
520
      // behavior as chdir is complex: chdir resolves the path once, thus
521
      // guaranteeing that all subsequent relative path operations work
522
      // on the same path the original chdir resulted in. This makes a
523
      // difference for example on network filesystems, where symlinks might be
524
      // switched during runtime of the tool. Fixing this depends on having a
525
      // file system abstraction that allows openat() style interactions.
526
405
      if (OverlayFileSystem->setCurrentWorkingDirectory(
527
405
              CompileCommand.Directory))
528
0
        llvm::report_fatal_error("Cannot chdir into \"" +
529
0
                                 Twine(CompileCommand.Directory) + "\"!");
530
531
      // Now fill the in-memory VFS with the relative file mappings so it will
532
      // have the correct relative paths. We never remove mappings but that
533
      // should be fine.
534
405
      if (SeenWorkingDirectories.insert(CompileCommand.Directory).second)
535
317
        for (const auto &MappedFile : MappedFileContents)
536
9.96k
          if (!llvm::sys::path::is_absolute(MappedFile.first))
537
9.90k
            InMemoryFileSystem->addFile(
538
9.90k
                MappedFile.first, 0,
539
9.90k
                llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
540
541
405
      std::vector<std::string> CommandLine = CompileCommand.CommandLine;
542
405
      if (ArgsAdjuster)
543
334
        CommandLine = ArgsAdjuster(CommandLine, CompileCommand.Filename);
544
405
      assert(!CommandLine.empty());
545
546
      // Add the resource dir based on the binary of this tool. argv[0] in the
547
      // compilation database may refer to a different compiler and we want to
548
      // pick up the very same standard library that compiler is using. The
549
      // builtin headers in the resource dir need to match the exact clang
550
      // version the tool is using.
551
      // FIXME: On linux, GetMainExecutable is independent of the value of the
552
      // first argument, thus allowing ClangTool and runToolOnCode to just
553
      // pass in made-up names here. Make sure this works on other platforms.
554
405
      injectResourceDir(CommandLine, "clang_tool", &StaticSymbol);
555
556
      // FIXME: We need a callback mechanism for the tool writer to output a
557
      // customized message for each file.
558
405
      LLVM_DEBUG({ llvm::dbgs() << "Processing: " << File << ".\n"; });
559
405
      ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
560
405
                                PCHContainerOps);
561
405
      Invocation.setDiagnosticConsumer(DiagConsumer);
562
563
405
      if (!Invocation.run()) {
564
        // FIXME: Diagnostics should be used instead.
565
23
        if (PrintErrorMessage)
566
20
          llvm::errs() << "Error while processing " << File << ".\n";
567
23
        ProcessingFailed = true;
568
23
      }
569
405
    }
570
400
  }
571
572
398
  if (!InitialWorkingDir.empty()) {
573
335
    if (auto EC =
574
0
            OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
575
0
      llvm::errs() << "Error when trying to restore working dir: "
576
0
                   << EC.message() << "\n";
577
335
  }
578
375
  return ProcessingFailed ? 
123
: (FileSkipped ?
20
: 0);
579
398
}
580
581
namespace {
582
583
class ASTBuilderAction : public ToolAction {
584
  std::vector<std::unique_ptr<ASTUnit>> &ASTs;
585
586
public:
587
5.89k
  ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
588
589
  bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
590
                     FileManager *Files,
591
                     std::shared_ptr<PCHContainerOperations> PCHContainerOps,
592
5.89k
                     DiagnosticConsumer *DiagConsumer) override {
593
5.89k
    std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
594
5.89k
        Invocation, std::move(PCHContainerOps),
595
5.89k
        CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts(),
596
5.89k
                                            DiagConsumer,
597
5.89k
                                            /*ShouldOwnClient=*/false),
598
5.89k
        Files);
599
5.89k
    if (!AST)
600
0
      return false;
601
602
5.89k
    ASTs.push_back(std::move(AST));
603
5.89k
    return true;
604
5.89k
  }
605
};
606
607
} // namespace
608
609
19
int ClangTool::buildASTs(std::vector<std::unique_ptr<ASTUnit>> &ASTs) {
610
19
  ASTBuilderAction Action(ASTs);
611
19
  return run(&Action);
612
19
}
613
614
59
void ClangTool::setRestoreWorkingDir(bool RestoreCWD) {
615
59
  this->RestoreCWD = RestoreCWD;
616
59
}
617
618
59
void ClangTool::setPrintErrorMessage(bool PrintErrorMessage) {
619
59
  this->PrintErrorMessage = PrintErrorMessage;
620
59
}
621
622
namespace clang {
623
namespace tooling {
624
625
std::unique_ptr<ASTUnit>
626
buildASTFromCode(StringRef Code, StringRef FileName,
627
148
                 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
628
148
  return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName,
629
148
                                  "clang-tool", std::move(PCHContainerOps));
630
148
}
631
632
std::unique_ptr<ASTUnit> buildASTFromCodeWithArgs(
633
    StringRef Code, const std::vector<std::string> &Args, StringRef FileName,
634
    StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
635
    ArgumentsAdjuster Adjuster, const FileContentMappings &VirtualMappedFiles,
636
5.87k
    DiagnosticConsumer *DiagConsumer) {
637
5.87k
  std::vector<std::unique_ptr<ASTUnit>> ASTs;
638
5.87k
  ASTBuilderAction Action(ASTs);
639
5.87k
  llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
640
5.87k
      new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
641
5.87k
  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
642
5.87k
      new llvm::vfs::InMemoryFileSystem);
643
5.87k
  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
644
5.87k
  llvm::IntrusiveRefCntPtr<FileManager> Files(
645
5.87k
      new FileManager(FileSystemOptions(), OverlayFileSystem));
646
647
5.87k
  ToolInvocation Invocation(
648
5.87k
      getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileName), FileName),
649
5.87k
      &Action, Files.get(), std::move(PCHContainerOps));
650
5.87k
  Invocation.setDiagnosticConsumer(DiagConsumer);
651
652
5.87k
  InMemoryFileSystem->addFile(FileName, 0,
653
5.87k
                              llvm::MemoryBuffer::getMemBufferCopy(Code));
654
0
  for (auto &FilenameWithContent : VirtualMappedFiles) {
655
0
    InMemoryFileSystem->addFile(
656
0
        FilenameWithContent.first, 0,
657
0
        llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
658
0
  }
659
660
5.87k
  if (!Invocation.run())
661
0
    return nullptr;
662
663
5.87k
  assert(ASTs.size() == 1);
664
5.87k
  return std::move(ASTs[0]);
665
5.87k
}
666
667
} // namespace tooling
668
} // namespace clang