Coverage Report

Created: 2022-01-22 13:19

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- ModuleDepCollector.cpp - Callbacks to collect deps -------*- C++ -*-===//
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/Tooling/DependencyScanning/ModuleDepCollector.h"
10
11
#include "clang/Frontend/CompilerInstance.h"
12
#include "clang/Lex/Preprocessor.h"
13
#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
14
#include "llvm/Support/StringSaver.h"
15
16
using namespace clang;
17
using namespace tooling;
18
using namespace dependencies;
19
20
static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts,
21
                                     ASTReader &Reader,
22
3
                                     const serialization::ModuleFile &MF) {
23
  // Only preserve search paths that were used during the dependency scan.
24
3
  std::vector<HeaderSearchOptions::Entry> Entries = Opts.UserEntries;
25
3
  Opts.UserEntries.clear();
26
42
  for (unsigned I = 0; I < Entries.size(); 
++I39
)
27
39
    if (MF.SearchPathUsage[I])
28
10
      Opts.UserEntries.push_back(Entries[I]);
29
3
}
30
31
CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths(
32
    const ModuleDeps &Deps,
33
52
    llvm::function_ref<void(CompilerInvocation &)> Optimize) const {
34
  // Make a deep copy of the original Clang invocation.
35
52
  CompilerInvocation CI(OriginalInvocation);
36
37
52
  CI.getLangOpts()->resetNonModularOptions();
38
52
  CI.getPreprocessorOpts().resetNonModularOptions();
39
40
  // Remove options incompatible with explicit module build or are likely to
41
  // differ between identical modules discovered from different translation
42
  // units.
43
52
  CI.getFrontendOpts().Inputs.clear();
44
52
  CI.getFrontendOpts().OutputFile.clear();
45
52
  CI.getCodeGenOpts().MainFileName.clear();
46
52
  CI.getCodeGenOpts().DwarfDebugFlags.clear();
47
48
52
  CI.getFrontendOpts().ProgramAction = frontend::GenerateModule;
49
52
  CI.getLangOpts()->ModuleName = Deps.ID.ModuleName;
50
52
  CI.getFrontendOpts().IsSystemModule = Deps.IsSystem;
51
52
52
  CI.getLangOpts()->ImplicitModules = false;
53
54
  // Report the prebuilt modules this module uses.
55
52
  for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps)
56
3
    CI.getFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile);
57
58
52
  Optimize(CI);
59
60
  // The original invocation probably didn't have strict context hash enabled.
61
  // We will use the context hash of this invocation to distinguish between
62
  // multiple incompatible versions of the same module and will use it when
63
  // reporting dependencies to the clients. Let's make sure we're using
64
  // **strict** context hash in order to prevent accidental sharing of
65
  // incompatible modules (e.g. with differences in search paths).
66
52
  CI.getHeaderSearchOpts().ModulesStrictContextHash = true;
67
68
52
  return CI;
