[clang] [ObjC][Preprocessor] Handle @import directive as a pp-directive (PR #157726)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 25 06:28:53 PDT 2026
https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/157726
>From 6c52cda20ec4f9c37381767c1dacb98df19740d4 Mon Sep 17 00:00:00 2001
From: "Wang, Yihan" <yronglin777 at gmail.com>
Date: Wed, 25 Mar 2026 21:28:12 +0800
Subject: [PATCH] [ObjC][Preprocessor] Handle @import directive as a
pp-directive
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
.../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 | 100 +++++++++++++-----
clang/lib/Lex/PPLexerChange.cpp | 13 +--
clang/lib/Lex/Preprocessor.cpp | 97 +----------------
clang/test/Modules/lookup.cpp | 7 +-
clang/test/Modules/no-stale-modtime.m | 3 +-
clang/test/Modules/objc-at-import.m | 42 ++++++++
.../Lex/DependencyDirectivesScannerTest.cpp | 2 +-
clang/utils/ClangVisualizers/clang.natvis | 1 -
14 files changed, 191 insertions(+), 179 deletions(-)
create mode 100644 clang/test/Modules/objc-at-import.m
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..f11436ee1bbc1 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();
@@ -1205,8 +1187,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);
@@ -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