[clang] 5a74e6a - [Modules] Add module structure output to -module-file-info.

Iain Sandoe via cfe-commits cfe-commits at lists.llvm.org
Wed Feb 23 02:26:56 PST 2022


Author: Iain Sandoe
Date: 2022-02-23T10:26:42Z
New Revision: 5a74e6a21c9520e0619c98a66815fc8b5117e321

URL: https://github.com/llvm/llvm-project/commit/5a74e6a21c9520e0619c98a66815fc8b5117e321
DIFF: https://github.com/llvm/llvm-project/commit/5a74e6a21c9520e0619c98a66815fc8b5117e321.diff

LOG: [Modules] Add module structure output to -module-file-info.

It is useful to be able to visualise the C++20 modules content of a PCM file
both for inspection and for testing.  In particular, when adding more module
types to support C++20 Partitions and Header Units, we would like to be able
to confirm that the output PCM has the intended structure.

The existing scheme for dumping data is restricted to the content of the AST
file control block, which does not include structural data beyond imports.

The change here makes use of the AST unit that is set up by BeginSourceFile
to query for the information on the primary and sub-modules.  We can then
inspect each of these in turn, accounting for Global, Private, Imported and
Exported modules/fragments and then showing the sub-stucture of the main
module(s).

The disadvantage of this mechanism is that it has no easy method to control
the granularity of the output.  Perhaps more detailed inspection would be
better handled by a stand-alone module inspection tool.

Differential Revision: https://reviews.llvm.org/D119823

Added: 
    clang/test/Modules/cxx20-module-file-info.cpp

Modified: 
    clang/lib/Frontend/FrontendActions.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index ad2e6039477f8..baf3ac34db620 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -11,6 +11,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/Basic/FileManager.h"
 #include "clang/Basic/LangStandard.h"
+#include "clang/Basic/Module.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Frontend/ASTConsumers.h"
 #include "clang/Frontend/CompilerInstance.h"
@@ -24,6 +25,7 @@
 #include "clang/Sema/TemplateInstCallback.h"
 #include "clang/Serialization/ASTReader.h"
 #include "clang/Serialization/ASTWriter.h"
+#include "clang/Serialization/ModuleFile.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -806,7 +808,25 @@ bool DumpModuleInfoAction::BeginInvocation(CompilerInstance &CI) {
   return true;
 }
 
