Coverage Report

Created: 2020-02-25 14:32

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/tools/clang-scan-deps/ClangScanDeps.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- ClangScanDeps.cpp - Implementation of clang-scan-deps --------------===//
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
#include "clang/Frontend/CompilerInstance.h"
10
#include "clang/Tooling/CommonOptionsParser.h"
11
#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
12
#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
13
#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
14
#include "clang/Tooling/JSONCompilationDatabase.h"
15
#include "llvm/Support/CommandLine.h"
16
#include "llvm/Support/FileUtilities.h"
17
#include "llvm/Support/InitLLVM.h"
18
#include "llvm/Support/JSON.h"
19
#include "llvm/Support/Program.h"
20
#include "llvm/Support/Signals.h"
21
#include "llvm/Support/ThreadPool.h"
22
#include "llvm/Support/Threading.h"
23
#include <mutex>
24
#include <thread>
25
26
using namespace clang;
27
using namespace tooling::dependencies;
28
29
namespace {
30
31
class SharedStream {
32
public:
33
54
  SharedStream(raw_ostream &OS) : OS(OS) {}
34
61
  void applyLocked(llvm::function_ref<void(raw_ostream &OS)> Fn) {
35
61
    std::unique_lock<std::mutex> LockGuard(Lock);
36
61
    Fn(OS);
37
61
    OS.flush();
38
61
  }
39
40
private:
41
  std::mutex Lock;
42
  raw_ostream &OS;
43
};
44
45
class ResourceDirectoryCache {
46
public:
47
  /// findResourceDir finds the resource directory relative to the clang
48
  /// compiler being used in Args, by running it with "-print-resource-dir"
49
  /// option and cache the results for reuse. \returns resource directory path
50
  /// associated with the given invocation command or empty string if the
51
  /// compiler path is NOT an absolute path.
52
65
  StringRef findResourceDir(const tooling::CommandLineArguments &Args) {
53
65
    if (Args.size() < 1)
54
0
      return "";
55
65
56
65
    const std::string &ClangBinaryPath = Args[0];
57
65
    if (!llvm::sys::path::is_absolute(ClangBinaryPath))
58
65
      return "";
59
0
60
0
    const std::string &ClangBinaryName =
61
0
        std::string(llvm::sys::path::filename(ClangBinaryPath));
62
0
63
0
    std::unique_lock<std::mutex> LockGuard(CacheLock);
64
0
    const auto &CachedResourceDir = Cache.find(ClangBinaryPath);
65
0
    if (CachedResourceDir != Cache.end())
66
0
      return CachedResourceDir->second;
67
0
68
0
    std::vector<StringRef> PrintResourceDirArgs{ClangBinaryName,
69
0
                                                "-print-resource-dir"};
70
0
    llvm::SmallString<64> OutputFile, ErrorFile;
71
0
    llvm::sys::fs::createTemporaryFile("print-resource-dir-output",
72
0
                                       "" /*no-suffix*/, OutputFile);
73
0
    llvm::sys::fs::createTemporaryFile("print-resource-dir-error",
74
0
                                       "" /*no-suffix*/, ErrorFile);
75
0
    llvm::FileRemover OutputRemover(OutputFile.c_str());
76
0
    llvm::FileRemover ErrorRemover(ErrorFile.c_str());
77
0
    llvm::Optional<StringRef> Redirects[] = {
78
0
        {""}, // Stdin
79
0
        StringRef(OutputFile),
80
0
        StringRef(ErrorFile),
81
0
    };
82
0
    if (const int RC = llvm::sys::ExecuteAndWait(
83
0
            ClangBinaryPath, PrintResourceDirArgs, {}, Redirects)) {
84
0
      auto ErrorBuf = llvm::MemoryBuffer::getFile(ErrorFile.c_str());
85
0
      llvm::errs() << ErrorBuf.get()->getBuffer();
86
0
      return "";
87
0
    }
88
0
89
0
    auto OutputBuf = llvm::MemoryBuffer::getFile(OutputFile.c_str());
90
0
    if (!OutputBuf)
91
0
      return "";
92
0
    StringRef Output = OutputBuf.get()->getBuffer().rtrim('\n');
93
0
94
0
    Cache[ClangBinaryPath] = Output.str();
95
0
    return Cache[ClangBinaryPath];
96
0
  }
97
98
private:
99
  std::map<std::string, std::string> Cache;
100
  std::mutex CacheLock;
101
};
102
103
llvm::cl::opt<bool> Help("h", llvm::cl::desc("Alias for -help"),
104
                         llvm::cl::Hidden);
105
106
llvm::cl::OptionCategory DependencyScannerCategory("Tool options");
107
108
static llvm::cl::opt<ScanningMode> ScanMode(
109
    "mode",
110
    llvm::cl::desc("The preprocessing mode used to compute the dependencies"),
111
    llvm::cl::values(
112
        clEnumValN(ScanningMode::MinimizedSourcePreprocessing,
113
                   "preprocess-minimized-sources",
114
                   "The set of dependencies is computed by preprocessing the "
115
                   "source files that were minimized to only include the "
116
                   "contents that might affect the dependencies"),
117
        clEnumValN(ScanningMode::CanonicalPreprocessing, "preprocess",
118
                   "The set of dependencies is computed by preprocessing the "
119
                   "unmodified source files")),
120
    llvm::cl::init(ScanningMode::MinimizedSourcePreprocessing),
121
    llvm::cl::cat(DependencyScannerCategory));
122
123
static llvm::cl::opt<ScanningOutputFormat> Format(
124
    "format", llvm::cl::desc("The output format for the dependencies"),
125
    llvm::cl::values(clEnumValN(ScanningOutputFormat::Make, "make",
126
                                "Makefile compatible dep file"),
127
                     clEnumValN(ScanningOutputFormat::Full, "experimental-full",
128
                                "Full dependency graph suitable"
129
                                " for explicitly building modules. This format "
130
                                "is experimental and will change.")),
131
    llvm::cl::init(ScanningOutputFormat::Make),
132
    llvm::cl::cat(DependencyScannerCategory));
133
134
static llvm::cl::opt<bool> FullCommandLine(
135
    "full-command-line",
136
    llvm::cl::desc("Include the full command lines to use to build modules"),
137
    llvm::cl::init(false), llvm::cl::cat(DependencyScannerCategory));
138
139
llvm::cl::opt<unsigned>
140
    NumThreads("j", llvm::cl::Optional,
141
               llvm::cl::desc("Number of worker threads to use (default: use "
142
                              "all concurrent threads)"),
143
               llvm::cl::init(0), llvm::cl::cat(DependencyScannerCategory));
144
145
llvm::cl::opt<std::string>
146
    CompilationDB("compilation-database",
147
                  llvm::cl::desc("Compilation database"), llvm::cl::Required,
148
                  llvm::cl::cat(DependencyScannerCategory));
149
150
llvm::cl::opt<bool> ReuseFileManager(
151
    "reuse-filemanager",
152
    llvm::cl::desc("Reuse the file manager and its cache between invocations."),
153
    llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory));
