[clang] [Clang] Implement P3034R1 Module Declarations Shouldn’t be Macros (PR #90574)

via cfe-commits cfe-commits at lists.llvm.org
Wed Jul 17 10:40:19 PDT 2024


https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/90574

>From 1dcb4c3ac1efaf3a6a4317751e23089a6c8ccac1 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Apr 2024 17:18:26 +0800
Subject: [PATCH 01/19] =?UTF-8?q?[Clang]=20Implement=20P3034R1=20Module=20?=
 =?UTF-8?q?Declarations=20Shouldn=E2=80=99t=20be=20Macros?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/docs/ReleaseNotes.rst                   |  2 ++
 .../clang/Basic/DiagnosticParseKinds.td       |  2 ++
 clang/lib/Parse/Parser.cpp                    |  7 ++++
 clang/test/CXX/cpp/cpp.module/p1.cppm         | 13 +++++++
 clang/test/CXX/cpp/cpp.module/version.h       |  8 +++++
 .../basic/basic.link/module-declaration.cpp   | 35 +++++++++++--------
 .../dcl.module/dcl.module.import/p1.cppm      |  4 +--
 clang/test/SemaCXX/modules.cppm               |  4 +++
 clang/www/cxx_status.html                     |  2 +-
 9 files changed, 59 insertions(+), 18 deletions(-)
 create mode 100644 clang/test/CXX/cpp/cpp.module/p1.cppm
 create mode 100644 clang/test/CXX/cpp/cpp.module/version.h

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 1abc00a25f1f4..40c6bd63e9948 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -153,6 +153,8 @@ C++2c Feature Support
 
 - Implemented `P2748R5 Disallow Binding a Returned Glvalue to a Temporary <https://wg21.link/P2748R5>`_.
 
+- Implemented `P3034R1 Module Declarations Shouldn’t be Macros <https://wg21.link/P3034R1>`_.
+
 Resolutions to C++ Defect Reports
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 - Substitute template parameter pack, when it is not explicitly specified
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index fdffb35ea0d95..0d4b526ec6d15 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1671,6 +1671,8 @@ def err_unexpected_module_decl : Error<
   "module declaration can only appear at the top level">;
 def err_module_expected_ident : Error<
   "expected a module name after '%select{module|import}0'">;
+def err_module_decl_cannot_be_macros : Error<
+  "module declaration cannot be a macro">;
 def err_attribute_not_module_attr : Error<
   "%0 attribute cannot be applied to a module">;
 def err_keyword_not_module_attr : Error<
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index adcbe5858bc78..ef66348a83125 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -2690,6 +2690,13 @@ bool Parser::ParseModuleName(
       return true;
     }
 
+    // P3034R1: Module Declarations Shouldn’t be Macros
+    if (!IsImport && Tok.getLocation().isMacroID()) {
+      Diag(Tok, diag::err_module_decl_cannot_be_macros);
+      SkipUntil(tok::semi);
+      return true;
+    }
+
     // Record this part of the module path.
     Path.push_back(std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()));
     ConsumeToken();
diff --git a/clang/test/CXX/cpp/cpp.module/p1.cppm b/clang/test/CXX/cpp/cpp.module/p1.cppm
new file mode 100644
index 0000000000000..b439366db3fba
--- /dev/null
+++ b/clang/test/CXX/cpp/cpp.module/p1.cppm
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -triple x86_64-linux-gnu -DTEST=1 -verify
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -triple x86_64-linux-gnu -DTEST=2 -verify
+
+module;
+export module x;
+#include "version.h"
+#if TEST == 1
+export module VERSION;  // expected-error {{module declaration cannot be a macro}}
+#endif // TEST == 1
+
+#if TEST == 2
+export module A.B;      // expected-error {{module declaration cannot be a macro}}
+#endif // TEST == 2
diff --git a/clang/test/CXX/cpp/cpp.module/version.h b/clang/test/CXX/cpp/cpp.module/version.h
new file mode 100644
index 0000000000000..4608934290950
--- /dev/null
+++ b/clang/test/CXX/cpp/cpp.module/version.h
@@ -0,0 +1,8 @@
+#ifndef VERSION_H
+#define VERSION_H
+
+#define VERSION libv5
+#define A a
+#define B b
+
+#endif
diff --git a/clang/test/CXX/module/basic/basic.link/module-declaration.cpp b/clang/test/CXX/module/basic/basic.link/module-declaration.cpp
index d71358cc7a571..aa4bb52a57fac 100644
--- a/clang/test/CXX/module/basic/basic.link/module-declaration.cpp
+++ b/clang/test/CXX/module/basic/basic.link/module-declaration.cpp
@@ -9,26 +9,26 @@
 //
 // Module implementation for unknown and known module. (The former is ill-formed.)
 // RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify -x c++ %t/M.cpp \
-// RUN:            -DTEST=1 -DEXPORT= -DMODULE_NAME=z
+// RUN:            -DTEST=1
 // RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x=%t/x.pcm -fmodule-file=x.y=%t/x.y.pcm -verify -x c++ %t/M.cpp \
-// RUN:            -DTEST=2 -DEXPORT= -DMODULE_NAME=x
+// RUN:            -DTEST=2
 //
 // Module interface for unknown and known module. (The latter is ill-formed due to
 // redefinition.)
 // RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
-// RUN:            -DTEST=3 -DEXPORT=export -DMODULE_NAME=z
+// RUN:            -DTEST=3
 // RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
-// RUN:            -DTEST=4 -DEXPORT=export -DMODULE_NAME=x
+// RUN:            -DTEST=4
 //
 // Miscellaneous syntax.
 // RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
-// RUN:            -DTEST=7 -DEXPORT=export -DMODULE_NAME='z elderberry'
+// RUN:            -DTEST=7
 // RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
-// RUN:            -DTEST=8 -DEXPORT=export -DMODULE_NAME='z [[]]'
+// RUN:            -DTEST=8
 // RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
-// RUN:            -DTEST=9 -DEXPORT=export -DMODULE_NAME='z [[fancy]]'
+// RUN:            -DTEST=9
 // RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
-// RUN:            -DTEST=10 -DEXPORT=export -DMODULE_NAME='z [[maybe_unused]]'
+// RUN:            -DTEST=10
 
 //--- x.cppm
 export module x;
@@ -40,15 +40,20 @@ int c;
 
 //--- M.cpp
 
-EXPORT module MODULE_NAME;
-#if TEST == 7
-// expected-error at -2 {{expected ';'}} expected-error at -2 {{a type specifier is required}}
+#if TEST == 1
+module z; // expected-error {{module 'z' not found}}
+#elif TEST == 2
+module x; // expected-no-diagnostics
+#elif TEST == 3
+export module z; // expected-no-diagnostics
+#elif TEST == 4
+export module x; // expected-no-diagnostics
+#elif TEST == 7
+export module z elderberry; // expected-error {{expected ';'}} expected-error {{a type specifier is required}}
 #elif TEST == 9
-// expected-warning at -4 {{unknown attribute 'fancy' ignored}}
+export module z [[fancy]]; // expected-warning {{unknown attribute 'fancy' ignored}}
 #elif TEST == 10
-// expected-error-re at -6 {{'maybe_unused' attribute cannot be applied to a module{{$}}}}
-#elif TEST == 1
-// expected-error at -8 {{module 'z' not found}}
+export module z [[maybe_unused]]; // expected-error-re {{'maybe_unused' attribute cannot be applied to a module{{$}}}}
 #else
 // expected-no-diagnostics
 #endif
diff --git a/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm b/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm
index 873e4c0edeac2..074589ccc2692 100644
--- a/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm
+++ b/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm
@@ -35,9 +35,9 @@ int use_3 = c; // expected-error {{use of undeclared identifier 'c'}}
 
 //--- test.cpp
 #ifdef INTERFACE
-export module MODULE_NAME;
+export module MODULE_NAME; // expected-error {{module declaration cannot be a macro}}
 #else
-module MODULE_NAME;
+module MODULE_NAME; // expected-error {{module declaration cannot be a macro}}
 #endif
 
 import x;
diff --git a/clang/test/SemaCXX/modules.cppm b/clang/test/SemaCXX/modules.cppm
index 41204be76eafa..75bbc5366d4a7 100644
--- a/clang/test/SemaCXX/modules.cppm
+++ b/clang/test/SemaCXX/modules.cppm
@@ -7,6 +7,10 @@
 // expected-no-diagnostics
 #endif
 
+#if TEST == 3
+// expected-error {{module declaration cannot be a macro}}
+#endif //  TEST == 3
+
 export module foo;
 
 static int m;
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 0996abc240585..8ae9a25caf360 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -182,7 +182,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
  <tr>
   <td>Module Declarations Shouldn’t be Macros</td>
   <td><a href="https://wg21.link/P3034R1">P3034R1</a> (<a href="#dr">DR</a>)</td>
-  <td class="none" align="center">No</td>
+  <td class="unreleased" align="center">Clang 19</td>
  </tr>
  <tr>
   <td>Trivial infinite loops are not Undefined Behavior</td>

>From 0b472f255ca8f9279e58f25e2350cd0eb31baad7 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Fri, 3 May 2024 20:06:55 +0800
Subject: [PATCH 02/19] Use split-file in test and improve implementation

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../clang/Basic/DiagnosticParseKinds.td       |  2 +-
 clang/include/clang/Basic/IdentifierTable.h   | 23 ++++-
 clang/include/clang/Lex/Preprocessor.h        |  4 +
 clang/include/clang/Parse/Parser.h            |  2 +-
 clang/lib/Basic/IdentifierTable.cpp           |  3 +-
 clang/lib/Lex/PPLexerChange.cpp               |  3 +-
 clang/lib/Lex/Preprocessor.cpp                | 45 +++++++++
 clang/lib/Parse/Parser.cpp                    | 56 ++++++-----
 clang/test/CXX/cpp/cpp.module/p1.cppm         | 13 ---
 clang/test/CXX/cpp/cpp.module/p2.cppm         | 46 +++++++++
 clang/test/CXX/cpp/cpp.module/version.h       |  8 --
 .../dcl.module/dcl.module.import/p1.cppm      |  4 +-
 clang/test/SemaCXX/modules.cppm               | 93 +++++++++++--------
 13 files changed, 212 insertions(+), 90 deletions(-)
 delete mode 100644 clang/test/CXX/cpp/cpp.module/p1.cppm
 create mode 100644 clang/test/CXX/cpp/cpp.module/p2.cppm
 delete mode 100644 clang/test/CXX/cpp/cpp.module/version.h

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 0d4b526ec6d15..6e3223187b699 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1672,7 +1672,7 @@ def err_unexpected_module_decl : Error<
 def err_module_expected_ident : Error<
   "expected a module name after '%select{module|import}0'">;
 def err_module_decl_cannot_be_macros : Error<
-  "module declaration cannot be a macro">;
+  "the name of a module%select{| partition}0 declaration cannot contains %select{an object-like|a function-like}1 macro %2">;
 def err_attribute_not_module_attr : Error<
   "%0 attribute cannot be applied to a module">;
 def err_keyword_not_module_attr : Error<
diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h
index a893e6f4d3d39..5bbb9219552b6 100644
--- a/clang/include/clang/Basic/IdentifierTable.h
+++ b/clang/include/clang/Basic/IdentifierTable.h
@@ -180,6 +180,10 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
   LLVM_PREFERRED_TYPE(bool)
   unsigned IsModulesImport : 1;
 
+  // True if this is the 'module' contextual keyword.
+  LLVM_PREFERRED_TYPE(bool)
+  unsigned IsModulesDecl : 1;
+
   // True if this is a mangled OpenMP variant name.
   LLVM_PREFERRED_TYPE(bool)
   unsigned IsMangledOpenMPVariantName : 1;
@@ -196,7 +200,7 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
   LLVM_PREFERRED_TYPE(bool)
   unsigned IsFinal : 1;
 
-  // 22 bits left in a 64-bit word.
+  // 21 bits left in a 64-bit word.
 
   // Managed by the language front-end.
   void *FETokenInfo = nullptr;
@@ -211,7 +215,7 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
         IsFutureCompatKeyword(false), IsPoisoned(false),
         IsCPPOperatorKeyword(false), NeedsHandleIdentifier(false),
         IsFromAST(false), ChangedAfterLoad(false), FEChangedAfterLoad(false),
-        RevertedTokenID(false), OutOfDate(false), IsModulesImport(false),
+        RevertedTokenID(false), OutOfDate(false), IsModulesImport(false), IsModulesDecl(false),
         IsMangledOpenMPVariantName(false), IsDeprecatedMacro(false),
         IsRestrictExpansion(false), IsFinal(false) {}
 
@@ -520,6 +524,18 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
       RecomputeNeedsHandleIdentifier();
   }
 
+  /// Determine whether this is the contextual keyword \c module.
+  bool isModulesDecl() const { return IsModulesDecl; }
+
+  /// Set whether this identifier is the contextual keyword \c module.
+  void setModulesDecl(bool I) {
+    IsModulesDecl = I;
+    if (I)
+      NeedsHandleIdentifier = true;
+    else
+      RecomputeNeedsHandleIdentifier();
+  }
+
   /// Determine whether this is the mangled name of an OpenMP variant.
   bool isMangledOpenMPVariantName() const { return IsMangledOpenMPVariantName; }
 
@@ -741,6 +757,9 @@ class IdentifierTable {
     if (Name.equals("import"))
       II->setModulesImport(true);
 
+    if (Name.equals("module"))
+      II->setModulesDecl(true);
+
     return *II;
   }
 
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index e89b4a2c5230e..ef2dba7177e1e 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -1735,6 +1735,7 @@ class Preprocessor {
   bool LexHeaderName(Token &Result, bool AllowMacroExpansion = true);
 
   bool LexAfterModuleImport(Token &Result);
+  bool LexAfterModuleDecl(Token &Result);
   void CollectPpImportSuffix(SmallVectorImpl<Token> &Toks);
 
   void makeModuleVisible(Module *M, SourceLocation Loc);
@@ -2937,6 +2938,9 @@ class Preprocessor {
   static bool CLK_LexAfterModuleImport(Preprocessor &P, Token &Result) {
     return P.LexAfterModuleImport(Result);
   }
+  static bool CLK_LexAfterModuleDecl(Preprocessor &P, Token &Result) {
+    return P.LexAfterModuleDecl(Result);
+  }
 };
 
 /// Abstract base class that describes a handler that will receive
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 81aab8c888ab6..1b429cebde508 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3811,7 +3811,7 @@ class Parser : public CodeCompletionHandler {
   bool ParseModuleName(
       SourceLocation UseLoc,
       SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path,
-      bool IsImport);
+      bool IsImport, bool IsPartition);
 
   //===--------------------------------------------------------------------===//
   // C++11/G++: Type Traits [Type-Traits.html in the GCC manual]
diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp
index feea84544d62f..76d5d1190a964 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -322,8 +322,9 @@ void IdentifierTable::AddKeywords(const LangOptions &LangOpts) {
   if (LangOpts.IEEE128)
     AddKeyword("__ieee128", tok::kw___float128, KEYALL, LangOpts, *this);
 
-  // Add the 'import' contextual keyword.
+  // Add the 'import' and 'module' contextual keyword.
   get("import").setModulesImport(true);
+  get("module").setModulesDecl(true);
 }
 
 /// Checks if the specified token kind represents a keyword in the
diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp
index 2ca2122ac7109..873a72cf9ba20 100644
--- a/clang/lib/Lex/PPLexerChange.cpp
+++ b/clang/lib/Lex/PPLexerChange.cpp
@@ -161,7 +161,8 @@ void Preprocessor::EnterMacro(Token &Tok, SourceLocation ILEnd,
   PushIncludeMacroStack();
   CurDirLookup = nullptr;
   CurTokenLexer = std::move(TokLexer);
-  if (CurLexerCallback != CLK_LexAfterModuleImport)
+  if (CurLexerCallback != CLK_LexAfterModuleImport &&
+      CurLexerCallback != CLK_LexAfterModuleDecl)
     CurLexerCallback = CLK_TokenLexer;
 }
 
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 0b70192743a39..b1571494f1e5f 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -862,6 +862,14 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
     ModuleImportExpectsIdentifier = true;
     CurLexerCallback = CLK_LexAfterModuleImport;
   }
+
+  if ((II.isModulesDecl() ||
+       Identifier.is(tok::kw_module)) &&
+      !InMacroArgs && !DisableMacroExpansion &&
+      (getLangOpts().Modules || getLangOpts().DebuggerSupport) &&
+      CurLexerCallback != CLK_CachingLexer) {
+    CurLexerCallback = CLK_LexAfterModuleDecl;
+  }
   return true;
 }
 
@@ -942,6 +950,7 @@ void Preprocessor::Lex(Token &Result) {
         } else if (Result.getIdentifierInfo() == getIdentifierInfo("module")) {
           TrackGMFState.handleModule(StdCXXImportSeqState.afterTopLevelSeq());
           ModuleDeclState.handleModule();
+          CurLexerCallback = CLK_LexAfterModuleDecl;
           break;
         }
       }
@@ -1329,6 +1338,42 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
   return true;
 }
 
+/// Lex a token following the 'module' contextual keyword.
+///
+/// [cpp.module]/p2:
+/// The pp-tokens, if any, of a pp-module shall be of the form:
+///       pp-module-name pp-module-partition[opt] pp-tokens[opt]
+///
+/// where the pp-tokens (if any) shall not begin with a ( preprocessing token
+/// and the grammar non-terminals are defined as:
+///       pp-module-name:
+///             pp-module-name-qualifierp[opt] identifier
+///       pp-module-partition:
+///             : pp-module-name-qualifier[opt] identifier
+///       pp-module-name-qualifier:
+///             identifier .
+///             pp-module-name-qualifier identifier .
+/// No identifier in the pp-module-name or pp-module-partition shall currently
+/// be defined as an object-like macro.
+///
+/// [cpp.module]/p3:
+/// Any preprocessing tokens after the module preprocessing token in the module
+/// directive are processed just as in normal text.
+bool Preprocessor::LexAfterModuleDecl(Token &Result) {
+  // Figure out what kind of lexer we actually have.
+  recomputeCurLexerKind();
+  LexUnexpandedToken(Result);
+
+  // pp-module:
+  //    export[opt] module pp-tokens[opt] ; new-line
+  // Processe tokens just as in normal text, until we see a ';', this means the
+  // end of the module directive.
+  if (!Result.is(tok::semi))
+    CurLexerCallback = CLK_LexAfterModuleDecl;
+
+  return true;
+}
+
 void Preprocessor::makeModuleVisible(Module *M, SourceLocation Loc) {
   CurSubmoduleState->VisibleModules.setVisible(
       M, Loc, [](Module *) {},
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index ef66348a83125..f1e35028e1ce6 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -2494,9 +2494,11 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
     return Actions.ActOnPrivateModuleFragmentDecl(ModuleLoc, PrivateLoc);
   }
 
+  bool HasError = false;
   SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
-  if (ParseModuleName(ModuleLoc, Path, /*IsImport*/ false))
-    return nullptr;
+  if (ParseModuleName(ModuleLoc, Path, /*IsImport=*/false,
+                      /*IsPartition=*/false))
+    HasError = true;
 
   // Parse the optional module-partition.
   SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition;
@@ -2506,8 +2508,9 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
       Diag(ColonLoc, diag::err_unsupported_module_partition)
           << SourceRange(ColonLoc, Partition.back().second);
     // Recover by ignoring the partition name.
-    else if (ParseModuleName(ModuleLoc, Partition, /*IsImport*/ false))
-      return nullptr;
+    else if (ParseModuleName(ModuleLoc, Partition, /*IsImport=*/false,
+                             /*IsPartition=*/true))
+      HasError = true;
   }
 
   // We don't support any module attributes yet; just parse them and diagnose.
@@ -2520,8 +2523,9 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
 
   ExpectAndConsumeSemi(diag::err_module_expected_semi);
 
-  return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, Partition,
-                                 ImportState);
+  return HasError ? nullptr
+                  : Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path,
+                                            Partition, ImportState);
 }
 
 /// Parse a module import declaration. This is essentially the same for
@@ -2571,12 +2575,14 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
       Diag(ColonLoc, diag::err_unsupported_module_partition)
           << SourceRange(ColonLoc, Path.back().second);
     // Recover by leaving partition empty.
-    else if (ParseModuleName(ColonLoc, Path, /*IsImport*/ true))
+    else if (ParseModuleName(ColonLoc, Path, /*IsImport=*/true,
+                             /*IsPartition=*/true))
       return nullptr;
     else
       IsPartition = true;
   } else {
-    if (ParseModuleName(ImportLoc, Path, /*IsImport*/ true))
+    if (ParseModuleName(ImportLoc, Path, /*IsImport=*/true,
+                        /*IsPartition=*/false))
       return nullptr;
   }
 
