Skip to content

Commit b72bf3e

Browse files
committed
[clang][scan-deps] Report a scanned TU's visible modules
1 parent 617af3c commit b72bf3e

File tree

8 files changed

+178
-26
lines changed

8 files changed

+178
-26
lines changed

clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ struct TranslationUnitDeps {
5757
/// determined that the differences are benign for this compilation.
5858
std::vector<ModuleID> ClangModuleDeps;
5959

60+
/// A list of module names that are visible to this translation unit. This
61+
/// includes both direct and transitive module dependencies.
62+
std::vector<std::string> VisibleModules;
63+
6064
/// A list of the C++20 named modules this translation unit depends on.
6165
std::vector<std::string> NamedModuleDeps;
6266

@@ -150,7 +154,7 @@ class DependencyScanningTool {
150154
/// Given a compilation context specified via the Clang driver command-line,
151155
/// gather modular dependencies of module with the given name, and return the
152156
/// information needed for explicit build.
153-
llvm::Expected<ModuleDepsGraph> getModuleDependencies(
157+
llvm::Expected<TranslationUnitDeps> getModuleDependencies(
154158
StringRef ModuleName, const std::vector<std::string> &CommandLine,
155159
StringRef CWD, const llvm::DenseSet<ModuleID> &AlreadySeen,
156160
LookupModuleOutputCallback LookupModuleOutput);
@@ -188,6 +192,10 @@ class FullDependencyConsumer : public DependencyConsumer {
188192
DirectModuleDeps.push_back(ID);
189193
}
190194

195+
void handleVisibleModule(std::string ModuleName) override {
196+
VisibleModules.push_back(ModuleName);
197+
}
198+
191199
void handleContextHash(std::string Hash) override {
192200
ContextHash = std::move(Hash);
193201
}
@@ -201,7 +209,6 @@ class FullDependencyConsumer : public DependencyConsumer {
201209
}
202210

203211
TranslationUnitDeps takeTranslationUnitDeps();
204-
ModuleDepsGraph takeModuleGraphDeps();
205212

206213
private:
207214
std::vector<std::string> Dependencies;
@@ -210,6 +217,7 @@ class FullDependencyConsumer : public DependencyConsumer {
210217
std::string ModuleName;
211218
std::vector<std::string> NamedModuleDeps;
212219
std::vector<ModuleID> DirectModuleDeps;
220+
std::vector<std::string> VisibleModules;
213221
std::vector<Command> Commands;
214222
std::string ContextHash;
215223
std::vector<std::string> OutputPaths;

clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ class DependencyConsumer {
5959

6060
virtual void handleDirectModuleDependency(ModuleID MD) = 0;
6161

62+
virtual void handleVisibleModule(std::string ModuleName) = 0;
63+
6264
virtual void handleContextHash(std::string Hash) = 0;
6365
};
6466

clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,11 @@ class ModuleDepCollector final : public DependencyCollector {
323323
llvm::MapVector<const Module *, PrebuiltModuleDep> DirectPrebuiltModularDeps;
324324
/// Working set of direct modular dependencies.
325325
llvm::SetVector<const Module *> DirectModularDeps;
326+
/// Working set of direct modular dependencies, as they were imported.
327+
llvm::SmallPtrSet<const Module *, 32> DirectImports;
328+
/// All direct and transitive visible modules.
329+
llvm::StringSet<> VisibleModules;
330+
326331
/// Options that control the dependency output generation.
327332
std::unique_ptr<DependencyOutputOptions> Opts;
328333
/// A Clang invocation that's based on the original TU invocation and that has
@@ -337,6 +342,9 @@ class ModuleDepCollector final : public DependencyCollector {
337342
/// Checks whether the module is known as being prebuilt.
338343
bool isPrebuiltModule(const Module *M);
339344

345+
/// Computes all visible modules resolved from direct imports.
346+
void addVisibleModules();
347+
340348
/// Adds \p Path to \c FileDeps, making it absolute if necessary.
341349
void addFileDep(StringRef Path);
342350
/// Adds \p Path to \c MD.FileDeps, making it absolute if necessary.

clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class MakeDependencyPrinterConsumer : public DependencyConsumer {
4040
void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {}
4141
void handleModuleDependency(ModuleDeps MD) override {}
4242
void handleDirectModuleDependency(ModuleID ID) override {}
43+
void handleVisibleModule(std::string ModuleName) override {}
4344
void handleContextHash(std::string Hash) override {}
4445

4546
void printDependencies(std::string &S) {
@@ -154,7 +155,8 @@ DependencyScanningTool::getTranslationUnitDependencies(
154155
return Consumer.takeTranslationUnitDeps();
155156
}
156157

157-
llvm::Expected<ModuleDepsGraph> DependencyScanningTool::getModuleDependencies(
158+
llvm::Expected<TranslationUnitDeps>
159+
DependencyScanningTool::getModuleDependencies(
158160
StringRef ModuleName, const std::vector<std::string> &CommandLine,
159161
StringRef CWD, const llvm::DenseSet<ModuleID> &AlreadySeen,
160162
LookupModuleOutputCallback LookupModuleOutput) {
@@ -164,7 +166,7 @@ llvm::Expected<ModuleDepsGraph> DependencyScanningTool::getModuleDependencies(
164166
Controller, ModuleName);
165167
if (Result)
166168
return std::move(Result);
167-
return Consumer.takeModuleGraphDeps();
169+
return Consumer.takeTranslationUnitDeps();
168170
}
169171

170172
TranslationUnitDeps FullDependencyConsumer::takeTranslationUnitDeps() {
@@ -175,6 +177,7 @@ TranslationUnitDeps FullDependencyConsumer::takeTranslationUnitDeps() {
175177
TU.NamedModuleDeps = std::move(NamedModuleDeps);
176178
TU.FileDeps = std::move(Dependencies);
177179
TU.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
180+
TU.VisibleModules = std::move(VisibleModules);
178181
TU.Commands = std::move(Commands);
179182

180183
for (auto &&M : ClangModuleDeps) {
@@ -190,19 +193,4 @@ TranslationUnitDeps FullDependencyConsumer::takeTranslationUnitDeps() {
190193
return TU;
191194
}
192195

193-
ModuleDepsGraph FullDependencyConsumer::takeModuleGraphDeps() {
194-
ModuleDepsGraph ModuleGraph;
195-
196-
for (auto &&M : ClangModuleDeps) {
197-
auto &MD = M.second;
198-
// TODO: Avoid handleModuleDependency even being called for modules
199-
// we've already seen.
200-
if (AlreadySeen.count(M.first))
201-
continue;
202-
ModuleGraph.push_back(std::move(MD));
203-
}
204-
205-
return ModuleGraph;
206-
}
207-
208196
CallbackActionController::~CallbackActionController() {}

clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,8 +673,10 @@ void ModuleDepCollectorPP::handleImport(const Module *Imported) {
673673
if (MDC.isPrebuiltModule(TopLevelModule))
674674
MDC.DirectPrebuiltModularDeps.insert(
675675
{TopLevelModule, PrebuiltModuleDep{TopLevelModule}});
676-
else
676+
else {
677677
MDC.DirectModularDeps.insert(TopLevelModule);
678+
MDC.DirectImports.insert(Imported);
679+
}
678680
}
679681

680682
void ModuleDepCollectorPP::EndOfMainFile() {
@@ -706,6 +708,8 @@ void ModuleDepCollectorPP::EndOfMainFile() {
706708
if (!MDC.isPrebuiltModule(M))
707709
MDC.DirectModularDeps.insert(M);
708710

711+
MDC.addVisibleModules();
712+
709713
for (const Module *M : MDC.DirectModularDeps)
710714
handleTopLevelModule(M);
711715

@@ -727,6 +731,9 @@ void ModuleDepCollectorPP::EndOfMainFile() {
727731
MDC.Consumer.handleDirectModuleDependency(It->second->ID);
728732
}
729733

734+
for (auto &&I : MDC.VisibleModules)
735+
MDC.Consumer.handleVisibleModule(std::string(I.getKey()));
736+
730737
for (auto &&I : MDC.FileDeps)
731738
MDC.Consumer.handleFileDependency(I);
732739

@@ -993,6 +1000,29 @@ bool ModuleDepCollector::isPrebuiltModule(const Module *M) {
9931000
return true;
9941001
}
9951002

1003+
void ModuleDepCollector::addVisibleModules() {
1004+
llvm::DenseSet<const Module *> ImportedModules;
1005+
auto InsertVisibleModules = [&](const Module *M) {
1006+
if (ImportedModules.contains(M))
1007+
return;
1008+
1009+
VisibleModules.insert(M->getTopLevelModuleName());
1010+
SmallVector<Module *> Stack;
1011+
M->getExportedModules(Stack);
1012+
while (!Stack.empty()) {
1013+
const Module *CurrModule = Stack.pop_back_val();
1014+
if (ImportedModules.contains(CurrModule))
1015+
continue;
1016+
ImportedModules.insert(CurrModule);
1017+
VisibleModules.insert(CurrModule->getTopLevelModuleName());
1018+
CurrModule->getExportedModules(Stack);
1019+
}
1020+
};
1021+
1022+
for (const Module *Import : DirectImports)
1023+
InsertVisibleModules(Import);
1024+
}
1025+
9961026
static StringRef makeAbsoluteAndPreferred(CompilerInstance &CI, StringRef Path,
9971027
SmallVectorImpl<char> &Storage) {
9981028
if (llvm::sys::path::is_absolute(Path) &&
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// This test verifies that the modules visible to the translation unit are computed in dependency scanning.
2+
// "client" represents the translation unit that imports an explicit submodule, that only exports one other module.
3+
// Thus, the dependencies of the top level module for the submodule differ from what is visible to the TU.
4+
5+
// RUN: rm -rf %t
6+
// RUN: split-file %s %t
7+
// RUN: sed -e "s|DIR|%/t|g" %t/compile-commands.json.in > %t/compile-commands.json
8+
// RUN: clang-scan-deps -emit-visible-modules -compilation-database %t/compile-commands.json \
9+
// RUN: -j 1 -format experimental-full 2>&1 > %t/result.json
10+
// RUN: cat %t/result.json | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t
11+
12+
13+
// RUN: %deps-to-rsp %t/result.json --module-name=transitive > %t/transitive.rsp
14+
// RUN: %deps-to-rsp %t/result.json --module-name=visible > %t/visible.rsp
15+
// RUN: %deps-to-rsp %t/result.json --module-name=invisible > %t/invisible.rsp
16+
// RUN: %deps-to-rsp %t/result.json --module-name=A > %t/A.rsp
17+
// RUN: %deps-to-rsp %t/result.json --tu-index=0 > %t/tu.rsp
18+
19+
// RUN: %clang @%t/transitive.rsp
20+
// RUN: %clang @%t/visible.rsp
21+
// RUN: %clang @%t/invisible.rsp
22+
// RUN: %clang @%t/A.rsp
23+
24+
// Verify compilation & scan agree with each other.
25+
// RUN: not %clang @%t/tu.rsp 2>&1 | FileCheck %s --check-prefix=COMPILE
26+
27+
// CHECK: "visible-clang-modules": [
28+
// CHECK-NEXT: "A",
29+
// CHECK-NEXT: "visible"
30+
// CHECK-NEXT: ]
31+
32+
// COMPILE-NOT: 'visible_t' must be declared before it is used
33+
// COMPILE: 'transitive_t' must be declared before it is used
34+
// COMPILE: 'invisible_t' must be declared before it is used
35+
36+
//--- compile-commands.json.in
37+
[
38+
{
39+
"directory": "DIR",
40+
"command": "clang -c DIR/client.c -isysroot DIR/Sysroot -IDIR/Sysroot/usr/include -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps",
41+
"file": "DIR/client.c"
42+
}
43+
]
44+
45+
//--- Sysroot/usr/include/A/module.modulemap
46+
module A {
47+
explicit module visibleToTU {
48+
header "visibleToTU.h"
49+
export visible
50+
}
51+
explicit module invisibleToTU {
52+
header "invisibleToTU.h"
53+
}
54+
}
55+
//--- Sysroot/usr/include/A/visibleToTU.h
56+
#include <visible/visible.h>
57+
typedef int A_visibleToTU;
58+
59+
//--- Sysroot/usr/include/A/invisibleToTU.h
60+
#include <invisible/invisible.h>
61+
typedef int A_invisibleToTU;
62+
63+
//--- Sysroot/usr/include/invisible/module.modulemap
64+
module invisible {
65+
umbrella "."
66+
}
67+
68+
//--- Sysroot/usr/include/invisible/invisible.h
69+
typedef int invisible_t;
70+
71+
//--- Sysroot/usr/include/visible/module.modulemap
72+
module visible {
73+
umbrella "."
74+
}
75+
76+
//--- Sysroot/usr/include/visible/visible.h
77+
#include <transitive/transitive.h>
78+
typedef int visible_t;
79+
80+
//--- Sysroot/usr/include/transitive/module.modulemap
81+
module transitive {
82+
umbrella "."
83+
}
84+
85+
//--- Sysroot/usr/include/transitive/transitive.h
86+
typedef int transitive_t;
87+
88+
//--- client.c
89+
#include <A/visibleToTU.h>
90+
visible_t foo_v(void);
91+
// Both decls are not visible, thus should fail to actually compile.
92+
transitive_t foo_t(void);
93+
invisible_t foo_i(void);

clang/tools/clang-scan-deps/ClangScanDeps.cpp

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ static bool DeprecatedDriverCommand;
9494
static ResourceDirRecipeKind ResourceDirRecipe;
9595
static bool Verbose;
9696
static bool PrintTiming;
97+
static bool EmitVisibleModules;
9798
static llvm::BumpPtrAllocator Alloc;
9899
static llvm::StringSaver Saver{Alloc};
99100
static std::vector<const char *> CommandLine;
@@ -232,6 +233,8 @@ static void ParseArgs(int argc, char **argv) {
232233

233234
PrintTiming = Args.hasArg(OPT_print_timing);
234235

236+
EmitVisibleModules = Args.hasArg(OPT_emit_visible_modules);
237+
235238
Verbose = Args.hasArg(OPT_verbose);
236239

237240
RoundTripArgs = Args.hasArg(OPT_round_trip_args);
@@ -380,6 +383,14 @@ static auto toJSONSorted(llvm::json::OStream &JOS,
380383
};
381384
}
382385

386+
static auto toJSONSorted(llvm::json::OStream &JOS, std::vector<std::string> V) {
387+
llvm::sort(V);
388+
return [&JOS, V = std::move(V)] {
389+
for (const StringRef Entry : V)
390+
JOS.value(Entry);
391+
};
392+
}
393+
383394
// Thread safe.
384395
class FullDeps {
385396
public:
@@ -396,6 +407,7 @@ class FullDeps {
396407
ID.NamedModule = std::move(TUDeps.ID.ModuleName);
397408
ID.NamedModuleDeps = std::move(TUDeps.NamedModuleDeps);
398409
ID.ClangModuleDeps = std::move(TUDeps.ClangModuleDeps);
410+
ID.VisibleModules = std::move(TUDeps.VisibleModules);
399411
ID.DriverCommandLine = std::move(TUDeps.DriverCommandLine);
400412
ID.Commands = std::move(TUDeps.Commands);
401413

@@ -525,6 +537,9 @@ class FullDeps {
525537
JOS.attributeArray("file-deps",
526538
toJSONStrings(JOS, I.FileDeps));
527539
JOS.attribute("input-file", StringRef(I.FileName));
540+
if (EmitVisibleModules)
541+
JOS.attributeArray("visible-clang-modules",
542+
toJSONSorted(JOS, I.VisibleModules));
528543
});
529544
}
530545
} else {
@@ -545,6 +560,9 @@ class FullDeps {
545560
JOS.attributeArray("file-deps",
546561
toJSONStrings(JOS, I.FileDeps));
547562
JOS.attribute("input-file", StringRef(I.FileName));
563+
if (EmitVisibleModules)
564+
JOS.attributeArray("visible-clang-modules",
565+
toJSONSorted(JOS, I.VisibleModules));
548566
});
549567
}
550568
});
@@ -596,6 +614,7 @@ class FullDeps {
596614
std::string NamedModule;
597615
std::vector<std::string> NamedModuleDeps;
598616
std::vector<ModuleID> ClangModuleDeps;
617+
std::vector<std::string> VisibleModules;
599618
std::vector<std::string> DriverCommandLine;
600619
std::vector<Command> Commands;
601620
};
@@ -623,11 +642,12 @@ static bool handleTranslationUnitResult(
623642
return false;
624643
}
625644

626-
static bool handleModuleResult(
627-
StringRef ModuleName, llvm::Expected<ModuleDepsGraph> &MaybeModuleGraph,
628-
FullDeps &FD, size_t InputIndex, SharedStream &OS, SharedStream &Errs) {
629-
if (!MaybeModuleGraph) {
630-
llvm::handleAllErrors(MaybeModuleGraph.takeError(),
645+
static bool handleModuleResult(StringRef ModuleName,
646+
llvm::Expected<TranslationUnitDeps> &MaybeTUDeps,
647+
FullDeps &FD, size_t InputIndex,
648+
SharedStream &OS, SharedStream &Errs) {
649+
if (!MaybeTUDeps) {
650+
llvm::handleAllErrors(MaybeTUDeps.takeError(),
631651
[&ModuleName, &Errs](llvm::StringError &Err) {
632652
Errs.applyLocked([&](raw_ostream &OS) {
633653
OS << "Error while scanning dependencies for "
@@ -637,7 +657,7 @@ static bool handleModuleResult(
637657
});
638658
return true;
639659
}
640-
FD.mergeDeps(std::move(*MaybeModuleGraph), InputIndex);
660+
FD.mergeDeps(std::move(MaybeTUDeps->ModuleGraph), InputIndex);
641661
return false;
642662
}
643663

clang/tools/clang-scan-deps/Opts.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ defm resource_dir_recipe : Eq<"resource-dir-recipe", "How to produce missing '-r
3737

3838
def print_timing : F<"print-timing", "Print timing information">;
3939

40+
def emit_visible_modules
41+
: F<"emit-visible-modules", "emit visible modules in primary output">;
42+
4043
def verbose : F<"v", "Use verbose output">;
4144

4245
def round_trip_args : F<"round-trip-args", "verify that command-line arguments are canonical by parsing and re-serializing">;

0 commit comments

Comments
 (0)