154
155
llvm::cl::opt<bool> SkipExcludedPPRanges(
156
    "skip-excluded-pp-ranges",
157
    llvm::cl::desc(
158
        "Use the preprocessor optimization that skips excluded conditionals by "
159
        "bumping the buffer pointer in the lexer instead of lexing the tokens  "
160
        "until reaching the end directive."),
161
    llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory));
162
163
llvm::cl::opt<bool> Verbose("v", llvm::cl::Optional,
164
                            llvm::cl::desc("Use verbose output."),
165
                            llvm::cl::init(false),
166
                            llvm::cl::cat(DependencyScannerCategory));
167
168
} // end anonymous namespace
169
170
/// \returns object-file path derived from source-file path.
171
31
static std::string getObjFilePath(StringRef SrcFile) {
172
31
  SmallString<128> ObjFileName(SrcFile);
173
31
  llvm::sys::path::replace_extension(ObjFileName, "o");
174
31
  return std::string(ObjFileName.str());
175
31
}
176
177
class SingleCommandCompilationDatabase : public tooling::CompilationDatabase {
178
public:
179
  SingleCommandCompilationDatabase(tooling::CompileCommand Cmd)
180
65
      : Command(std::move(Cmd)) {}
181
182
  virtual std::vector<tooling::CompileCommand>
183
63
  getCompileCommands(StringRef FilePath) const {
184
63
    return {Command};
185
63
  }
186
187
194
  virtual std::vector<tooling::CompileCommand> getAllCompileCommands() const {
188
194
    return {Command};
189
194
  }
190
191
private:
192
  tooling::CompileCommand Command;
193
};
194
195
/// Takes the result of a dependency scan and prints error / dependency files
196
/// based on the result.
197
///
198
/// \returns True on error.
199
static bool
200
handleMakeDependencyToolResult(const std::string &Input,
201
                               llvm::Expected<std::string> &MaybeFile,
202
61
                               SharedStream &OS, SharedStream &Errs) {
203
61
  if (!MaybeFile) {
204
3
    llvm::handleAllErrors(
205
3
        MaybeFile.takeError(), [&Input, &Errs](llvm::StringError &Err) {
206
3
          Errs.applyLocked([&](raw_ostream &OS) {
207
3
            OS << "Error while scanning dependencies for " << Input << ":\n";
208
3
            OS << Err.getMessage();
209
3
          });
210
3
        });
211
3
    return true;
212
3
  }
213
58
  OS.applyLocked([&](raw_ostream &OS) { OS << *MaybeFile; });
214
58
  return false;
215
58
}
216
217
3
static llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) {
218
3
  std::vector<llvm::StringRef> Strings;
219
3
  for (auto &&I : Set)
220
6
    Strings.push_back(I.getKey());
221
3
  std::sort(Strings.begin(), Strings.end());
222
3
  return llvm::json::Array(Strings);
223
3
}
224
225
7
static llvm::json::Array toJSONSorted(std::vector<ClangModuleDep> V) {
226
7
  std::sort(V.begin(), V.end(),
227
7
            [](const ClangModuleDep &A, const ClangModuleDep &B) {
228
0
              return std::tie(A.ModuleName, A.ContextHash) <
229
0
                     std::tie(B.ModuleName, B.ContextHash);
230
0
            });
231
7
232
7
  llvm::json::Array Ret;
233
7
  for (const ClangModuleDep &CMD : V)
234
5
    Ret.push_back(llvm::json::Object(
235
5
        {{"module-name", CMD.ModuleName}, {"context-hash", CMD.ContextHash}}));
236
7
  return Ret;
237
7
}
238
239
// Thread safe.
240
class FullDeps {
241
public:
242
  void mergeDeps(StringRef Input, FullDependenciesResult FDR,
243
4
                 size_t InputIndex) {
244
4
    const FullDependencies &FD = FDR.FullDeps;
245
4
246
4
    InputDeps ID;
247
4
    ID.FileName = std::string(Input);
248
4
    ID.ContextHash = std::move(FD.ContextHash);
249
4
    ID.FileDeps = std::move(FD.FileDeps);
250
4
    ID.ModuleDeps = std::move(FD.ClangModuleDeps);
251
4
252
4
    std::unique_lock<std::mutex> ul(Lock);
253
5
    for (const ModuleDeps &MD : FDR.DiscoveredModules) {
254
5
      auto I = Modules.find({MD.ContextHash, MD.ModuleName, 0});
255
5
      if (I != Modules.end()) {
256
2
        I->first.InputIndex = std::min(I->first.InputIndex, InputIndex);
257
2
        continue;
258
2
      }
259
3
      Modules.insert(
260
3
          I, {{MD.ContextHash, MD.ModuleName, InputIndex}, std::move(MD)});
261
3
    }
262
4
263
4
    if (FullCommandLine)
264
4
      ID.AdditonalCommandLine = FD.getAdditionalCommandLine(
265
5
          [&](ClangModuleDep CMD) { return lookupPCMPath(CMD); },
266
5
          [&](ClangModuleDep CMD) -> const ModuleDeps & {
267
5
            return lookupModuleDeps(CMD);
268
5
          });
269
4
270
4
    Inputs.push_back(std::move(ID));
271
4
  }
272
273
1
  void printFullOutput(raw_ostream &OS) {
274
1
    // Sort the modules by name to get a deterministic order.
275
1
    std::vector<ContextModulePair> ModuleNames;
276
1
    for (auto &&M : Modules)
277
3
      ModuleNames.push_back(M.first);
278
1
    std::sort(ModuleNames.begin(), ModuleNames.end(),
279
3
              [](const ContextModulePair &A, const ContextModulePair &B) {
280
3
                return std::tie(A.ModuleName, A.InputIndex) <
281
3
                       std::tie(B.ModuleName, B.InputIndex);
282
3
              });
283
1
284
1
    std::sort(Inputs.begin(), Inputs.end(),
285
3
              [](const InputDeps &A, const InputDeps &B) {
286
3
                return A.FileName < B.FileName;
287
3
              });
288
1
289
1
    using namespace llvm::json;
290
1
291
1
    Array OutModules;
292
3
    for (auto &&ModName : ModuleNames) {
293
3
      auto &MD = Modules[ModName];
294
3
      Object O{
295
3
          {"name", MD.ModuleName},
296
3
          {"context-hash", MD.ContextHash},
297
3
          {"file-deps", toJSONSorted(MD.FileDeps)},
298
3
          {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)},
299
3
          {"clang-modulemap-file", MD.ClangModuleMapFile},
300
3
          {"command-line",
301
3
           FullCommandLine
302
3
               ? MD.getFullCommandLine(
303
3
                     [&](ClangModuleDep CMD) 
{ return lookupPCMPath(CMD); }1
,
304
3
                     [&](ClangModuleDep CMD) -> const ModuleDeps & {
305
1
                       return lookupModuleDeps(CMD);
306
1
                     })
307
3
               : 
MD.NonPathCommandLine0
},
308
3
      };
309
3
      OutModules.push_back(std::move(O));
310
3
    }
311
1
312
1
    Array TUs;
313
4
    for (auto &&I : Inputs) {
314
4
      Object O{
315
4
          {"input-file", I.FileName},
316
4
          {"clang-context-hash", I.ContextHash},
317
4
          {"file-deps", I.FileDeps},
318
4
          {"clang-module-deps", toJSONSorted(I.ModuleDeps)},
319
4
          {"command-line", I.AdditonalCommandLine},
320
4
      };
321
4
      TUs.push_back(std::move(O));
322
4
    }
323
1
324
1
    Object Output{
325
1
        {"modules", std::move(OutModules)},
326
1
        {"translation-units", std::move(TUs)},
327
1
    };
328
1
329
1
    OS << llvm::formatv("{0:2}\n", Value(std::move(Output)));
330
1
  }
