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

>From ee275f4109d953efe0eada061babe72e90d8f7c5 Mon Sep 17 00:00:00 2001
From: yronglin <[email protected]>
Date: Sat, 21 Feb 2026 21:52:19 -0800
Subject: [PATCH 1/3] [clang] Add HandleModuleName to avoid redundant module
 name handling code

Signed-off-by: yronglin <[email protected]>
---
 clang/include/clang/Lex/Preprocessor.h |  9 +++-
 clang/lib/Lex/PPDirectives.cpp         | 73 ++++----------------------
 clang/lib/Lex/Preprocessor.cpp         | 34 +++++++++++-
 3 files changed, 49 insertions(+), 67 deletions(-)

diff --git a/clang/include/clang/Lex/Preprocessor.h 
b/clang/include/clang/Lex/Preprocessor.h
index b6e42a6151ac3..c97187a1a244f 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -1833,8 +1833,13 @@ class Preprocessor {
   bool LexModuleNameContinue(Token &Tok, SourceLocation UseLoc,
                              SmallVectorImpl<Token> &Suffix,
                              SmallVectorImpl<IdentifierLoc> &Path,
-                             bool AllowMacroExpansion = true,
-                             bool IsPartition = false);
+                             bool IsPartition = false,
+                             bool AllowMacroExpansion = true);
+  bool HandleModuleName(StringRef DirType, SourceLocation UseLoc, Token &Tok,
+                        SmallVectorImpl<IdentifierLoc> &Path,
+                        SmallVectorImpl<Token> &DirToks,
+                        bool IsPartition = false,
+                        bool AllowMacroExpansion = true);
   void EnterModuleSuffixTokenStream(ArrayRef<Token> Toks);
   void HandleCXXImportDirective(Token Import);
   void HandleCXXModuleDirective(Token Module);
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 4a854c213926b..66990454f7ea4 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -4207,7 +4207,7 @@ void Preprocessor::HandleCXXImportDirective(Token 
ImportTok) {
   SmallVector<IdentifierLoc, 2> Path;
   bool ImportingHeader = false;
   bool IsPartition = false;
-  std::string FlatName;
+
   switch (Tok.getKind()) {
   case tok::header_name:
     ImportingHeader = true;
@@ -4221,27 +4221,11 @@ void Preprocessor::HandleCXXImportDirective(Token 
ImportTok) {
     Lex(Tok);
     [[fallthrough]];
   case tok::identifier: {
-    bool LeadingSpace = Tok.hasLeadingSpace();
-    unsigned NumToksInDirective = DirToks.size();
-    if (LexModuleNameContinue(Tok, UseLoc, DirToks, Path)) {
-      if (Tok.isNot(tok::eod))
-        CheckEndOfDirective(ImportTok.getIdentifierInfo()->getName(),
-                            /*EnableMacros=*/false, &DirToks);
-      EnterModuleSuffixTokenStream(DirToks);
+    if (HandleModuleName(ImportTok.getIdentifierInfo()->getName(), UseLoc, Tok,
+                         Path, DirToks, IsPartition))
       return;
-    }
-
-    // Clean the module-name tokens and replace these tokens with
-    // annot_module_name.
-    DirToks.resize(NumToksInDirective);
-    ModuleNameLoc *NameLoc = ModuleNameLoc::Create(*this, Path);
-    DirToks.emplace_back();
-    DirToks.back().setKind(tok::annot_module_name);
-    DirToks.back().setAnnotationRange(NameLoc->getRange());
-    DirToks.back().setAnnotationValue(static_cast<void *>(NameLoc));
-    DirToks.back().setFlagValue(Token::LeadingSpace, LeadingSpace);
-    DirToks.push_back(Tok);
 
+    std::string FlatName;
     bool IsValid =
         (IsPartition && ModuleDeclState.isNamedModule()) || !IsPartition;
     if (Callbacks && IsValid) {
@@ -4382,57 +4366,20 @@ void Preprocessor::HandleCXXModuleDirective(Token 
ModuleTok) {
     DirToks.push_back(Tok);
     break;
   case tok::identifier: {
-    bool LeadingSpace = Tok.hasLeadingSpace();
-    unsigned NumToksInDirective = DirToks.size();
-
-    // C++ [cpp.module]p3: Any preprocessing tokens after the module
-    // preprocessing token in the module directive are processed just as in
-    // normal text.
-    //
-    // P3034R1 Module Declarations Shouldn’t be Macros.
-    if (LexModuleNameContinue(Tok, UseLoc, DirToks, Path,
-                              /*AllowMacroExpansion=*/false)) {
-      if (Tok.isNot(tok::eod))
-        CheckEndOfDirective(ModuleTok.getIdentifierInfo()->getName(),
-                            /*EnableMacros=*/false, &DirToks);
-      EnterModuleSuffixTokenStream(DirToks);
+    if (HandleModuleName(ModuleTok.getIdentifierInfo()->getName(), UseLoc, Tok,
+                         Path, DirToks, /*IsPartition=*/false,
+                         /*AllowMacroExpansion=*/false))
       return;
-    }
-
-    ModuleNameLoc *NameLoc = ModuleNameLoc::Create(*this, Path);
-    DirToks.resize(NumToksInDirective);
-    DirToks.emplace_back();
-    DirToks.back().setKind(tok::annot_module_name);
-    DirToks.back().setAnnotationRange(NameLoc->getRange());
-    DirToks.back().setAnnotationValue(static_cast<void *>(NameLoc));
-    DirToks.back().setFlagValue(Token::LeadingSpace, LeadingSpace);
-    DirToks.push_back(Tok);
 
     // C++20 [cpp.module]p
     //   The pp-tokens, if any, of a pp-module shall be of the form:
     //     pp-module-name pp-module-partition[opt] pp-tokens[opt]
     if (Tok.is(tok::colon)) {
-      NumToksInDirective = DirToks.size();
       LexUnexpandedToken(Tok);
-      LeadingSpace = Tok.hasLeadingSpace();
-      if (LexModuleNameContinue(Tok, UseLoc, DirToks, Partition,
-                                /*AllowMacroExpansion=*/false,
-                                /*IsPartition=*/true)) {
-        if (Tok.isNot(tok::eod))
-          CheckEndOfDirective(ModuleTok.getIdentifierInfo()->getName(),
-                              /*EnableMacros=*/false, &DirToks);
-        EnterModuleSuffixTokenStream(DirToks);
+      if (HandleModuleName(ModuleTok.getIdentifierInfo()->getName(), UseLoc,
+                           Tok, Partition, DirToks, /*IsPartition=*/true,
+                           /*AllowMacroExpansion=*/false))
         return;
-      }
-
-      ModuleNameLoc *PartitionLoc = ModuleNameLoc::Create(*this, Partition);
-      DirToks.resize(NumToksInDirective);
-      DirToks.emplace_back();
-      DirToks.back().setKind(tok::annot_module_name);
-      DirToks.back().setAnnotationRange(NameLoc->getRange());
-      DirToks.back().setAnnotationValue(static_cast<void *>(PartitionLoc));
-      DirToks.back().setFlagValue(Token::LeadingSpace, LeadingSpace);
-      DirToks.push_back(Tok);
     }
 
     // If the current token is a macro definition, put it back to token stream
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index a531f51408dae..44d4471015dd4 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -1205,8 +1205,8 @@ ModuleNameLoc *ModuleNameLoc::Create(Preprocessor &PP, 
ModuleIdPath Path) {
 bool Preprocessor::LexModuleNameContinue(Token &Tok, SourceLocation UseLoc,
                                          SmallVectorImpl<Token> &Suffix,
                                          SmallVectorImpl<IdentifierLoc> &Path,
-                                         bool AllowMacroExpansion,
-                                         bool IsPartition) {
+                                         bool IsPartition,
+                                         bool AllowMacroExpansion) {
   auto ConsumeToken = [&]() {
     if (AllowMacroExpansion)
       Lex(Tok);
@@ -1250,6 +1250,36 @@ bool Preprocessor::LexModuleNameContinue(Token &Tok, 
SourceLocation UseLoc,
   }
 }
 
+bool Preprocessor::HandleModuleName(StringRef DirType, SourceLocation UseLoc,
+                                    Token &Tok,
+                                    SmallVectorImpl<IdentifierLoc> &Path,
+                                    SmallVectorImpl<Token> &DirToks,
+                                    bool IsPartition,
+                                    bool AllowMacroExpansion) {
+  bool LeadingSpace = Tok.hasLeadingSpace();
+  unsigned NumToksInDirective = DirToks.size();
+  if (LexModuleNameContinue(Tok, UseLoc, DirToks, Path, IsPartition,
+                            AllowMacroExpansion)) {
+    if (Tok.isNot(tok::eod))
+      CheckEndOfDirective(DirType,
+                          /*EnableMacros=*/false, &DirToks);
+    EnterModuleSuffixTokenStream(DirToks);
+    return true;
+  }
+
+  // Clean the module-name tokens and replace these tokens with
+  // annot_module_name.
+  DirToks.resize(NumToksInDirective);
+  ModuleNameLoc *NameLoc = ModuleNameLoc::Create(*this, Path);
+  DirToks.emplace_back();
+  DirToks.back().setKind(tok::annot_module_name);
+  DirToks.back().setAnnotationRange(NameLoc->getRange());
+  DirToks.back().setAnnotationValue(static_cast<void *>(NameLoc));
+  DirToks.back().setFlagValue(Token::LeadingSpace, LeadingSpace);
+  DirToks.push_back(Tok);
+  return false;
+}
+
 /// [cpp.pre]/p2:
 /// A preprocessing directive consists of a sequence of preprocessing tokens
 /// that satisfies the following constraints: At the start of translation phase

>From b42cd940e0b6467ef334c66b9925fb0045087f61 Mon Sep 17 00:00:00 2001
From: yronglin <[email protected]>
Date: Fri, 27 Feb 2026 07:23:23 -0800
Subject: [PATCH 2/3] [ObjC][Preprocessor] Handle @import directive as a
 pp-directive

Signed-off-by: yronglin <[email protected]>
---
 .../include/clang/Basic/DiagnosticLexKinds.td |  2 +-
 .../include/clang/Frontend/CompilerInstance.h | 11 +--
 clang/include/clang/Lex/Preprocessor.h        | 11 +--
 clang/lib/Frontend/CompilerInstance.cpp       | 27 ++---
 clang/lib/Lex/DependencyDirectivesScanner.cpp | 18 ++--
 clang/lib/Lex/Lexer.cpp                       | 36 ++++++-
 clang/lib/Lex/PPDirectives.cpp                | 99 ++++++++++++++-----
 clang/lib/Lex/PPLexerChange.cpp               | 13 +--
 clang/lib/Lex/Preprocessor.cpp                | 91 +----------------
 clang/test/Modules/lookup.cpp                 |  7 +-
 clang/test/Modules/no-stale-modtime.m         |  3 +-
 .../Lex/DependencyDirectivesScannerTest.cpp   |  2 +-
 clang/utils/ClangVisualizers/clang.natvis     |  1 -
 13 files changed, 146 insertions(+), 175 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td 
b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 77feea9f869e9..d88aa891dc3e5 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -503,7 +503,7 @@ def warn_cxx98_compat_variadic_macro : Warning<
   InGroup<CXX98CompatPedantic>, DefaultIgnore;
 def ext_named_variadic_macro : Extension<
   "named variadic macros are a GNU extension">, InGroup<VariadicMacros>;
-def err_embedded_directive : Error<"embedding a %select{#|C++ }0%1 directive "
+def err_embedded_directive : Error<"embedding a %select{|#}0%1 directive "
                                    "within macro arguments is not supported">;
 def ext_embedded_directive : Extension<
   "embedding a directive within macro arguments has undefined behavior">,
diff --git a/clang/include/clang/Frontend/CompilerInstance.h 
b/clang/include/clang/Frontend/CompilerInstance.h
index 217efa3fe756e..fa811ae4fe8b2 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -167,13 +167,10 @@ class CompilerInstance : public ModuleLoader {
   /// Should we delete the BuiltModules when we're done?
   bool DeleteBuiltModules = true;
 
-  /// The location of the module-import keyword for the last module
-  /// import.
-  SourceLocation LastModuleImportLoc;
-
-  /// The result of the last module import.
-  ///
-  ModuleLoadResult LastModuleImportResult;
+  /// Cache of module import results keyed by import location.
+  /// It is important to eliminate redundant diagnostics
+  /// when both the preprocessor and parser see the same import declaration.
+  llvm::SmallDenseMap<SourceLocation, ModuleLoadResult, 4> ModuleImportResults;
 
   /// Whether we should (re)build the global module index once we
   /// have finished with this translation unit.
diff --git a/clang/include/clang/Lex/Preprocessor.h 
b/clang/include/clang/Lex/Preprocessor.h
index c97187a1a244f..1e0b51eb7e7b4 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -381,12 +381,6 @@ class Preprocessor {
   llvm::DenseMap<FileID, SmallVector<const char *>> CheckPoints;
   unsigned CheckPointCounter = 0;
 
-  /// Whether the import is an `@import` or a standard c++ modules import.
-  bool IsAtImport = false;
-
-  /// Whether the last token we lexed was an '@'.
-  bool LastTokenWasAt = false;
-
   /// Whether we're importing a standard C++20 named Modules.
   bool ImportingCXXNamedModules = false;
 
@@ -1859,7 +1853,6 @@ class Preprocessor {
     return FirstPPTokenLoc;
   }
 
-  bool LexAfterModuleImport(Token &Result);
   void CollectPPImportSuffix(SmallVectorImpl<Token> &Toks,
                              bool StopUntilEOD = false);
   bool CollectPPImportSuffixAndEnterStream(SmallVectorImpl<Token> &Toks,
@@ -2916,6 +2909,7 @@ class Preprocessor {
   void HandleIncludeMacrosDirective(SourceLocation HashLoc, Token &Tok);
   void HandleImportDirective(SourceLocation HashLoc, Token &Tok);
   void HandleMicrosoftImportDirective(Token &Tok);
+  void HandleObjCImportDirective(Token &AtTok, Token &ImportTok);
 
 public:
   /// Check that the given module is available, producing a diagnostic if not.
@@ -3179,9 +3173,6 @@ class Preprocessor {
   static bool CLK_DependencyDirectivesLexer(Preprocessor &P, Token &Result) {
     return P.CurLexer->LexDependencyDirectiveToken(Result);
   }
-  static bool CLK_LexAfterModuleImport(Preprocessor &P, Token &Result) {
-    return P.LexAfterModuleImport(Result);
-  }
 };
 
 /// Abstract base class that describes a handler that will receive
diff --git a/clang/lib/Frontend/CompilerInstance.cpp 
b/clang/lib/Frontend/CompilerInstance.cpp
index 9f1a3c56feec1..f282863e7cb80 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -1003,6 +1003,8 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) 
{
     if (hasSourceManager() && !Act.isModelParsingAction())
       getSourceManager().clearIDTables();
 
+    ModuleImportResults.clear();
+
     if (Act.BeginSourceFile(*this, FIF)) {
       if (llvm::Error Err = Act.Execute()) {
         consumeError(std::move(Err)); // FIXME this drops errors on the floor.
@@ -1955,14 +1957,15 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
   SourceLocation ModuleNameLoc = Path[0].getLoc();
 
   // If we've already handled this import, just return the cached result.
-  // This one-element cache is important to eliminate redundant diagnostics
-  // when both the preprocessor and parser see the same import declaration.
-  if (ImportLoc.isValid() && LastModuleImportLoc == ImportLoc) {
-    // Make the named module visible.
-    if (LastModuleImportResult && ModuleName != getLangOpts().CurrentModule)
-      TheASTReader->makeModuleVisible(LastModuleImportResult, Visibility,
-                                      ImportLoc);
-    return LastModuleImportResult;
+  // This cache eliminates redundant diagnostics when both the preprocessor
+  // and parser see the same import declaration.
+  if (ImportLoc.isValid()) {
+    auto CacheIt = ModuleImportResults.find(ImportLoc);
+    if (CacheIt != ModuleImportResults.end()) {
+      if (CacheIt->second && ModuleName != getLangOpts().CurrentModule)
+        TheASTReader->makeModuleVisible(CacheIt->second, Visibility, 
ImportLoc);
+      return CacheIt->second;
+    }
   }
 
   // If we don't already have information on this module, load the module now.
@@ -2138,8 +2141,7 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
                                              *Module, getDiagnostics())) {
       getDiagnostics().Report(ImportLoc, diag::note_module_import_here)
           << SourceRange(Path.front().getLoc(), Path.back().getLoc());
-      LastModuleImportLoc = ImportLoc;
-      LastModuleImportResult = ModuleLoadResult();
+      ModuleImportResults[ImportLoc] = ModuleLoadResult();
       return ModuleLoadResult();
     }
 
@@ -2152,9 +2154,8 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
       .getModuleMap()
       .resolveLinkAsDependencies(Module->getTopLevelModule());
 
-  LastModuleImportLoc = ImportLoc;
-  LastModuleImportResult = ModuleLoadResult(Module);
-  return LastModuleImportResult;
+  ModuleImportResults[ImportLoc] = ModuleLoadResult(Module);
+  return ModuleLoadResult(Module);
 }
 
 void CompilerInstance::createModuleFromSource(SourceLocation ImportLoc,
diff --git a/clang/lib/Lex/DependencyDirectivesScanner.cpp 
b/clang/lib/Lex/DependencyDirectivesScanner.cpp
index 8320b3ddbca31..f31c76f9f675b 100644
--- a/clang/lib/Lex/DependencyDirectivesScanner.cpp
+++ b/clang/lib/Lex/DependencyDirectivesScanner.cpp
@@ -581,15 +581,12 @@ bool Scanner::lexModuleDirectiveBody(DirectiveKind Kind, 
const char *&First,
     return false;
   }
 
+  const auto &Tok = lexToken(First, End);
   pushDirective(Kind);
-  skipWhitespace(First, End);
-  if (First == End)
+  if (Tok.is(tok::eof) || Tok.is(tok::eod))
     return false;
-  if (!isVerticalWhitespace(*First))
-    return reportError(
-        DirectiveLoc, 
diag::err_dep_source_scanner_unexpected_tokens_at_import);
-  skipNewline(First, End);
-  return false;
+  return reportError(DirectiveLoc,
+                     diag::err_dep_source_scanner_unexpected_tokens_at_import);
 }
 
 dependency_directives_scan::Token &Scanner::lexToken(const char *&First,
@@ -951,10 +948,6 @@ bool Scanner::lexPPLine(const char *&First, const char 
*const End) {
     CurDirToks.clear();
   });
 
-  // FIXME: Shoule we handle @import as a preprocessing directive?
-  if (*First == '@')
-    return lexAt(First, End);
-
   bool IsPreprocessedModule =
       isStartWithPreprocessedModuleDirective(First, End);
   if (*First == '_' && !IsPreprocessedModule) {
@@ -969,6 +962,9 @@ bool Scanner::lexPPLine(const char *&First, const char 
*const End) {
   llvm::scope_exit ScEx2(
       [&]() { TheLexer.setParsingPreprocessorDirective(false); });
 
+  if (*First == '@')
+    return lexAt(First, End);
+
   // Handle module directives for C++20 modules.
   if (*First == 'i' || *First == 'e' || *First == 'm' || IsPreprocessedModule)
     return lexModule(First, End);
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index 92c3046a6fd19..3e4548d76b140 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -34,6 +34,7 @@
 #include "llvm/Support/ConvertUTF.h"
 #include "llvm/Support/MemoryBufferRef.h"
 #include "llvm/Support/NativeFormatting.h"
+#include "llvm/Support/SaveAndRestore.h"
 #include "llvm/Support/Unicode.h"
 #include "llvm/Support/UnicodeCharRanges.h"
 #include <algorithm>
@@ -4436,9 +4437,28 @@ bool Lexer::LexTokenInternal(Token &Result) {
 
   case '@':
     // Objective C support.
-    if (CurPtr[-1] == '@' && LangOpts.ObjC)
-      Kind = tok::at;
-    else
+    if (CurPtr[-1] == '@' && LangOpts.ObjC) {
+      FormTokenWithChars(Result, CurPtr, tok::at);
+      if (PP && Result.isAtPhysicalStartOfLine() && !LexingRawMode &&
+          !Is_PragmaLexer) {
+        Token NextPPTok;
+        NextPPTok.startToken();
+        {
+          llvm::SaveAndRestore<bool> SavedParsingPreprocessorDirective(
+              this->ParsingPreprocessorDirective, true);
+          auto NextTokOr = peekNextPPToken();
+          if (NextTokOr.has_value()) {
+            NextPPTok = *NextTokOr;
+          }
+        }
+        if (NextPPTok.is(tok::raw_identifier) &&
+            NextPPTok.getRawIdentifier() == "import") {
+          PP->HandleDirective(Result);
+          return false;
+        }
+      }
+      return true;
+    } else
       Kind = tok::unknown;
     break;
 
@@ -4600,6 +4620,16 @@ bool Lexer::LexDependencyDirectiveToken(Token &Result) {
       return true;
     return false;
   }
+  if (Result.is(tok::at) && Result.isAtStartOfLine()) {
+    auto NextTok = peekNextPPToken();
+    if (NextTok && NextTok->is(tok::raw_identifier) &&
+        NextTok->getRawIdentifier() == "import") {
+      PP->HandleDirective(Result);
+      if (PP->hadModuleLoaderFatalFailure())
+        return true;
+      return false;
+    }
+  }
   if (Result.is(tok::raw_identifier)) {
     Result.setRawIdentifierData(TokPtr);
     if (!isLexingRawMode()) {
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 66990454f7ea4..3f2e66e66a458 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -1313,9 +1313,9 @@ void 
Preprocessor::HandleSkippedDirectiveWhileUsingPCH(Token &Result,
 void Preprocessor::HandleDirective(Token &Result) {
   // FIXME: Traditional: # with whitespace before it not recognized by K&R?
 
-  // We just parsed a # character at the start of a line, so we're in directive
-  // mode.  Tell the lexer this so any newlines we see will be converted into 
an
-  // EOD token (which terminates the directive).
+  // We just parsed a # or @ character at the start of a line, so we're in
+  // directive mode.  Tell the lexer this so any newlines we see will be
+  // converted into an EOD token (which terminates the directive).
   CurPPLexer->ParsingPreprocessorDirective = true;
   if (CurLexer) CurLexer->SetKeepWhitespaceMode(false);
 
@@ -1330,13 +1330,13 @@ void Preprocessor::HandleDirective(Token &Result) {
   // pp-directive.
   bool ReadAnyTokensBeforeDirective 
=CurPPLexer->MIOpt.getHasReadAnyTokensVal();
 
-  // Save the directive-introducing token('#' and import/module in C++20) in
-  // case we need to return it later.
+  // Save the directive-introducing token ('#', '@', or import/module in C++20)
+  // in case we need to return it later.
   Token Introducer = Result;
 
   // Read the next token, the directive flavor.  This isn't expanded due to
   // C99 6.10.3p8.
-  if (Introducer.is(tok::hash))
+  if (Introducer.isOneOf(tok::hash, tok::at))
     LexUnexpandedToken(Result);
 
   // C99 6.10.3p11: Is this preprocessor directive in macro invocation?  e.g.:
@@ -1360,10 +1360,7 @@ void Preprocessor::HandleDirective(Token &Result) {
       case tok::pp___preprocessed_module:
       case tok::pp___preprocessed_import:
         Diag(Result, diag::err_embedded_directive)
-            << (getLangOpts().CPlusPlusModules &&
-                Introducer.isModuleContextualKeyword(
-                    /*AllowExport=*/false))
-            << II->getName();
+            << Introducer.is(tok::hash) << II->getName();
         Diag(*ArgMacro, diag::note_macro_expansion_here)
             << ArgMacro->getIdentifierInfo();
         DiscardUntilEndOfDirective();
@@ -1464,11 +1461,16 @@ void Preprocessor::HandleDirective(Token &Result) {
       return HandleCXXImportDirective(Result);
     // GNU Extensions.
     case tok::pp_import:
-      if (getLangOpts().CPlusPlusModules &&
-          Introducer.isModuleContextualKeyword(
-              /*AllowExport=*/false))
+      switch (Introducer.getKind()) {
+      case tok::hash:
+        return HandleImportDirective(Introducer.getLocation(), Result);
+      case tok::at:
+        return HandleObjCImportDirective(Introducer, Result);
+      case tok::kw_import:
         return HandleCXXImportDirective(Result);
-      return HandleImportDirective(Introducer.getLocation(), Result);
+      default:
+        llvm_unreachable("not a valid import directive");
+      }
     case tok::pp_include_next:
       return HandleIncludeNextDirective(Introducer.getLocation(), Result);
 
@@ -4192,9 +4194,6 @@ void Preprocessor::HandleCXXImportDirective(Token 
ImportTok) {
   llvm::SaveAndRestore<bool> SaveImportingCXXModules(
       this->ImportingCXXNamedModules, true);
 
-  if (LastExportKeyword.is(tok::kw_export))
-    LastExportKeyword.startToken();
-
   Token Tok;
   if (LexHeaderName(Tok)) {
     if (Tok.isNot(tok::eod))
@@ -4334,13 +4333,7 @@ void Preprocessor::HandleCXXImportDirective(Token 
ImportTok) {
 /// The lexed module name are replaced by annot_module_name token.
 void Preprocessor::HandleCXXModuleDirective(Token ModuleTok) {
   assert(getLangOpts().CPlusPlusModules && ModuleTok.is(tok::kw_module));
-  Token Introducer = ModuleTok;
-  if (LastExportKeyword.is(tok::kw_export)) {
-    Introducer = LastExportKeyword;
-    LastExportKeyword.startToken();
-  }
-
-  SourceLocation StartLoc = Introducer.getLocation();
+  SourceLocation StartLoc = ModuleTok.getLocation();
 
   Token Tok;
   SourceLocation UseLoc = ModuleTok.getLocation();
@@ -4444,3 +4437,61 @@ void Preprocessor::HandleCXXModuleDirective(Token 
ModuleTok) {
   }
   EnterModuleSuffixTokenStream(DirToks);
 }
+
+/// Lex a token following the 'import' contextual keyword.
+///
+///     pp-import:
+/// [ObjC]    @ import module-name ;
+///
+///     module-name:
+///           module-name-qualifier[opt] identifier
+///
+///     module-name-qualifier
+///           module-name-qualifier[opt] identifier .
+///
+/// We respond to a pp-import by importing macros from the named module.
+void Preprocessor::HandleObjCImportDirective(Token &AtTok, Token &ImportTok) {
+  assert(getLangOpts().ObjC && AtTok.is(tok::at) &&
+         ImportTok.isObjCAtKeyword(tok::objc_import));
+  ImportTok.setKind(tok::kw_import);
+  SmallVector<Token, 32> DirToks{AtTok, ImportTok};
+  SmallVector<IdentifierLoc, 3> Path;
+  SourceLocation UseLoc = ImportTok.getLocation();
+  ModuleImportLoc = ImportTok.getLocation();
+  Token Tok;
+  Lex(Tok);
+  if (HandleModuleName(ImportTok.getIdentifierInfo()->getName(), UseLoc, Tok,
+                       Path, DirToks, /*IsPartition=*/false,
+                       /*AllowMacroExpansion=*/true))
+    return;
+
+  // Consume the pp-import-suffix and expand any macros in it now, if we're not
+  // at the semicolon already.
+  if (!DirToks.back().isOneOf(tok::semi, tok::eod))
+    CollectPPImportSuffix(DirToks);
+
+  if (DirToks.back().isNot(tok::eod))
+    CheckEndOfDirective(ImportTok.getIdentifierInfo()->getName());
+  else
+    DirToks.pop_back();
+
+  // This is not a pp-import after all.
+  if (DirToks.back().isNot(tok::semi)) {
+    EnterModuleSuffixTokenStream(DirToks);
+    return;
+  }
+
+  Module *Imported = nullptr;
+  SourceLocation SemiLoc = DirToks.back().getLocation();
+  if (getLangOpts().Modules) {
+    Imported = TheModuleLoader.loadModule(ModuleImportLoc, Path, 
Module::Hidden,
+                                          /*IsInclusionDirective=*/false);
+    if (Imported)
+      makeModuleVisible(Imported, SemiLoc);
+  }
+
+  if (Callbacks)
+    Callbacks->moduleImport(ModuleImportLoc, Path, Imported);
+
+  EnterModuleSuffixTokenStream(DirToks);
+}
diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp
index 05affedd48a86..35fc23092dcc1 100644
--- a/clang/lib/Lex/PPLexerChange.cpp
+++ b/clang/lib/Lex/PPLexerChange.cpp
@@ -115,10 +115,9 @@ void Preprocessor::EnterSourceFileWithLexer(Lexer 
*TheLexer,
   CurPPLexer = TheLexer;
   CurDirLookup = CurDir;
   CurLexerSubmodule = nullptr;
-  if (CurLexerCallback != CLK_LexAfterModuleImport)
-    CurLexerCallback = TheLexer->isDependencyDirectivesLexer()
-                           ? CLK_DependencyDirectivesLexer
-                           : CLK_Lexer;
+  CurLexerCallback = TheLexer->isDependencyDirectivesLexer()
+                         ? CLK_DependencyDirectivesLexer
+                         : CLK_Lexer;
 
   // Notify the client, if desired, that we are in a new source file.
   if (Callbacks && !CurLexer->Is_PragmaLexer) {
@@ -154,8 +153,7 @@ void Preprocessor::EnterMacro(Token &Tok, SourceLocation 
ILEnd,
   PushIncludeMacroStack();
   CurDirLookup = nullptr;
   CurTokenLexer = std::move(TokLexer);
-  if (CurLexerCallback != CLK_LexAfterModuleImport)
-    CurLexerCallback = CLK_TokenLexer;
+  CurLexerCallback = CLK_TokenLexer;
 }
 
 /// EnterTokenStream - Add a "macro" context to the top of the include stack,
@@ -209,8 +207,7 @@ void Preprocessor::EnterTokenStream(const Token *Toks, 
unsigned NumToks,
   PushIncludeMacroStack();
   CurDirLookup = nullptr;
   CurTokenLexer = std::move(TokLexer);
-  if (CurLexerCallback != CLK_LexAfterModuleImport)
-    CurLexerCallback = CLK_TokenLexer;
+  CurLexerCallback = CLK_TokenLexer;
 }
 
 /// Compute the relative path that names the given file relative to
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 44d4471015dd4..a1641fdc50ece 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -889,23 +889,6 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
     return hadModuleLoaderFatalFailure();
   }
 
-  // If this is the 'import' contextual keyword following an '@', note
-  // that the next token indicates a module name.
-  //
-  // Note that we do not treat 'import' as a contextual
-  // keyword when we're in a caching lexer, because caching lexers only get
-  // used in contexts where import declarations are disallowed.
-  //
-  // Likewise if this is the standard C++ import keyword.
-  if (((LastTokenWasAt && II.isImportKeyword()) ||
-       Identifier.is(tok::kw_import)) &&
-      !InMacroArgs &&
-      (!DisableMacroExpansion || MacroExpansionInDirectivesOverride) &&
-      CurLexerCallback != CLK_CachingLexer) {
-    ModuleImportLoc = Identifier.getLocation();
-    IsAtImport = true;
-    CurLexerCallback = CLK_LexAfterModuleImport;
-  }
   return true;
 }
 
@@ -1005,7 +988,6 @@ void Preprocessor::Lex(Token &Result) {
     CheckPointCounter = 0;
   }
 
-  LastTokenWasAt = Result.is(tok::at);
   if (Result.isNot(tok::kw_export))
     LastExportKeyword.startToken();
 
@@ -1344,8 +1326,7 @@ bool Preprocessor::HandleModuleContextualKeyword(Token 
&Result) {
       CurPPLexer->ParsingFilename,
       Result.getIdentifierInfo()->isImportKeyword());
 
-  std::optional<Token> NextTok =
-      CurLexer ? CurLexer->peekNextPPToken() : 
CurTokenLexer->peekNextPPToken();
+  std::optional<Token> NextTok = peekNextPPToken();
   if (!NextTok)
     return false;
 
@@ -1357,7 +1338,6 @@ bool Preprocessor::HandleModuleContextualKeyword(Token 
&Result) {
                          tok::header_name)) {
       Result.setKind(tok::kw_import);
       ModuleImportLoc = Result.getLocation();
-      IsAtImport = false;
       return true;
     }
   }
@@ -1414,75 +1394,6 @@ void 
Preprocessor::EnterModuleSuffixTokenStream(ArrayRef<Token> Toks) {
   CurTokenLexer->setLexingCXXModuleDirective();
 }
 
-/// Lex a token following the 'import' contextual keyword.
-///
-///     pp-import: [C++20]
-///           import header-name pp-import-suffix[opt] ;
-///           import header-name-tokens pp-import-suffix[opt] ;
-/// [ObjC]    @ import module-name ;
-/// [Clang]   import module-name ;
-///
-///     header-name-tokens:
-///           string-literal
-///           < [any sequence of preprocessing-tokens other than >] >
-///
-///     module-name:
-///           module-name-qualifier[opt] identifier
-///
-///     module-name-qualifier
-///           module-name-qualifier[opt] identifier .
-///
-/// We respond to a pp-import by importing macros from the named module.
-bool Preprocessor::LexAfterModuleImport(Token &Result) {
-  // Figure out what kind of lexer we actually have.
-  recomputeCurLexerKind();
-
-  SmallVector<Token, 32> Suffix;
-  SmallVector<IdentifierLoc, 3> Path;
-  Lex(Result);
-  if (LexModuleNameContinue(Result, ModuleImportLoc, Suffix, Path))
-    return CollectPPImportSuffixAndEnterStream(Suffix);
-
-  ModuleNameLoc *NameLoc = ModuleNameLoc::Create(*this, Path);
-  Suffix.clear();
-  Suffix.emplace_back();
-  Suffix.back().setKind(tok::annot_module_name);
-  Suffix.back().setAnnotationRange(NameLoc->getRange());
-  Suffix.back().setAnnotationValue(static_cast<void *>(NameLoc));
-  Suffix.push_back(Result);
-
-  // Consume the pp-import-suffix and expand any macros in it now, if we're not
-  // at the semicolon already.
-  SourceLocation SemiLoc = Result.getLocation();
-  if (Suffix.back().isNot(tok::semi)) {
-    if (Suffix.back().isNot(tok::eof))
-      CollectPPImportSuffix(Suffix);
-    if (Suffix.back().isNot(tok::semi)) {
-      // This is not an import after all.
-      EnterModuleSuffixTokenStream(Suffix);
-      return false;
-    }
-    SemiLoc = Suffix.back().getLocation();
-  }
-
-  Module *Imported = nullptr;
-  if (getLangOpts().Modules) {
-    Imported = TheModuleLoader.loadModule(ModuleImportLoc, Path, 
Module::Hidden,
-                                          /*IsInclusionDirective=*/false);
-    if (Imported)
-      makeModuleVisible(Imported, SemiLoc);
-  }
-
-  if (Callbacks)
-    Callbacks->moduleImport(ModuleImportLoc, Path, Imported);
-
-  if (!Suffix.empty()) {
-    EnterModuleSuffixTokenStream(Suffix);
-    return false;
-  }
-  return true;
-}
-
 void Preprocessor::makeModuleVisible(Module *M, SourceLocation Loc,
                                      bool IncludeExports) {
   CurSubmoduleState->VisibleModules.setVisible(
diff --git a/clang/test/Modules/lookup.cpp b/clang/test/Modules/lookup.cpp
index 95c30bf3eedff..6e2e403df8814 100644
--- a/clang/test/Modules/lookup.cpp
+++ b/clang/test/Modules/lookup.cpp
@@ -1,9 +1,6 @@
 
-#define import @import
-import lookup_left_cxx;
-#undef import
-#define IMPORT(X) @import X
-IMPORT(lookup_right_cxx);
+@import lookup_left_cxx;
+@import lookup_right_cxx;
 
 // expected-warning@Inputs/lookup_left.hpp:3 {{weak identifier 
'weak_identifier' never declared}}
 
diff --git a/clang/test/Modules/no-stale-modtime.m 
b/clang/test/Modules/no-stale-modtime.m
index aa16eb1db5f68..60b47487f448a 100644
--- a/clang/test/Modules/no-stale-modtime.m
+++ b/clang/test/Modules/no-stale-modtime.m
@@ -23,7 +23,8 @@
 // RUN:     -I %t -fsyntax-only %t/main.m -Rmodule-build -verify
 
 //--- b.h
-@import l; @import r;
+@import l;
+@import r;
 
 //--- l.h
 @import t; // fromt l
diff --git a/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp 
b/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp
index 79e2832798917..9b20c328c918c 100644
--- a/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp
+++ b/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp
@@ -639,7 +639,7 @@ TEST(MinimizeSourceToDependencyDirectivesTest, AtImport) {
   ASSERT_FALSE(minimizeSourceToDependencyDirectives(" @ import  A;\n", Out));
   EXPECT_STREQ("@import A;\n", Out.data());
 
-  ASSERT_FALSE(minimizeSourceToDependencyDirectives("@import A\n;", Out));
+  ASSERT_FALSE(minimizeSourceToDependencyDirectives("@import A;\n", Out));
   EXPECT_STREQ("@import A;\n", Out.data());
 
   ASSERT_FALSE(minimizeSourceToDependencyDirectives("@import A.B;\n", Out));
diff --git a/clang/utils/ClangVisualizers/clang.natvis 
b/clang/utils/ClangVisualizers/clang.natvis
index 0755f0ffcbf56..828557b7279b9 100644
--- a/clang/utils/ClangVisualizers/clang.natvis
+++ b/clang/utils/ClangVisualizers/clang.natvis
@@ -807,7 +807,6 @@ For later versions of Visual Studio, no setup is required-->
     <DisplayString Condition="IncludeMacroStack._Mypair._Myval2._Mylast - 
IncludeMacroStack._Mypair._Myval2._Myfirst">
       {this,view(cached)}
     </DisplayString>
-    <DisplayString>CLK_LexAfterModuleImport</DisplayString>
   </Type>
   <Type Name="clang::Parser">
     <DisplayString>[{Tok}] {PP,na}</DisplayString>

>From a7fef84aff1a661a99407bcc1fcb973e5944e470 Mon Sep 17 00:00:00 2001
From: yronglin <[email protected]>
Date: Fri, 27 Feb 2026 08:01:15 -0800
Subject: [PATCH 3/3] Add more test

Signed-off-by: yronglin <[email protected]>
---
 clang/test/Modules/objc-at-import.m | 42 +++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 clang/test/Modules/objc-at-import.m

diff --git a/clang/test/Modules/objc-at-import.m 
b/clang/test/Modules/objc-at-import.m
new file mode 100644
index 0000000000000..98978df3c42e4
--- /dev/null
+++ b/clang/test/Modules/objc-at-import.m
@@ -0,0 +1,42 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t/cache 
-fimplicit-module-maps \
+// RUN:   -I%S/Inputs -verify -x objective-c %t/macro-module-name.m
+//
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t/cache 
-fimplicit-module-maps \
+// RUN:   -I%S/Inputs -verify -x objective-c %t/macro-at-import.m
+//
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t/cache 
-fimplicit-module-maps \
+// RUN:   -I%S/Inputs -verify -x objective-c %t/macro-func-import.m
+
+//--- macro-module-name.m
+// expected-no-diagnostics
+#define M dummy
+@import M;
+
+#ifndef DUMMY_H
+#error "macros from module not visible after @import with macro module name"
+#endif
+
+void *p = &dummy1;
+
+//--- macro-at-import.m
+#define imp @import
+imp dummy;
+
+#ifdef DUMMY_H
+#error "module should not be imported via macro-constructed @import"
+#endif
+
+void *p = &dummy1; // expected-error {{use of undeclared identifier 'dummy1'}}
+
+//--- macro-func-import.m
+#define IMPORT(X) @import X
+IMPORT(dummy);
+
+#ifdef DUMMY_H
+#error "module should not be imported via function-like macro @import"
+#endif
+
+void *p = &dummy1; // expected-error {{use of undeclared identifier 'dummy1'}}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to