69
52
}
70
71
static std::vector<std::string>
72
44
serializeCompilerInvocation(const CompilerInvocation &CI) {
73
  // Set up string allocator.
74
44
  llvm::BumpPtrAllocator Alloc;
75
44
  llvm::StringSaver Strings(Alloc);
76
2.34k
  auto SA = [&Strings](const Twine &Arg) { return Strings.save(Arg).data(); };
77
78
  // Synthesize full command line from the CompilerInvocation, including "-cc1".
79
44
  SmallVector<const char *, 32> Args{"-cc1"};
80
44
  CI.generateCC1CommandLine(Args, SA);
81
82
  // Convert arguments to the return type.
83
44
  return std::vector<std::string>{Args.begin(), Args.end()};
84
44
}
85
86
std::vector<std::string> ModuleDeps::getCanonicalCommandLine(
87
    std::function<StringRef(ModuleID)> LookupPCMPath,
88
21
    std::function<const ModuleDeps &(ModuleID)> LookupModuleDeps) const {
89
21
  CompilerInvocation CI(BuildInvocation);
90
21
  FrontendOptions &FrontendOpts = CI.getFrontendOpts();
91
92
21
  InputKind ModuleMapInputKind(FrontendOpts.DashX.getLanguage(),
93
21
                               InputKind::Format::ModuleMap);
94
21
  FrontendOpts.Inputs.emplace_back(ClangModuleMapFile, ModuleMapInputKind);
95
21
  FrontendOpts.OutputFile = std::string(LookupPCMPath(ID));
96
97
21
  dependencies::detail::collectPCMAndModuleMapPaths(
98
21
      ClangModuleDeps, LookupPCMPath, LookupModuleDeps,
99
21
      FrontendOpts.ModuleFiles, FrontendOpts.ModuleMapFiles);
100
101
21
  return serializeCompilerInvocation(CI);
102
21
}
103
104
std::vector<std::string>
105
23
ModuleDeps::getCanonicalCommandLineWithoutModulePaths() const {
106
23
  return serializeCompilerInvocation(BuildInvocation);
107
23
}
108
109
void dependencies::detail::collectPCMAndModuleMapPaths(
110
    llvm::ArrayRef<ModuleID> Modules,
111
    std::function<StringRef(ModuleID)> LookupPCMPath,
112
    std::function<const ModuleDeps &(ModuleID)> LookupModuleDeps,
113
42
    std::vector<std::string> &PCMPaths, std::vector<std::string> &ModMapPaths) {
114
42
  llvm::StringSet<> AlreadyAdded;
115
116
42
  std::function<void(llvm::ArrayRef<ModuleID>)> AddArgs =
117
70
      [&](llvm::ArrayRef<ModuleID> Modules) {
118
70
        for (const ModuleID &MID : Modules) {
119
28
          if (!AlreadyAdded.insert(MID.ModuleName + MID.ContextHash).second)
120
0
            continue;
121
28
          const ModuleDeps &M = LookupModuleDeps(MID);
122
          // Depth first traversal.
123
28
          AddArgs(M.ClangModuleDeps);
124
28
          PCMPaths.push_back(LookupPCMPath(MID).str());
125
28
          if (!M.ClangModuleMapFile.empty())
126
28
            ModMapPaths.push_back(M.ClangModuleMapFile);
127
28
        }
128
70
      };
129
130
42
  AddArgs(Modules);
131
42
}
132
133
void ModuleDepCollectorPP::FileChanged(SourceLocation Loc,
134
                                       FileChangeReason Reason,
135
                                       SrcMgr::CharacteristicKind FileType,
136
219
                                       FileID PrevFID) {
137
219
  if (Reason != PPCallbacks::EnterFile)
138
108
    return;
139
140
  // This has to be delayed as the context hash can change at the start of
141
  // `CompilerInstance::ExecuteAction`.
142
111
  if (MDC.ContextHash.empty()) {
143
38
    MDC.ContextHash = MDC.ScanInstance.getInvocation().getModuleHash();
144
38
    MDC.Consumer.handleContextHash(MDC.ContextHash);
145
38
  }
146
147
111
  SourceManager &SM = MDC.ScanInstance.getSourceManager();
148
149
  // Dependency generation really does want to go all the way to the
150
  // file entry for a source location to find out what is depended on.
151
  // We do not want #line markers to affect dependency generation!
152
111
  if (Optional<StringRef> Filename =
153
111
          SM.getNonBuiltinFilenameForID(SM.getFileID(SM.getExpansionLoc(Loc))))
154
41
    MDC.FileDeps.push_back(
155
41
        std::string(llvm::sys::path::remove_leading_dotslash(*Filename)));
156
111
}
157
158
void ModuleDepCollectorPP::InclusionDirective(
159
    SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
160
    bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File,
161
    StringRef SearchPath, StringRef RelativePath, const Module *Imported,
162
46
    SrcMgr::CharacteristicKind FileType) {
163
46
  if (!File && 
!Imported0
) {
164
    // This is a non-modular include that HeaderSearch failed to find. Add it
165
    // here as `FileChanged` will never see it.
166
0
    MDC.FileDeps.push_back(std::string(FileName));
167
0
  }
168
46
  handleImport(Imported);
169
46
}
170
171
void ModuleDepCollectorPP::moduleImport(SourceLocation ImportLoc,
172
                                        ModuleIdPath Path,
173
5
                                        const Module *Imported) {
174
5
  handleImport(Imported);
175
5
}
176
177
51
void ModuleDepCollectorPP::handleImport(const Module *Imported) {
178
51
  if (!Imported)
179
3
    return;
180
181
48
  const Module *TopLevelModule = Imported->getTopLevelModule();
182
183
48
  if (MDC.isPrebuiltModule(TopLevelModule))
184
2
    DirectPrebuiltModularDeps.insert(TopLevelModule);
185
46
  else
186
46
    DirectModularDeps.insert(TopLevelModule);
187
48
}
188
189
41
void ModuleDepCollectorPP::EndOfMainFile() {
190
41
  FileID MainFileID = MDC.ScanInstance.getSourceManager().getMainFileID();
191
41
  MDC.MainFile = std::string(MDC.ScanInstance.getSourceManager()
192
41
                                 .getFileEntryForID(MainFileID)
193
41
                                 ->getName());
194
195
41
  if (!MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
196
5
    MDC.FileDeps.push_back(
197
5
        MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude);
198
199
43
  for (const Module *M : DirectModularDeps) {
200
    // A top-level module might not be actually imported as a module when
201
    // -fmodule-name is used to compile a translation unit that imports this
202
    // module. In that case it can be skipped. The appropriate header
203
    // dependencies will still be reported as expected.
204
43
    if (!M->getASTFile())
205
1
      continue;
206
42
    handleTopLevelModule(M);
207
42
  }
208
209
41
  MDC.Consumer.handleDependencyOutputOpts(*MDC.Opts);
210
211
41
  for (auto &&I : MDC.ModularDeps)
212
52
    MDC.Consumer.handleModuleDependency(I.second);
213
214
41
  for (auto &&I : MDC.FileDeps)
215
47
    MDC.Consumer.handleFileDependency(I);
216
217
41
  for (auto &&I : DirectPrebuiltModularDeps)
218
2
    MDC.Consumer.handlePrebuiltModuleDependency(PrebuiltModuleDep{I});
219
41
}
220
221
50
ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
222
50
  assert(M == M->getTopLevelModule() && "Expected top level module!");
223
224
  // If this module has been handled already, just return its ID.
225
0
  auto ModI = MDC.ModularDeps.insert({M, ModuleDeps{}});
226
50
  if (!ModI.second)
227
0
    return ModI.first->second.ID;
228
229
50
  ModuleDeps &MD = ModI.first->second;
230
231
50
  MD.ID.ModuleName = M->getFullModuleName();
232
50
  MD.ImportedByMainFile = DirectModularDeps.contains(M);
233
50
  MD.ImplicitModulePCMPath = std::string(M->getASTFile()->getName());
234
50
  MD.IsSystem = M->IsSystem;
235
236
50
  const FileEntry *ModuleMap = MDC.ScanInstance.getPreprocessor()
237
50
                                   .getHeaderSearchInfo()
238
50
                                   .getModuleMap()
239
50
                                   .getModuleMapFileForUniquing(M);
240
241
52
  if (
ModuleMap50
) {
242
52
    StringRef Path = ModuleMap->tryGetRealPathName();
243
52
    if (Path.empty())
244
0
      Path = ModuleMap->getName();
245
52
    MD.ClangModuleMapFile = std::string(Path);
246
52
  }
247
248
50
  serialization::ModuleFile *MF =
249
50
      MDC.ScanInstance.getASTReader()->getModuleManager().lookup(
250
50
          M->getASTFile());
251
50
  MDC.ScanInstance.getASTReader()->visitInputFiles(
252
118
      *MF, true, true, [&](const serialization::InputFile &IF, bool isSystem) {
253
        // __inferred_module.map is the result of the way in which an implicit
254
        // module build handles inferred modules. It adds an overlay VFS with
255
        // this file in the proper directory and relies on the rest of Clang to
256
        // handle it like normal. With explicitly built modules we don't need
257
        // to play VFS tricks, so replace it with the correct module map.
258
118
        if (IF.getFile()->getName().endswith("__inferred_module.map")) {
259
2
          MD.FileDeps.insert(ModuleMap->getName());
260
2
          return;
261
2
        }
262
116
        MD.FileDeps.insert(IF.getFile()->getName());
263
116
      });
264
265
  // Add direct prebuilt module dependencies now, so that we can use them when
266
  // creating a CompilerInvocation and computing context hash for this
267
  // ModuleDeps instance.
268
50
  llvm::DenseSet<const Module *> SeenModules;
269
50
  addAllSubmodulePrebuiltDeps(M, MD, SeenModules);
270
271
50
  MD.BuildInvocation = MDC.makeInvocationForModuleBuildWithoutPaths(
272
52
      MD, [&](CompilerInvocation &BuildInvocation) {
273
52
        if (MDC.OptimizeArgs)
274
3
          optimizeHeaderSearchOpts(BuildInvocation.getHeaderSearchOpts(),
275
3
                                   *MDC.ScanInstance.getASTReader(), *MF);
276
52
      });
277
50
  MD.ID.ContextHash = MD.BuildInvocation.getModuleHash();
278
279
50
  llvm::DenseSet<const Module *> AddedModules;
280
50
  addAllSubmoduleDeps(M, MD, AddedModules);
281
282
50
  return MD.ID;
283
50
}
284
285
void ModuleDepCollectorPP::addAllSubmodulePrebuiltDeps(
286
    const Module *M, ModuleDeps &MD,
287
56
    llvm::DenseSet<const Module *> &SeenSubmodules) {
288
56
  addModulePrebuiltDeps(M, MD, SeenSubmodules);
289
290
56
  for (const Module *SubM : M->submodules())
291
4
    addAllSubmodulePrebuiltDeps(SubM, MD, SeenSubmodules);
292
56
}
293
294
void ModuleDepCollectorPP::addModulePrebuiltDeps(
295
    const Module *M, ModuleDeps &MD,
296
56
    llvm::DenseSet<const Module *> &SeenSubmodules) {
297
56
  for (const Module *Import : M->Imports)
298
10
    if (Import->getTopLevelModule() != M->getTopLevelModule())
299
10
      if (MDC.isPrebuiltModule(Import->getTopLevelModule()))
300
3
        if (SeenSubmodules.insert(Import->getTopLevelModule()).second)
301
3
          MD.PrebuiltModuleDeps.emplace_back(Import->getTopLevelModule());
302
56
}
303
304
void ModuleDepCollectorPP::addAllSubmoduleDeps(
305
    const Module *M, ModuleDeps &MD,
306
56
    llvm::DenseSet<const Module *> &AddedModules) {
307
56
  addModuleDep(M, MD, AddedModules);
308
309
56
  for (const Module *SubM : M->submodules())
310
4
    addAllSubmoduleDeps(SubM, MD, AddedModules);
311
56
}
312
313
void ModuleDepCollectorPP::addModuleDep(
314
    const Module *M, ModuleDeps &MD,
315
56
    llvm::DenseSet<const Module *> &AddedModules) {
316
56
  for (const Module *Import : M->Imports) {
317
10
    if (Import->getTopLevelModule() != M->getTopLevelModule() &&
318
10
        !MDC.isPrebuiltModule(Import)) {
319
7
      ModuleID ImportID = handleTopLevelModule(Import->getTopLevelModule());
320
7
      if (AddedModules.insert(Import->getTopLevelModule()).second)
321
7
        MD.ClangModuleDeps.push_back(ImportID);
322
7
    }
323
10
  }
324
56
}
325
326
ModuleDepCollector::ModuleDepCollector(
327
    std::unique_ptr<DependencyOutputOptions> Opts,
328
    CompilerInstance &ScanInstance, DependencyConsumer &C,
329
    CompilerInvocation &&OriginalCI, bool OptimizeArgs)
330
    : ScanInstance(ScanInstance), Consumer(C), Opts(std::move(Opts)),
331
43
      OriginalInvocation(std::move(OriginalCI)), OptimizeArgs(OptimizeArgs) {}
332
333
42
void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) {
334
42
  PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(*this));
335
42
}
336
337
43
void ModuleDepCollector::attachToASTReader(ASTReader &R) {}
338
339
68
bool ModuleDepCollector::isPrebuiltModule(const Module *M) {
340
68
  std::string Name(M->getTopLevelModuleName());
341
68
  const auto &PrebuiltModuleFiles =
342
68
      ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles;
343
68
  auto PrebuiltModuleFileIt = PrebuiltModuleFiles.find(Name);
344
68
  if (PrebuiltModuleFileIt == PrebuiltModuleFiles.end())
345
59
    return false;
346
9
  assert("Prebuilt module came from the expected AST file" &&
347
9
         PrebuiltModuleFileIt->second == M->getASTFile()->getName());
348
0
  return true;
349
68
}