331
332
private:
333
6
  StringRef lookupPCMPath(ClangModuleDep CMD) {
334
6
    return Modules[ContextModulePair{CMD.ContextHash, CMD.ModuleName, 0}]
335
6
        .ImplicitModulePCMPath;
336
6
  }
337
338
6
  const ModuleDeps &lookupModuleDeps(ClangModuleDep CMD) {
339
6
    auto I =
340
6
        Modules.find(ContextModulePair{CMD.ContextHash, CMD.ModuleName, 0});
341
6
    assert(I != Modules.end());
342
6
    return I->second;
343
6
  };
344
345
  struct ContextModulePair {
346
    std::string ContextHash;
347
    std::string ModuleName;
348
    mutable size_t InputIndex;
349
350
18
    bool operator==(const ContextModulePair &Other) const {
351
18
      return ContextHash == Other.ContextHash && ModuleName == Other.ModuleName;
352
18
    }
353
  };
354
355
  struct ContextModulePairHasher {
356
23
    std::size_t operator()(const ContextModulePair &CMP) const {
357
23
      using llvm::hash_combine;
358
23
359
23
      return hash_combine(CMP.ContextHash, CMP.ModuleName);
360
23
    }
361
  };
362
363
  struct InputDeps {
364
    std::string FileName;
365
    std::string ContextHash;
366
    std::vector<std::string> FileDeps;
367
    std::vector<ClangModuleDep> ModuleDeps;
368
    std::vector<std::string> AdditonalCommandLine;
369
  };