+static StringRef ModuleKindName(Module::ModuleKind MK) {
+  switch (MK) {
+  case Module::ModuleMapModule:
+    return "Module Map Module";
+  case Module::ModuleInterfaceUnit:
+    return "Interface Unit";
+  case Module::ModulePartitionInterface:
+    return "Partition Interface";
+  case Module::ModulePartitionImplementation:
+    return "Partition Implementation";
+  case Module::GlobalModuleFragment:
+    return "Global Module Fragment";
+  case Module::PrivateModuleFragment:
+    return "Private Module Fragment";
+  }
+}
+
 void DumpModuleInfoAction::ExecuteAction() {
+  assert(isCurrentFileAST() && "dumping non-AST?");
   // Set up the output file.
   std::unique_ptr<llvm::raw_fd_ostream> OutFile;
   StringRef OutputFileName = getCompilerInstance().getFrontendOpts().OutputFile;
@@ -827,8 +847,87 @@ void DumpModuleInfoAction::ExecuteAction() {
 
   Preprocessor &PP = getCompilerInstance().getPreprocessor();
   DumpModuleInfoListener Listener(Out);
-  HeaderSearchOptions &HSOpts =
-      PP.getHeaderSearchInfo().getHeaderSearchOpts();
+  HeaderSearchOptions &HSOpts = PP.getHeaderSearchInfo().getHeaderSearchOpts();
+
+  // The FrontendAction::BeginSourceFile () method loads the AST so that much
+  // of the information is already available and modules should have been
+  // loaded.
+
+  const LangOptions &LO = getCurrentASTUnit().getLangOpts();
+  if (LO.CPlusPlusModules && !LO.CurrentModule.empty()) {
+
+    ASTReader *R = getCurrentASTUnit().getASTReader().get();
+    unsigned SubModuleCount = R->getTotalNumSubmodules();
+    serialization::ModuleFile &MF = R->getModuleManager().getPrimaryModule();
+    Out << "  ====== C++20 Module structure ======\n";
+
+    if (MF.ModuleName != LO.CurrentModule)
+      Out << "  Mismatched module names : " << MF.ModuleName << " and "
+          << LO.CurrentModule << "\n";
+
+    struct SubModInfo {
+      unsigned Idx;
+      Module *Mod;
+      Module::ModuleKind Kind;
+      std::string &Name;
+      bool Seen;
+    };
+    std::map<std::string, SubModInfo> SubModMap;
+    auto PrintSubMapEntry = [&](std::string Name, Module::ModuleKind Kind) {
+      Out << "    " << ModuleKindName(Kind) << " '" << Name << "'";
+      auto I = SubModMap.find(Name);
+      if (I == SubModMap.end())
+        Out << " was not found in the sub modules!\n";
+      else {
+        I->second.Seen = true;
+        Out << " is at index #" << I->second.Idx << "\n";
+      }
+    };
+    Module *Primary = nullptr;
+    for (unsigned Idx = 0; Idx <= SubModuleCount; ++Idx) {
+      Module *M = R->getModule(Idx);
+      if (!M)
+        continue;
+      if (M->Name == LO.CurrentModule) {
+        Primary = M;
+        Out << "  " << ModuleKindName(M->Kind) << " '" << LO.CurrentModule
+            << "' is the Primary Module at index #" << Idx << "\n";
+        SubModMap.insert({M->Name, {Idx, M, M->Kind, M->Name, true}});
+      } else
+        SubModMap.insert({M->Name, {Idx, M, M->Kind, M->Name, false}});
+    }
+    if (Primary) {
+      if (!Primary->submodules().empty())
+        Out << "   Sub Modules:\n";
+      for (auto MI : Primary->submodules()) {
+        PrintSubMapEntry(MI->Name, MI->Kind);
+      }
+      if (!Primary->Imports.empty())
+        Out << "   Imports:\n";
+      for (auto IMP : Primary->Imports) {
+        PrintSubMapEntry(IMP->Name, IMP->Kind);
+      }
+      if (!Primary->Exports.empty())
+        Out << "   Exports:\n";
+      for (unsigned MN = 0, N = Primary->Exports.size(); MN != N; ++MN) {
+        if (Module *M = Primary->Exports[MN].getPointer()) {
+          PrintSubMapEntry(M->Name, M->Kind);
+        }
+      }
+    }
+    // Now let's print out any modules we did not see as part of the Primary.
+    for (auto SM : SubModMap) {
+      if (!SM.second.Seen && SM.second.Mod) {
+        Out << "  " << ModuleKindName(SM.second.Kind) << " '" << SM.first
+            << "' at index #" << SM.second.Idx
+            << " has no direct reference in the Primary\n";
+      }
+    }
+    Out << "  ====== ======\n";
+  }
+
+  // The reminder of the output is produced from the listener as the AST
+  // FileCcontrolBlock is (re-)parsed.
   ASTReader::readASTFileControlBlock(
       getCurrentFile(), FileMgr, getCompilerInstance().getPCHContainerReader(),
       /*FindModuleFileExtensions=*/true, Listener,

diff  --git a/clang/test/Modules/cxx20-module-file-info.cpp b/clang/test/Modules/cxx20-module-file-info.cpp
new file mode 100644
index 0000000000000..99a215645e8fe
--- /dev/null
+++ b/clang/test/Modules/cxx20-module-file-info.cpp
@@ -0,0 +1,64 @@
+// Test output from -module-file-info about C++20 modules.
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/mod-info-tu1.cpp \
+// RUN:  -o %t/A.pcm
+
+// RUN: %clang_cc1 -std=c++20 -module-file-info %t/A.pcm | FileCheck \
+// RUN:  --check-prefix=CHECK-A %s
+
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/mod-info-tu2.cpp \
+// RUN:  -o %t/B.pcm
+
+// RUN: %clang_cc1 -std=c++20 -module-file-info %t/B.pcm | FileCheck \
+// RUN:  --check-prefix=CHECK-B %s
+
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/mod-info-tu3.cpp \
+// RUN:  -fmodule-file=%t/A.pcm -fmodule-file=%t/B.pcm -o %t/Foo.pcm
+
+// RUN: %clang_cc1 -std=c++20 -module-file-info %t/Foo.pcm | FileCheck \
+// RUN:  --check-prefix=CHECK-FOO %s
+
+// expected-no-diagnostics
+
+//--- mod-info-tu1.cpp
+export module A;
+
+void a();
+
+// CHECK-A: ====== C++20
+// CHECK-A-NEXT: Interface Unit 'A' is the Primary Module at index #1
+
+//--- mod-info-tu2.cpp
+export module B;
+
+void b();
+
+// CHECK-B: ====== C++20
+// CHECK-B-NEXT: Interface Unit 'B' is the Primary Module at index #1
+
+//--- mod-info-tu3.cpp
+module;
+
+export module Foo;
+
+import A;
+export import B;
+
+namespace hello {
+export void say(const char *);
+}
+
+void foo() {}
+
+// CHECK-FOO: ====== C++20
+// CHECK-FOO-NEXT:  Interface Unit 'Foo' is the Primary Module at index #3
+// CHECK-FOO-NEXT:   Sub Modules:
+// CHECK-FOO-NEXT:    Global Module Fragment '<global>' is at index #4
+// CHECK-FOO-NEXT:   Imports:
+// CHECK-FOO-NEXT:    Interface Unit 'A' is at index #1
+// CHECK-FOO-NEXT:   Exports:
+// CHECK-FOO-NEXT:    Interface Unit 'B' is at index #2


        


More information about the cfe-commits mailing list