@@ -2675,7 +2681,8 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
 bool Parser::ParseModuleName(
     SourceLocation UseLoc,
     SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path,
-    bool IsImport) {
+    bool IsImport, bool IsPartition) {
+  bool HasMacroInModuleName = false;
   // Parse the module path.
   while (true) {
     if (!Tok.is(tok::identifier)) {
@@ -2686,25 +2693,30 @@ bool Parser::ParseModuleName(
       }
 
       Diag(Tok, diag::err_module_expected_ident) << IsImport;
-      SkipUntil(tok::semi);
-      return true;
-    }
-
-    // P3034R1: Module Declarations Shouldn’t be Macros
-    if (!IsImport && Tok.getLocation().isMacroID()) {
-      Diag(Tok, diag::err_module_decl_cannot_be_macros);
-      SkipUntil(tok::semi);
+      SkipUntil(tok::semi, StopBeforeMatch);
       return true;
     }
 
-    // Record this part of the module path.
-    Path.push_back(std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()));
+    Token Identifier = Tok;
     ConsumeToken();
 
-    if (Tok.isNot(tok::period))
-      return false;
+    // P3034R1: Module Declarations Shouldn’t be Macros.
+    const auto *MI = PP.getMacroInfo(Identifier.getIdentifierInfo());
+    if (!IsImport && MI) {
+      HasMacroInModuleName = true;
+      if (MI->isFunctionLike())
+        SkipUntil(tok::r_paren, tok::period, tok::colon, StopAtSemi | StopBeforeMatch);
+      Diag(Identifier, diag::err_module_decl_cannot_be_macros)
+            << Identifier.getLocation()
+            << IsPartition << MI->isFunctionLike()
+            << Identifier.getIdentifierInfo();
+    } else if (!HasMacroInModuleName) {
+      // Record this part of the module path.
+      Path.push_back(std::make_pair(Identifier.getIdentifierInfo(), Identifier.getLocation()));
+    }
 
-    ConsumeToken();
+    if (!TryConsumeToken(tok::period))
+      return HasMacroInModuleName;
   }
 }
 
diff --git a/clang/test/CXX/cpp/cpp.module/p1.cppm b/clang/test/CXX/cpp/cpp.module/p1.cppm
deleted file mode 100644
index b439366db3fba..0000000000000
--- a/clang/test/CXX/cpp/cpp.module/p1.cppm
+++ /dev/null
@@ -1,13 +0,0 @@
-// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -triple x86_64-linux-gnu -DTEST=1 -verify
-// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -triple x86_64-linux-gnu -DTEST=2 -verify
-
-module;
-export module x;
-#include "version.h"
-#if TEST == 1
-export module VERSION;  // expected-error {{module declaration cannot be a macro}}
-#endif // TEST == 1
-
-#if TEST == 2
-export module A.B;      // expected-error {{module declaration cannot be a macro}}
-#endif // TEST == 2
diff --git a/clang/test/CXX/cpp/cpp.module/p2.cppm b/clang/test/CXX/cpp/cpp.module/p2.cppm
new file mode 100644
index 0000000000000..b75dfd7346211
--- /dev/null
+++ b/clang/test/CXX/cpp/cpp.module/p2.cppm
@@ -0,0 +1,46 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/A.cppm -triple x86_64-linux-gnu -verify
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/B.cppm -triple x86_64-linux-gnu -verify
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/C.cppm -triple x86_64-linux-gnu -verify
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/D.cppm -triple x86_64-linux-gnu -verify
+
+//--- version.h
+#ifndef VERSION_H
+#define VERSION_H
+
+#define VERSION libv5
+#define A a
+#define B b
+#define C c
+#define FUNC_LIKE(X) function_like_##X
+
+#endif
+
+//--- A.cppm
+export module x;
+#include "version.h"
+export module VERSION;  // expected-error {{the name of a module declaration cannot contains an object-like macro 'VERSION'}}
+
+//--- B.cppm
+export module x;
+#include "version.h"
+export module A.B;      // expected-error {{the name of a module declaration cannot contains an object-like macro 'A'}} \
+                        // expected-error {{the name of a module declaration cannot contains an object-like macro 'B'}}
+
+//--- C.cppm
+export module x;
+#include "version.h"
+export module A.FUNC_LIKE(foo):C;   // expected-error {{the name of a module declaration cannot contains an object-like macro 'A'}} \
+                                    // expected-error {{the name of a module declaration cannot contains a function-like macro 'FUNC_LIKE'}} \
+                                    // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C'}}
+
+//--- D.cppm
+export module x;
+#include "version.h"
+export module B.A.FUNC_LIKE(bar):C;   // expected-error {{the name of a module declaration cannot contains an object-like macro 'B'}} \
+                                      // expected-error {{the name of a module declaration cannot contains an object-like macro 'A'}} \
+                                      // expected-error {{the name of a module declaration cannot contains a function-like macro 'FUNC_LIKE'}} \
+                                      // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C'}}
diff --git a/clang/test/CXX/cpp/cpp.module/version.h b/clang/test/CXX/cpp/cpp.module/version.h
deleted file mode 100644
index 4608934290950..0000000000000
--- a/clang/test/CXX/cpp/cpp.module/version.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef VERSION_H
-#define VERSION_H
-
-#define VERSION libv5
-#define A a
-#define B b
-
-#endif
diff --git a/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm b/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm
index 074589ccc2692..81c645a872d7a 100644
--- a/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm
+++ b/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm
@@ -35,9 +35,9 @@ int use_3 = c; // expected-error {{use of undeclared identifier 'c'}}
 
 //--- test.cpp
 #ifdef INTERFACE
-export module MODULE_NAME; // expected-error {{module declaration cannot be a macro}}
+export module MODULE_NAME; // expected-error {{the name of a module declaration cannot contains an object-like macro 'MODULE_NAME'}}
 #else
-module MODULE_NAME; // expected-error {{module declaration cannot be a macro}}
+module MODULE_NAME; // expected-error {{the name of a module declaration cannot contains an object-like macro 'MODULE_NAME'}}
 #endif
 
 import x;
diff --git a/clang/test/SemaCXX/modules.cppm b/clang/test/SemaCXX/modules.cppm
index 75bbc5366d4a7..98c3ad3f4feff 100644
--- a/clang/test/SemaCXX/modules.cppm
+++ b/clang/test/SemaCXX/modules.cppm
@@ -1,23 +1,17 @@
-// RUN:     %clang_cc1 -std=c++20 -emit-module-interface %s -o %t.0.pcm -verify -DTEST=0
-// RUN:     %clang_cc1 -std=c++20 -emit-module-interface %s -o %t.1.pcm -verify -DTEST=1
-// RUN:     %clang_cc1 -std=c++20 -emit-module-interface %s -fmodule-file=foo=%t.0.pcm -o %t.2.pcm -verify -DTEST=2
-// RUN:     %clang_cc1 -std=c++20 -emit-module-interface %s -fmodule-file=foo=%t.0.pcm -o %t.3.pcm -verify -Dfoo=bar -DTEST=3
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
 
-#if TEST == 0 || TEST == 2
-// expected-no-diagnostics
-#endif
-
-#if TEST == 3
-// expected-error {{module declaration cannot be a macro}}
-#endif //  TEST == 3
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/A.cppm -o %t.0.pcm -verify
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/B.cppm -o %t.1.pcm -verify
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/C.cppm -fmodule-file=foo=%t.0.pcm -o %t.2.pcm -verify
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/D.cppm -fmodule-file=foo=%t.0.pcm -o %t.3.pcm -verify
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/E.cppm -fmodule-file=foo=%t.0.pcm -o %t.3.pcm -verify -Dfoo=bar
 
+//--- A.cppm
 export module foo;
-
 static int m;
-
 int n;
-
-#if TEST == 0
 export {
   int a;
   int b;
@@ -31,7 +25,43 @@ export void f() {}
 
 export struct T {
 } t;
-#elif TEST == 3
+// expected-no-diagnostics
+
+//--- B.cppm
+export module foo;
+static int m;
+int n;
+struct S {
+  export int n;        // expected-error {{expected member name or ';'}}
+  export static int n; // expected-error {{expected member name or ';'}}
+};
+
+// FIXME: Exports of declarations without external linkage are disallowed.
+// Exports of declarations with non-external-linkage types are disallowed.
+
+// Cannot export within another export. This isn't precisely covered by the
+// language rules right now, but (per personal correspondence between zygoloid
+// and gdr) is the intent.
+export { // expected-note {{export block begins here}}
+  extern "C++" {
+    namespace NestedExport {
+      export { // expected-error {{export declaration appears within another export declaration}}
+        int q;
+      }
+    } // namespace NestedExport
+  }
+}
+
+//--- C.cppm
+export module foo;
+static int m;
+int n;
+// expected-no-diagnostics
+
+//--- D.cppm
+export module foo;
+static int m;
+int n;
 int use_a = a; // expected-error {{use of undeclared identifier 'a'}}
 
 #undef foo
@@ -50,29 +80,14 @@ int use_n = n; // FIXME: this should not be visible, because it is not exported
 
 extern int n;
 static_assert(&n != p); // expected-error{{use of undeclared identifier 'p'}}
-#endif
 
-#if TEST == 1
-struct S {
-  export int n;        // expected-error {{expected member name or ';'}}
-  export static int n; // expected-error {{expected member name or ';'}}
-};
-#endif
+//--- E.cppm
+export module foo; // expected-error {{the name of a module declaration cannot contains an object-like macro 'foo'}}
+static int m;
+int n;
+int use_a = a; // expected-error {{use of undeclared identifier 'a'}}
 
-// FIXME: Exports of declarations without external linkage are disallowed.
-// Exports of declarations with non-external-linkage types are disallowed.
+#undef foo
+import foo;
 
-// Cannot export within another export. This isn't precisely covered by the
-// language rules right now, but (per personal correspondence between zygoloid
-// and gdr) is the intent.
-#if TEST == 1
-export { // expected-note {{export block begins here}}
-  extern "C++" {
-  namespace NestedExport {
-  export { // expected-error {{export declaration appears within another export declaration}}
-    int q;
-  }
-  } // namespace NestedExport
-  }
-}
-#endif
+export {} // expected-error {{export declaration can only be used within a module purview}}

>From fd75d83d8d9c1ea3e30ccf2c4646667f50c0c490 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Fri, 3 May 2024 20:07:49 +0800
Subject: [PATCH 03/19] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/include/clang/Basic/IdentifierTable.h |  6 +++---
 clang/lib/Lex/Preprocessor.cpp              |  5 ++---
 clang/lib/Parse/Parser.cpp                  | 11 ++++++-----
 3 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h
index 5bbb9219552b6..645f7ae913fa3 100644
--- a/clang/include/clang/Basic/IdentifierTable.h
+++ b/clang/include/clang/Basic/IdentifierTable.h
@@ -215,9 +215,9 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
         IsFutureCompatKeyword(false), IsPoisoned(false),
         IsCPPOperatorKeyword(false), NeedsHandleIdentifier(false),
         IsFromAST(false), ChangedAfterLoad(false), FEChangedAfterLoad(false),
-        RevertedTokenID(false), OutOfDate(false), IsModulesImport(false), IsModulesDecl(false),
-        IsMangledOpenMPVariantName(false), IsDeprecatedMacro(false),
-        IsRestrictExpansion(false), IsFinal(false) {}
+        RevertedTokenID(false), OutOfDate(false), IsModulesImport(false),
+        IsModulesDecl(false), IsMangledOpenMPVariantName(false),
+        IsDeprecatedMacro(false), IsRestrictExpansion(false), IsFinal(false) {}
 
 public:
   IdentifierInfo(const IdentifierInfo &) = delete;
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index b1571494f1e5f..5982c9a72d93e 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -863,9 +863,8 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
     CurLexerCallback = CLK_LexAfterModuleImport;
   }
 