370
371
  std::mutex Lock;
372
  std::unordered_map<ContextModulePair, ModuleDeps, ContextModulePairHasher>
373
      Modules;
374
  std::vector<InputDeps> Inputs;
375
};
376
377
static bool handleFullDependencyToolResult(
378
    const std::string &Input,
379
    llvm::Expected<FullDependenciesResult> &MaybeFullDeps, FullDeps &FD,
380
4
    size_t InputIndex, SharedStream &OS, SharedStream &Errs) {
381
4
  if (!MaybeFullDeps) {
382
0
    llvm::handleAllErrors(
383
0
        MaybeFullDeps.takeError(), [&Input, &Errs](llvm::StringError &Err) {
384
0
          Errs.applyLocked([&](raw_ostream &OS) {
385
0
            OS << "Error while scanning dependencies for " << Input << ":\n";
386
0
            OS << Err.getMessage();
387
0
          });
388
0
        });
389
0
    return true;
390
0
  }
391
4
  FD.mergeDeps(Input, std::move(*MaybeFullDeps), InputIndex);
392
4
  return false;
393
4
}
394
395
27
int main(int argc, const char **argv) {
396
27
  llvm::InitLLVM X(argc, argv);
397
27
  llvm::cl::HideUnrelatedOptions(DependencyScannerCategory);
398
27
  if (!llvm::cl::ParseCommandLineOptions(argc, argv))
399
0
    return 1;
400
27
401
27
  std::string ErrorMessage;
402
27
  std::unique_ptr<tooling::JSONCompilationDatabase> Compilations =
403
27
      tooling::JSONCompilationDatabase::loadFromFile(
404
27
          CompilationDB, ErrorMessage,
405
27
          tooling::JSONCommandLineSyntax::AutoDetect);
406
27
  if (!Compilations) {
407
0
    llvm::errs() << "error: " << ErrorMessage << "\n";
408
0
    return 1;
409
0
  }
410
27
411
27
  llvm::cl::PrintOptionValues();
412
27
413
27
  // The command options are rewritten to run Clang in preprocessor only mode.
414
27
  auto AdjustingCompilations =
415
27
      std::make_unique<tooling::ArgumentsAdjustingCompilations>(
416
27
          std::move(Compilations));
417
27
  ResourceDirectoryCache ResourceDirCache;
418
27
  AdjustingCompilations->appendArgumentsAdjuster(
419
27
      [&ResourceDirCache](const tooling::CommandLineArguments &Args,
420
65
                          StringRef FileName) {
421
65
        std::string LastO = "";
422
65
        bool HasMT = false;
423
65
        bool HasMQ = false;
424
65
        bool HasMD = false;
425
65
        bool HasResourceDir = false;
426
65
        // We need to find the last -o value.
427
65
        if (!Args.empty()) {
428
65
          std::size_t Idx = Args.size() - 1;
429
583
          for (auto It = Args.rbegin(); It != Args.rend(); 
++It518
) {
430
518
            if (It != Args.rbegin()) {
431
453
              if (Args[Idx] == "-o")
432
20
                LastO = Args[Idx + 1];
433
453
              if (Args[Idx] == "-MT")
434
0
                HasMT = true;
435
453
              if (Args[Idx] == "-MQ")
436
0
                HasMQ = true;
437
453
              if (Args[Idx] == "-MD")
438
14
                HasMD = true;
439
453
              if (Args[Idx] == "-resource-dir")
440
0
                HasResourceDir = true;
441
453
            }
442
518
            --Idx;
443
518
          }
444
65
        }
445
65
        // If there's no -MT/-MQ Driver would add -MT with the value of the last
446
65
        // -o option.
447
65
        tooling::CommandLineArguments AdjustedArgs = Args;
448
65
        AdjustedArgs.push_back("-o");
449
65
        AdjustedArgs.push_back("/dev/null");
450
65
        if (!HasMT && !HasMQ) {
451
65
          AdjustedArgs.push_back("-M");
452
65
          AdjustedArgs.push_back("-MT");
453
65
          // We're interested in source dependencies of an object file.
454
65
          if (!HasMD) {
455
51
            // FIXME: We are missing the directory unless the -o value is an
456
51
            // absolute path.
457
51
            AdjustedArgs.push_back(!LastO.empty() ? 
LastO20
458
51
                                                  : 
getObjFilePath(FileName)31
);
459
51
          } else {
460
14
            AdjustedArgs.push_back(std::string(FileName));
461
14
          }
462
65
        }
463
65
        AdjustedArgs.push_back("-Xclang");
464
65
        AdjustedArgs.push_back("-Eonly");
465
65
        AdjustedArgs.push_back("-Xclang");
466
65
        AdjustedArgs.push_back("-sys-header-deps");
467
65
        AdjustedArgs.push_back("-Wno-error");
468
65
469
65
        if (!HasResourceDir) {
470
65
          StringRef ResourceDir =
471
65
              ResourceDirCache.findResourceDir(Args);
472
65
          if (!ResourceDir.empty()) {
473
0
            AdjustedArgs.push_back("-resource-dir");
474
0
            AdjustedArgs.push_back(std::string(ResourceDir));
475
0
          }
476
65
        }
477
65
        return AdjustedArgs;
478
65
      });
479
27
  AdjustingCompilations->appendArgumentsAdjuster(
480
27
      tooling::getClangStripSerializeDiagnosticAdjuster());
481
27
482
27
  SharedStream Errs(llvm::errs());
483
27
  // Print out the dependency results to STDOUT by default.
484
27
  SharedStream DependencyOS(llvm::outs());
485
27
486
27
  DependencyScanningService Service(ScanMode, Format, ReuseFileManager,
487
27
                                    SkipExcludedPPRanges);
488
27
  llvm::ThreadPool Pool(llvm::hardware_concurrency(NumThreads));
489
27
  std::vector<std::unique_ptr<DependencyScanningTool>> WorkerTools;
490
80
  for (unsigned I = 0; I < Pool.getThreadCount(); 
++I53
)
491
53
    WorkerTools.push_back(std::make_unique<DependencyScanningTool>(Service));
492
27
493
27
  std::vector<SingleCommandCompilationDatabase> Inputs;
494
27
  for (tooling::CompileCommand Cmd :
495
27
       AdjustingCompilations->getAllCompileCommands())
496
65
    Inputs.emplace_back(Cmd);
497
27
498
27
  std::atomic<bool> HadErrors(false);
499
27
  FullDeps FD;
500
27
  std::mutex Lock;
501
27
  size_t Index = 0;
502
27
503
27
  if (Verbose) {
504
0
    llvm::outs() << "Running clang-scan-deps on " << Inputs.size()
505
0
                 << " files using " << Pool.getThreadCount() << " workers\n";
506
0
  }
507
80
  for (unsigned I = 0; I < Pool.getThreadCount(); 
++I53
) {
508
53
    Pool.async([I, &Lock, &Index, &Inputs, &HadErrors, &FD, &WorkerTools,
509
53
                &DependencyOS, &Errs]() {
510
53
      llvm::StringSet<> AlreadySeenModules;
511
118
      while (true) {
512
118
        const SingleCommandCompilationDatabase *Input;
513
118
        std::string Filename;
514
118
        std::string CWD;
515
118
        size_t LocalIndex;
516
118
        // Take the next input.
517
118
        {
518
118
          std::unique_lock<std::mutex> LockGuard(Lock);
519
118
          if (Index >= Inputs.size())
520
53
            return;
521
65
          LocalIndex = Index;
522
65
          Input = &Inputs[Index++];
523
65
          tooling::CompileCommand Cmd = Input->getAllCompileCommands()[0];
524
65
          Filename = std::move(Cmd.Filename);
525
65
          CWD = std::move(Cmd.Directory);
526
65
        }
527
65
        // Run the tool on it.
528
65
        if (Format == ScanningOutputFormat::Make) {
529
61
          auto MaybeFile = WorkerTools[I]->getDependencyFile(*Input, CWD);
530
61
          if (handleMakeDependencyToolResult(Filename, MaybeFile, DependencyOS,
531
61
                                             Errs))
532
3
            HadErrors = true;
533
61
        } else {
534
4
          auto MaybeFullDeps = WorkerTools[I]->getFullDependencies(
535
4
              *Input, CWD, AlreadySeenModules);
536
4
          if (handleFullDependencyToolResult(Filename, MaybeFullDeps, FD,
537
4
                                             LocalIndex, DependencyOS, Errs))
538
0
            HadErrors = true;
539
4
        }
540
65
      }
541
53
    });
542
53
  }
543
27
  Pool.wait();
544
27
545
27
  if (Format == ScanningOutputFormat::Full)
546
1
    FD.printFullOutput(llvm::outs());
547
27
548
27
  return HadErrors;
549
27
}