[clang] 49c6e4f - [ObjC][Preprocessor] Handle @import directive as a pp-directive (#157726)

via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 26 05:15:20 PDT 2026


Author: yronglin
Date: 2026-03-26T20:15:16+08:00
New Revision: 49c6e4f8efeb9ab301edb2c78ec5d6535139030a

URL: https://github.com/llvm/llvm-project/commit/49c6e4f8efeb9ab301edb2c78ec5d6535139030a
DIFF: https://github.com/llvm/llvm-project/commit/49c6e4f8efeb9ab301edb2c78ec5d6535139030a.diff

LOG: [ObjC][Preprocessor] Handle @import directive as a pp-directive (#157726)

Depends: https://github.com/llvm/llvm-project/pull/107168
This patch handle `@import` as a preprocessing directive, and since this
patch, the following import directive will be ill-formed:
```
@import Foo\n;
```

Signed-off-by: yronglin <yronglin777 at gmail.com>

Added: 
    clang/test/Modules/objc-at-import.m

Modified: 
    clang/include/clang/Basic/DiagnosticLexKinds.td
    clang/include/clang/Frontend/CompilerInstance.h
    clang/include/clang/Lex/Preprocessor.h
    clang/lib/Frontend/CompilerInstance.cpp
    clang/lib/Lex/DependencyDirectivesScanner.cpp
    clang/lib/Lex/Lexer.cpp
    clang/lib/Lex/PPDirectives.cpp
    clang/lib/Lex/PPLexerChange.cpp
    clang/lib/Lex/Preprocessor.cpp
    clang/test/Modules/lookup.cpp
    clang/test/Modules/no-stale-modtime.m
    clang/unittests/Lex/DependencyDirectivesScannerTest.cpp
    clang/utils/ClangVisualizers/clang.natvis

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 5eceeced311f2..2ef13f568667d 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 0d684d5c7f9fe..f206d012eacc9 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -168,13 +168,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 c7e152a75f51f..8830294ea1658 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;
 
@@ -1857,7 +1851,6 @@ class Preprocessor {
     return FirstPPTokenLoc;
   }
 
-  bool LexAfterModuleImport(Token &Result);
   void CollectPPImportSuffix(SmallVectorImpl<Token> &Toks,
                              bool StopUntilEOD = false);
   bool CollectPPImportSuffixAndEnterStream(SmallVectorImpl<Token> &Toks,
@@ -2914,6 +2907,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.
@@ -3177,9 +3171,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 33919bc1c4634..1f1b6701c38df 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -985,6 +985,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.
@@ -1980,14 +1982,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.
@@ -2163,8 +2166,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();
     }
 
@@ -2177,9 +2179,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 f2e0d52c2cbba..ede5d49860fa4 100644
--- a/clang/lib/Lex/DependencyDirectivesScanner.cpp
+++ b/clang/lib/Lex/DependencyDirectivesScanner.cpp
@@ -582,15 +582,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,
@@ -952,10 +949,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) {
@@ -970,6 +963,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 10246552bb13d..7a3d79e744ef2 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>
@@ -4448,9 +4449,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;
 
@@ -4612,6 +4632,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 c89402fa137c0..055c946b3322b 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))
@@ -4335,13 +4334,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();
@@ -4445,3 +4438,62 @@ 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,
+                       /*AllowMacroExpansion=*/true,
+                       /*IsPartition=*/false))
+    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 2cfefac1052a4..4f5423f0e9b79 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,77 +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,
-                            /*AllowMacroExpansion=*/true,
-                            /*IsPartition=*/false))
-    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);
+ at import lookup_left_cxx;
+ at import lookup_right_cxx;
 
 // expected-warning at 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
- at import l; @import r;
+ at import l;
+ at import r;
 
 //--- l.h
 @import t; // fromt l

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
+ at 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'}}

diff  --git a/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp b/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp
index 2c51330957721..91bda85a43f57 100644
--- a/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp
+++ b/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp
@@ -640,7 +640,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>


        


More information about the cfe-commits mailing list