-  if ((II.isModulesDecl() ||
-       Identifier.is(tok::kw_module)) &&
-      !InMacroArgs && !DisableMacroExpansion &&
+  if ((II.isModulesDecl() || Identifier.is(tok::kw_module)) && !InMacroArgs &&
+      !DisableMacroExpansion &&
       (getLangOpts().Modules || getLangOpts().DebuggerSupport) &&
       CurLexerCallback != CLK_CachingLexer) {
     CurLexerCallback = CLK_LexAfterModuleDecl;
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index f1e35028e1ce6..2c45e037a1a90 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -2705,14 +2705,15 @@ bool Parser::ParseModuleName(
     if (!IsImport && MI) {
       HasMacroInModuleName = true;
       if (MI->isFunctionLike())
-        SkipUntil(tok::r_paren, tok::period, tok::colon, StopAtSemi | StopBeforeMatch);
+        SkipUntil(tok::r_paren, tok::period, tok::colon,
+                  StopAtSemi | StopBeforeMatch);
       Diag(Identifier, diag::err_module_decl_cannot_be_macros)
-            << Identifier.getLocation()
-            << IsPartition << MI->isFunctionLike()
-            << Identifier.getIdentifierInfo();
+          << Identifier.getLocation() << IsPartition << MI->isFunctionLike()
+          << Identifier.getIdentifierInfo();
     } else if (!HasMacroInModuleName) {
       // Record this part of the module path.
-      Path.push_back(std::make_pair(Identifier.getIdentifierInfo(), Identifier.getLocation()));
+      Path.push_back(std::make_pair(Identifier.getIdentifierInfo(),
+                                    Identifier.getLocation()));
     }
 
     if (!TryConsumeToken(tok::period))

>From 265189b67d63f7322be08eaf6b4e0ff5fb9b03a3 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Fri, 3 May 2024 21:30:03 +0800
Subject: [PATCH 04/19] Do not treat no '(' following function-like macro as a
 macro name

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../clang/Basic/DiagnosticParseKinds.td       |  3 +-
 clang/lib/Parse/Parser.cpp                    | 17 ++++--
 clang/test/CXX/cpp/cpp.module/p2.cppm         | 26 ++++++----
 .../basic/basic.link/module-declaration.cpp   | 52 +++++++++----------
 .../dcl.module/dcl.module.import/p1.cppm      |  4 +-
 clang/test/SemaCXX/modules.cppm               |  2 +-
 6 files changed, 58 insertions(+), 46 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 6e3223187b699..02a66b4c87e12 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1672,7 +1672,8 @@ def err_unexpected_module_decl : Error<
 def err_module_expected_ident : Error<
   "expected a module name after '%select{module|import}0'">;
 def err_module_decl_cannot_be_macros : Error<
-  "the name of a module%select{| partition}0 declaration cannot contains %select{an object-like|a function-like}1 macro %2">;
+  "the name of a module%select{| partition}0 declaration cannot contains "
+  "%select{an object-like|a function-like}1 macro %2, and the macro will not expand">;
 def err_attribute_not_module_attr : Error<
   "%0 attribute cannot be applied to a module">;
 def err_keyword_not_module_attr : Error<
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 2c45e037a1a90..2923ac720a668 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -2701,15 +2701,24 @@ bool Parser::ParseModuleName(
     ConsumeToken();
 
     // P3034R1: Module Declarations Shouldn’t be Macros.
+    // The function-like macro are only replaced when a '(' follows.
+    // Therefore, if no '(' following, it's will not treated as a macro
+    // name, and it's a valid module name.
     const auto *MI = PP.getMacroInfo(Identifier.getIdentifierInfo());
-    if (!IsImport && MI) {
+    if (!IsImport && MI &&
+        (MI->isObjectLike() ||
+         (MI->isFunctionLike() && Tok.is(tok::l_paren)))) {
       HasMacroInModuleName = true;
-      if (MI->isFunctionLike())
+      SourceLocation StartLoc = Identifier.getLocation();
+      SourceLocation EndLoc = Identifier.getLocation();
+      if (MI->isFunctionLike()) {
         SkipUntil(tok::r_paren, tok::period, tok::colon,
                   StopAtSemi | StopBeforeMatch);
+        EndLoc = PrevTokLocation;
+      }
       Diag(Identifier, diag::err_module_decl_cannot_be_macros)
-          << Identifier.getLocation() << IsPartition << MI->isFunctionLike()
-          << Identifier.getIdentifierInfo();
+          << SourceRange(StartLoc, EndLoc) << IsPartition
+          << MI->isFunctionLike() << Identifier.getIdentifierInfo();
     } else if (!HasMacroInModuleName) {
       // Record this part of the module path.
       Path.push_back(std::make_pair(Identifier.getIdentifierInfo(),
diff --git a/clang/test/CXX/cpp/cpp.module/p2.cppm b/clang/test/CXX/cpp/cpp.module/p2.cppm
index b75dfd7346211..a94451ae6dfba 100644
--- a/clang/test/CXX/cpp/cpp.module/p2.cppm
+++ b/clang/test/CXX/cpp/cpp.module/p2.cppm
@@ -22,25 +22,31 @@
 //--- A.cppm
 export module x;
 #include "version.h"
-export module VERSION;  // expected-error {{the name of a module declaration cannot contains an object-like macro 'VERSION'}}
+export module VERSION;  // expected-error {{the name of a module declaration cannot contains an object-like macro 'VERSION', and the macro will not expand}}
 
 //--- B.cppm
 export module x;
 #include "version.h"
-export module A.B;      // expected-error {{the name of a module declaration cannot contains an object-like macro 'A'}} \
-                        // expected-error {{the name of a module declaration cannot contains an object-like macro 'B'}}
+export module A.B;      // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand}} \
+                        // expected-error {{the name of a module declaration cannot contains an object-like macro 'B', and the macro will not expand}}
 
 //--- C.cppm
 export module x;
 #include "version.h"
-export module A.FUNC_LIKE(foo):C;   // expected-error {{the name of a module declaration cannot contains an object-like macro 'A'}} \
-                                    // expected-error {{the name of a module declaration cannot contains a function-like macro 'FUNC_LIKE'}} \
-                                    // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C'}}
+export module A.FUNC_LIKE(foo):C;   // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand}} \
+                                    // expected-error {{the name of a module declaration cannot contains a function-like macro 'FUNC_LIKE', and the macro will not expand}} \
+                                    // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C', and the macro will not expand}}
 
 //--- D.cppm
 export module x;
 #include "version.h"
-export module B.A.FUNC_LIKE(bar):C;   // expected-error {{the name of a module declaration cannot contains an object-like macro 'B'}} \
-                                      // expected-error {{the name of a module declaration cannot contains an object-like macro 'A'}} \
-                                      // expected-error {{the name of a module declaration cannot contains a function-like macro 'FUNC_LIKE'}} \
-                                      // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C'}}
+export module B.A.FUNC_LIKE(bar):C;   // expected-error {{the name of a module declaration cannot contains an object-like macro 'B', and the macro will not expand}} \
+                                      // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand}} \
+                                      // expected-error {{the name of a module declaration cannot contains a function-like macro 'FUNC_LIKE', and the macro will not expand}} \
+                                      // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C', and the macro will not expand}}
+
+//--- E.cppm
+export module x;
+#include "version.h"
+export module a.FUNC_LIKE:c // OK, FUNC_LIKE would not be treated as a macro name.
+// expected-no-diagnostics
\ No newline at end of file
diff --git a/clang/test/CXX/module/basic/basic.link/module-declaration.cpp b/clang/test/CXX/module/basic/basic.link/module-declaration.cpp
index aa4bb52a57fac..14bbc911febfc 100644
--- a/clang/test/CXX/module/basic/basic.link/module-declaration.cpp
+++ b/clang/test/CXX/module/basic/basic.link/module-declaration.cpp
@@ -8,27 +8,19 @@
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface -fmodule-file=x=%t/x.pcm %t/x.y.cppm -o %t/x.y.pcm
 //
 // Module implementation for unknown and known module. (The former is ill-formed.)
-// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify -x c++ %t/M.cpp \
-// RUN:            -DTEST=1
-// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x=%t/x.pcm -fmodule-file=x.y=%t/x.y.pcm -verify -x c++ %t/M.cpp \
-// RUN:            -DTEST=2
+// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify -x c++ %t/M1.cpp
+// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x=%t/x.pcm -fmodule-file=x.y=%t/x.y.pcm -verify -x c++ %t/M2.cpp
 //
 // Module interface for unknown and known module. (The latter is ill-formed due to
 // redefinition.)
-// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
-// RUN:            -DTEST=3
-// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
-// RUN:            -DTEST=4
+// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M3.cpp
+// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M4.cpp
 //
 // Miscellaneous syntax.
-// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
-// RUN:            -DTEST=7
-// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
-// RUN:            -DTEST=8
-// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
-// RUN:            -DTEST=9
-// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
-// RUN:            -DTEST=10
+// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M5.cpp
+// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M6.cpp
+// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M7.cpp
+// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M8.cpp
 
 //--- x.cppm
 export module x;
@@ -38,22 +30,26 @@ int a, b;
 export module x.y;
 int c;
 
-//--- M.cpp
-
-#if TEST == 1
+//--- M1.cpp
 module z; // expected-error {{module 'z' not found}}
-#elif TEST == 2
+
+//--- M2.cpp
 module x; // expected-no-diagnostics
-#elif TEST == 3
+
+//--- M3.cpp
 export module z; // expected-no-diagnostics
-#elif TEST == 4
+
+//--- M4.cpp
 export module x; // expected-no-diagnostics
-#elif TEST == 7
+
+//--- M5.cpp
 export module z elderberry; // expected-error {{expected ';'}} expected-error {{a type specifier is required}}
-#elif TEST == 9
+
+//--- M6.cpp
+export module z [[]]; // expected-no-diagnostics
+
+//--- M7.cpp
 export module z [[fancy]]; // expected-warning {{unknown attribute 'fancy' ignored}}
-#elif TEST == 10
+
+//--- M8.cpp
 export module z [[maybe_unused]]; // expected-error-re {{'maybe_unused' attribute cannot be applied to a module{{$}}}}
-#else
-// expected-no-diagnostics
-#endif
diff --git a/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm b/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm
index 81c645a872d7a..860ac3d2894e0 100644
--- a/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm
+++ b/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm
@@ -35,9 +35,9 @@ int use_3 = c; // expected-error {{use of undeclared identifier 'c'}}
 
 //--- test.cpp
 #ifdef INTERFACE
-export module MODULE_NAME; // expected-error {{the name of a module declaration cannot contains an object-like macro 'MODULE_NAME'}}
+export module MODULE_NAME; // expected-error {{the name of a module declaration cannot contains an object-like macro 'MODULE_NAME', and the macro will not expand}}
 #else
-module MODULE_NAME; // expected-error {{the name of a module declaration cannot contains an object-like macro 'MODULE_NAME'}}
+module MODULE_NAME; // expected-error {{the name of a module declaration cannot contains an object-like macro 'MODULE_NAME', and the macro will not expand}}
 #endif
 
 import x;
diff --git a/clang/test/SemaCXX/modules.cppm b/clang/test/SemaCXX/modules.cppm
index 98c3ad3f4feff..c0082d5150069 100644
--- a/clang/test/SemaCXX/modules.cppm
+++ b/clang/test/SemaCXX/modules.cppm
@@ -82,7 +82,7 @@ extern int n;
 static_assert(&n != p); // expected-error{{use of undeclared identifier 'p'}}
 
 //--- E.cppm
-export module foo; // expected-error {{the name of a module declaration cannot contains an object-like macro 'foo'}}
+export module foo; // expected-error {{the name of a module declaration cannot contains an object-like macro 'foo', and the macro will not expand}}
 static int m;
 int n;
 int use_a = a; // expected-error {{use of undeclared identifier 'a'}}

>From 611e1d8b177599957439d3046a60c09ab039a4c9 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Fri, 3 May 2024 22:44:11 +0800
Subject: [PATCH 05/19] Add new line at the end of file

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/test/CXX/cpp/cpp.module/p2.cppm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/CXX/cpp/cpp.module/p2.cppm b/clang/test/CXX/cpp/cpp.module/p2.cppm
index a94451ae6dfba..c966b966cd08b 100644
--- a/clang/test/CXX/cpp/cpp.module/p2.cppm
+++ b/clang/test/CXX/cpp/cpp.module/p2.cppm
@@ -49,4 +49,4 @@ export module B.A.FUNC_LIKE(bar):C;   // expected-error {{the name of a module d
 export module x;
 #include "version.h"
 export module a.FUNC_LIKE:c // OK, FUNC_LIKE would not be treated as a macro name.
-// expected-no-diagnostics
\ No newline at end of file
+// expected-no-diagnostics

>From 224e1d0c5379e7a7c0226a8c2155c34cc5b26905 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 7 May 2024 01:05:12 +0800
Subject: [PATCH 06/19] Move implementation to Preprocessor

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../include/clang/Basic/DiagnosticLexKinds.td |   6 +
 .../clang/Basic/DiagnosticParseKinds.td       |   3 -
 clang/include/clang/Lex/Preprocessor.h        |   7 ++
 clang/include/clang/Parse/Parser.h            |   2 +-
 clang/lib/Lex/Preprocessor.cpp                | 109 +++++++++++++++++-
 clang/lib/Parse/Parser.cpp                    |  46 ++------
 clang/test/CXX/cpp/cpp.module/p2.cppm         |  60 +++++++---
 7 files changed, 172 insertions(+), 61 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index ad6bacfb118d4..25c44bdac6731 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -932,6 +932,12 @@ def warn_module_conflict : Warning<
   InGroup<ModuleConflict>;
 
 // C++20 modules
+def err_module_decl_cannot_be_macros : Error<
+  "the name of a module%select{| partition}0 declaration cannot contains "
+  "an object-like macro %1, and the macro will not expand"
+  "%select{|; did you mean '%3'?}2">;
+def err_unxepected_paren_in_module_decl : Error<
+  "unexpected '(' after the name of a module%select{| partition}0 declaration">;
 def err_header_import_semi_in_macro : Error<
   "semicolon terminating header import declaration cannot be produced "
   "by a macro">;
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 02a66b4c87e12..fdffb35ea0d95 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1671,9 +1671,6 @@ def err_unexpected_module_decl : Error<
   "module declaration can only appear at the top level">;
 def err_module_expected_ident : Error<
   "expected a module name after '%select{module|import}0'">;
-def err_module_decl_cannot_be_macros : Error<
-  "the name of a module%select{| partition}0 declaration cannot contains "
-  "%select{an object-like|a function-like}1 macro %2, and the macro will not expand">;
 def err_attribute_not_module_attr : Error<
   "%0 attribute cannot be applied to a module">;
 def err_keyword_not_module_attr : Error<
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index ef2dba7177e1e..42442f08886b1 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -609,6 +609,13 @@ class Preprocessor {
   /// it expects a '.' or ';'.
   bool ModuleImportExpectsIdentifier = false;
 
+  /// Whether the module declaration expects an identifier next. Otherwise,
+  /// it expects a '.' or ';'.
+  bool ModuleDeclExpectsIdentifier = false;
+
+  /// Whether lexing the name of module partition declaration.
+  bool ModuleDeclLexingPartitionName = false;
+
   /// The identifier and source location of the currently-active
   /// \#pragma clang arc_cf_code_audited begin.
   std::pair<IdentifierInfo *, SourceLocation> PragmaARCCFCodeAuditedInfo;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index c37348cc33461..daefd4f28f011 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3812,7 +3812,7 @@ class Parser : public CodeCompletionHandler {
   bool ParseModuleName(
       SourceLocation UseLoc,
       SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path,
-      bool IsImport, bool IsPartition);
+      bool IsImport);
 
   //===--------------------------------------------------------------------===//
   // C++11/G++: Type Traits [Type-Traits.html in the GCC manual]
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 5982c9a72d93e..25ca8ebadf056 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -55,6 +55,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
@@ -867,6 +868,8 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
       !DisableMacroExpansion &&
       (getLangOpts().Modules || getLangOpts().DebuggerSupport) &&
       CurLexerCallback != CLK_CachingLexer) {
+    ModuleDeclExpectsIdentifier = true;
+    ModuleDeclLexingPartitionName = false;
     CurLexerCallback = CLK_LexAfterModuleDecl;
   }
   return true;
@@ -949,6 +952,8 @@ void Preprocessor::Lex(Token &Result) {
         } else if (Result.getIdentifierInfo() == getIdentifierInfo("module")) {
           TrackGMFState.handleModule(StdCXXImportSeqState.afterTopLevelSeq());
           ModuleDeclState.handleModule();
+          ModuleDeclExpectsIdentifier = true;
+          ModuleDeclLexingPartitionName = false;
           CurLexerCallback = CLK_LexAfterModuleDecl;
           break;
         }
@@ -1363,12 +1368,99 @@ bool Preprocessor::LexAfterModuleDecl(Token &Result) {
   recomputeCurLexerKind();
   LexUnexpandedToken(Result);
 
-  // pp-module:
-  //    export[opt] module pp-tokens[opt] ; new-line
-  // Processe tokens just as in normal text, until we see a ';', this means the
-  // end of the module directive.
-  if (!Result.is(tok::semi))
+  auto EnterTokens = [this](ArrayRef<Token> Toks, bool DisableMacroExpansion) {
+    auto ToksCopy = std::make_unique<Token[]>(Toks.size());
+    std::copy(Toks.begin(), Toks.end(), ToksCopy.get());
+    EnterTokenStream(std::move(ToksCopy), Toks.size(), DisableMacroExpansion,
+                     /*IsReinject=*/false);
+  };
+
+  // If we not expect an identifier but got an identifier, it's not a part of
+  // module name.
+  if (!ModuleDeclExpectsIdentifier && Result.is(tok::identifier)) {
+    EnterTokens(Result, /*DisableMacroExpansion=*/false);
+    return false;
+  }
+
+  // The token sequence
+  //
+  // export[opt] module identifier (. identifier)*
+  //
+  // indicates a module import directive. We already saw the 'module'
+  // contextual keyword, so now we're looking for the identifiers.
+  if (ModuleDeclExpectsIdentifier && Result.is(tok::identifier)) {
+    auto *MI = getMacroInfo(Result.getIdentifierInfo());
+    if (MI && MI->isObjectLike()) {
+      auto BuildFixItHint =
+          [&](std::string &Replacement) -> std::optional<FixItHint> {
+        EnterTokens(Result, /*DisableMacroExpansion=*/false);
+        if (!CurTokenLexer)
+          return std::nullopt;
+        Token Tok;
+        bool HasUnknownToken = false;
+        llvm::raw_string_ostream OS(Replacement);
+        do {
+          Lex(Tok);
+          if (const char *Punc = tok::getPunctuatorSpelling(Tok.getKind()))
+            OS << Punc;
+          else if (Tok.isLiteral() && Tok.getLiteralData())
+            OS << StringRef(Tok.getLiteralData(), Tok.getLength());
+          else if (auto *II = Tok.getIdentifierInfo())
+            OS << II->getName();
+          else
+            HasUnknownToken = true;
+        } while (CurTokenLexer && !CurTokenLexer->isAtEnd());
+        if (HasUnknownToken)
+          return std::nullopt;
+        return FixItHint::CreateReplacement(Result.getLocation(), Replacement);
+      };
+      SourceLocation EndLoc = Result.getLocation();
+      std::string Replacement;
+      auto FixIt = BuildFixItHint(Replacement);
+      auto DB = Diag(Result, diag::err_module_decl_cannot_be_macros)
+                << SourceRange(Result.getLocation(), EndLoc)
+                << ModuleDeclLexingPartitionName << Result.getIdentifierInfo()
+                << FixIt.has_value();
+      if (FixIt)
+        DB << Replacement << *FixIt;
+    }
+    ModuleDeclExpectsIdentifier = false;
+    CurLexerCallback = CLK_LexAfterModuleDecl;
+    return true;
+  }
+
+  // If we're expecting a '.', a ':' or a ';', and we got a '.', then wait until
+  // we see the next identifier.
+  if (!ModuleDeclExpectsIdentifier && Result.isOneOf(tok::period, tok::colon)) {
+    ModuleDeclExpectsIdentifier = true;
+    ModuleDeclLexingPartitionName = Result.is(tok::colon);
+    CurLexerCallback = CLK_LexAfterModuleDecl;
+    return true;
+  }
+
+  // [cpp.module]/p2: where the pp-tokens (if any) shall not begin with a (
+  // preprocessing token [...]
+  if (!ModuleDeclExpectsIdentifier && Result.is(tok::l_paren)) {
+    ModuleDeclExpectsIdentifier = false;
+    Diag(Result, diag::err_unxepected_paren_in_module_decl)
+        << ModuleDeclLexingPartitionName;
+    Token Tok;
+    // We already have a '('.
+    unsigned NumParens = 1;
+    while (true) {
+      LexUnexpandedToken(Tok);
+      if (Tok.isOneOf(tok::eod, tok::eof, tok::semi, tok::period, tok::colon)) {
+        EnterTokens(Tok, /*DisableMacroExpansion=*/true);
+        break;
+      }
+      if (Tok.is(tok::l_paren))
+        NumParens++;
+      else if (Tok.is(tok::r_paren) && --NumParens == 0)
+        break;
+    }
     CurLexerCallback = CLK_LexAfterModuleDecl;
+    return false;
+  }
 
   return true;
 }
@@ -1626,3 +1718,10 @@ const char *Preprocessor::getCheckPoint(FileID FID, const char *Start) const {
 
   return nullptr;
 }
+
+#if 0
+def err_module_decl_cannot_be_macros : Error<
+  "the name of a module%select{| partition}0 declaration cannot contains "
+  "%select{an object-like|a function-like}1 macro %2, and the macro will not expand"
+  "%select{|; did you mean '%4'?}3">;
+#endif
\ No newline at end of file
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 2923ac720a668..af4409ef86bcb 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -2496,10 +2496,8 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
 
   bool HasError = false;
   SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
-  if (ParseModuleName(ModuleLoc, Path, /*IsImport=*/false,
-                      /*IsPartition=*/false))
+  if (ParseModuleName(ModuleLoc, Path, /*IsImport=*/false))
     HasError = true;
-
   // Parse the optional module-partition.
   SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition;
   if (Tok.is(tok::colon)) {
@@ -2508,8 +2506,7 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
       Diag(ColonLoc, diag::err_unsupported_module_partition)
           << SourceRange(ColonLoc, Partition.back().second);
     // Recover by ignoring the partition name.
-    else if (ParseModuleName(ModuleLoc, Partition, /*IsImport=*/false,
-                             /*IsPartition=*/true))
+    else if (ParseModuleName(ModuleLoc, Partition, /*IsImport=*/false))
       HasError = true;
   }
 
@@ -2575,14 +2572,12 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
       Diag(ColonLoc, diag::err_unsupported_module_partition)
           << SourceRange(ColonLoc, Path.back().second);
     // Recover by leaving partition empty.
-    else if (ParseModuleName(ColonLoc, Path, /*IsImport=*/true,
-                             /*IsPartition=*/true))
+    else if (ParseModuleName(ColonLoc, Path, /*IsImport=*/true))
       return nullptr;
     else
       IsPartition = true;
   } else {
-    if (ParseModuleName(ImportLoc, Path, /*IsImport=*/true,
-                        /*IsPartition=*/false))
+    if (ParseModuleName(ImportLoc, Path, /*IsImport=*/true))
       return nullptr;
   }
 
@@ -2681,7 +2676,7 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
 bool Parser::ParseModuleName(
     SourceLocation UseLoc,
     SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path,
-    bool IsImport, bool IsPartition) {
+    bool IsImport) {
   bool HasMacroInModuleName = false;
   // Parse the module path.
   while (true) {
@@ -2697,33 +2692,12 @@ bool Parser::ParseModuleName(
       return true;
     }
 
-    Token Identifier = Tok;
-    ConsumeToken();
+    const auto *MI = PP.getMacroInfo(Tok.getIdentifierInfo());
+    HasMacroInModuleName = !IsImport && MI && MI->isObjectLike();
 
-    // P3034R1: Module Declarations Shouldn’t be Macros.
-    // The function-like macro are only replaced when a '(' follows.
-    // Therefore, if no '(' following, it's will not treated as a macro
-    // name, and it's a valid module name.
-    const auto *MI = PP.getMacroInfo(Identifier.getIdentifierInfo());
-    if (!IsImport && MI &&
-        (MI->isObjectLike() ||
-         (MI->isFunctionLike() && Tok.is(tok::l_paren)))) {
-      HasMacroInModuleName = true;
-      SourceLocation StartLoc = Identifier.getLocation();
-      SourceLocation EndLoc = Identifier.getLocation();
-      if (MI->isFunctionLike()) {
-        SkipUntil(tok::r_paren, tok::period, tok::colon,
-                  StopAtSemi | StopBeforeMatch);
-        EndLoc = PrevTokLocation;
-      }
-      Diag(Identifier, diag::err_module_decl_cannot_be_macros)
-          << SourceRange(StartLoc, EndLoc) << IsPartition
-          << MI->isFunctionLike() << Identifier.getIdentifierInfo();
-    } else if (!HasMacroInModuleName) {
-      // Record this part of the module path.
-      Path.push_back(std::make_pair(Identifier.getIdentifierInfo(),
-                                    Identifier.getLocation()));
-    }
+    // Record this part of the module path.
+    Path.push_back(std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()));
+    ConsumeToken();
 
     if (!TryConsumeToken(tok::period))
       return HasMacroInModuleName;
diff --git a/clang/test/CXX/cpp/cpp.module/p2.cppm b/clang/test/CXX/cpp/cpp.module/p2.cppm
index c966b966cd08b..c13d9f1eee29d 100644
--- a/clang/test/CXX/cpp/cpp.module/p2.cppm
+++ b/clang/test/CXX/cpp/cpp.module/p2.cppm
@@ -2,10 +2,15 @@
 // RUN: mkdir -p %t
 // RUN: split-file %s %t
 
-// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/A.cppm -triple x86_64-linux-gnu -verify
-// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/B.cppm -triple x86_64-linux-gnu -verify
-// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/C.cppm -triple x86_64-linux-gnu -verify
-// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/D.cppm -triple x86_64-linux-gnu -verify
+// RUN: %clang_cc1 -std=c++20 %t/A.cppm -triple x86_64-linux-gnu -verify
+// RUN: %clang_cc1 -std=c++20 %t/B.cppm -triple x86_64-linux-gnu -verify
+// RUN: %clang_cc1 -std=c++20 %t/C.cppm -triple x86_64-linux-gnu -verify
+// RUN: %clang_cc1 -std=c++20 %t/D.cppm -triple x86_64-linux-gnu -verify
+// RUN: %clang_cc1 -std=c++20 %t/E.cppm -triple x86_64-linux-gnu -verify
+// RUN: %clang_cc1 -std=c++20 %t/F.cppm -triple x86_64-linux-gnu -verify
+// RUN: %clang_cc1 -std=c++20 %t/G.cppm -triple x86_64-linux-gnu -verify
+// RUN: %clang_cc1 -std=c++20 %t/H.cppm -triple x86_64-linux-gnu -verify
+// RUN: %clang_cc1 -std=c++20 %t/I.cppm -triple x86_64-linux-gnu -verify
 
 //--- version.h
 #ifndef VERSION_H
@@ -16,37 +21,60 @@
 #define B b
 #define C c
 #define FUNC_LIKE(X) function_like_##X
+#define ATTR [[]]
 
 #endif
 
 //--- A.cppm
-export module x;
 #include "version.h"
 export module VERSION;  // expected-error {{the name of a module declaration cannot contains an object-like macro 'VERSION', and the macro will not expand}}
 
 //--- B.cppm
-export module x;
 #include "version.h"
 export module A.B;      // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand}} \
                         // expected-error {{the name of a module declaration cannot contains an object-like macro 'B', and the macro will not expand}}
 
 //--- C.cppm
-export module x;
 #include "version.h"
-export module A.FUNC_LIKE(foo):C;   // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand}} \
-                                    // expected-error {{the name of a module declaration cannot contains a function-like macro 'FUNC_LIKE', and the macro will not expand}} \
+export module A.FUNC_LIKE(foo):C;   // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand; did you mean 'a'?}} \
+                                    // expected-error {{unexpected '(' after the name of a module declaration}} \
                                     // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C', and the macro will not expand}}
 
 //--- D.cppm
-export module x;
 #include "version.h"
-export module B.A.FUNC_LIKE(bar):C;   // expected-error {{the name of a module declaration cannot contains an object-like macro 'B', and the macro will not expand}} \
-                                      // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand}} \
-                                      // expected-error {{the name of a module declaration cannot contains a function-like macro 'FUNC_LIKE', and the macro will not expand}} \
-                                      // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C', and the macro will not expand}}
+export module B.A.FUNC_LIKE(bar):C;   // expected-error {{the name of a module declaration cannot contains an object-like macro 'B', and the macro will not expand; did you mean 'b'?}} \
+                                      // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand; did you mean 'a'?}} \
+                                      // expected-error {{unexpected '(' after the name of a module declaration}} \
+                                      // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C', and the macro will not expand; did you mean 'c'?}}
 
 //--- E.cppm
-export module x;
 #include "version.h"
-export module a.FUNC_LIKE:c // OK, FUNC_LIKE would not be treated as a macro name.
+export module a.FUNC_LIKE:c; // OK, FUNC_LIKE would not be treated as a macro name.
 // expected-no-diagnostics
+
+//--- F.cppm
+#include "version.h"
+export module a.FUNC_LIKE:c ATTR; // OK, FUNC_LIKE would not be treated as a macro name.
+// expected-no-diagnostics
+
+//--- G.cppm
+#include "version.h"
+export module A.FUNC_LIKE(B c:C ATTR  // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand; did you mean 'a'?}} \
+                                      // expected-error {{unexpected '(' after the name of a module declaration}} \
+                                      // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C', and the macro will not expand; did you mean 'c'?}} \
+                                      // expected-error {{expected ';' after module name}}
+
+//--- H.cppm
+#include "version.h"
+export module A.FUNC_LIKE(B,). c:C ATTR   // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand; did you mean 'a'?}} \
+                                          // expected-error {{unexpected '(' after the name of a module declaration}} \
+                                          // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C', and the macro will not expand; did you mean 'c'?}} \
+                                          // expected-error {{expected ';' after module name}}
+
+//--- I.cppm
+#include "version.h"
+export module A.FUNC_LIKE(B,) c:C ATTR    // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand; did you mean 'a'?}} \
+                                          // expected-error {{unexpected '(' after the name of a module declaration}} \
+                                          // expected-error {{expected ';' after module name}} \
+                                          // expected-error {{unknown type name 'c'}} \
+                                          // expected-error {{expected unqualified-id}}

>From 8ff13ca6728c4b7acd94cae0aa8f64e9d2219c62 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 7 May 2024 01:11:28 +0800
Subject: [PATCH 07/19] Remove dead code

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/lib/Lex/Preprocessor.cpp | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 25ca8ebadf056..b23cfe8ec345e 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -55,7 +55,6 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
@@ -1718,10 +1717,3 @@ const char *Preprocessor::getCheckPoint(FileID FID, const char *Start) const {
 
   return nullptr;
 }
-
-#if 0
-def err_module_decl_cannot_be_macros : Error<
-  "the name of a module%select{| partition}0 declaration cannot contains "
-  "%select{an object-like|a function-like}1 macro %2, and the macro will not expand"
-  "%select{|; did you mean '%4'?}3">;
-#endif
\ No newline at end of file

>From bb4a9233d275a3e6458abb35761fb704f508c19d Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 7 May 2024 01:39:44 +0800
Subject: [PATCH 08/19] Add more test

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/test/CXX/cpp/cpp.module/p2.cppm | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/clang/test/CXX/cpp/cpp.module/p2.cppm b/clang/test/CXX/cpp/cpp.module/p2.cppm
index c13d9f1eee29d..76de2ada5584f 100644
--- a/clang/test/CXX/cpp/cpp.module/p2.cppm
+++ b/clang/test/CXX/cpp/cpp.module/p2.cppm
@@ -11,6 +11,7 @@
 // RUN: %clang_cc1 -std=c++20 %t/G.cppm -triple x86_64-linux-gnu -verify
 // RUN: %clang_cc1 -std=c++20 %t/H.cppm -triple x86_64-linux-gnu -verify
 // RUN: %clang_cc1 -std=c++20 %t/I.cppm -triple x86_64-linux-gnu -verify
+// RUN: %clang_cc1 -std=c++20 %t/J.cppm -triple x86_64-linux-gnu -verify
 
 //--- version.h
 #ifndef VERSION_H
@@ -22,6 +23,7 @@
 #define C c
 #define FUNC_LIKE(X) function_like_##X
 #define ATTR [[]]
+#define SEMICOLON ;
 
 #endif
 
@@ -54,27 +56,32 @@ export module a.FUNC_LIKE:c; // OK, FUNC_LIKE would not be treated as a macro na
 
 //--- F.cppm
 #include "version.h"
-export module a.FUNC_LIKE:c ATTR; // OK, FUNC_LIKE would not be treated as a macro name.
+export module a.FUNC_LIKE:c ATTRS; // OK, FUNC_LIKE would not be treated as a macro name.
 // expected-no-diagnostics
 
 //--- G.cppm
 #include "version.h"
-export module A.FUNC_LIKE(B c:C ATTR  // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand; did you mean 'a'?}} \
+export module A.FUNC_LIKE(B c:C ATTRS // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand; did you mean 'a'?}} \
                                       // expected-error {{unexpected '(' after the name of a module declaration}} \
                                       // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C', and the macro will not expand; did you mean 'c'?}} \
                                       // expected-error {{expected ';' after module name}}
 
 //--- H.cppm
 #include "version.h"
-export module A.FUNC_LIKE(B,). c:C ATTR   // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand; did you mean 'a'?}} \
+export module A.FUNC_LIKE(B,). c:C ATTRS  // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand; did you mean 'a'?}} \
                                           // expected-error {{unexpected '(' after the name of a module declaration}} \
                                           // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C', and the macro will not expand; did you mean 'c'?}} \
                                           // expected-error {{expected ';' after module name}}
 
 //--- I.cppm
 #include "version.h"
-export module A.FUNC_LIKE(B,) c:C ATTR    // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand; did you mean 'a'?}} \
+export module A.FUNC_LIKE(B,) c:C ATTRS   // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand; did you mean 'a'?}} \
                                           // expected-error {{unexpected '(' after the name of a module declaration}} \
                                           // expected-error {{expected ';' after module name}} \
                                           // expected-error {{unknown type name 'c'}} \
                                           // expected-error {{expected unqualified-id}}
+
+//--- J.cppm
+#include "version.h"
+export module unexpanded : unexpanded ATTRS SEMICOLON // OK, ATTRS and SEMICOLON can be expanded.
+// expected-no-diagnostics

>From 597b6a184c7b4dbbe5419e5dfaf737f5c52962a8 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 7 May 2024 01:43:49 +0800
Subject: [PATCH 09/19] Fix  test

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/test/CXX/cpp/cpp.module/p2.cppm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/CXX/cpp/cpp.module/p2.cppm b/clang/test/CXX/cpp/cpp.module/p2.cppm
index 76de2ada5584f..f21c891fe811b 100644
--- a/clang/test/CXX/cpp/cpp.module/p2.cppm
+++ b/clang/test/CXX/cpp/cpp.module/p2.cppm
@@ -22,7 +22,7 @@
 #define B b
 #define C c
 #define FUNC_LIKE(X) function_like_##X
-#define ATTR [[]]
+#define ATTRS [[]]
 #define SEMICOLON ;
 
 #endif

>From b3e843bea7f5dcadaeb6b6591696dfa23d4463bb Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 9 May 2024 00:44:36 +0800
Subject: [PATCH 10/19] Address review comments

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../include/clang/Basic/DiagnosticLexKinds.td |  8 ++--
 clang/lib/Lex/Preprocessor.cpp                | 39 ++-------------
 clang/lib/Parse/Parser.cpp                    | 16 ++-----
 clang/test/CXX/cpp/cpp.module/p2.cppm         | 48 +++++++++++--------
 .../dcl.module/dcl.module.import/p1.cppm      | 39 +++++++++++----
 clang/test/SemaCXX/modules.cppm               |  6 +--
 6 files changed, 75 insertions(+), 81 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 25c44bdac6731..6401d1c20b32c 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -933,11 +933,11 @@ def warn_module_conflict : Warning<
 
 // C++20 modules
 def err_module_decl_cannot_be_macros : Error<
-  "the name of a module%select{| partition}0 declaration cannot contains "
-  "an object-like macro %1, and the macro will not expand"
-  "%select{|; did you mean '%3'?}2">;
+  "the module name in a module%select{| partition}0 declaration cannot contain "
+  "an object-like macro %1, it's consists of one or more identifiers "
+  "separated by '.'">;
 def err_unxepected_paren_in_module_decl : Error<
-  "unexpected '(' after the name of a module%select{| partition}0 declaration">;
+  "unexpected '(' after the module name in a module%select{| partition}0 declaration">;
 def err_header_import_semi_in_macro : Error<
   "semicolon terminating header import declaration cannot be produced "
   "by a macro">;
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index b23cfe8ec345e..e2ac204b86f6d 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -1374,7 +1374,7 @@ bool Preprocessor::LexAfterModuleDecl(Token &Result) {
                      /*IsReinject=*/false);
   };
 
-  // If we not expect an identifier but got an identifier, it's not a part of
+  // If we don't expect an identifier but got an identifier, it's not a part of
   // module name.
   if (!ModuleDeclExpectsIdentifier && Result.is(tok::identifier)) {
     EnterTokens(Result, /*DisableMacroExpansion=*/false);
@@ -1385,43 +1385,14 @@ bool Preprocessor::LexAfterModuleDecl(Token &Result) {
   //
   // export[opt] module identifier (. identifier)*
   //
-  // indicates a module import directive. We already saw the 'module'
+  // indicates a module directive. We already saw the 'module'
   // contextual keyword, so now we're looking for the identifiers.
   if (ModuleDeclExpectsIdentifier && Result.is(tok::identifier)) {
     auto *MI = getMacroInfo(Result.getIdentifierInfo());
     if (MI && MI->isObjectLike()) {
-      auto BuildFixItHint =
-          [&](std::string &Replacement) -> std::optional<FixItHint> {
-        EnterTokens(Result, /*DisableMacroExpansion=*/false);
-        if (!CurTokenLexer)
-          return std::nullopt;
-        Token Tok;
-        bool HasUnknownToken = false;
-        llvm::raw_string_ostream OS(Replacement);
-        do {
-          Lex(Tok);
-          if (const char *Punc = tok::getPunctuatorSpelling(Tok.getKind()))
-            OS << Punc;
-          else if (Tok.isLiteral() && Tok.getLiteralData())
-            OS << StringRef(Tok.getLiteralData(), Tok.getLength());
-          else if (auto *II = Tok.getIdentifierInfo())
-            OS << II->getName();
-          else
-            HasUnknownToken = true;
-        } while (CurTokenLexer && !CurTokenLexer->isAtEnd());
-        if (HasUnknownToken)
-          return std::nullopt;
-        return FixItHint::CreateReplacement(Result.getLocation(), Replacement);
-      };
-      SourceLocation EndLoc = Result.getLocation();
-      std::string Replacement;
-      auto FixIt = BuildFixItHint(Replacement);
-      auto DB = Diag(Result, diag::err_module_decl_cannot_be_macros)
-                << SourceRange(Result.getLocation(), EndLoc)
-                << ModuleDeclLexingPartitionName << Result.getIdentifierInfo()
-                << FixIt.has_value();
-      if (FixIt)
-        DB << Replacement << *FixIt;
+      Diag(Result, diag::err_module_decl_cannot_be_macros)
+          << Result.getLocation() << ModuleDeclLexingPartitionName
+          << Result.getIdentifierInfo();
     }
     ModuleDeclExpectsIdentifier = false;
     CurLexerCallback = CLK_LexAfterModuleDecl;
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index af4409ef86bcb..4bdf522561f92 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -2494,10 +2494,9 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
     return Actions.ActOnPrivateModuleFragmentDecl(ModuleLoc, PrivateLoc);
   }
 
-  bool HasError = false;
   SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
   if (ParseModuleName(ModuleLoc, Path, /*IsImport=*/false))
-    HasError = true;
+    return nullptr;
   // Parse the optional module-partition.
   SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition;
   if (Tok.is(tok::colon)) {
@@ -2507,7 +2506,7 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
           << SourceRange(ColonLoc, Partition.back().second);
     // Recover by ignoring the partition name.
     else if (ParseModuleName(ModuleLoc, Partition, /*IsImport=*/false))
-      HasError = true;
+      return nullptr;
   }
 
   // We don't support any module attributes yet; just parse them and diagnose.
@@ -2520,9 +2519,8 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
 
   ExpectAndConsumeSemi(diag::err_module_expected_semi);
 
-  return HasError ? nullptr
-                  : Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path,
-                                            Partition, ImportState);
+  return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, Partition,
+                                 ImportState);
 }
 
 /// Parse a module import declaration. This is essentially the same for
@@ -2677,7 +2675,6 @@ bool Parser::ParseModuleName(
     SourceLocation UseLoc,
     SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path,
     bool IsImport) {
-  bool HasMacroInModuleName = false;
   // Parse the module path.
   while (true) {
     if (!Tok.is(tok::identifier)) {
@@ -2692,15 +2689,12 @@ bool Parser::ParseModuleName(
       return true;
     }
 
-    const auto *MI = PP.getMacroInfo(Tok.getIdentifierInfo());
-    HasMacroInModuleName = !IsImport && MI && MI->isObjectLike();
-
     // Record this part of the module path.
     Path.push_back(std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()));
     ConsumeToken();
 
     if (!TryConsumeToken(tok::period))
-      return HasMacroInModuleName;
+      return false;
   }
 }
 
diff --git a/clang/test/CXX/cpp/cpp.module/p2.cppm b/clang/test/CXX/cpp/cpp.module/p2.cppm
index f21c891fe811b..add88ce0841bb 100644
--- a/clang/test/CXX/cpp/cpp.module/p2.cppm
+++ b/clang/test/CXX/cpp/cpp.module/p2.cppm
@@ -25,63 +25,73 @@
 #define ATTRS [[]]
 #define SEMICOLON ;
 
-#endif
+#endif // VERSION_H
 
 //--- A.cppm
+module;
 #include "version.h"
-export module VERSION;  // expected-error {{the name of a module declaration cannot contains an object-like macro 'VERSION', and the macro will not expand}}
+export module VERSION;  // expected-error {{the module name in a module declaration cannot contain an object-like macro 'VERSION', it's consists of one or more identifiers separated by '.'}}
 
 //--- B.cppm
+module;
 #include "version.h"
-export module A.B;      // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand}} \
-                        // expected-error {{the name of a module declaration cannot contains an object-like macro 'B', and the macro will not expand}}
+export module A.B;      // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A', it's consists of one or more identifiers separated by '.'}} \
+                        // expected-error {{the module name in a module declaration cannot contain an object-like macro 'B', it's consists of one or more identifiers separated by '.'}}
 
 //--- C.cppm
+module;
 #include "version.h"
-export module A.FUNC_LIKE(foo):C;   // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand; did you mean 'a'?}} \
-                                    // expected-error {{unexpected '(' after the name of a module declaration}} \
-                                    // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C', and the macro will not expand}}
+export module A.FUNC_LIKE(foo):C;   // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A', it's consists of one or more identifiers separated by '.'}} \
+                                    // expected-error {{unexpected '(' after the module name in a module declaration}} \
+                                    // expected-error {{the module name in a module partition declaration cannot contain an object-like macro 'C', it's consists of one or more identifiers separated by '.'}}
 
 //--- D.cppm
+module;
 #include "version.h"
-export module B.A.FUNC_LIKE(bar):C;   // expected-error {{the name of a module declaration cannot contains an object-like macro 'B', and the macro will not expand; did you mean 'b'?}} \
-                                      // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand; did you mean 'a'?}} \
-                                      // expected-error {{unexpected '(' after the name of a module declaration}} \
-                                      // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C', and the macro will not expand; did you mean 'c'?}}
+export module B.A.FUNC_LIKE(bar):C;   // expected-error {{the module name in a module declaration cannot contain an object-like macro 'B', it's consists of one or more identifiers separated by '.'}} \
+                                      // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A', it's consists of one or more identifiers separated by '.'}} \
+                                      // expected-error {{unexpected '(' after the module name in a module declaration}} \
+                                      // expected-error {{the module name in a module partition declaration cannot contain an object-like macro 'C', it's consists of one or more identifiers separated by '.'}}
 
 //--- E.cppm
+module;
 #include "version.h"
 export module a.FUNC_LIKE:c; // OK, FUNC_LIKE would not be treated as a macro name.
 // expected-no-diagnostics
 
 //--- F.cppm
+module;
 #include "version.h"
 export module a.FUNC_LIKE:c ATTRS; // OK, FUNC_LIKE would not be treated as a macro name.
 // expected-no-diagnostics
 
 //--- G.cppm
+module;
 #include "version.h"
-export module A.FUNC_LIKE(B c:C ATTRS // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand; did you mean 'a'?}} \
-                                      // expected-error {{unexpected '(' after the name of a module declaration}} \
-                                      // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C', and the macro will not expand; did you mean 'c'?}} \
+export module A.FUNC_LIKE(B c:C ATTRS // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A', it's consists of one or more identifiers separated by '.'}} \
+                                      // expected-error {{unexpected '(' after the module name in a module declaration}} \
+                                      // expected-error {{the module name in a module partition declaration cannot contain an object-like macro 'C', it's consists of one or more identifiers separated by '.'}} \
                                       // expected-error {{expected ';' after module name}}
 
 //--- H.cppm
+module;
 #include "version.h"
-export module A.FUNC_LIKE(B,). c:C ATTRS  // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand; did you mean 'a'?}} \
-                                          // expected-error {{unexpected '(' after the name of a module declaration}} \
-                                          // expected-error {{the name of a module partition declaration cannot contains an object-like macro 'C', and the macro will not expand; did you mean 'c'?}} \
+export module A.FUNC_LIKE(B,). c:C ATTRS  // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A', it's consists of one or more identifiers separated by '.'}} \
+                                          // expected-error {{unexpected '(' after the module name in a module declaration}} \
+                                          // expected-error {{the module name in a module partition declaration cannot contain an object-like macro 'C', it's consists of one or more identifiers separated by '.'}} \
                                           // expected-error {{expected ';' after module name}}
 
 //--- I.cppm
+module;
 #include "version.h"
-export module A.FUNC_LIKE(B,) c:C ATTRS   // expected-error {{the name of a module declaration cannot contains an object-like macro 'A', and the macro will not expand; did you mean 'a'?}} \
-                                          // expected-error {{unexpected '(' after the name of a module declaration}} \
+export module A.FUNC_LIKE(B,) c:C ATTRS   // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A', it's consists of one or more identifiers separated by '.'}} \
+                                          // expected-error {{unexpected '(' after the module name in a module declaration}} \
                                           // expected-error {{expected ';' after module name}} \
                                           // expected-error {{unknown type name 'c'}} \
                                           // expected-error {{expected unqualified-id}}
 
 //--- J.cppm
+module;
 #include "version.h"
 export module unexpanded : unexpanded ATTRS SEMICOLON // OK, ATTRS and SEMICOLON can be expanded.
 // expected-no-diagnostics
diff --git a/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm b/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm
index 860ac3d2894e0..ecad4db32a7e9 100644
--- a/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm
+++ b/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm
@@ -6,10 +6,12 @@
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface -fmodule-file=x=%t/x.pcm %t/x.y.cppm -o %t/x.y.pcm
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/a.b.cppm -o %t/a.b.pcm
 //
-// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -fmodule-file=x=%t/x.pcm -verify %t/test.cpp \
-// RUN:            -DMODULE_NAME=z -DINTERFACE
+// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -fmodule-file=x=%t/x.pcm -verify %t/test-interface.cpp \
+// RUN:            -DINTERFACE
 // RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -fmodule-file=x=%t/x.pcm \
-// RUN:            -fmodule-file=a.b=%t/a.b.pcm -verify %t/test.cpp -DMODULE_NAME=a.b
+// RUN:            -fmodule-file=a.b=%t/a.b.pcm -verify %t/test.cpp
+// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -fmodule-file=x=%t/x.pcm \
+// RUN:            -verify %t/test-module-not-found.cpp
 // RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -fmodule-file=x=%t/x.pcm -verify %t/test.x.cpp
 
 //--- x.cppm
@@ -34,11 +36,8 @@ int use_2 = b; // ok
 int use_3 = c; // expected-error {{use of undeclared identifier 'c'}}
 
 //--- test.cpp
-#ifdef INTERFACE
-export module MODULE_NAME; // expected-error {{the name of a module declaration cannot contains an object-like macro 'MODULE_NAME', and the macro will not expand}}
-#else
-module MODULE_NAME; // expected-error {{the name of a module declaration cannot contains an object-like macro 'MODULE_NAME', and the macro will not expand}}
-#endif
+module;
+module a.b;
 
 import x;
 
@@ -51,6 +50,28 @@ import x.y;
 import x.; // expected-error {{expected a module name after 'import'}}
 import .x; // expected-error {{expected a module name after 'import'}}
 
-import blarg; // expected-error {{module 'blarg' not found}}
+int use_4 = c; // ok
+
+
+//--- test-interface.cpp
+module;
+export module z;
+
+import x;
+
+import x [[]];
+import x [[foo]]; // expected-warning {{unknown attribute 'foo' ignored}}
+import x [[noreturn]]; // expected-error {{'noreturn' attribute cannot be applied to a module import}}
+import x [[blarg::noreturn]]; // expected-warning {{unknown attribute 'noreturn' ignored}}
+
+import x.y;
+import x.; // expected-error {{expected a module name after 'import'}}
+import .x; // expected-error {{expected a module name after 'import'}}
 
 int use_4 = c; // ok
+
+//--- test-module-not-found.cpp
+module;
+
+import blarg; // expected-error {{module 'blarg' not found}}
+
diff --git a/clang/test/SemaCXX/modules.cppm b/clang/test/SemaCXX/modules.cppm
index c0082d5150069..267417bf5da2c 100644
--- a/clang/test/SemaCXX/modules.cppm
+++ b/clang/test/SemaCXX/modules.cppm
@@ -82,12 +82,10 @@ extern int n;
 static_assert(&n != p); // expected-error{{use of undeclared identifier 'p'}}
 
 //--- E.cppm
-export module foo; // expected-error {{the name of a module declaration cannot contains an object-like macro 'foo', and the macro will not expand}}
+export module foo; // expected-error {{the module name in a module declaration cannot contain an object-like macro 'foo'}}
 static int m;
 int n;
 int use_a = a; // expected-error {{use of undeclared identifier 'a'}}
 
 #undef foo
-import foo;
-
-export {} // expected-error {{export declaration can only be used within a module purview}}
+import foo; // expected-error {{imports must immediately follow the module declaration}}

>From ed0e90b60f37fe195c01aad5dc38d902ffe593be Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Sat, 11 May 2024 22:57:10 +0800
Subject: [PATCH 11/19] Address review comments

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../include/clang/Basic/DiagnosticLexKinds.td |  3 +-
 clang/include/clang/Basic/IdentifierTable.h   |  6 ++--
 clang/include/clang/Lex/Preprocessor.h        |  4 +--
 clang/lib/Basic/IdentifierTable.cpp           |  2 +-
 clang/lib/Lex/Preprocessor.cpp                | 35 ++++++++++---------
 clang/test/CXX/cpp/cpp.module/p2.cppm         | 26 +++++++-------
 6 files changed, 39 insertions(+), 37 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 6401d1c20b32c..fd31ea9421e58 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -934,8 +934,7 @@ def warn_module_conflict : Warning<
 // C++20 modules
 def err_module_decl_cannot_be_macros : Error<
   "the module name in a module%select{| partition}0 declaration cannot contain "
-  "an object-like macro %1, it's consists of one or more identifiers "
-  "separated by '.'">;
+  "an object-like macro %1">;
 def err_unxepected_paren_in_module_decl : Error<
   "unexpected '(' after the module name in a module%select{| partition}0 declaration">;
 def err_header_import_semi_in_macro : Error<
diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h
index 645f7ae913fa3..2735b892af28d 100644
--- a/clang/include/clang/Basic/IdentifierTable.h
+++ b/clang/include/clang/Basic/IdentifierTable.h
@@ -525,10 +525,10 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
   }
 
   /// Determine whether this is the contextual keyword \c module.
-  bool isModulesDecl() const { return IsModulesDecl; }
+  bool isModulesDeclaration() const { return IsModulesDecl; }
 
   /// Set whether this identifier is the contextual keyword \c module.
-  void setModulesDecl(bool I) {
+  void setModulesDeclaration(bool I) {
     IsModulesDecl = I;
     if (I)
       NeedsHandleIdentifier = true;
@@ -758,7 +758,7 @@ class IdentifierTable {
       II->setModulesImport(true);
 
     if (Name.equals("module"))
-      II->setModulesDecl(true);
+      II->setModulesDeclaration(true);
 
     return *II;
   }
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 42442f08886b1..ea01375956974 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -611,10 +611,10 @@ class Preprocessor {
 
   /// Whether the module declaration expects an identifier next. Otherwise,
   /// it expects a '.' or ';'.
-  bool ModuleDeclExpectsIdentifier = false;
+  bool ModuleDeclarationExpectsIdentifier = false;
 
   /// Whether lexing the name of module partition declaration.
-  bool ModuleDeclLexingPartitionName = false;
+  bool ModuleDeclarationLexingPartitionName = false;
 
   /// The identifier and source location of the currently-active
   /// \#pragma clang arc_cf_code_audited begin.
diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp
index 76d5d1190a964..6475fc92043cd 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -324,7 +324,7 @@ void IdentifierTable::AddKeywords(const LangOptions &LangOpts) {
 
   // Add the 'import' and 'module' contextual keyword.
   get("import").setModulesImport(true);
-  get("module").setModulesDecl(true);
+  get("module").setModulesDeclaration(true);
 }
 
 /// Checks if the specified token kind represents a keyword in the
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index e2ac204b86f6d..a943a22db3cd1 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -863,12 +863,12 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
     CurLexerCallback = CLK_LexAfterModuleImport;
   }
 
-  if ((II.isModulesDecl() || Identifier.is(tok::kw_module)) && !InMacroArgs &&
-      !DisableMacroExpansion &&
+  if ((II.isModulesDeclaration() || Identifier.is(tok::kw_module)) &&
+      !InMacroArgs && !DisableMacroExpansion &&
       (getLangOpts().Modules || getLangOpts().DebuggerSupport) &&
       CurLexerCallback != CLK_CachingLexer) {
-    ModuleDeclExpectsIdentifier = true;
-    ModuleDeclLexingPartitionName = false;
+    ModuleDeclarationExpectsIdentifier = true;
+    ModuleDeclarationLexingPartitionName = false;
     CurLexerCallback = CLK_LexAfterModuleDecl;
   }
   return true;
@@ -951,8 +951,8 @@ void Preprocessor::Lex(Token &Result) {
         } else if (Result.getIdentifierInfo() == getIdentifierInfo("module")) {
           TrackGMFState.handleModule(StdCXXImportSeqState.afterTopLevelSeq());
           ModuleDeclState.handleModule();
-          ModuleDeclExpectsIdentifier = true;
-          ModuleDeclLexingPartitionName = false;
+          ModuleDeclarationExpectsIdentifier = true;
+          ModuleDeclarationLexingPartitionName = false;
           CurLexerCallback = CLK_LexAfterModuleDecl;
           break;
         }
@@ -1376,7 +1376,7 @@ bool Preprocessor::LexAfterModuleDecl(Token &Result) {
 
   // If we don't expect an identifier but got an identifier, it's not a part of
   // module name.
-  if (!ModuleDeclExpectsIdentifier && Result.is(tok::identifier)) {
+  if (!ModuleDeclarationExpectsIdentifier && Result.is(tok::identifier)) {
     EnterTokens(Result, /*DisableMacroExpansion=*/false);
     return false;
   }
@@ -1387,33 +1387,36 @@ bool Preprocessor::LexAfterModuleDecl(Token &Result) {
   //
   // indicates a module directive. We already saw the 'module'
   // contextual keyword, so now we're looking for the identifiers.
-  if (ModuleDeclExpectsIdentifier && Result.is(tok::identifier)) {
+  if (ModuleDeclarationExpectsIdentifier && Result.is(tok::identifier)) {
     auto *MI = getMacroInfo(Result.getIdentifierInfo());
     if (MI && MI->isObjectLike()) {
       Diag(Result, diag::err_module_decl_cannot_be_macros)
-          << Result.getLocation() << ModuleDeclLexingPartitionName
+          << Result.getLocation() << ModuleDeclarationLexingPartitionName
           << Result.getIdentifierInfo();
     }
-    ModuleDeclExpectsIdentifier = false;
+    ModuleDeclarationExpectsIdentifier = false;
     CurLexerCallback = CLK_LexAfterModuleDecl;
     return true;
   }
 
   // If we're expecting a '.', a ':' or a ';', and we got a '.', then wait until
   // we see the next identifier.
-  if (!ModuleDeclExpectsIdentifier && Result.isOneOf(tok::period, tok::colon)) {
-    ModuleDeclExpectsIdentifier = true;
-    ModuleDeclLexingPartitionName = Result.is(tok::colon);
+  if (!ModuleDeclarationExpectsIdentifier &&
+      Result.isOneOf(tok::period, tok::colon)) {
+    ModuleDeclarationExpectsIdentifier = true;
+    ModuleDeclarationLexingPartitionName = Result.is(tok::colon);
     CurLexerCallback = CLK_LexAfterModuleDecl;
     return true;
   }
 
   // [cpp.module]/p2: where the pp-tokens (if any) shall not begin with a (
   // preprocessing token [...]
-  if (!ModuleDeclExpectsIdentifier && Result.is(tok::l_paren)) {
-    ModuleDeclExpectsIdentifier = false;
+  if (!ModuleDeclarationExpectsIdentifier && Result.is(tok::l_paren)) {
+    ModuleDeclarationExpectsIdentifier = false;
     Diag(Result, diag::err_unxepected_paren_in_module_decl)
-        << ModuleDeclLexingPartitionName;
+        << ModuleDeclarationLexingPartitionName;
+
+    // Skip until see one of module name separator '.', ':' or ';'.
     Token Tok;
     // We already have a '('.
     unsigned NumParens = 1;
diff --git a/clang/test/CXX/cpp/cpp.module/p2.cppm b/clang/test/CXX/cpp/cpp.module/p2.cppm
index add88ce0841bb..450a68166feb2 100644
--- a/clang/test/CXX/cpp/cpp.module/p2.cppm
+++ b/clang/test/CXX/cpp/cpp.module/p2.cppm
@@ -30,28 +30,28 @@
 //--- A.cppm
 module;
 #include "version.h"
-export module VERSION;  // expected-error {{the module name in a module declaration cannot contain an object-like macro 'VERSION', it's consists of one or more identifiers separated by '.'}}
+export module VERSION;  // expected-error {{the module name in a module declaration cannot contain an object-like macro 'VERSION'}}
 
 //--- B.cppm
 module;
 #include "version.h"
-export module A.B;      // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A', it's consists of one or more identifiers separated by '.'}} \
-                        // expected-error {{the module name in a module declaration cannot contain an object-like macro 'B', it's consists of one or more identifiers separated by '.'}}
+export module A.B;      // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
+                        // expected-error {{the module name in a module declaration cannot contain an object-like macro 'B'}}
 
 //--- C.cppm
 module;
 #include "version.h"
-export module A.FUNC_LIKE(foo):C;   // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A', it's consists of one or more identifiers separated by '.'}} \
+export module A.FUNC_LIKE(foo):C;   // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
                                     // expected-error {{unexpected '(' after the module name in a module declaration}} \
-                                    // expected-error {{the module name in a module partition declaration cannot contain an object-like macro 'C', it's consists of one or more identifiers separated by '.'}}
+                                    // expected-error {{the module name in a module partition declaration cannot contain an object-like macro 'C'}}
 
 //--- D.cppm
 module;
 #include "version.h"
-export module B.A.FUNC_LIKE(bar):C;   // expected-error {{the module name in a module declaration cannot contain an object-like macro 'B', it's consists of one or more identifiers separated by '.'}} \
-                                      // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A', it's consists of one or more identifiers separated by '.'}} \
+export module B.A.FUNC_LIKE(bar):C;   // expected-error {{the module name in a module declaration cannot contain an object-like macro 'B'}} \
+                                      // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
                                       // expected-error {{unexpected '(' after the module name in a module declaration}} \
-                                      // expected-error {{the module name in a module partition declaration cannot contain an object-like macro 'C', it's consists of one or more identifiers separated by '.'}}
+                                      // expected-error {{the module name in a module partition declaration cannot contain an object-like macro 'C'}}
 
 //--- E.cppm
 module;
@@ -68,23 +68,23 @@ export module a.FUNC_LIKE:c ATTRS; // OK, FUNC_LIKE would not be treated as a ma
 //--- G.cppm
 module;
 #include "version.h"
-export module A.FUNC_LIKE(B c:C ATTRS // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A', it's consists of one or more identifiers separated by '.'}} \
+export module A.FUNC_LIKE(B c:C ATTRS // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
                                       // expected-error {{unexpected '(' after the module name in a module declaration}} \
-                                      // expected-error {{the module name in a module partition declaration cannot contain an object-like macro 'C', it's consists of one or more identifiers separated by '.'}} \
+                                      // expected-error {{the module name in a module partition declaration cannot contain an object-like macro 'C'}} \
                                       // expected-error {{expected ';' after module name}}
 
 //--- H.cppm
 module;
 #include "version.h"
-export module A.FUNC_LIKE(B,). c:C ATTRS  // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A', it's consists of one or more identifiers separated by '.'}} \
+export module A.FUNC_LIKE(B,). c:C ATTRS  // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
                                           // expected-error {{unexpected '(' after the module name in a module declaration}} \
-                                          // expected-error {{the module name in a module partition declaration cannot contain an object-like macro 'C', it's consists of one or more identifiers separated by '.'}} \
+                                          // expected-error {{the module name in a module partition declaration cannot contain an object-like macro 'C'}} \
                                           // expected-error {{expected ';' after module name}}
 
 //--- I.cppm
 module;
 #include "version.h"
-export module A.FUNC_LIKE(B,) c:C ATTRS   // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A', it's consists of one or more identifiers separated by '.'}} \
+export module A.FUNC_LIKE(B,) c:C ATTRS   // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
                                           // expected-error {{unexpected '(' after the module name in a module declaration}} \
                                           // expected-error {{expected ';' after module name}} \
                                           // expected-error {{unknown type name 'c'}} \

>From 45d40a08d42489f635aad0d3de6ecbfedd48b7d1 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 30 May 2024 07:13:26 +0800
Subject: [PATCH 12/19] Skip unexpect tokens in parser

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/lib/Lex/Preprocessor.cpp        | 20 +-----------------
 clang/lib/Parse/Parser.cpp            | 11 +++++++++-
 clang/test/CXX/cpp/cpp.module/p2.cppm | 29 +++++++++------------------
 3 files changed, 21 insertions(+), 39 deletions(-)

diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index a943a22db3cd1..e9e93867de704 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -865,7 +865,7 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
 
   if ((II.isModulesDeclaration() || Identifier.is(tok::kw_module)) &&
       !InMacroArgs && !DisableMacroExpansion &&
-      (getLangOpts().Modules || getLangOpts().DebuggerSupport) &&
+      (getLangOpts().CPlusPlusModules || getLangOpts().DebuggerSupport) &&
       CurLexerCallback != CLK_CachingLexer) {
     ModuleDeclarationExpectsIdentifier = true;
     ModuleDeclarationLexingPartitionName = false;
@@ -1415,24 +1415,6 @@ bool Preprocessor::LexAfterModuleDecl(Token &Result) {
     ModuleDeclarationExpectsIdentifier = false;
     Diag(Result, diag::err_unxepected_paren_in_module_decl)
         << ModuleDeclarationLexingPartitionName;
-
-    // Skip until see one of module name separator '.', ':' or ';'.
-    Token Tok;
-    // We already have a '('.
-    unsigned NumParens = 1;
-    while (true) {
-      LexUnexpandedToken(Tok);
-      if (Tok.isOneOf(tok::eod, tok::eof, tok::semi, tok::period, tok::colon)) {
-        EnterTokens(Tok, /*DisableMacroExpansion=*/true);
-        break;
-      }
-      if (Tok.is(tok::l_paren))
-        NumParens++;
-      else if (Tok.is(tok::r_paren) && --NumParens == 0)
-        break;
-    }
-    CurLexerCallback = CLK_LexAfterModuleDecl;
-    return false;
   }
 
   return true;
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 4bdf522561f92..bfbac10ac9121 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -2693,8 +2693,17 @@ bool Parser::ParseModuleName(
     Path.push_back(std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()));
     ConsumeToken();
 
-    if (!TryConsumeToken(tok::period))
+    if (!TryConsumeToken(tok::period)) {
+      // [cpp.module]/p2: where the pp-tokens (if any) shall not begin with a (
+      // preprocessing token [...]
+      //
+      // We already diagnose in preprocessor and just skip to semicolon.
+      if (Tok.is(tok::l_paren)) {
+        SkipUntil(tok::semi, StopBeforeMatch);
+        return true;
+      }
       return false;
+    }
   }
 }
 
diff --git a/clang/test/CXX/cpp/cpp.module/p2.cppm b/clang/test/CXX/cpp/cpp.module/p2.cppm
index 450a68166feb2..966a88ccfa972 100644
--- a/clang/test/CXX/cpp/cpp.module/p2.cppm
+++ b/clang/test/CXX/cpp/cpp.module/p2.cppm
@@ -39,19 +39,17 @@ export module A.B;      // expected-error {{the module name in a module declarat
                         // expected-error {{the module name in a module declaration cannot contain an object-like macro 'B'}}
 
 //--- C.cppm
-module;
+module;                             // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
 #include "version.h"
 export module A.FUNC_LIKE(foo):C;   // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
-                                    // expected-error {{unexpected '(' after the module name in a module declaration}} \
-                                    // expected-error {{the module name in a module partition declaration cannot contain an object-like macro 'C'}}
+                                    // expected-error {{unexpected '(' after the module name in a module declaration}}
 
 //--- D.cppm
-module;
+module;                               // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
 #include "version.h"
 export module B.A.FUNC_LIKE(bar):C;   // expected-error {{the module name in a module declaration cannot contain an object-like macro 'B'}} \
                                       // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
-                                      // expected-error {{unexpected '(' after the module name in a module declaration}} \
-                                      // expected-error {{the module name in a module partition declaration cannot contain an object-like macro 'C'}}
+                                      // expected-error {{unexpected '(' after the module name in a module declaration}}
 
 //--- E.cppm
 module;
@@ -66,29 +64,22 @@ export module a.FUNC_LIKE:c ATTRS; // OK, FUNC_LIKE would not be treated as a ma
 // expected-no-diagnostics
 
 //--- G.cppm
-module;
+module;                               // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
 #include "version.h"
 export module A.FUNC_LIKE(B c:C ATTRS // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
-                                      // expected-error {{unexpected '(' after the module name in a module declaration}} \
-                                      // expected-error {{the module name in a module partition declaration cannot contain an object-like macro 'C'}} \
-                                      // expected-error {{expected ';' after module name}}
+                                      // expected-error {{unexpected '(' after the module name in a module declaration}}
 
 //--- H.cppm
-module;
+module;                                   // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
 #include "version.h"
 export module A.FUNC_LIKE(B,). c:C ATTRS  // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
-                                          // expected-error {{unexpected '(' after the module name in a module declaration}} \
-                                          // expected-error {{the module name in a module partition declaration cannot contain an object-like macro 'C'}} \
-                                          // expected-error {{expected ';' after module name}}
+                                          // expected-error {{unexpected '(' after the module name in a module declaration}}
 
 //--- I.cppm
-module;
+module;                                   // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
 #include "version.h"
 export module A.FUNC_LIKE(B,) c:C ATTRS   // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
-                                          // expected-error {{unexpected '(' after the module name in a module declaration}} \
-                                          // expected-error {{expected ';' after module name}} \
-                                          // expected-error {{unknown type name 'c'}} \
-                                          // expected-error {{expected unqualified-id}}
+                                          // expected-error {{unexpected '(' after the module name in a module declaration}}
 
 //--- J.cppm
 module;

>From 969014064289027e922f06df6e6643ed0fd3e11e Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Fri, 21 Jun 2024 01:00:04 +0800
Subject: [PATCH 13/19] Introduce annot_module_name token and refactor module
 name lexcial analysis

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/include/clang/Basic/IdentifierTable.h   |   2 +-
 clang/include/clang/Basic/TokenKinds.def      |   1 +
 clang/include/clang/Lex/Preprocessor.h        |   2 +
 clang/include/clang/Lex/Token.h               |  11 +
 .../lib/Frontend/PrintPreprocessedOutput.cpp  |  19 +-
 clang/lib/Lex/PPLexerChange.cpp               |   4 +-
 clang/lib/Lex/Preprocessor.cpp                | 278 ++++++++++++------
 clang/lib/Lex/TokenConcatenation.cpp          |   5 +
 clang/lib/Parse/ParseDecl.cpp                 |   8 +-
 clang/lib/Parse/Parser.cpp                    |  57 ++--
 10 files changed, 269 insertions(+), 118 deletions(-)

diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h
index cd04d9aa37e1d..858be8e209221 100644
--- a/clang/include/clang/Basic/IdentifierTable.h
+++ b/clang/include/clang/Basic/IdentifierTable.h
@@ -757,7 +757,7 @@ class IdentifierTable {
     if (Name == "import")
       II->setModulesImport(true);
 
-    if (Name.equals("module"))
+    if (Name == "module")
       II->setModulesDeclaration(true);
 
     return *II;
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 9c4b17465e18a..073d98128355d 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -991,6 +991,7 @@ PRAGMA_ANNOTATION(pragma_riscv)
 ANNOTATION(module_include)
 ANNOTATION(module_begin)
 ANNOTATION(module_end)
+ANNOTATION(module_name)
 
 // Annotation for a header_name token that has been looked up and transformed
 // into the name of a header unit.
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 394f7316b9679..32f57f0408079 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -1741,6 +1741,8 @@ class Preprocessor {
   /// Lex a token, forming a header-name token if possible.
   bool LexHeaderName(Token &Result, bool AllowMacroExpansion = true);
 
+  void LexModuleName(Token &Result, const Token FirstName,
+                     bool AllowMacroExpansion = true);
   bool LexAfterModuleImport(Token &Result);
   bool LexAfterModuleDecl(Token &Result);
   void CollectPpImportSuffix(SmallVectorImpl<Token> &Toks);
diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h
index 4f29fb7d11415..8e8382fdbf784 100644
--- a/clang/include/clang/Lex/Token.h
+++ b/clang/include/clang/Lex/Token.h
@@ -345,6 +345,17 @@ struct PragmaLoopHintInfo {
   Token Option;
   ArrayRef<Token> Toks;
 };
+
+/// Module/Partition name token sequance.
+///
+///     module-name:
+///           module-name-qualifier[opt] identifier
+///
+///     module-name-qualifier
+///           module-name-qualifier[opt] identifier .
+struct ModuleNameInfo {
+  ArrayRef<Token> Toks;
+};
 } // end namespace clang
 
 #endif // LLVM_CLANG_LEX_TOKEN_H
diff --git a/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp
index a26d2c3ab8582..7cd477cb519be 100644
--- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp
+++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp
@@ -676,9 +676,10 @@ void PrintPPOutputPPCallbacks::HandleWhitespaceBeforeTok(const Token &Tok,
   // These tokens are not expanded to anything and don't need whitespace before
   // them.
   if (Tok.is(tok::eof) ||
-      (Tok.isAnnotation() && !Tok.is(tok::annot_header_unit) &&
-       !Tok.is(tok::annot_module_begin) && !Tok.is(tok::annot_module_end) &&
-       !Tok.is(tok::annot_repl_input_end)))
+      (Tok.isAnnotation() && Tok.isNot(tok::annot_header_unit) &&
+       Tok.isNot(tok::annot_module_begin) && Tok.isNot(tok::annot_module_end) &&
+       Tok.isNot(tok::annot_module_name) &&
+       Tok.isNot(tok::annot_repl_input_end)))
     return;
 
   // EmittedDirectiveOnThisLine takes priority over RequireSameLine.
@@ -869,6 +870,18 @@ static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok,
       PP.Lex(Tok);
       IsStartOfLine = true;
       continue;
+    } else if (Tok.is(tok::annot_module_name)) {
+      auto *Info = static_cast<ModuleNameInfo *>(Tok.getAnnotationValue());
+      for (auto &NameTok : Info->Toks) {
+        if (NameTok.is(tok::identifier))
+          *Callbacks->OS << NameTok.getIdentifierInfo()->getName();
+        else if (NameTok.is(tok::period))
+          *Callbacks->OS << '.';
+        else
+          llvm_unreachable("unexpected token in a module name");
+      }
+      PP.Lex(Tok);
+      continue;
     } else if (Tok.is(tok::annot_header_unit)) {
       // This is a header-name that has been (effectively) converted into a
       // module-name.
diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp
index 8c0a9b09db4fe..e9b92947be80a 100644
--- a/clang/lib/Lex/PPLexerChange.cpp
+++ b/clang/lib/Lex/PPLexerChange.cpp
@@ -161,9 +161,7 @@ void Preprocessor::EnterMacro(Token &Tok, SourceLocation ILEnd,
   PushIncludeMacroStack();
   CurDirLookup = nullptr;
   CurTokenLexer = std::move(TokLexer);
-  if (CurLexerCallback != CLK_LexAfterModuleImport &&
-      CurLexerCallback != CLK_LexAfterModuleDecl)
-    CurLexerCallback = CLK_TokenLexer;
+  CurLexerCallback = CLK_TokenLexer;
 }
 
 /// EnterTokenStream - Add a "macro" context to the top of the include stack,
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 4abb7d3fa42c0..6d513936315d2 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -934,6 +934,23 @@ void Preprocessor::Lex(Token &Result) {
     case tok::period:
       ModuleDeclState.handlePeriod();
       break;
+    case tok::annot_module_name: {
+      auto *Info = static_cast<ModuleNameInfo *>(Result.getAnnotationValue());
+      for (const auto &Tok : Info->Toks) {
+        if (Tok.is(tok::identifier))
+          ModuleDeclState.handleIdentifier(Tok.getIdentifierInfo());
+        else if (Tok.is(tok::period))
+          ModuleDeclState.handlePeriod();
+        else
+          llvm_unreachable("Expects an identifier or a '.' in module name");
+      }
+      if (ModuleDeclState.isModuleCandidate())
+        break;
+      TrackGMFState.handleMisc();
+      StdCXXImportSeqState.handleMisc();
+      ModuleDeclState.handleMisc();
+      break;
+    }
     case tok::identifier:
       // Check "import" and "module" when there is no open bracket. The two
       // identifiers are not meaningful with open brackets.
@@ -1133,6 +1150,125 @@ void Preprocessor::CollectPpImportSuffix(SmallVectorImpl<Token> &Toks) {
   }
 }
 
+/// Lex a module name or a partition name.
+///
+///     module-name:
+///           module-name-qualifier[opt] identifier
+///
+///     partition-name: [C++20]
+///           : module-name-qualifier[opt] identifier
+///
+///     module-name-qualifier
+///           module-name-qualifier[opt] identifier .
+///
+/// \param Result The lex result, guaranteed to be tok::annot_module_name.
+///
+/// \param FirstName The fist identifier in the module name or partition name.
+/// Normally, lex a module name is only tried when an identifier is encountered.
+/// To avoid entering unnecessary Caching-Lex-Mode and putting the obtained
+/// identifier back into the token stream, we pass the first module name
+/// identifier as a parameter. We assumed that \p FirstName is an identifier.
+///
+/// \param AllowMacroExpansion Whether allow macro expansion in module name. In
+/// C++20 Modules, and since P3034R1, module declarations shouldn't be macros.
+///
+/// If there is only a module name, tok::annot_module_name is returned. If there
+/// both have a module-name and a partition name, three tokens will be returned,
+/// the first one is the module-name (if it exists), the second one is ':', and
+/// the third one is the partition-name.
+void Preprocessor::LexModuleName(Token &Result, const Token FirstName,
+                                 bool AllowMacroExpansion) {
+  assert(FirstName.is(tok::identifier) &&
+         "The first token must be an identifier");
+  bool ExpectsIdentifier = true, SkipLexFirstName = true;
+  SmallVector<Token, 8> ModuleName, PartitionName;
+  auto *CurrLexingName = &ModuleName;
+  Token ColonTok, Tok = FirstName;
+
+  while (true) {
+    // Since we already have the FirstName token, skip to lex a new token.
+    if (SkipLexFirstName) {
+      SkipLexFirstName = false;
+    } else {
+      if (AllowMacroExpansion)
+        Lex(Tok);
+      else
+        LexUnexpandedToken(Tok);
+    }
+
+    if (ExpectsIdentifier && Tok.is(tok::identifier)) {
+      auto *MI = getMacroInfo(Tok.getIdentifierInfo());
+      if (getLangOpts().CPlusPlusModules && !AllowMacroExpansion && MI &&
+          MI->isObjectLike()) {
+        Diag(Tok, diag::err_module_decl_cannot_be_macros)
+            << Tok.getLocation() << (CurrLexingName == &PartitionName)
+            << Tok.getIdentifierInfo();
+      }
+      CurrLexingName->push_back(Tok);
+      ExpectsIdentifier = false;
+      continue;
+    }
+
+    if (!ExpectsIdentifier && Tok.is(tok::period)) {
+      CurrLexingName->push_back(Tok);
+      ExpectsIdentifier = true;
+      continue;
+    }
+
+    // Module partition only allowed in C++20 Modules.
+    if (getLangOpts().CPlusPlusModules && !ExpectsIdentifier &&
+        Tok.is(tok::colon)) {
+      ColonTok = Tok;
+      CurrLexingName = &PartitionName;
+      ExpectsIdentifier = true;
+      continue;
+    }
+
+    // [cpp.module]/p2: where the pp-tokens (if any) shall not begin with a (
+    // preprocessing token [...]
+    //
+    // We only emit diagnostic in the preprocessor, and in the parser we skip
+    // invalid tokens and recover from errors.
+    if (getLangOpts().CPlusPlusModules && !ExpectsIdentifier &&
+        Tok.is(tok::l_paren))
+      Diag(Tok, diag::err_unxepected_paren_in_module_decl)
+          << (CurrLexingName == &PartitionName);
+    break;
+  }
+
+  auto CreateAnnotTok = [&](ArrayRef<Token> Names) {
+    Token NameTok;
+    NameTok.startToken();
+    NameTok.setKind(tok::annot_module_name);
+    NameTok.setLocation(Names.front().getLocation());
+    NameTok.setAnnotationEndLoc(Names.back().getLocation());
+    auto *Info = getPreprocessorAllocator().Allocate<ModuleNameInfo>();
+    Info->Toks = Names.copy(getPreprocessorAllocator());
+    NameTok.setAnnotationValue(static_cast<void *>(Info));
+    return NameTok;
+  };
+
+  // Return the first annot_module_name token and put the rest tokens into
+  // stream.
+  Result = CreateAnnotTok(ModuleName);
+  SmallVector<Token, 4> Toks;
+  if (CurrLexingName == &PartitionName) {
+    Toks.push_back(ColonTok);
+    if (!PartitionName.empty())
+      Toks.push_back(CreateAnnotTok(PartitionName));
+  }
+
+  // Put the last token back to stream, it's not a valid part of module name.
+  // We lexed it unexpanded but it might be a valid macro expansion
+  Tok.clearFlag(Token::DisableExpand);
+  Toks.push_back(Tok);
+
+  auto ToksCopy = std::make_unique<Token[]>(Toks.size());
+  std::copy(Toks.begin(), Toks.end(), ToksCopy.get());
+  EnterTokenStream(std::move(ToksCopy), Toks.size(),
+                   /*DisableMacroExpansion=*/false,
+                   /*IsReinject=*/false);
+}
 
 /// Lex a token following the 'import' contextual keyword.
 ///
@@ -1157,6 +1293,15 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
   // Figure out what kind of lexer we actually have.
   recomputeCurLexerKind();
 
+  // Allocate a holding buffer for a sequence of tokens and introduce it into
+  // the token stream.
+  auto EnterTokens = [this](ArrayRef<Token> Toks) {
+    auto ToksCopy = std::make_unique<Token[]>(Toks.size());
+    std::copy(Toks.begin(), Toks.end(), ToksCopy.get());
+    EnterTokenStream(std::move(ToksCopy), Toks.size(),
+                     /*DisableMacroExpansion*/ true, /*IsReinject*/ false);
+  };
+
   // Lex the next token. The header-name lexing rules are used at the start of
   // a pp-import.
   //
@@ -1166,28 +1311,10 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
   if (NamedModuleImportPath.empty() && getLangOpts().CPlusPlusModules) {
     if (LexHeaderName(Result))
       return true;
-
-    if (Result.is(tok::colon) && ModuleDeclState.isNamedModule()) {
-      std::string Name = ModuleDeclState.getPrimaryName().str();
-      Name += ":";
-      NamedModuleImportPath.push_back(
-          {getIdentifierInfo(Name), Result.getLocation()});
-      CurLexerCallback = CLK_LexAfterModuleImport;
-      return true;
-    }
   } else {
     Lex(Result);
   }
 
-  // Allocate a holding buffer for a sequence of tokens and introduce it into
-  // the token stream.
-  auto EnterTokens = [this](ArrayRef<Token> Toks) {
-    auto ToksCopy = std::make_unique<Token[]>(Toks.size());
-    std::copy(Toks.begin(), Toks.end(), ToksCopy.get());
-    EnterTokenStream(std::move(ToksCopy), Toks.size(),
-                     /*DisableMacroExpansion*/ true, /*IsReinject*/ false);
-  };
-
   bool ImportingHeader = Result.is(tok::header_name);
   // Check for a header-name.
   SmallVector<Token, 32> Suffix;
@@ -1260,28 +1387,53 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
     return false;
   }
 
-  // The token sequence
-  //
-  //   import identifier (. identifier)*
-  //
-  // indicates a module import directive. We already saw the 'import'
-  // contextual keyword, so now we're looking for the identifiers.
-  if (ModuleImportExpectsIdentifier && Result.getKind() == tok::identifier) {
-    // We expected to see an identifier here, and we did; continue handling
-    // identifiers.
-    NamedModuleImportPath.push_back(
-        std::make_pair(Result.getIdentifierInfo(), Result.getLocation()));
-    ModuleImportExpectsIdentifier = false;
-    CurLexerCallback = CLK_LexAfterModuleImport;
-    return true;
+  // Import a module partition only allowed in C++20 Modules.
+  // It's must in a named module fragment. When we meet an unexpect
+  // partition-name, return a ':' and partition-name tokens, but don't load the
+  // module.
+  bool UnexpectedPartitionName = false;
+  std::optional<Token> ImportPartitionColon;
+  // Meet a partition name.
+  if (Result.is(tok::colon)) {
+    ImportPartitionColon = Result;
+    // If next token is an identifier, try lex module names.
+    Token NextTok;
+    Lex(NextTok);
+
+    // If it's not an identifier, put it back and return ':'.
+    if (NextTok.isNot(tok::identifier)) {
+      EnterTokens({NextTok});
+      return true;
+    }
+
+    Result = NextTok;
+    if (NamedModuleImportPath.empty() && getLangOpts().CPlusPlusModules &&
+        ModuleDeclState.isNamedModule()) {
+      std::string Name = ModuleDeclState.getPrimaryName().str();
+      Name += ":";
+      NamedModuleImportPath.push_back(
+          {getIdentifierInfo(Name), Result.getLocation()});
+    } else {
+      UnexpectedPartitionName = true;
+    }
   }
 
-  // If we're expecting a '.' or a ';', and we got a '.', then wait until we
-  // see the next identifier. (We can also see a '[[' that begins an
-  // attribute-specifier-seq here under the Standard C++ Modules.)
-  if (!ModuleImportExpectsIdentifier && Result.getKind() == tok::period) {
-    ModuleImportExpectsIdentifier = true;
-    CurLexerCallback = CLK_LexAfterModuleImport;
+  if (Result.is(tok::identifier)) {
+    LexModuleName(Result, Result);
+    assert(Result.is(tok::annot_module_name) && "Expects a module name");
+    auto *Info = static_cast<ModuleNameInfo *>(Result.getAnnotationValue());
+    for (const auto &Tok : Info->Toks) {
+      if (Tok.is(tok::identifier))
+        NamedModuleImportPath.push_back(
+            std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()));
+    }
+
+    if (ImportPartitionColon.has_value()) {
+      EnterTokens(Result);
+      Result = *ImportPartitionColon;
+    }
+    if (!UnexpectedPartitionName)
+      CurLexerCallback = CLK_LexAfterModuleImport;
     return true;
   }
 
@@ -1368,56 +1520,14 @@ bool Preprocessor::LexAfterModuleDecl(Token &Result) {
   recomputeCurLexerKind();
   LexUnexpandedToken(Result);
 
-  auto EnterTokens = [this](ArrayRef<Token> Toks, bool DisableMacroExpansion) {
-    auto ToksCopy = std::make_unique<Token[]>(Toks.size());
-    std::copy(Toks.begin(), Toks.end(), ToksCopy.get());
-    EnterTokenStream(std::move(ToksCopy), Toks.size(), DisableMacroExpansion,
-                     /*IsReinject=*/false);
-  };
-
   // If we don't expect an identifier but got an identifier, it's not a part of
   // module name.
-  if (!ModuleDeclarationExpectsIdentifier && Result.is(tok::identifier)) {
-    EnterTokens(Result, /*DisableMacroExpansion=*/false);
+  if (Result.isNot(tok::identifier)) {
+    EnterToken(Result, /*IsReinject=*/false);
     return false;
   }
-
-  // The token sequence
-  //
-  // export[opt] module identifier (. identifier)*
-  //
-  // indicates a module directive. We already saw the 'module'
-  // contextual keyword, so now we're looking for the identifiers.
-  if (ModuleDeclarationExpectsIdentifier && Result.is(tok::identifier)) {
-    auto *MI = getMacroInfo(Result.getIdentifierInfo());
-    if (MI && MI->isObjectLike()) {
-      Diag(Result, diag::err_module_decl_cannot_be_macros)
-          << Result.getLocation() << ModuleDeclarationLexingPartitionName
-          << Result.getIdentifierInfo();
-    }
-    ModuleDeclarationExpectsIdentifier = false;
-    CurLexerCallback = CLK_LexAfterModuleDecl;
-    return true;
-  }
-
-  // If we're expecting a '.', a ':' or a ';', and we got a '.', then wait until
-  // we see the next identifier.
-  if (!ModuleDeclarationExpectsIdentifier &&
-      Result.isOneOf(tok::period, tok::colon)) {
-    ModuleDeclarationExpectsIdentifier = true;
-    ModuleDeclarationLexingPartitionName = Result.is(tok::colon);
-    CurLexerCallback = CLK_LexAfterModuleDecl;
-    return true;
-  }
-
-  // [cpp.module]/p2: where the pp-tokens (if any) shall not begin with a (
-  // preprocessing token [...]
-  if (!ModuleDeclarationExpectsIdentifier && Result.is(tok::l_paren)) {
-    ModuleDeclarationExpectsIdentifier = false;
-    Diag(Result, diag::err_unxepected_paren_in_module_decl)
-        << ModuleDeclarationLexingPartitionName;
-  }
-
+  LexModuleName(Result, Result, /*AllowMacroExpansion=*/false);
+  assert(Result.is(tok::annot_module_name) && "Expects a module name");
   return true;
 }
 
diff --git a/clang/lib/Lex/TokenConcatenation.cpp b/clang/lib/Lex/TokenConcatenation.cpp
index 1b3201bd805bf..51f887b75302c 100644
--- a/clang/lib/Lex/TokenConcatenation.cpp
+++ b/clang/lib/Lex/TokenConcatenation.cpp
@@ -160,6 +160,8 @@ static char GetFirstChar(const Preprocessor &PP, const Token &Tok) {
 bool TokenConcatenation::AvoidConcat(const Token &PrevPrevTok,
                                      const Token &PrevTok,
                                      const Token &Tok) const {
+  if (PrevTok.is(tok::annot_module_name))
+    return false;
   // Conservatively assume that every annotation token that has a printable
   // form requires whitespace.
   if (PrevTok.isAnnotation())
@@ -190,6 +192,9 @@ bool TokenConcatenation::AvoidConcat(const Token &PrevPrevTok,
       return true;
     ConcatInfo &= ~aci_avoid_equal;
   }
+
+  if (Tok.is(tok::annot_module_name))
+    return true;
   if (Tok.isAnnotation()) {
     // Modules annotation can show up when generated automatically for includes.
     assert(Tok.isOneOf(tok::annot_module_include, tok::annot_module_begin,
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index c528917437332..645cc739bef57 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3922,7 +3922,13 @@ void Parser::ParseDeclarationSpecifiers(
 
       // We're done with the declaration-specifiers.
       goto DoneWithDeclSpec;
-
+    case tok::annot_module_name: {
+      PP.EnterTokenStream(
+          static_cast<ModuleNameInfo *>(Tok.getAnnotationValue())->Toks,
+          /*DisableMacroExpansion=*/true, /*IsReinject=*/false);
+      ConsumeAnyToken();
+      [[fallthrough]];
+    }
       // typedef-name
     case tok::kw___super:
     case tok::kw_decltype:
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index e79674c1faef8..0b726e217b328 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -2681,36 +2681,41 @@ bool Parser::ParseModuleName(
     SourceLocation UseLoc,
     SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path,
     bool IsImport) {
-  // Parse the module path.
-  while (true) {
-    if (!Tok.is(tok::identifier)) {
-      if (Tok.is(tok::code_completion)) {
-        cutOffParsing();
-        Actions.CodeCompletion().CodeCompleteModuleImport(UseLoc, Path);
-        return true;
-      }
-
-      Diag(Tok, diag::err_module_expected_ident) << IsImport;
-      SkipUntil(tok::semi, StopBeforeMatch);
+  auto ExpectIdentifier = [&](Token T) {
+    if (Tok.is(tok::code_completion)) {
+      cutOffParsing();
+      Actions.CodeCompletion().CodeCompleteModuleImport(UseLoc, Path);
       return true;
     }
+    Diag(T, diag::err_module_expected_ident) << IsImport;
+    SkipUntil(tok::semi, StopBeforeMatch);
+    return true;
+  };
+  if (Tok.isNot(tok::annot_module_name))
+    return ExpectIdentifier(Tok);
+  auto *Info = static_cast<ModuleNameInfo *>(Tok.getAnnotationValue());
+  ConsumeAnnotationToken();
 
-    // Record this part of the module path.
-    Path.push_back(std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()));
-    ConsumeToken();
-
-    if (!TryConsumeToken(tok::period)) {
-      // [cpp.module]/p2: where the pp-tokens (if any) shall not begin with a (
-      // preprocessing token [...]
-      //
-      // We already diagnose in preprocessor and just skip to semicolon.
-      if (Tok.is(tok::l_paren)) {
-        SkipUntil(tok::semi, StopBeforeMatch);
-        return true;
-      }
-      return false;
-    }
+  // Parse the module path.
+  for (const auto &T : Info->Toks) {
+    if (T.is(tok::identifier))
+      Path.push_back(std::make_pair(T.getIdentifierInfo(), T.getLocation()));
+  }
+
+  // Invoke the code completion action if the last token in module name sequence
+  // is '.' but not an identifier.
+  if (Info->Toks.back().isNot(tok::identifier))
+    return ExpectIdentifier(Info->Toks.back());
+
+  // [cpp.module]/p2: where the pp-tokens (if any) shall not begin with a (
+  // preprocessing token [...]
+  //
+  // Skip unitl ';' to recovery.
+  if (getLangOpts().CPlusPlusModules && Tok.is(tok::l_paren)) {
+    SkipUntil(tok::semi, StopBeforeMatch);
+    return true;
   }
+  return false;
 }
 
 /// Try recover parser when module annotation appears where it must not

>From bb4c89f645403e08405d8bb6c1f2194b1913281a Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Sun, 23 Jun 2024 01:32:28 +0800
Subject: [PATCH 14/19] Fix clang-repl lit issue

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/lib/Lex/PPLexerChange.cpp | 6 ++++--
 clang/lib/Lex/Preprocessor.cpp  | 2 ++
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp
index e9b92947be80a..c3a903917e9ce 100644
--- a/clang/lib/Lex/PPLexerChange.cpp
+++ b/clang/lib/Lex/PPLexerChange.cpp
@@ -122,7 +122,8 @@ void Preprocessor::EnterSourceFileWithLexer(Lexer *TheLexer,
   CurPPLexer = TheLexer;
   CurDirLookup = CurDir;
   CurLexerSubmodule = nullptr;
-  if (CurLexerCallback != CLK_LexAfterModuleImport)
+  if (CurLexerCallback != CLK_LexAfterModuleImport &&
+      CurLexerCallback != CLK_LexAfterModuleDecl)
     CurLexerCallback = TheLexer->isDependencyDirectivesLexer()
                            ? CLK_DependencyDirectivesLexer
                            : CLK_Lexer;
@@ -215,7 +216,8 @@ void Preprocessor::EnterTokenStream(const Token *Toks, unsigned NumToks,
   PushIncludeMacroStack();
   CurDirLookup = nullptr;
   CurTokenLexer = std::move(TokLexer);
-  if (CurLexerCallback != CLK_LexAfterModuleImport)
+  if (CurLexerCallback != CLK_LexAfterModuleImport &&
+      CurLexerCallback != CLK_LexAfterModuleDecl)
     CurLexerCallback = CLK_TokenLexer;
 }
 
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 6d513936315d2..bf9da4b53ebd7 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -35,6 +35,7 @@
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TokenKinds.h"
 #include "clang/Lex/CodeCompletionHandler.h"
 #include "clang/Lex/ExternalPreprocessorSource.h"
 #include "clang/Lex/HeaderSearch.h"
@@ -914,6 +915,7 @@ void Preprocessor::Lex(Token &Result) {
     // This token is injected to represent the translation of '#include "a.h"'
     // into "import a.h;". Mimic the notional ';'.
     case tok::annot_module_include:
+    case tok::annot_repl_input_end:
     case tok::semi:
       TrackGMFState.handleSemi();
       StdCXXImportSeqState.handleSemi();

>From e22a353b5dfd9164e51fa3630ae89c7814226e20 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Sun, 23 Jun 2024 13:48:28 +0800
Subject: [PATCH 15/19] Remove unused vars

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/include/clang/Lex/Preprocessor.h | 11 -----------
 clang/lib/Lex/Preprocessor.cpp         |  9 ++-------
 2 files changed, 2 insertions(+), 18 deletions(-)

diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 0fe27b543f5f6..1dc730b6d0589 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -615,17 +615,6 @@ class Preprocessor {
 
   ModuleDeclSeq ModuleDeclState;
 
-  /// Whether the module import expects an identifier next. Otherwise,
-  /// it expects a '.' or ';'.
-  bool ModuleImportExpectsIdentifier = false;
-
-  /// Whether the module declaration expects an identifier next. Otherwise,
-  /// it expects a '.' or ';'.
-  bool ModuleDeclarationExpectsIdentifier = false;
-
-  /// Whether lexing the name of module partition declaration.
-  bool ModuleDeclarationLexingPartitionName = false;
-
   /// The identifier and source location of the currently-active
   /// \#pragma clang arc_cf_code_audited begin.
   std::pair<IdentifierInfo *, SourceLocation> PragmaARCCFCodeAuditedInfo;
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index bf9da4b53ebd7..96653a8797b48 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -861,7 +861,6 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
     ModuleImportLoc = Identifier.getLocation();
     NamedModuleImportPath.clear();
     IsAtImport = true;
-    ModuleImportExpectsIdentifier = true;
     CurLexerCallback = CLK_LexAfterModuleImport;
   }
 
@@ -869,8 +868,6 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
       !InMacroArgs && !DisableMacroExpansion &&
       (getLangOpts().CPlusPlusModules || getLangOpts().DebuggerSupport) &&
       CurLexerCallback != CLK_CachingLexer) {
-    ModuleDeclarationExpectsIdentifier = true;
-    ModuleDeclarationLexingPartitionName = false;
     CurLexerCallback = CLK_LexAfterModuleDecl;
   }
   return true;
@@ -964,15 +961,13 @@ void Preprocessor::Lex(Token &Result) {
             ModuleImportLoc = Result.getLocation();
             NamedModuleImportPath.clear();
             IsAtImport = false;
-            ModuleImportExpectsIdentifier = true;
             CurLexerCallback = CLK_LexAfterModuleImport;
           }
           break;
-        } else if (Result.getIdentifierInfo() == getIdentifierInfo("module")) {
+        }
+        if (Result.getIdentifierInfo()->isModulesDeclaration()) {
           TrackGMFState.handleModule(StdCXXImportSeqState.afterTopLevelSeq());
           ModuleDeclState.handleModule();
-          ModuleDeclarationExpectsIdentifier = true;
-          ModuleDeclarationLexingPartitionName = false;
           CurLexerCallback = CLK_LexAfterModuleDecl;
           break;
         }

>From fde07d724c6d058a9690398cd8b01bac504bfc4d Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Fri, 12 Jul 2024 08:10:48 -0400
Subject: [PATCH 16/19] Update clang/include/clang/Basic/IdentifierTable.h

Co-authored-by: cor3ntin <corentinjabot at gmail.com>
---
 clang/include/clang/Basic/IdentifierTable.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h
index 858be8e209221..89a2faaec0ee2 100644
--- a/clang/include/clang/Basic/IdentifierTable.h
+++ b/clang/include/clang/Basic/IdentifierTable.h
@@ -757,7 +757,7 @@ class IdentifierTable {
     if (Name == "import")
       II->setModulesImport(true);
 
-    if (Name == "module")
+   else if (Name == "module")
       II->setModulesDeclaration(true);
 
     return *II;

>From 6eb51ee06eabd607059557dec473feb09e3f4d3d Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 11 Jul 2024 21:40:32 +0800
Subject: [PATCH 17/19] Address review comments

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/include/clang/Basic/IdentifierTable.h | 3 +--
 clang/include/clang/Lex/Preprocessor.h      | 2 +-
 clang/lib/Lex/Preprocessor.cpp              | 1 -
 3 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h
index 89a2faaec0ee2..f40f74d0355ad 100644
--- a/clang/include/clang/Basic/IdentifierTable.h
+++ b/clang/include/clang/Basic/IdentifierTable.h
@@ -756,8 +756,7 @@ class IdentifierTable {
     // If this is the 'import' contextual keyword, mark it as such.
     if (Name == "import")
       II->setModulesImport(true);
-
-   else if (Name == "module")
+    else if (Name == "module")
       II->setModulesDeclaration(true);
 
     return *II;
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 1dc730b6d0589..ae2862f699e82 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -1742,7 +1742,7 @@ class Preprocessor {
 
   void LexModuleName(Token &Result, const Token FirstName,
                      bool AllowMacroExpansion = true);
-  
+
   /// Lex the parameters for an #embed directive, returns nullopt on error.
   std::optional<LexEmbedParametersResult> LexEmbedParameters(Token &Current,
                                                              bool ForHasEmbed);
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 96653a8797b48..29328eacf451c 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -35,7 +35,6 @@
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TargetInfo.h"
-#include "clang/Basic/TokenKinds.h"
 #include "clang/Lex/CodeCompletionHandler.h"
 #include "clang/Lex/ExternalPreprocessorSource.h"
 #include "clang/Lex/HeaderSearch.h"

>From 00d5aa9fb23fc650bf09ffb0b92a5baf384285b8 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Sat, 13 Jul 2024 22:33:28 +0800
Subject: [PATCH 18/19] Add a comment to explain changes in
 TokenConcatenation::AvoidConcat

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/lib/Lex/TokenConcatenation.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/clang/lib/Lex/TokenConcatenation.cpp b/clang/lib/Lex/TokenConcatenation.cpp
index ff0e2792ac793..cdb636923b9e9 100644
--- a/clang/lib/Lex/TokenConcatenation.cpp
+++ b/clang/lib/Lex/TokenConcatenation.cpp
@@ -160,6 +160,11 @@ static char GetFirstChar(const Preprocessor &PP, const Token &Tok) {
 bool TokenConcatenation::AvoidConcat(const Token &PrevPrevTok,
                                      const Token &PrevTok,
                                      const Token &Tok) const {
+  // If previous token is a module name, we need avoid concat it with current
+  // token, otherwise, there will has an extra space between 'M' and ';' for the
+  // following code:
+  //
+  // import M;
   if (PrevTok.is(tok::annot_module_name))
     return false;
   // Conservatively assume that every annotation token that has a printable

>From f21af2753200e188fcf9279a377a496a7f759e30 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 18 Jul 2024 01:38:31 +0800
Subject: [PATCH 19/19] [Clang] Lexing the partition along with the module name

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/include/clang/Lex/Preprocessor.h        |  43 +-
 clang/include/clang/Lex/Token.h               |  14 +-
 clang/include/clang/Parse/Parser.h            |   1 +
 .../lib/Frontend/PrintPreprocessedOutput.cpp  |   9 +-
 clang/lib/Lex/Preprocessor.cpp                | 438 +++++++++---------
 clang/lib/Parse/ParseDecl.cpp                 |   2 +-
 clang/lib/Parse/Parser.cpp                    |  79 ++--
 7 files changed, 307 insertions(+), 279 deletions(-)

diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index ae2862f699e82..d6fdb92bab3d3 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -1740,8 +1740,8 @@ class Preprocessor {
   /// Lex a token, forming a header-name token if possible.
   bool LexHeaderName(Token &Result, bool AllowMacroExpansion = true);
 
-  void LexModuleName(Token &Result, const Token FirstName,
-                     bool AllowMacroExpansion = true);
+  /// Lex a module name or a partition name.
+  bool LexModuleName(Token &Result, bool IsImport);
 
   /// Lex the parameters for an #embed directive, returns nullopt on error.
   std::optional<LexEmbedParametersResult> LexEmbedParameters(Token &Current,
@@ -3072,6 +3072,45 @@ struct EmbedAnnotationData {
 /// Registry of pragma handlers added by plugins
 using PragmaHandlerRegistry = llvm::Registry<PragmaHandler>;
 
+/// Module/Partition name token sequance.
+///
+///     module-name:
+///           module-name-qualifier[opt] identifier
+///
+///     module-name-qualifier
+///           module-name-qualifier[opt] identifier .
+class ModuleNameInfo {
+  friend class Preprocessor;
+  ArrayRef<Token> ModuleName;
+  ArrayRef<Token> PartitionName;
+
+  ModuleNameInfo(ArrayRef<Token> Module, ArrayRef<Token> Partition)
+      : ModuleName(Module), PartitionName(Partition) {}
+
+public:
+  ArrayRef<Token> getTokens() const {
+    if (ModuleName.empty())
+      return PartitionName;
+    if (PartitionName.empty())
+      return ModuleName;
+    return ArrayRef(ModuleName.begin(), PartitionName.end());
+  }
+  bool hasModuleName() const { return !ModuleName.empty(); }
+  bool hasPartitionName() const { return !PartitionName.empty(); }
+  ArrayRef<Token> getModuleName() const { return ModuleName; }
+  ArrayRef<Token> getPartitionName() const { return PartitionName; }
+  Token getColonToken() const {
+    assert(hasPartitionName() && "Do not have a partition name");
+    return getPartitionName().front();
+  }
+  std::string getFlatName() const;
+  void getModuleIdPath(
+      SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path) const;
+  static void getModuleIdPath(
+      ArrayRef<Token> ModuleName,
+      SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path);
+};
+
 } // namespace clang
 
 #endif // LLVM_CLANG_LEX_PREPROCESSOR_H
diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h
index 8e8382fdbf784..2be3ad39529f0 100644
--- a/clang/include/clang/Lex/Token.h
+++ b/clang/include/clang/Lex/Token.h
@@ -235,6 +235,9 @@ class Token {
     assert(isAnnotation() && "Used AnnotVal on non-annotation token");
     return PtrData;
   }
+  template <class T> T getAnnotationValueAs() const {
+    return static_cast<T>(getAnnotationValue());
+  }
   void setAnnotationValue(void *val) {
     assert(isAnnotation() && "Used AnnotVal on non-annotation token");
     PtrData = val;
@@ -345,17 +348,6 @@ struct PragmaLoopHintInfo {
   Token Option;
   ArrayRef<Token> Toks;
 };
-
-/// Module/Partition name token sequance.
-///
-///     module-name:
-///           module-name-qualifier[opt] identifier
-///
-///     module-name-qualifier
-///           module-name-qualifier[opt] identifier .
-struct ModuleNameInfo {
-  ArrayRef<Token> Toks;
-};
 } // end namespace clang
 
 #endif // LLVM_CLANG_LEX_TOKEN_H
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 6880fa4bb0b03..d1e40f644dc66 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3874,6 +3874,7 @@ class Parser : public CodeCompletionHandler {
 
   bool ParseModuleName(
       SourceLocation UseLoc,
+      ArrayRef<Token> ModuleName,
       SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path,
       bool IsImport);
 
diff --git a/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp
index d869750f262b9..1fff88ccf0405 100644
--- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp
+++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp
@@ -954,14 +954,7 @@ static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok,
       continue;
     } else if (Tok.is(tok::annot_module_name)) {
       auto *Info = static_cast<ModuleNameInfo *>(Tok.getAnnotationValue());
-      for (auto &NameTok : Info->Toks) {
-        if (NameTok.is(tok::identifier))
-          *Callbacks->OS << NameTok.getIdentifierInfo()->getName();
-        else if (NameTok.is(tok::period))
-          *Callbacks->OS << '.';
-        else
-          llvm_unreachable("unexpected token in a module name");
-      }
+      *Callbacks->OS << Info->getFlatName();
       PP.Lex(Tok);
       continue;
     } else if (Tok.is(tok::annot_header_unit)) {
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 29328eacf451c..bb5517ea173e9 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -934,13 +934,20 @@ void Preprocessor::Lex(Token &Result) {
       break;
     case tok::annot_module_name: {
       auto *Info = static_cast<ModuleNameInfo *>(Result.getAnnotationValue());
-      for (const auto &Tok : Info->Toks) {
-        if (Tok.is(tok::identifier))
+      for (const auto &Tok : Info->getTokens()) {
+        switch (Tok.getKind()) {
+        case tok::identifier:
           ModuleDeclState.handleIdentifier(Tok.getIdentifierInfo());
-        else if (Tok.is(tok::period))
+          break;
+        case tok::period:
           ModuleDeclState.handlePeriod();
-        else
-          llvm_unreachable("Expects an identifier or a '.' in module name");
+          break;
+        case tok::colon:
+          ModuleDeclState.handleColon();
+          break;
+        default:
+          llvm_unreachable("Unexpected token in module name");
+        }
       }
       if (ModuleDeclState.isModuleCandidate())
         break;
@@ -1146,6 +1153,50 @@ void Preprocessor::CollectPpImportSuffix(SmallVectorImpl<Token> &Toks) {
   }
 }
 
+void
+buildModuleIdPath(ArrayRef<Token> Toks, SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path) {
+  for (const auto &Tok : Toks) {
+    if (Tok.is(tok::identifier))
+      Path.push_back(
+          std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()));
+  }
+}
+
+std::string ModuleNameInfo::getFlatName() const {
+  std::string FlatModuleName;
+  for (auto &Tok : getTokens()) {
+    switch (Tok.getKind()) {
+    case tok::identifier:
+      FlatModuleName += Tok.getIdentifierInfo()->getName();
+      break;
+    case tok::period:
+      FlatModuleName += '.';
+      break;
+    case tok::colon:
+      FlatModuleName += ':';
+      break;
+    default:
+      llvm_unreachable("Unexpected token in module name");
+    }
+  }
+  return FlatModuleName;
+}
+
+void ModuleNameInfo::getModuleIdPath(
+    SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path) const {
+  return getModuleIdPath(getTokens(), Path);
+}
+
+void ModuleNameInfo::getModuleIdPath(
+    ArrayRef<Token> ModuleName,
+    SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path) {
+  for (const auto &Tok : ModuleName) {
+    if (Tok.is(tok::identifier))
+      Path.push_back(
+          std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()));
+  }
+}
+
 /// Lex a module name or a partition name.
 ///
 ///     module-name:
@@ -1156,67 +1207,59 @@ void Preprocessor::CollectPpImportSuffix(SmallVectorImpl<Token> &Toks) {
 ///
 ///     module-name-qualifier
 ///           module-name-qualifier[opt] identifier .
-///
-/// \param Result The lex result, guaranteed to be tok::annot_module_name.
-///
-/// \param FirstName The fist identifier in the module name or partition name.
-/// Normally, lex a module name is only tried when an identifier is encountered.
-/// To avoid entering unnecessary Caching-Lex-Mode and putting the obtained
-/// identifier back into the token stream, we pass the first module name
-/// identifier as a parameter. We assumed that \p FirstName is an identifier.
-///
-/// \param AllowMacroExpansion Whether allow macro expansion in module name. In
-/// C++20 Modules, and since P3034R1, module declarations shouldn't be macros.
-///
-/// If there is only a module name, tok::annot_module_name is returned. If there
-/// both have a module-name and a partition name, three tokens will be returned,
-/// the first one is the module-name (if it exists), the second one is ':', and
-/// the third one is the partition-name.
-void Preprocessor::LexModuleName(Token &Result, const Token FirstName,
-                                 bool AllowMacroExpansion) {
-  assert(FirstName.is(tok::identifier) &&
-         "The first token must be an identifier");
-  bool ExpectsIdentifier = true, SkipLexFirstName = true;
-  SmallVector<Token, 8> ModuleName, PartitionName;
-  auto *CurrLexingName = &ModuleName;
-  Token ColonTok, Tok = FirstName;
-
-  while (true) {
-    // Since we already have the FirstName token, skip to lex a new token.
-    if (SkipLexFirstName) {
-      SkipLexFirstName = false;
-    } else {
-      if (AllowMacroExpansion)
+bool Preprocessor::LexModuleName(Token &Result, bool IsImport) {
+  bool ExpectsIdentifier = true, IsLexingPartition = false;
+  SmallVector<Token, 8> ModuleName;
+  std::optional<unsigned> ColonTokIndex;
+  auto LexNextToken = [&](Token &Tok) {
+    if (IsImport)
         Lex(Tok);
       else
         LexUnexpandedToken(Tok);
-    }
+  };
 
-    if (ExpectsIdentifier && Tok.is(tok::identifier)) {
-      auto *MI = getMacroInfo(Tok.getIdentifierInfo());
-      if (getLangOpts().CPlusPlusModules && !AllowMacroExpansion && MI &&
+  while (true) {
+    LexNextToken(Result);
+    if (ExpectsIdentifier && Result.is(tok::identifier)) {
+      auto *MI = getMacroInfo(Result.getIdentifierInfo());
+      if (getLangOpts().CPlusPlusModules && !IsImport && MI &&
           MI->isObjectLike()) {
-        Diag(Tok, diag::err_module_decl_cannot_be_macros)
-            << Tok.getLocation() << (CurrLexingName == &PartitionName)
-            << Tok.getIdentifierInfo();
+        Diag(Result, diag::err_module_decl_cannot_be_macros)
+            << Result.getLocation() << IsLexingPartition
+            << Result.getIdentifierInfo();
       }
-      CurrLexingName->push_back(Tok);
+      ModuleName.push_back(Result);
       ExpectsIdentifier = false;
       continue;
     }
 
-    if (!ExpectsIdentifier && Tok.is(tok::period)) {
-      CurrLexingName->push_back(Tok);
+    if (!ExpectsIdentifier && Result.is(tok::period)) {
+      ModuleName.push_back(Result);
       ExpectsIdentifier = true;
       continue;
     }
 
     // Module partition only allowed in C++20 Modules.
-    if (getLangOpts().CPlusPlusModules && !ExpectsIdentifier &&
-        Tok.is(tok::colon)) {
-      ColonTok = Tok;
-      CurrLexingName = &PartitionName;
-      ExpectsIdentifier = true;
+    if (getLangOpts().CPlusPlusModules && Result.is(tok::colon)) {
+      // Handle the form like: import :P;
+      // If the token after ':' is not an identifier, this is a invalid module
+      // name.
+      if (ModuleName.empty()) {
+        Token Tmp;
+        LexNextToken(Tmp);
+        EnterToken(Tmp, /*IsReiject=*/false);
+        // A private-module-fragment, module :private;
+        if (!IsImport && Tmp.is(tok::kw_private))
+          return true;
+        // import :N;
+        if (IsImport && Tmp.isNot(tok::identifier))
+          return false;
+      } else if (!ExpectsIdentifier) {
+        ExpectsIdentifier = true;
+      }
+      IsLexingPartition = true;
+      ColonTokIndex = ModuleName.size();
+      ModuleName.push_back(Result);
       continue;
     }
 
@@ -1226,44 +1269,38 @@ void Preprocessor::LexModuleName(Token &Result, const Token FirstName,
     // We only emit diagnostic in the preprocessor, and in the parser we skip
     // invalid tokens and recover from errors.
     if (getLangOpts().CPlusPlusModules && !ExpectsIdentifier &&
-        Tok.is(tok::l_paren))
-      Diag(Tok, diag::err_unxepected_paren_in_module_decl)
-          << (CurrLexingName == &PartitionName);
+        Result.is(tok::l_paren))
+      Diag(Result, diag::err_unxepected_paren_in_module_decl) << IsLexingPartition;
     break;
   }
 
-  auto CreateAnnotTok = [&](ArrayRef<Token> Names) {
-    Token NameTok;
-    NameTok.startToken();
-    NameTok.setKind(tok::annot_module_name);
-    NameTok.setLocation(Names.front().getLocation());
-    NameTok.setAnnotationEndLoc(Names.back().getLocation());
-    auto *Info = getPreprocessorAllocator().Allocate<ModuleNameInfo>();
-    Info->Toks = Names.copy(getPreprocessorAllocator());
-    NameTok.setAnnotationValue(static_cast<void *>(Info));
-    return NameTok;
-  };
-
-  // Return the first annot_module_name token and put the rest tokens into
-  // stream.
-  Result = CreateAnnotTok(ModuleName);
-  SmallVector<Token, 4> Toks;
-  if (CurrLexingName == &PartitionName) {
-    Toks.push_back(ColonTok);
-    if (!PartitionName.empty())
-      Toks.push_back(CreateAnnotTok(PartitionName));
-  }
-
   // Put the last token back to stream, it's not a valid part of module name.
   // We lexed it unexpanded but it might be a valid macro expansion
-  Tok.clearFlag(Token::DisableExpand);
-  Toks.push_back(Tok);
-
-  auto ToksCopy = std::make_unique<Token[]>(Toks.size());
-  std::copy(Toks.begin(), Toks.end(), ToksCopy.get());
-  EnterTokenStream(std::move(ToksCopy), Toks.size(),
+  Result.clearFlag(Token::DisableExpand);
+  auto ToksCopy = std::make_unique<Token[]>(1);
+  *ToksCopy.get() = Result;
+  EnterTokenStream(std::move(ToksCopy), 1,
                    /*DisableMacroExpansion=*/false,
                    /*IsReinject=*/false);
+
+  if (ModuleName.empty())
+    return false;
+  Result.startToken();
+  Result.setKind(tok::annot_module_name);
+  Result.setLocation(ModuleName.front().getLocation());
+  Result.setAnnotationEndLoc(ModuleName.back().getLocation());
+  auto AnnotToks = ArrayRef(ModuleName).copy(getPreprocessorAllocator());
+  ArrayRef<Token> ModuleNameToks, PartitionNameToks;
+  if (ColonTokIndex.has_value()) {
+    ModuleNameToks = ArrayRef(AnnotToks.begin(), AnnotToks.begin() + *ColonTokIndex);
+    PartitionNameToks = ArrayRef(AnnotToks.begin() + *ColonTokIndex, AnnotToks.end());
+  } else {
+    ModuleNameToks = AnnotToks;
+  }
+  ModuleNameInfo *Info = new (getPreprocessorAllocator())
+      ModuleNameInfo(ModuleNameToks, PartitionNameToks);
+  Result.setAnnotationValue(static_cast<void *>(Info));
+  return true;
 }
 
 /// Lex a token following the 'import' contextual keyword.
@@ -1298,6 +1335,8 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
                      /*DisableMacroExpansion*/ true, /*IsReinject*/ false);
   };
 
+  SmallVector<Token, 32> Suffix;
+
   // Lex the next token. The header-name lexing rules are used at the start of
   // a pp-import.
   //
@@ -1307,130 +1346,109 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
   if (NamedModuleImportPath.empty() && getLangOpts().CPlusPlusModules) {
     if (LexHeaderName(Result))
       return true;
-  } else {
-    Lex(Result);
-  }
 
-  bool ImportingHeader = Result.is(tok::header_name);
-  // Check for a header-name.
-  SmallVector<Token, 32> Suffix;
-  if (ImportingHeader) {
-    // Enter the header-name token into the token stream; a Lex action cannot
-    // both return a token and cache tokens (doing so would corrupt the token
-    // cache if the call to Lex comes from CachingLex / PeekAhead).
-    Suffix.push_back(Result);
-
-    // Consume the pp-import-suffix and expand any macros in it now. We'll add
-    // it back into the token stream later.
-    CollectPpImportSuffix(Suffix);
-    if (Suffix.back().isNot(tok::semi)) {
-      // This is not a pp-import after all.
-      EnterTokens(Suffix);
-      return false;
-    }
+    // Check for a header-name.
+    if (Result.is(tok::header_name)) {
+      // Enter the header-name token into the token stream; a Lex action cannot
+      // both return a token and cache tokens (doing so would corrupt the token
+      // cache if the call to Lex comes from CachingLex / PeekAhead).
+      Suffix.push_back(Result);
+
+      // Consume the pp-import-suffix and expand any macros in it now. We'll add
+      // it back into the token stream later.
+      CollectPpImportSuffix(Suffix);
+      if (Suffix.back().isNot(tok::semi)) {
+        // This is not a pp-import after all.
+        EnterTokens(Suffix);
+        return false;
+      }
 
-    // C++2a [cpp.module]p1:
-    //   The ';' preprocessing-token terminating a pp-import shall not have
-    //   been produced by macro replacement.
-    SourceLocation SemiLoc = Suffix.back().getLocation();
-    if (SemiLoc.isMacroID())
-      Diag(SemiLoc, diag::err_header_import_semi_in_macro);
-
-    // Reconstitute the import token.
-    Token ImportTok;
-    ImportTok.startToken();
-    ImportTok.setKind(tok::kw_import);
-    ImportTok.setLocation(ModuleImportLoc);
-    ImportTok.setIdentifierInfo(getIdentifierInfo("import"));
-    ImportTok.setLength(6);
-
-    auto Action = HandleHeaderIncludeOrImport(
-        /*HashLoc*/ SourceLocation(), ImportTok, Suffix.front(), SemiLoc);
-    switch (Action.Kind) {
-    case ImportAction::None:
-      break;
+      // C++2a [cpp.module]p1:
+      //   The ';' preprocessing-token terminating a pp-import shall not have
+      //   been produced by macro replacement.
+      SourceLocation SemiLoc = Suffix.back().getLocation();
+      if (SemiLoc.isMacroID())
+        Diag(SemiLoc, diag::err_header_import_semi_in_macro);
+
+      // Reconstitute the import token.
+      Token ImportTok;
+      ImportTok.startToken();
+      ImportTok.setKind(tok::kw_import);
+      ImportTok.setLocation(ModuleImportLoc);
+      ImportTok.setIdentifierInfo(getIdentifierInfo("import"));
+      ImportTok.setLength(6);
+
+      auto Action = HandleHeaderIncludeOrImport(
+          /*HashLoc*/ SourceLocation(), ImportTok, Suffix.front(), SemiLoc);
+      switch (Action.Kind) {
+      case ImportAction::None:
+        break;
 
-    case ImportAction::ModuleBegin:
-      // Let the parser know we're textually entering the module.
-      Suffix.emplace_back();
-      Suffix.back().startToken();
-      Suffix.back().setKind(tok::annot_module_begin);
-      Suffix.back().setLocation(SemiLoc);
-      Suffix.back().setAnnotationEndLoc(SemiLoc);
-      Suffix.back().setAnnotationValue(Action.ModuleForHeader);
-      [[fallthrough]];
+      case ImportAction::ModuleBegin:
+        // Let the parser know we're textually entering the module.
+        Suffix.emplace_back();
+        Suffix.back().startToken();
+        Suffix.back().setKind(tok::annot_module_begin);
+        Suffix.back().setLocation(SemiLoc);
+        Suffix.back().setAnnotationEndLoc(SemiLoc);
+        Suffix.back().setAnnotationValue(Action.ModuleForHeader);
+        [[fallthrough]];
+
+      case ImportAction::ModuleImport:
+      case ImportAction::HeaderUnitImport:
+      case ImportAction::SkippedModuleImport:
+        // We chose to import (or textually enter) the file. Convert the
+        // header-name token into a header unit annotation token.
+        Suffix[0].setKind(tok::annot_header_unit);
+        Suffix[0].setAnnotationEndLoc(Suffix[0].getLocation());
+        Suffix[0].setAnnotationValue(Action.ModuleForHeader);
+        // FIXME: Call the moduleImport callback?
+        break;
+      case ImportAction::Failure:
+        assert(TheModuleLoader.HadFatalFailure &&
+               "This should be an early exit only to a fatal error");
+        Result.setKind(tok::eof);
+        CurLexer->cutOffLexing();
+        EnterTokens(Suffix);
+        return true;
+      }
 
-    case ImportAction::ModuleImport:
-    case ImportAction::HeaderUnitImport:
-    case ImportAction::SkippedModuleImport:
-      // We chose to import (or textually enter) the file. Convert the
-      // header-name token into a header unit annotation token.
-      Suffix[0].setKind(tok::annot_header_unit);
-      Suffix[0].setAnnotationEndLoc(Suffix[0].getLocation());
-      Suffix[0].setAnnotationValue(Action.ModuleForHeader);
-      // FIXME: Call the moduleImport callback?
-      break;
-    case ImportAction::Failure:
-      assert(TheModuleLoader.HadFatalFailure &&
-             "This should be an early exit only to a fatal error");
-      Result.setKind(tok::eof);
-      CurLexer->cutOffLexing();
       EnterTokens(Suffix);
-      return true;
+      return false;
     }
-
-    EnterTokens(Suffix);
-    return false;
+  } else {
+    Lex(Result);
   }
 
-  // Import a module partition only allowed in C++20 Modules.
-  // It's must in a named module fragment. When we meet an unexpect
-  // partition-name, return a ':' and partition-name tokens, but don't load the
-  // module.
-  bool UnexpectedPartitionName = false;
-  std::optional<Token> ImportPartitionColon;
-  // Meet a partition name.
-  if (Result.is(tok::colon)) {
-    ImportPartitionColon = Result;
-    // If next token is an identifier, try lex module names.
-    Token NextTok;
-    Lex(NextTok);
-
-    // If it's not an identifier, put it back and return ':'.
-    if (NextTok.isNot(tok::identifier)) {
-      EnterTokens({NextTok});
+  if (Result.isOneOf(tok::identifier, tok::colon)) {
+    EnterToken(Result, /*IsReinject=*/false);
+    if (!LexModuleName(Result, /*IsImport=*/true))
       return true;
-    }
-
-    Result = NextTok;
-    if (NamedModuleImportPath.empty() && getLangOpts().CPlusPlusModules &&
-        ModuleDeclState.isNamedModule()) {
-      std::string Name = ModuleDeclState.getPrimaryName().str();
-      Name += ":";
-      NamedModuleImportPath.push_back(
-          {getIdentifierInfo(Name), Result.getLocation()});
+    auto *Info = Result.getAnnotationValueAs<ModuleNameInfo *>();
+    if (getLangOpts().CPlusPlusModules) {
+      // Under the standard C++ Modules, the dot is just part of the module
+      // name, and not a real hierarchy separator. Flatten such module names
+      // now.
+      //
+      // FIXME: Is this the right level to be performing this transformation?
+      std::string FlatModuleName;
+      if (Info->getTokens().front().is(tok::colon)) {
+        // Import a module partition allowed in C++20 Modules.
+        // We can import a partition in named module TU.
+        if (NamedModuleImportPath.empty() && ModuleDeclState.isNamedModule())
+          FlatModuleName = llvm::Twine(ModuleDeclState.getPrimaryName())
+                               .concat(Info->getFlatName())
+                               .str();
+        else
+          return true;
+      } else {
+        FlatModuleName = Info->getFlatName();
+      }
+      NamedModuleImportPath.emplace_back(getIdentifierInfo(FlatModuleName),
+                                         Result.getLocation());
     } else {
-      UnexpectedPartitionName = true;
-    }
-  }
-
-  if (Result.is(tok::identifier)) {
-    LexModuleName(Result, Result);
-    assert(Result.is(tok::annot_module_name) && "Expects a module name");
-    auto *Info = static_cast<ModuleNameInfo *>(Result.getAnnotationValue());
-    for (const auto &Tok : Info->Toks) {
-      if (Tok.is(tok::identifier))
-        NamedModuleImportPath.push_back(
-            std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()));
-    }
-
-    if (ImportPartitionColon.has_value()) {
-      EnterTokens(Result);
-      Result = *ImportPartitionColon;
+      Info->getModuleIdPath(NamedModuleImportPath);
     }
-    if (!UnexpectedPartitionName)
-      CurLexerCallback = CLK_LexAfterModuleImport;
-    return true;
   }
 
   // If we didn't recognize a module name at all, this is not a (valid) import.
@@ -1451,24 +1469,6 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
     SemiLoc = Suffix.back().getLocation();
   }
 
-  // Under the standard C++ Modules, the dot is just part of the module name,
-  // and not a real hierarchy separator. Flatten such module names now.
-  //
-  // FIXME: Is this the right level to be performing this transformation?
-  std::string FlatModuleName;
-  if (getLangOpts().CPlusPlusModules) {
-    for (auto &Piece : NamedModuleImportPath) {
-      // If the FlatModuleName ends with colon, it implies it is a partition.
-      if (!FlatModuleName.empty() && FlatModuleName.back() != ':')
-        FlatModuleName += ".";
-      FlatModuleName += Piece.first->getName();
-    }
-    SourceLocation FirstPathLoc = NamedModuleImportPath[0].second;
-    NamedModuleImportPath.clear();
-    NamedModuleImportPath.push_back(
-        std::make_pair(getIdentifierInfo(FlatModuleName), FirstPathLoc));
-  }
-
   Module *Imported = nullptr;
   // We don't/shouldn't load the standard c++20 modules when preprocessing.
   if (getLangOpts().Modules && !isInImportingCXXNamedModules()) {
@@ -1514,17 +1514,7 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
 bool Preprocessor::LexAfterModuleDecl(Token &Result) {
   // Figure out what kind of lexer we actually have.
   recomputeCurLexerKind();
-  LexUnexpandedToken(Result);
-
-  // If we don't expect an identifier but got an identifier, it's not a part of
-  // module name.
-  if (Result.isNot(tok::identifier)) {
-    EnterToken(Result, /*IsReinject=*/false);
-    return false;
-  }
-  LexModuleName(Result, Result, /*AllowMacroExpansion=*/false);
-  assert(Result.is(tok::annot_module_name) && "Expects a module name");
-  return true;
+  return LexModuleName(Result, /*IsImport=*/false);
 }
 
 void Preprocessor::makeModuleVisible(Module *M, SourceLocation Loc) {
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 3cbd230aca745..577527d0318f2 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3960,7 +3960,7 @@ void Parser::ParseDeclarationSpecifiers(
       goto DoneWithDeclSpec;
     case tok::annot_module_name: {
       PP.EnterTokenStream(
-          static_cast<ModuleNameInfo *>(Tok.getAnnotationValue())->Toks,
+          Tok.getAnnotationValueAs<ModuleNameInfo *>()->getTokens(),
           /*DisableMacroExpansion=*/true, /*IsReinject=*/false);
       ConsumeAnyToken();
       [[fallthrough]];
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 53cd853371139..b093747984f3a 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -2511,17 +2511,28 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
   }
 
   SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
-  if (ParseModuleName(ModuleLoc, Path, /*IsImport=*/false))
+  if (Tok.isNot(tok::annot_module_name)) {
+    Diag(Tok, diag::err_module_expected_ident) << /*IsImport=*/false;
+    SkipUntil(tok::semi, StopBeforeMatch);
+    return nullptr;
+  }
+
+  auto *Info = Tok.getAnnotationValueAs<ModuleNameInfo *>();
+  ConsumeAnnotationToken();
+  if (ParseModuleName(ModuleLoc, Info->getModuleName(), Path,
+                      /*IsImport=*/false))
     return nullptr;
+
   // Parse the optional module-partition.
   SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition;
-  if (Tok.is(tok::colon)) {
-    SourceLocation ColonLoc = ConsumeToken();
+  if (Info->hasPartitionName()) {
+    SourceLocation ColonLoc = Info->getColonToken().getLocation();
     if (!getLangOpts().CPlusPlusModules)
       Diag(ColonLoc, diag::err_unsupported_module_partition)
           << SourceRange(ColonLoc, Partition.back().second);
     // Recover by ignoring the partition name.
-    else if (ParseModuleName(ModuleLoc, Partition, /*IsImport=*/false))
+    else if (ParseModuleName(ModuleLoc, Info->getPartitionName(), Partition,
+                             /*IsImport=*/false))
       return nullptr;
   }
 
@@ -2580,20 +2591,34 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
     // This is a header import that the preprocessor mapped to a module import.
     HeaderUnit = reinterpret_cast<Module *>(Tok.getAnnotationValue());
     ConsumeAnnotationToken();
-  } else if (Tok.is(tok::colon)) {
-    SourceLocation ColonLoc = ConsumeToken();
-    if (!getLangOpts().CPlusPlusModules)
-      Diag(ColonLoc, diag::err_unsupported_module_partition)
-          << SourceRange(ColonLoc, Path.back().second);
-    // Recover by leaving partition empty.
-    else if (ParseModuleName(ColonLoc, Path, /*IsImport=*/true))
-      return nullptr;
-    else
-      IsPartition = true;
   } else {
-    if (ParseModuleName(ImportLoc, Path, /*IsImport=*/true))
+    if (Tok.isNot(tok::annot_module_name)) {
+      if (Tok.is(tok::code_completion)) {
+        cutOffParsing();
+        Actions.CodeCompletion().CodeCompleteModuleImport(ImportLoc, Path);
+        return nullptr;
+      }
+      Diag(Tok, diag::err_module_expected_ident) << /*IsImport=*/true;
+      SkipUntil(tok::semi, StopBeforeMatch);
       return nullptr;
-  }
+    }
+    auto *Info = Tok.getAnnotationValueAs<ModuleNameInfo *>();
+    ConsumeAnnotationToken();
+    if (Info->hasPartitionName()) {
+      SourceLocation ColonLoc = Info->getColonToken().getLocation();
+      if (!getLangOpts().CPlusPlusModules)
+        Diag(ColonLoc, diag::err_unsupported_module_partition)
+            << SourceRange(ColonLoc, Path.back().second);
+      // Recover by leaving partition empty.
+      else if (ParseModuleName(ColonLoc, Info->getPartitionName(), Path,
+                              /*IsImport=*/true))
+        return nullptr;
+      else
+        IsPartition = true;
+    } else if (ParseModuleName(ImportLoc, Info->getModuleName(), Path,
+                               /*IsImport=*/true))
+      return nullptr;
+  } 
 
   ParsedAttributes Attrs(AttrFactory);
   MaybeParseCXX11Attributes(Attrs);
@@ -2689,34 +2714,22 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
 ///           module-name-qualifier[opt] identifier '.'
 bool Parser::ParseModuleName(
     SourceLocation UseLoc,
+    ArrayRef<Token> ModuleName,
     SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path,
     bool IsImport) {
-  auto ExpectIdentifier = [&](Token T) {
+  ModuleNameInfo::getModuleIdPath(ModuleName, Path);
+  // Eg. import A.B.
+  if (ModuleName.back().isNot(tok::identifier)) {
     if (Tok.is(tok::code_completion)) {
       cutOffParsing();
       Actions.CodeCompletion().CodeCompleteModuleImport(UseLoc, Path);
       return true;
     }
-    Diag(T, diag::err_module_expected_ident) << IsImport;
+    Diag(ModuleName.back(), diag::err_module_expected_ident) << IsImport;
     SkipUntil(tok::semi, StopBeforeMatch);
     return true;
-  };
-  if (Tok.isNot(tok::annot_module_name))
-    return ExpectIdentifier(Tok);
-  auto *Info = static_cast<ModuleNameInfo *>(Tok.getAnnotationValue());
-  ConsumeAnnotationToken();
-
-  // Parse the module path.
-  for (const auto &T : Info->Toks) {
-    if (T.is(tok::identifier))
-      Path.push_back(std::make_pair(T.getIdentifierInfo(), T.getLocation()));
   }
 
-  // Invoke the code completion action if the last token in module name sequence
-  // is '.' but not an identifier.
-  if (Info->Toks.back().isNot(tok::identifier))
-    return ExpectIdentifier(Info->Toks.back());
-
   // [cpp.module]/p2: where the pp-tokens (if any) shall not begin with a (
   // preprocessing token [...]
   //



More information about the cfe-commits mailing list