[clang] [ObjC] Handle @import directive as a pp-directive (PR #157726)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 9 10:51:17 PDT 2025
https://github.com/yronglin created https://github.com/llvm/llvm-project/pull/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;
```
>From 2cce3d119d755a805b093a69b391d541d50beb78 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Sat, 17 Aug 2024 16:54:26 +0800
Subject: [PATCH 01/22] [Clang] Add peekNextPPToken, makes peek next token
without side-effects
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/include/clang/Lex/Lexer.h | 10 ++++----
clang/include/clang/Lex/Preprocessor.h | 8 ++++++-
clang/include/clang/Lex/TokenLexer.h | 7 +++---
clang/lib/Lex/Lexer.cpp | 21 +++++++++--------
clang/lib/Lex/PPMacroExpansion.cpp | 32 ++++++++++++--------------
clang/lib/Lex/TokenLexer.cpp | 10 ++++----
6 files changed, 46 insertions(+), 42 deletions(-)
diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h
index bb65ae010cffa..a595cda1eaa77 100644
--- a/clang/include/clang/Lex/Lexer.h
+++ b/clang/include/clang/Lex/Lexer.h
@@ -124,7 +124,7 @@ class Lexer : public PreprocessorLexer {
//===--------------------------------------------------------------------===//
// Context that changes as the file is lexed.
// NOTE: any state that mutates when in raw mode must have save/restore code
- // in Lexer::isNextPPTokenLParen.
+ // in Lexer::peekNextPPToken.
// BufferPtr - Current pointer into the buffer. This is the next character
// to be lexed.
@@ -642,10 +642,10 @@ class Lexer : public PreprocessorLexer {
BufferPtr = TokEnd;
}
- /// isNextPPTokenLParen - Return 1 if the next unexpanded token will return a
- /// tok::l_paren token, 0 if it is something else and 2 if there are no more
- /// tokens in the buffer controlled by this lexer.
- unsigned isNextPPTokenLParen();
+ /// peekNextPPToken - Return std::nullopt if there are no more tokens in the
+ /// buffer controlled by this lexer, otherwise return the next unexpanded
+ /// token.
+ std::optional<Token> peekNextPPToken();
//===--------------------------------------------------------------------===//
// Lexer character reading interfaces.
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index f2dfd3a349b8b..52f4765280582 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -2282,7 +2282,9 @@ class Preprocessor {
/// Determine whether the next preprocessor token to be
/// lexed is a '('. If so, consume the token and return true, if not, this
/// method should have no observable side-effect on the lexed tokens.
- bool isNextPPTokenLParen();
+ bool isNextPPTokenLParen() {
+ return peekNextPPToken().value_or(Token{}).is(tok::l_paren);
+ }
private:
/// Identifiers used for SEH handling in Borland. These are only
@@ -2661,6 +2663,10 @@ class Preprocessor {
void removeCachedMacroExpandedTokensOfLastLexer();
+ /// Peek the next token. If so, return the token, if not, this
+ /// method should have no observable side-effect on the lexed tokens.
+ std::optional<Token> peekNextPPToken();
+
/// After reading "MACRO(", this method is invoked to read all of the formal
/// arguments specified for the macro invocation. Returns null on error.
MacroArgs *ReadMacroCallArgumentList(Token &MacroName, MacroInfo *MI,
diff --git a/clang/include/clang/Lex/TokenLexer.h b/clang/include/clang/Lex/TokenLexer.h
index 4d229ae610674..777b4e6266c71 100644
--- a/clang/include/clang/Lex/TokenLexer.h
+++ b/clang/include/clang/Lex/TokenLexer.h
@@ -139,10 +139,9 @@ class TokenLexer {
void Init(const Token *TokArray, unsigned NumToks, bool DisableMacroExpansion,
bool OwnsTokens, bool IsReinject);
- /// If the next token lexed will pop this macro off the
- /// expansion stack, return 2. If the next unexpanded token is a '(', return
- /// 1, otherwise return 0.
- unsigned isNextTokenLParen() const;
+ /// If the next token lexed will pop this macro off the expansion stack,
+ /// return std::nullopt, otherwise return the next unexpanded token.
+ std::optional<Token> peekNextPPToken() const;
/// Lex and return a token from this macro stream.
bool Lex(Token &Tok);
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index 93200458f04b4..8e977eac8c983 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -3200,18 +3200,19 @@ bool Lexer::LexEndOfFile(Token &Result, const char *CurPtr) {
return PP->HandleEndOfFile(Result, isPragmaLexer());
}
-/// isNextPPTokenLParen - Return 1 if the next unexpanded token lexed from
-/// the specified lexer will return a tok::l_paren token, 0 if it is something
-/// else and 2 if there are no more tokens in the buffer controlled by the
-/// lexer.
-unsigned Lexer::isNextPPTokenLParen() {
+/// peekNextPPToken - Return std::nullopt if there are no more tokens in the
+/// buffer controlled by this lexer, otherwise return the next unexpanded
+/// token.
+std::optional<Token> Lexer::peekNextPPToken() {
assert(!LexingRawMode && "How can we expand a macro from a skipping buffer?");
if (isDependencyDirectivesLexer()) {
if (NextDepDirectiveTokenIndex == DepDirectives.front().Tokens.size())
- return 2;
- return DepDirectives.front().Tokens[NextDepDirectiveTokenIndex].is(
- tok::l_paren);
+ return std::nullopt;
+ Token Result;
+ (void)convertDependencyDirectiveToken(
+ DepDirectives.front().Tokens[NextDepDirectiveTokenIndex], Result);
+ return Result;
}
// Switch to 'skipping' mode. This will ensure that we can lex a token
@@ -3240,8 +3241,8 @@ unsigned Lexer::isNextPPTokenLParen() {
LexingRawMode = false;
if (Tok.is(tok::eof))
- return 2;
- return Tok.is(tok::l_paren);
+ return std::nullopt;
+ return Tok;
}
/// Find the end of a version control conflict marker.
diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp
index 37ac1bf07e9c0..585990f60c98a 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -418,42 +418,40 @@ static bool isTrivialSingleTokenExpansion(const MacroInfo *MI,
return !llvm::is_contained(MI->params(), II);
}
-/// isNextPPTokenLParen - Determine whether the next preprocessor token to be
-/// lexed is a '('. If so, consume the token and return true, if not, this
-/// method should have no observable side-effect on the lexed tokens.
-bool Preprocessor::isNextPPTokenLParen() {
+/// isNextPPTokenLParen - Peek the next token. If so, return the token, if not,
+/// this method should have no observable side-effect on the lexed tokens.
+std::optional<Token> Preprocessor::peekNextPPToken() {
// Do some quick tests for rejection cases.
- unsigned Val;
+ std::optional<Token> Val;
if (CurLexer)
- Val = CurLexer->isNextPPTokenLParen();
+ Val = CurLexer->peekNextPPToken();
else
- Val = CurTokenLexer->isNextTokenLParen();
+ Val = CurTokenLexer->peekNextPPToken();
- if (Val == 2) {
+ if (!Val) {
// We have run off the end. If it's a source file we don't
// examine enclosing ones (C99 5.1.1.2p4). Otherwise walk up the
// macro stack.
if (CurPPLexer)
- return false;
+ return std::nullopt;
for (const IncludeStackInfo &Entry : llvm::reverse(IncludeMacroStack)) {
if (Entry.TheLexer)
- Val = Entry.TheLexer->isNextPPTokenLParen();
+ Val = Entry.TheLexer->peekNextPPToken();
else
- Val = Entry.TheTokenLexer->isNextTokenLParen();
+ Val = Entry.TheTokenLexer->peekNextPPToken();
- if (Val != 2)
+ if (Val)
break;
// Ran off the end of a source file?
if (Entry.ThePPLexer)
- return false;
+ return std::nullopt;
}
}
- // Okay, if we know that the token is a '(', lex it and return. Otherwise we
- // have found something that isn't a '(' or we found the end of the
- // translation unit. In either case, return false.
- return Val == 1;
+ // Okay, we found the token and return. Otherwise we found the end of the
+ // translation unit.
+ return Val;
}
/// HandleMacroExpandedIdentifier - If an identifier token is read that is to be
diff --git a/clang/lib/Lex/TokenLexer.cpp b/clang/lib/Lex/TokenLexer.cpp
index 6e93416e01c0c..fbb8c4262d6da 100644
--- a/clang/lib/Lex/TokenLexer.cpp
+++ b/clang/lib/Lex/TokenLexer.cpp
@@ -921,13 +921,13 @@ bool TokenLexer::pasteTokens(Token &LHSTok, ArrayRef<Token> TokenStream,
}
/// isNextTokenLParen - If the next token lexed will pop this macro off the
-/// expansion stack, return 2. If the next unexpanded token is a '(', return
-/// 1, otherwise return 0.
-unsigned TokenLexer::isNextTokenLParen() const {
+/// expansion stack, return std::nullopt, otherwise return the next unexpanded
+/// token.
+std::optional<Token> TokenLexer::peekNextPPToken() const {
// Out of tokens?
if (isAtEnd())
- return 2;
- return Tokens[CurTokenIdx].is(tok::l_paren);
+ return std::nullopt;
+ return Tokens[CurTokenIdx];
}
/// isParsingPreprocessorDirective - Return true if we are in the middle of a
>From 04ddbf6499b27d4094efdb528825c85683c64ea6 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 26 May 2025 23:17:00 +0800
Subject: [PATCH 02/22] [C++20][Modules] Implement P1857R3 Modules Dependency
Discovery
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
.../AnnotateFunctions/AnnotateFunctions.cpp | 2 +-
.../include/clang/Basic/DiagnosticLexKinds.td | 20 +-
.../clang/Basic/DiagnosticParseKinds.td | 62 ++-
clang/include/clang/Basic/IdentifierTable.h | 26 +-
clang/include/clang/Basic/TokenKinds.def | 6 +
.../include/clang/Frontend/CompilerInstance.h | 2 +-
.../include/clang/Lex/CodeCompletionHandler.h | 8 +
clang/include/clang/Lex/Preprocessor.h | 115 ++++--
clang/include/clang/Lex/Token.h | 7 +
clang/include/clang/Parse/Parser.h | 2 +
clang/include/clang/Sema/Sema.h | 6 +-
clang/lib/Basic/IdentifierTable.cpp | 4 +-
clang/lib/Frontend/CompilerInstance.cpp | 12 +-
.../lib/Frontend/PrintPreprocessedOutput.cpp | 8 +-
clang/lib/Lex/DependencyDirectivesScanner.cpp | 29 +-
clang/lib/Lex/Lexer.cpp | 43 +-
clang/lib/Lex/PPDirectives.cpp | 254 +++++++++++-
clang/lib/Lex/Preprocessor.cpp | 382 +++++++++---------
clang/lib/Lex/TokenConcatenation.cpp | 8 +-
clang/lib/Lex/TokenLexer.cpp | 4 +-
clang/lib/Parse/Parser.cpp | 93 ++---
clang/lib/Sema/SemaModule.cpp | 75 ++--
.../DependencyScanning/ModuleDepCollector.cpp | 2 +-
clang/test/CXX/basic/basic.link/p1.cpp | 145 +++++--
clang/test/CXX/basic/basic.link/p3.cpp | 67 +--
.../basic.scope/basic.scope.namespace/p2.cpp | 82 ++--
clang/test/CXX/lex/lex.pptoken/p3-2a.cpp | 15 +-
.../CXX/module/basic/basic.def.odr/p6.cppm | 174 ++++++--
.../basic/basic.link/module-declaration.cpp | 64 +--
.../dcl.module/dcl.module.import/p1.cppm | 38 +-
.../dcl.module/dcl.module.interface/p1.cppm | 39 +-
.../test/CXX/module/dcl.dcl/dcl.module/p1.cpp | 44 +-
.../test/CXX/module/dcl.dcl/dcl.module/p5.cpp | 65 ++-
clang/test/CXX/module/module.interface/p1.cpp | 42 +-
clang/test/CXX/module/module.interface/p2.cpp | 26 +-
clang/test/CXX/module/module.unit/p8.cpp | 48 ++-
clang/test/Driver/modules.cpp | 31 +-
clang/test/Modules/pr121066.cpp | 3 +-
...-aware-new-delete-transparent-contexts.cpp | 20 +-
.../ASTMatchers/ASTMatchersNodeTest.cpp | 3 +-
.../Lex/DependencyDirectivesScannerTest.cpp | 10 +-
clang/unittests/Lex/ModuleDeclStateTest.cpp | 2 +-
42 files changed, 1376 insertions(+), 712 deletions(-)
diff --git a/clang/examples/AnnotateFunctions/AnnotateFunctions.cpp b/clang/examples/AnnotateFunctions/AnnotateFunctions.cpp
index d872020c2d8a3..22a3eb97f938b 100644
--- a/clang/examples/AnnotateFunctions/AnnotateFunctions.cpp
+++ b/clang/examples/AnnotateFunctions/AnnotateFunctions.cpp
@@ -65,7 +65,7 @@ class PragmaAnnotateHandler : public PragmaHandler {
Token Tok;
PP.LexUnexpandedToken(Tok);
if (Tok.isNot(tok::eod))
- PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
+ PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "#pragma";
if (HandledDecl) {
DiagnosticsEngine &D = PP.getDiagnostics();
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 723f5d48b4f5f..b2df3e7929434 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -466,6 +466,9 @@ def err_pp_embed_device_file : Error<
def ext_pp_extra_tokens_at_eol : ExtWarn<
"extra tokens at end of #%0 directive">, InGroup<ExtraTokens>;
+def ext_pp_extra_tokens_at_module_directive_eol
+ : ExtWarn<"extra tokens at end of '%0' directive">,
+ InGroup<ExtraTokens>;
def ext_pp_comma_expr : Extension<"comma operator in operand of #if">;
def ext_pp_bad_vaargs_use : Extension<
@@ -495,8 +498,8 @@ 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 #%0 directive within macro arguments is not supported">;
+def err_embedded_directive : Error<"embedding a %select{#|C++ }0%1 directive "
+ "within macro arguments is not supported">;
def ext_embedded_directive : Extension<
"embedding a directive within macro arguments has undefined behavior">,
InGroup<DiagGroup<"embedded-directive">>;
@@ -983,6 +986,19 @@ def warn_module_conflict : Warning<
InGroup<ModuleConflict>;
// C++20 modules
+def err_pp_expected_module_name_or_header_name
+ : Error<"expected module name or header name">;
+def err_pp_expected_semi_after_module_or_import
+ : Error<"'%select{module|import}0' directive must end with a ';' on the "
+ "same line">;
+def err_module_decl_in_header
+ : Error<"module declaration must not come from an #include directive">;
+def err_pp_cond_span_module_decl
+ : Error<"preprocessor conditionals shall not span a module declaration">;
+def err_pp_module_expected_ident
+ : Error<"expected a module name after '%select{module|import}0'">;
+def err_pp_unsupported_module_partition
+ : Error<"module partitions are only supported for C++20 onwards">;
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 3aa36ad59d0b9..ac10fbfd67e17 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1760,38 +1760,36 @@ def ext_bit_int : Extension<
} // end of Parse Issue category.
let CategoryName = "Modules Issue" in {
-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_attribute_not_module_attr : Error<
- "%0 attribute cannot be applied to a module">;
-def err_keyword_not_module_attr : Error<
- "%0 cannot be applied to a module">;
-def err_attribute_not_import_attr : Error<
- "%0 attribute cannot be applied to a module import">;
-def err_keyword_not_import_attr : Error<
- "%0 cannot be applied to a module import">;
-def err_module_expected_semi : Error<
- "expected ';' after module name">;
-def err_global_module_introducer_not_at_start : Error<
- "'module;' introducing a global module fragment can appear only "
- "at the start of the translation unit">;
-def err_module_fragment_exported : Error<
- "%select{global|private}0 module fragment cannot be exported">;
-def err_private_module_fragment_expected_semi : Error<
- "expected ';' after private module fragment declaration">;
-def err_missing_before_module_end : Error<"expected %0 at end of module">;
-def err_unsupported_module_partition : Error<
- "module partitions are only supported for C++20 onwards">;
-def err_import_not_allowed_here : Error<
- "imports must immediately follow the module declaration">;
-def err_partition_import_outside_module : Error<
- "module partition imports must be within a module purview">;
-def err_import_in_wrong_fragment : Error<
- "module%select{| partition}0 imports cannot be in the %select{global|private}1 module fragment">;
-
-def err_export_empty : Error<"export declaration cannot be empty">;
+ def err_unexpected_module_import_decl
+ : Error<"%select{module|import}0 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_attribute_not_module_attr
+ : Error<"%0 attribute cannot be applied to a module">;
+ def err_keyword_not_module_attr : Error<"%0 cannot be applied to a module">;
+ def err_attribute_not_import_attr
+ : Error<"%0 attribute cannot be applied to a module import">;
+ def err_keyword_not_import_attr
+ : Error<"%0 cannot be applied to a module import">;
+ def err_module_expected_semi : Error<"expected ';' after module name">;
+ def err_global_module_introducer_not_at_start
+ : Error<"'module;' introducing a global module fragment can appear only "
+ "at the start of the translation unit">;
+ def err_module_fragment_exported
+ : Error<"%select{global|private}0 module fragment cannot be exported">;
+ def err_private_module_fragment_expected_semi
+ : Error<"expected ';' after private module fragment declaration">;
+ def err_missing_before_module_end : Error<"expected %0 at end of module">;
+ def err_import_not_allowed_here
+ : Error<"imports must immediately follow the module declaration">;
+ def err_partition_import_outside_module
+ : Error<"module partition imports must be within a module purview">;
+ def err_import_in_wrong_fragment
+ : Error<"module%select{| partition}0 imports cannot be in the "
+ "%select{global|private}1 module fragment">;
+
+ def err_export_empty : Error<"export declaration cannot be empty">;
}
let CategoryName = "Generics Issue" in {
diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h
index 54540193cfcc0..add6c6ac629a1 100644
--- a/clang/include/clang/Basic/IdentifierTable.h
+++ b/clang/include/clang/Basic/IdentifierTable.h
@@ -179,6 +179,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;
@@ -215,8 +219,9 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
IsCPPOperatorKeyword(false), NeedsHandleIdentifier(false),
IsFromAST(false), ChangedAfterLoad(false), FEChangedAfterLoad(false),
RevertedTokenID(false), OutOfDate(false), IsModulesImport(false),
- IsMangledOpenMPVariantName(false), IsDeprecatedMacro(false),
- IsRestrictExpansion(false), IsFinal(false), IsKeywordInCpp(false) {}
+ IsModulesDecl(false), IsMangledOpenMPVariantName(false),
+ IsDeprecatedMacro(false), IsRestrictExpansion(false), IsFinal(false),
+ IsKeywordInCpp(false) {}
public:
IdentifierInfo(const IdentifierInfo &) = delete;
@@ -528,6 +533,18 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
RecomputeNeedsHandleIdentifier();
}
+ /// Determine whether this is the contextual keyword \c module.
+ bool isModulesDeclaration() const { return IsModulesDecl; }
+
+ /// Set whether this identifier is the contextual keyword \c module.
+ void setModulesDeclaration(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; }
@@ -745,10 +762,11 @@ class IdentifierTable {
// contents.
II->Entry = &Entry;
- // If this is the 'import' contextual keyword, mark it as such.
+ // If this is the 'import' or 'module' contextual keyword, mark it as such.
if (Name == "import")
II->setModulesImport(true);
-
+ else 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 94e72fea56a68..7750c84dbef78 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -133,6 +133,9 @@ PPKEYWORD(pragma)
// C23 & C++26 #embed
PPKEYWORD(embed)
+// C++20 Module Directive
+PPKEYWORD(module)
+
// GNU Extensions.
PPKEYWORD(import)
PPKEYWORD(include_next)
@@ -1023,6 +1026,9 @@ ANNOTATION(module_include)
ANNOTATION(module_begin)
ANNOTATION(module_end)
+// Annotations for C++, Clang and Objective-C named modules.
+ANNOTATION(module_name)
+
// Annotation for a header_name token that has been looked up and transformed
// into the name of a header unit.
ANNOTATION(header_unit)
diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h
index 0ae490f0e8073..112d3b00160fd 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -863,7 +863,7 @@ class CompilerInstance : public ModuleLoader {
/// load it.
ModuleLoadResult findOrCompileModuleAndReadAST(StringRef ModuleName,
SourceLocation ImportLoc,
- SourceLocation ModuleNameLoc,
+ SourceRange ModuleNameRange,
bool IsInclusionDirective);
/// Creates a \c CompilerInstance for compiling a module.
diff --git a/clang/include/clang/Lex/CodeCompletionHandler.h b/clang/include/clang/Lex/CodeCompletionHandler.h
index bd3e05a36bb33..2ef29743415ae 100644
--- a/clang/include/clang/Lex/CodeCompletionHandler.h
+++ b/clang/include/clang/Lex/CodeCompletionHandler.h
@@ -13,12 +13,15 @@
#ifndef LLVM_CLANG_LEX_CODECOMPLETIONHANDLER_H
#define LLVM_CLANG_LEX_CODECOMPLETIONHANDLER_H
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/StringRef.h"
namespace clang {
class IdentifierInfo;
class MacroInfo;
+using ModuleIdPath = ArrayRef<IdentifierLoc>;
/// Callback handler that receives notifications when performing code
/// completion within the preprocessor.
@@ -70,6 +73,11 @@ class CodeCompletionHandler {
/// file where we expect natural language, e.g., a comment, string, or
/// \#error directive.
virtual void CodeCompleteNaturalLanguage() { }
+
+ /// Callback invoked when performing code completion inside the module name
+ /// part of an import directive.
+ virtual void CodeCompleteModuleImport(SourceLocation ImportLoc,
+ ModuleIdPath Path) {}
};
}
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 52f4765280582..32dfcfea4256b 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -48,6 +48,7 @@
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Registry.h"
+#include "llvm/Support/TrailingObjects.h"
#include <cassert>
#include <cstddef>
#include <cstdint>
@@ -82,6 +83,7 @@ class PreprocessorLexer;
class PreprocessorOptions;
class ScratchBuffer;
class TargetInfo;
+class ModuleNameLoc;
namespace Builtin {
class Context;
@@ -332,8 +334,9 @@ class Preprocessor {
/// lexed, if any.
SourceLocation ModuleImportLoc;
- /// The import path for named module that we're currently processing.
- SmallVector<IdentifierLoc, 2> NamedModuleImportPath;
+ /// The source location of the \c module contextual keyword we just
+ /// lexed, if any.
+ SourceLocation ModuleDeclLoc;
llvm::DenseMap<FileID, SmallVector<const char *>> CheckPoints;
unsigned CheckPointCounter = 0;
@@ -344,6 +347,21 @@ class Preprocessor {
/// Whether the last token we lexed was an '@'.
bool LastTokenWasAt = false;
+ /// Whether we're importing a standard C++20 named Modules.
+ bool ImportingCXXNamedModules = false;
+
+ /// Whether we're declaring a standard C++20 named Modules.
+ bool DeclaringCXXNamedModules = false;
+
+ struct ExportContextualKeywordInfo {
+ Token ExportTok;
+ bool TokAtPhysicalStartOfLine;
+ };
+
+ /// Whether the last token we lexed was an 'export' keyword.
+ std::optional<ExportContextualKeywordInfo> LastTokenWasExportKeyword =
+ std::nullopt;
+
/// A position within a C++20 import-seq.
class StdCXXImportSeq {
public:
@@ -547,12 +565,7 @@ class Preprocessor {
reset();
}
- void handleIdentifier(IdentifierInfo *Identifier) {
- if (isModuleCandidate() && Identifier)
- Name += Identifier->getName().str();
- else if (!isNamedModule())
- reset();
- }
+ void handleModuleName(ModuleNameLoc *Path);
void handleColon() {
if (isModuleCandidate())
@@ -561,13 +574,6 @@ class Preprocessor {
reset();
}
- void handlePeriod() {
- if (isModuleCandidate())
- Name += ".";
- else if (!isNamedModule())
- reset();
- }
-
void handleSemi() {
if (!Name.empty() && isModuleCandidate()) {
if (State == InterfaceCandidate)
@@ -622,10 +628,6 @@ class Preprocessor {
ModuleDeclSeq ModuleDeclState;
- /// Whether the module import expects an identifier next. Otherwise,
- /// it expects a '.' or ';'.
- bool ModuleImportExpectsIdentifier = false;
-
/// The identifier and source location of the currently-active
/// \#pragma clang arc_cf_code_audited begin.
IdentifierLoc PragmaARCCFCodeAuditedInfo;
@@ -1759,6 +1761,21 @@ class Preprocessor {
/// Lex the parameters for an #embed directive, returns nullopt on error.
std::optional<LexEmbedParametersResult> LexEmbedParameters(Token &Current,
bool ForHasEmbed);
+ bool LexModuleNameContinue(Token &Tok, SourceLocation UseLoc,
+ SmallVectorImpl<IdentifierLoc> &Path,
+ bool AllowMacroExpansion = true);
+ void EnterModuleSuffixTokenStream(ArrayRef<Token> Toks);
+ void HandleCXXImportDirective(Token Import);
+ void HandleCXXModuleDirective(Token Module);
+
+ /// Callback invoked when the lexer sees one of export, import or module token
+ /// at the start of a line.
+ ///
+ /// This consumes the import, module directive, modifies the
+ /// lexer/preprocessor state, and advances the lexer(s) so that the next token
+ /// read is the correct one.
+ bool HandleModuleContextualKeyword(Token &Result,
+ bool TokAtPhysicalStartOfLine);
bool LexAfterModuleImport(Token &Result);
void CollectPpImportSuffix(SmallVectorImpl<Token> &Toks);
@@ -2344,7 +2361,7 @@ class Preprocessor {
///
/// \return The location of the end of the directive (the terminating
/// newline).
- SourceLocation CheckEndOfDirective(const char *DirType,
+ SourceLocation CheckEndOfDirective(StringRef DirType,
bool EnableMacros = false);
/// Read and discard all tokens remaining on the current line until
@@ -2426,11 +2443,12 @@ class Preprocessor {
}
/// If we're importing a standard C++20 Named Modules.
- bool isInImportingCXXNamedModules() const {
- // NamedModuleImportPath will be non-empty only if we're importing
- // Standard C++ named modules.
- return !NamedModuleImportPath.empty() && getLangOpts().CPlusPlusModules &&
- !IsAtImport;
+ bool isImportingCXXNamedModules() const {
+ return getLangOpts().CPlusPlusModules && ImportingCXXNamedModules;
+ }
+
+ bool isDeclaringCXXNamedModules() const {
+ return getLangOpts().CPlusPlusModules && DeclaringCXXNamedModules;
}
/// Allocate a new MacroInfo object with the provided SourceLocation.
@@ -3084,6 +3102,53 @@ struct EmbedAnnotationData {
StringRef FileName;
};
+/// Represents module name annotation data.
+///
+/// module-name:
+/// module-name-qualifier[opt] identifier
+///
+/// partition-name: [C++20]
+/// : module-name-qualifier[opt] identifier
+///
+/// module-name-qualifier
+/// module-name-qualifier[opt] identifier .
+class ModuleNameLoc final
+ : llvm::TrailingObjects<ModuleNameLoc, IdentifierLoc> {
+ friend TrailingObjects;
+ unsigned NumIdentifierLocs;
+
+ unsigned numTrailingObjects(OverloadToken<IdentifierLoc>) const {
+ return getNumIdentifierLocs();
+ }
+
+ ModuleNameLoc(ModuleIdPath Path) : NumIdentifierLocs(Path.size()) {
+ (void)llvm::copy(Path, getTrailingObjects<IdentifierLoc>());
+ }
+
+public:
+ static std::string stringFromModuleIdPath(ModuleIdPath Path);
+ static ModuleNameLoc *Create(Preprocessor &PP, ModuleIdPath Path);
+ static Token CreateAnnotToken(Preprocessor &PP, ModuleIdPath Path);
+ unsigned getNumIdentifierLocs() const { return NumIdentifierLocs; }
+ ModuleIdPath getModuleIdPath() const {
+ return {getTrailingObjects<IdentifierLoc>(), getNumIdentifierLocs()};
+ }
+
+ SourceLocation getBeginLoc() const {
+ return getModuleIdPath().front().getLoc();
+ }
+ SourceLocation getEndLoc() const {
+ auto &Last = getModuleIdPath().back();
+ return Last.getLoc().getLocWithOffset(
+ Last.getIdentifierInfo()->getLength());
+ }
+ SourceRange getRange() const { return {getBeginLoc(), getEndLoc()}; }
+
+ std::string str() const;
+ void print(llvm::raw_ostream &OS) const;
+ void dump() const { print(llvm::errs()); }
+};
+
/// Registry of pragma handlers added by plugins
using PragmaHandlerRegistry = llvm::Registry<PragmaHandler>;
diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h
index 4f29fb7d11415..8e81207ddf8d7 100644
--- a/clang/include/clang/Lex/Token.h
+++ b/clang/include/clang/Lex/Token.h
@@ -231,6 +231,9 @@ class Token {
PtrData = const_cast<char*>(Ptr);
}
+ template <class T> T getAnnotationValueAs() const {
+ return static_cast<T>(getAnnotationValue());
+ }
void *getAnnotationValue() const {
assert(isAnnotation() && "Used AnnotVal on non-annotation token");
return PtrData;
@@ -289,6 +292,10 @@ class Token {
/// Return the ObjC keyword kind.
tok::ObjCKeywordKind getObjCKeywordID() const;
+ /// Return true if we have an C++20 Modules contextual keyword(export, import
+ /// or module).
+ bool isModuleContextualKeyword(bool AllowExport = true) const;
+
bool isSimpleTypeSpecifier(const LangOptions &LangOpts) const;
/// Return true if this token has trigraphs or escaped newlines in it.
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index c4bef4729fd36..a59a99bbac7c6 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1079,6 +1079,8 @@ class Parser : public CodeCompletionHandler {
unsigned ArgumentIndex) override;
void CodeCompleteIncludedFile(llvm::StringRef Dir, bool IsAngled) override;
void CodeCompleteNaturalLanguage() override;
+ void CodeCompleteModuleImport(SourceLocation ImportLoc,
+ ModuleIdPath Path) override;
///@}
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 6ce9ae588b637..5fa6b17c2df63 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -146,6 +146,7 @@ class MangleNumberingContext;
typedef ArrayRef<IdentifierLoc> ModuleIdPath;
class ModuleLoader;
class MultiLevelTemplateArgumentList;
+class ModuleNameLoc;
struct NormalizedConstraint;
class ObjCInterfaceDecl;
class ObjCMethodDecl;
@@ -9918,7 +9919,8 @@ class Sema final : public SemaBase {
/// of a module interface or implementation.
DeclGroupPtrTy ActOnModuleDecl(SourceLocation StartLoc,
SourceLocation ModuleLoc, ModuleDeclKind MDK,
- ModuleIdPath Path, ModuleIdPath Partition,
+ ModuleNameLoc *PathLoc,
+ ModuleNameLoc *PartitionLoc,
ModuleImportState &ImportState);
/// The parser has processed a global-module-fragment declaration that begins
@@ -9943,7 +9945,7 @@ class Sema final : public SemaBase {
/// \param IsPartition If the name is for a partition.
DeclResult ActOnModuleImport(SourceLocation StartLoc,
SourceLocation ExportLoc,
- SourceLocation ImportLoc, ModuleIdPath Path,
+ SourceLocation ImportLoc, ModuleNameLoc *PathLoc,
bool IsPartition = false);
DeclResult ActOnModuleImport(SourceLocation StartLoc,
SourceLocation ExportLoc,
diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp
index 099f2e8a18da5..fd9ed7721cb27 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -350,8 +350,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").setModulesDeclaration(true);
}
/// Checks if the specified token kind represents a keyword in the
@@ -483,6 +484,7 @@ tok::PPKeywordKind IdentifierInfo::getPPKeywordID() const {
CASE( 6, 'd', 'f', define);
CASE( 6, 'i', 'n', ifndef);
CASE( 6, 'i', 'p', import);
+ CASE(6, 'm', 'd', module);
CASE( 6, 'p', 'a', pragma);
CASE( 7, 'd', 'f', defined);
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index cc39049167687..b3b9c1abc3618 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -1866,8 +1866,8 @@ static ModuleSource selectModuleSource(
}
ModuleLoadResult CompilerInstance::findOrCompileModuleAndReadAST(
- StringRef ModuleName, SourceLocation ImportLoc,
- SourceLocation ModuleNameLoc, bool IsInclusionDirective) {
+ StringRef ModuleName, SourceLocation ImportLoc, SourceRange ModuleNameRange,
+ bool IsInclusionDirective) {
// Search for a module with the given name.
HeaderSearch &HS = PP->getHeaderSearchInfo();
Module *M =
@@ -1884,10 +1884,11 @@ ModuleLoadResult CompilerInstance::findOrCompileModuleAndReadAST(
std::string ModuleFilename;
ModuleSource Source =
selectModuleSource(M, ModuleName, ModuleFilename, BuiltModules, HS);
+ SourceLocation ModuleNameLoc = ModuleNameRange.getBegin();
if (Source == MS_ModuleNotFound) {
// We can't find a module, error out here.
getDiagnostics().Report(ModuleNameLoc, diag::err_module_not_found)
- << ModuleName << SourceRange(ImportLoc, ModuleNameLoc);
+ << ModuleName << ModuleNameRange;
return nullptr;
}
if (ModuleFilename.empty()) {
@@ -2073,8 +2074,11 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
MM.cacheModuleLoad(*Path[0].getIdentifierInfo(), Module);
} else {
+ SourceLocation ModuleNameEndLoc = Path.back().getLoc().getLocWithOffset(
+ Path.back().getIdentifierInfo()->getLength());
ModuleLoadResult Result = findOrCompileModuleAndReadAST(
- ModuleName, ImportLoc, ModuleNameLoc, IsInclusionDirective);
+ ModuleName, ImportLoc, SourceRange{ModuleNameLoc, ModuleNameEndLoc},
+ IsInclusionDirective);
if (!Result.isNormal())
return Result;
if (!Result)
diff --git a/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp
index 22ba4cee182af..79f0769b8bdf5 100644
--- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp
+++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp
@@ -759,7 +759,8 @@ void PrintPPOutputPPCallbacks::HandleWhitespaceBeforeTok(const Token &Tok,
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.is(tok::annot_embed)))
+ !Tok.is(tok::annot_repl_input_end) && !Tok.is(tok::annot_embed) &&
+ !Tok.is(tok::annot_module_name)))
return;
// EmittedDirectiveOnThisLine takes priority over RequireSameLine.
@@ -979,6 +980,11 @@ static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok,
*Callbacks->OS << static_cast<int>(Byte);
PrintComma = true;
}
+ } else if (Tok.is(tok::annot_module_name)) {
+ Tok.getAnnotationValueAs<ModuleNameLoc *>()->print(*Callbacks->OS);
+ PP.Lex(Tok);
+ IsStartOfLine = true;
+ continue;
} else if (Tok.isAnnotation()) {
// Ignore annotation tokens created by pragmas - the pragmas themselves
// will be reproduced in the preprocessed output.
diff --git a/clang/lib/Lex/DependencyDirectivesScanner.cpp b/clang/lib/Lex/DependencyDirectivesScanner.cpp
index 088d1cc96e3a2..245ce25b823d9 100644
--- a/clang/lib/Lex/DependencyDirectivesScanner.cpp
+++ b/clang/lib/Lex/DependencyDirectivesScanner.cpp
@@ -497,21 +497,32 @@ bool Scanner::lexModuleDirectiveBody(DirectiveKind Kind, const char *&First,
const char *DirectiveLoc = Input.data() + CurDirToks.front().Offset;
for (;;) {
const dependency_directives_scan::Token &Tok = lexToken(First, End);
- if (Tok.is(tok::eof))
+ if (Tok.isOneOf(tok::eof, tok::eod))
return reportError(
DirectiveLoc,
diag::err_dep_source_scanner_missing_semi_after_at_import);
if (Tok.is(tok::semi))
break;
}
+
+ // Skip extra tokens after semi in C++20 Modules directive.
+ bool IsCXXModules = Kind == DirectiveKind::cxx_export_import_decl ||
+ Kind == DirectiveKind::cxx_export_module_decl ||
+ Kind == DirectiveKind::cxx_import_decl ||
+ Kind == DirectiveKind::cxx_module_decl;
+ if (IsCXXModules)
+ lexPPDirectiveBody(First, End);
pushDirective(Kind);
skipWhitespace(First, End);
if (First == End)
return false;
- if (!isVerticalWhitespace(*First))
- return reportError(
- DirectiveLoc, diag::err_dep_source_scanner_unexpected_tokens_at_import);
- skipNewline(First, End);
+ if (!IsCXXModules) {
+ if (!isVerticalWhitespace(*First))
+ return reportError(
+ DirectiveLoc,
+ diag::err_dep_source_scanner_unexpected_tokens_at_import);
+ skipNewline(First, End);
+ }
return false;
}
@@ -846,8 +857,9 @@ bool Scanner::lexPPLine(const char *&First, const char *const End) {
if (*First == '@')
return lexAt(First, End);
- if (*First == 'i' || *First == 'e' || *First == 'm')
- return lexModule(First, End);
+ // if (!LangOpts.CPlusPlusModules && (*First == 'i' || *First == 'e' || *First
+ // == 'm'))
+ // return lexModule(First, End);
if (*First == '_') {
if (isNextIdentifierOrSkipLine("_Pragma", First, End))
@@ -860,7 +872,8 @@ bool Scanner::lexPPLine(const char *&First, const char *const End) {
TheLexer.setParsingPreprocessorDirective(true);
auto ScEx2 = make_scope_exit(
[&]() { TheLexer.setParsingPreprocessorDirective(false); });
-
+ if (*First == 'i' || *First == 'e' || *First == 'm')
+ return lexModule(First, End);
// Lex '#'.
const dependency_directives_scan::Token &HashTok = lexToken(First, End);
if (HashTok.is(tok::hashhash)) {
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index 8e977eac8c983..df9e0f1350903 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -73,6 +73,19 @@ tok::ObjCKeywordKind Token::getObjCKeywordID() const {
return specId ? specId->getObjCKeywordID() : tok::objc_not_keyword;
}
+/// Return true if we have an C++20 Modules contextual keyword(export, import
+/// or module).
+bool Token::isModuleContextualKeyword(bool AllowExport) const {
+ if (AllowExport && is(tok::kw_export))
+ return true;
+ if (isOneOf(tok::kw_import, tok::kw_module))
+ return true;
+ if (isNot(tok::identifier))
+ return false;
+ const auto *II = getIdentifierInfo();
+ return II->isModulesImport() || II->isModulesDeclaration();
+}
+
/// Determine whether the token kind starts a simple-type-specifier.
bool Token::isSimpleTypeSpecifier(const LangOptions &LangOpts) const {
switch (getKind()) {
@@ -4024,11 +4037,17 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
case 'o': case 'p': case 'q': case 'r': case 's': case 't': /*'u'*/
case 'v': case 'w': case 'x': case 'y': case 'z':
- case '_':
+ case '_': {
// Notify MIOpt that we read a non-whitespace/non-comment token.
MIOpt.ReadToken();
- return LexIdentifierContinue(Result, CurPtr);
-
+ bool returnedToken = LexIdentifierContinue(Result, CurPtr);
+ if (returnedToken && Result.isModuleContextualKeyword() &&
+ LangOpts.CPlusPlusModules &&
+ PP->HandleModuleContextualKeyword(Result, TokAtPhysicalStartOfLine) &&
+ !LexingRawMode && !Is_PragmaLexer)
+ goto HandleDirective;
+ return returnedToken;
+ }
case '$': // $ in identifiers.
if (LangOpts.DollarIdents) {
if (!isLexingRawMode())
@@ -4511,8 +4530,8 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
HandleDirective:
// We parsed a # character and it's the start of a preprocessing directive.
-
- FormTokenWithChars(Result, CurPtr, tok::hash);
+ if (!Result.isOneOf(tok::kw_import, tok::kw_module))
+ FormTokenWithChars(Result, CurPtr, tok::hash);
PP->HandleDirective(Result);
if (PP->hadModuleLoaderFatalFailure())
@@ -4535,6 +4554,10 @@ const char *Lexer::convertDependencyDirectiveToken(
Result.setKind(DDTok.Kind);
Result.setFlag((Token::TokenFlags)DDTok.Flags);
Result.setLength(DDTok.Length);
+ if (Result.is(tok::raw_identifier))
+ Result.setRawIdentifierData(TokPtr);
+ else if (Result.isLiteral())
+ Result.setLiteralData(TokPtr);
BufferPtr = TokPtr + DDTok.Length;
return TokPtr;
}
@@ -4589,15 +4612,19 @@ bool Lexer::LexDependencyDirectiveToken(Token &Result) {
Result.setRawIdentifierData(TokPtr);
if (!isLexingRawMode()) {
const IdentifierInfo *II = PP->LookUpIdentifierInfo(Result);
+ if (Result.isModuleContextualKeyword() &&
+ PP->HandleModuleContextualKeyword(Result, Result.isAtStartOfLine())) {
+ PP->HandleDirective(Result);
+ return false;
+ }
if (II->isHandleIdentifierCase())
return PP->HandleIdentifier(Result);
}
return true;
}
- if (Result.isLiteral()) {
- Result.setLiteralData(TokPtr);
+ if (Result.isLiteral())
return true;
- }
+
if (Result.is(tok::colon)) {
// Convert consecutive colons to 'tok::coloncolon'.
if (*BufferPtr == ':') {
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 04a30f66fb736..f95b9d582c5cc 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -439,7 +439,7 @@ void Preprocessor::ReadMacroName(Token &MacroNameTok, MacroUse isDefineUndef,
/// true, then we consider macros that expand to zero tokens as being ok.
///
/// Returns the location of the end of the directive.
-SourceLocation Preprocessor::CheckEndOfDirective(const char *DirType,
+SourceLocation Preprocessor::CheckEndOfDirective(StringRef DirType,
bool EnableMacros) {
Token Tmp;
// Lex unexpanded tokens for most directives: macros might expand to zero
@@ -466,7 +466,14 @@ SourceLocation Preprocessor::CheckEndOfDirective(const char *DirType,
if ((LangOpts.GNUMode || LangOpts.C99 || LangOpts.CPlusPlus) &&
!CurTokenLexer)
Hint = FixItHint::CreateInsertion(Tmp.getLocation(),"//");
- Diag(Tmp, diag::ext_pp_extra_tokens_at_eol) << DirType << Hint;
+
+ unsigned DiagID = diag::ext_pp_extra_tokens_at_eol;
+ // C++20 import or module directive has no '#' prefix.
+ if (getLangOpts().CPlusPlusModules &&
+ (DirType == "import" || DirType == "module"))
+ DiagID = diag::ext_pp_extra_tokens_at_module_directive_eol;
+
+ Diag(Tmp, DiagID) << DirType << Hint;
return DiscardUntilEndOfDirective().getEnd();
}
@@ -1245,9 +1252,14 @@ void Preprocessor::HandleDirective(Token &Result) {
// Save the '#' token in case we need to return it later.
Token SavedHash = Result;
+ bool IsCXX20ImportOrModuleDirective =
+ getLangOpts().CPlusPlusModules &&
+ Result.isModuleContextualKeyword(/*AllowExport=*/false);
+
// Read the next token, the directive flavor. This isn't expanded due to
// C99 6.10.3p8.
- LexUnexpandedToken(Result);
+ if (!IsCXX20ImportOrModuleDirective)
+ LexUnexpandedToken(Result);
// C99 6.10.3p11: Is this preprocessor directive in macro invocation? e.g.:
// #define A(x) #x
@@ -1266,7 +1278,9 @@ void Preprocessor::HandleDirective(Token &Result) {
case tok::pp___include_macros:
case tok::pp_pragma:
case tok::pp_embed:
- Diag(Result, diag::err_embedded_directive) << II->getName();
+ case tok::pp_module:
+ Diag(Result, diag::err_embedded_directive)
+ << IsCXX20ImportOrModuleDirective << II->getName();
Diag(*ArgMacro, diag::note_macro_expansion_here)
<< ArgMacro->getIdentifierInfo();
DiscardUntilEndOfDirective();
@@ -1357,9 +1371,12 @@ void Preprocessor::HandleDirective(Token &Result) {
// C99 6.10.6 - Pragma Directive.
case tok::pp_pragma:
return HandlePragmaDirective({PIK_HashPragma, SavedHash.getLocation()});
-
+ case tok::pp_module:
+ return HandleCXXModuleDirective(Result);
// GNU Extensions.
case tok::pp_import:
+ if (IsCXX20ImportOrModuleDirective)
+ return HandleCXXImportDirective(Result);
return HandleImportDirective(SavedHash.getLocation(), Result);
case tok::pp_include_next:
return HandleIncludeNextDirective(SavedHash.getLocation(), Result);
@@ -4065,3 +4082,230 @@ void Preprocessor::HandleEmbedDirective(SourceLocation HashLoc, Token &EmbedTok,
StringRef(static_cast<char *>(Mem), OriginalFilename.size());
HandleEmbedDirectiveImpl(HashLoc, *Params, BinaryContents, FilenameToGo);
}
+
+void Preprocessor::HandleCXXImportDirective(Token ImportTok) {
+ assert(getLangOpts().CPlusPlusModules && ImportTok.is(tok::kw_import));
+ llvm::SaveAndRestore<bool> SaveImportingCXXModules(
+ this->ImportingCXXNamedModules);
+ ImportingCXXNamedModules = true;
+
+ if (LastTokenWasExportKeyword)
+ LastTokenWasExportKeyword.reset();
+
+ Token Tok;
+ if (LexHeaderName(Tok)) {
+ if (Tok.isNot(tok::eod))
+ CheckEndOfDirective(ImportTok.getIdentifierInfo()->getName());
+ return;
+ }
+
+ SourceLocation UseLoc = ImportTok.getLocation();
+ SmallVector<Token, 4> DirToks{ImportTok};
+ SmallVector<IdentifierLoc, 2> Path;
+ bool ImportingHeader = false;
+ bool IsPartition = false;
+ std::string FlatName;
+ switch (Tok.getKind()) {
+ case tok::header_name:
+ ImportingHeader = true;
+ DirToks.push_back(Tok);
+ break;
+ case tok::colon:
+ IsPartition = true;
+ DirToks.push_back(Tok);
+ UseLoc = Tok.getLocation();
+ Lex(Tok);
+ [[fallthrough]];
+ case tok::identifier: {
+ if (LexModuleNameContinue(Tok, UseLoc, Path)) {
+ if (Tok.isNot(tok::eod))
+ CheckEndOfDirective(ImportTok.getIdentifierInfo()->getName());
+ return;
+ }
+
+ bool IsValid =
+ (IsPartition && ModuleDeclState.isNamedModule()) || !IsPartition;
+ if (Callbacks && IsValid) {
+ if (IsPartition && ModuleDeclState.isNamedModule()) {
+ FlatName += ModuleDeclState.getPrimaryName();
+ FlatName += ":";
+ }
+
+ FlatName += ModuleNameLoc::stringFromModuleIdPath(Path);
+ SourceLocation StartLoc = IsPartition ? UseLoc : Path[0].getLoc();
+ IdentifierLoc FlatNameLoc(StartLoc, getIdentifierInfo(FlatName));
+
+ // We don't/shouldn't load the standard c++20 modules when preprocessing.
+ // so the imported module is nullptr.
+ Callbacks->moduleImport(ImportTok.getLocation(),
+ ModuleIdPath(FlatNameLoc),
+ /*Imported=*/nullptr);
+ }
+ DirToks.push_back(ModuleNameLoc::CreateAnnotToken(*this, Path));
+ DirToks.push_back(Tok);
+ break;
+ }
+ default:
+ Diag(ImportTok, diag::err_pp_expected_module_name_or_header_name);
+ break;
+ }
+
+ // 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);
+
+ // This is not a pp-import after all.
+ if (DirToks.back().isNot(tok::semi)) {
+ Diag(DirToks.back(), diag::err_pp_expected_semi_after_module_or_import)
+ << /*IsImport*/ true
+ << FixItHint::CreateInsertion(DirToks.back().getLocation(),
+ tok::getPunctuatorSpelling(tok::semi));
+ return;
+ }
+
+ if (DirToks.back().isNot(tok::eod))
+ CheckEndOfDirective(ImportTok.getIdentifierInfo()->getName());
+ else
+ DirToks.pop_back();
+
+ // C++2a [cpp.module]p1:
+ // The ';' preprocessing-token terminating a pp-import shall not have
+ // been produced by macro replacement.
+ SourceLocation SemiLoc = DirToks.back().getLocation();
+ if (SemiLoc.isMacroID())
+ Diag(SemiLoc, diag::err_header_import_semi_in_macro);
+
+ if (ImportingHeader) {
+ auto Action = HandleHeaderIncludeOrImport(
+ /*HashLoc*/ SourceLocation(), ImportTok, Tok, SemiLoc);
+ switch (Action.Kind) {
+ case ImportAction::None:
+ break;
+
+ case ImportAction::ModuleBegin:
+ // Let the parser know we're textually entering the module.
+ DirToks.emplace_back();
+ DirToks.back().startToken();
+ DirToks.back().setKind(tok::annot_module_begin);
+ DirToks.back().setLocation(SemiLoc);
+ DirToks.back().setAnnotationEndLoc(SemiLoc);
+ DirToks.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.
+ DirToks[1].setKind(tok::annot_header_unit);
+ DirToks[1].setAnnotationEndLoc(DirToks[0].getLocation());
+ DirToks[1].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");
+ CurLexer->cutOffLexing();
+ return;
+ }
+ }
+
+ EnterModuleSuffixTokenStream(DirToks);
+}
+
+void Preprocessor::HandleCXXModuleDirective(Token ModuleTok) {
+ assert(getLangOpts().CPlusPlusModules && ModuleTok.is(tok::kw_module));
+ SourceLocation StartLoc = ModuleTok.getLocation();
+ if (LastTokenWasExportKeyword) {
+ StartLoc = LastTokenWasExportKeyword->ExportTok.getLocation();
+ LastTokenWasExportKeyword.reset();
+ }
+ bool IsInHeaderInclusion = !IncludeMacroStack.empty();
+ bool IsInConditionBlock = CurPPLexer->getConditionalStackDepth() != 0;
+
+ Token Tok;
+ SourceLocation UseLoc = ModuleTok.getLocation();
+ SmallVector<Token, 4> DirToks{ModuleTok};
+ SmallVector<IdentifierLoc, 2> Path, Partition;
+ LexUnexpandedToken(Tok);
+
+ switch (Tok.getKind()) {
+ // Global Module Fragment.
+ case tok::semi:
+ DirToks.push_back(Tok);
+ break;
+ case tok::colon:
+ DirToks.push_back(Tok);
+ LexUnexpandedToken(Tok);
+ if (Tok.isNot(tok::kw_private)) {
+ Diag(DirToks.back().getLocation(), diag::err_pp_module_expected_ident)
+ << /*IsImport=*/false
+ << FixItHint::CreateReplacement(
+ {Tok.getLocation(), Tok.getEndLoc()},
+ tok::getKeywordSpelling(tok::kw_private));
+ return;
+ }
+ DirToks.push_back(Tok);
+ break;
+ case tok::identifier: {
+ if (LexModuleNameContinue(Tok, UseLoc, Path)) {
+ if (Tok.isNot(tok::eod))
+ CheckEndOfDirective(ModuleTok.getIdentifierInfo()->getName());
+ return;
+ }
+
+ DirToks.push_back(ModuleNameLoc::CreateAnnotToken(*this, Path));
+
+ // 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)) {
+ DirToks.push_back(Tok);
+ LexUnexpandedToken(Tok);
+ if (LexModuleNameContinue(Tok, UseLoc, Partition)) {
+ if (Tok.isNot(tok::eod))
+ CheckEndOfDirective(ModuleTok.getIdentifierInfo()->getName());
+ return;
+ }
+ DirToks.push_back(ModuleNameLoc::CreateAnnotToken(*this, Partition));
+ }
+ DirToks.push_back(Tok);
+ break;
+ }
+ default:
+ break;
+ ;
+ }
+
+ // Consume the pp-import-suffix and expand any macros in it now, if we're not
+ // at the semicolon already.
+ SourceLocation End = DirToks.back().getLocation();
+ if (!DirToks.back().isOneOf(tok::semi, tok::eod)) {
+ CollectPpImportSuffix(DirToks);
+ End = DirToks.back().getLocation();
+ }
+
+ // This is not a pp-import after all.
+ if (DirToks.back().isNot(tok::semi)) {
+ Diag(DirToks.back(), diag::err_pp_expected_semi_after_module_or_import)
+ << /*IsImport*/ false
+ << FixItHint::CreateInsertion(DirToks.back().getLocation(),
+ tok::getPunctuatorSpelling(tok::semi));
+ return;
+ }
+
+ if (DirToks.back().isNot(tok::eod))
+ End = CheckEndOfDirective(ModuleTok.getIdentifierInfo()->getName());
+ else
+ End = DirToks.pop_back_val().getLocation();
+ if (IsInHeaderInclusion)
+ Diag(StartLoc, diag::err_module_decl_in_header)
+ << SourceRange(StartLoc, End);
+
+ if (IsInConditionBlock)
+ Diag(StartLoc, diag::err_pp_cond_span_module_decl)
+ << SourceRange(StartLoc, End);
+
+ EnterModuleSuffixTokenStream(DirToks);
+}
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 21fc7a2b6fae2..f1d94561f365c 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -859,17 +859,21 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
if (((LastTokenWasAt && II.isModulesImport()) ||
Identifier.is(tok::kw_import)) &&
!InMacroArgs && !DisableMacroExpansion &&
- (getLangOpts().Modules || getLangOpts().DebuggerSupport) &&
CurLexerCallback != CLK_CachingLexer) {
ModuleImportLoc = Identifier.getLocation();
- NamedModuleImportPath.clear();
IsAtImport = true;
- ModuleImportExpectsIdentifier = true;
CurLexerCallback = CLK_LexAfterModuleImport;
}
return true;
}
+void Preprocessor::ModuleDeclSeq::handleModuleName(ModuleNameLoc *Path) {
+ if (isModuleCandidate() && Path)
+ Name += Path->str();
+ else if (!isNamedModule())
+ reset();
+}
+
void Preprocessor::Lex(Token &Result) {
++LexLevel;
@@ -913,6 +917,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();
@@ -930,31 +935,21 @@ void Preprocessor::Lex(Token &Result) {
case tok::colon:
ModuleDeclState.handleColon();
break;
- case tok::period:
- ModuleDeclState.handlePeriod();
+ case tok::kw_import:
+ if (StdCXXImportSeqState.atTopLevel()) {
+ TrackGMFState.handleImport(StdCXXImportSeqState.afterTopLevelSeq());
+ StdCXXImportSeqState.handleImport();
+ }
break;
- case tok::identifier:
- // Check "import" and "module" when there is no open bracket. The two
- // identifiers are not meaningful with open brackets.
+ case tok::kw_module:
if (StdCXXImportSeqState.atTopLevel()) {
- if (Result.getIdentifierInfo()->isModulesImport()) {
- TrackGMFState.handleImport(StdCXXImportSeqState.afterTopLevelSeq());
- StdCXXImportSeqState.handleImport();
- if (StdCXXImportSeqState.afterImportSeq()) {
- ModuleImportLoc = Result.getLocation();
- NamedModuleImportPath.clear();
- IsAtImport = false;
- ModuleImportExpectsIdentifier = true;
- CurLexerCallback = CLK_LexAfterModuleImport;
- }
- break;
- } else if (Result.getIdentifierInfo() == getIdentifierInfo("module")) {
- TrackGMFState.handleModule(StdCXXImportSeqState.afterTopLevelSeq());
- ModuleDeclState.handleModule();
- break;
- }
+ TrackGMFState.handleModule(StdCXXImportSeqState.afterTopLevelSeq());
+ ModuleDeclState.handleModule();
}
- ModuleDeclState.handleIdentifier(Result.getIdentifierInfo());
+ break;
+ case tok::annot_module_name:
+ ModuleDeclState.handleModuleName(
+ Result.getAnnotationValueAs<ModuleNameLoc *>());
if (ModuleDeclState.isModuleCandidate())
break;
[[fallthrough]];
@@ -972,6 +967,9 @@ void Preprocessor::Lex(Token &Result) {
}
LastTokenWasAt = Result.is(tok::at);
+ if (Result.isNot(tok::kw_export))
+ LastTokenWasExportKeyword.reset();
+
--LexLevel;
if ((LexLevel == 0 || PreprocessToken) &&
@@ -1094,43 +1092,171 @@ bool Preprocessor::LexHeaderName(Token &FilenameTok, bool AllowMacroExpansion) {
return false;
}
+ModuleNameLoc *ModuleNameLoc::Create(Preprocessor &PP, ModuleIdPath Path) {
+ assert(!Path.empty() && "expect at least one identifier in a module name");
+ void *Mem = PP.getPreprocessorAllocator().Allocate(
+ totalSizeToAlloc<IdentifierLoc>(Path.size()), alignof(ModuleNameLoc));
+ return new (Mem) ModuleNameLoc(Path);
+}
+
+Token ModuleNameLoc::CreateAnnotToken(Preprocessor &PP, ModuleIdPath Path) {
+ auto *NameLoc = Create(PP, Path);
+ Token ModuleNameTok;
+ ModuleNameTok.startToken();
+ ModuleNameTok.setKind(tok::annot_module_name);
+ ModuleNameTok.setAnnotationRange(NameLoc->getRange());
+ ModuleNameTok.setAnnotationValue(static_cast<void *>(NameLoc));
+ return ModuleNameTok;
+}
+
+// We represent the primary and partition names as 'Paths' which are sections
+// of the hierarchical access path for a clang module. However for C++20
+// the periods in a name are just another character, and we will need to
+// flatten them into a string.
+std::string ModuleNameLoc::stringFromModuleIdPath(ModuleIdPath Path) {
+ std::string Name;
+ if (Path.empty())
+ return Name;
+
+ for (auto &Piece : Path) {
+ assert(Piece.getIdentifierInfo() && Piece.getLoc().isValid());
+ if (!Name.empty())
+ Name += ".";
+ Name += Piece.getIdentifierInfo()->getName();
+ }
+ return Name;
+}
+
+std::string ModuleNameLoc::str() const {
+ return stringFromModuleIdPath(getModuleIdPath());
+}
+
+void ModuleNameLoc::print(llvm::raw_ostream &OS) const { OS << str(); }
+
+bool Preprocessor::LexModuleNameContinue(Token &Tok, SourceLocation UseLoc,
+ SmallVectorImpl<IdentifierLoc> &Path,
+ bool AllowMacroExpansion) {
+ auto ConsumeToken = [&]() {
+ return AllowMacroExpansion ? Lex(Tok) : LexUnexpandedToken(Tok);
+ };
+
+ while (true) {
+ if (Tok.isNot(tok::identifier)) {
+ if (Tok.is(tok::code_completion)) {
+ CurLexer->cutOffLexing();
+ Tok.setKind(tok::eof);
+ this->getCodeCompletionHandler()->CodeCompleteModuleImport(UseLoc,
+ Path);
+ }
+ Diag(Tok.getLocation(), diag::err_pp_expected_module_name)
+ << Path.empty();
+ return true;
+ }
+
+ // Record this part of the module path.
+ Path.emplace_back(Tok.getLocation(), Tok.getIdentifierInfo());
+ ConsumeToken();
+
+ if (Tok.isNot(tok::period))
+ return false;
+
+ ConsumeToken();
+ }
+}
+
+/// P1857R3: Modules Dependency Discovery
+///
+/// At the start of phase 4 an import or module token is treated as starting a
+/// directive and are converted to their respective keywords iff:
+/// • After skipping horizontal whitespace are
+/// • at the start of a logical line, or
+/// • preceded by an 'export' at the start of the logical line.
+/// • Are followed by an identifier pp token (before macro expansion), or
+/// • <, ", or : (but not ::) pp tokens for 'import', or
+/// • ; for 'module'
+/// Otherwise the token is treated as an identifier.
+bool Preprocessor::HandleModuleContextualKeyword(
+ Token &Result, bool TokAtPhysicalStartOfLine) {
+ if (!getLangOpts().CPlusPlusModules || !Result.isModuleContextualKeyword())
+ return false;
+
+ if (Result.is(tok::kw_export)) {
+ LastTokenWasExportKeyword = {Result, TokAtPhysicalStartOfLine};
+ return false;
+ }
+
+ if (LastTokenWasExportKeyword) {
+ auto Export = *LastTokenWasExportKeyword;
+ // The export keyword was not at the start of line, it's not a
+ // directive-introducing token.
+ if (!Export.TokAtPhysicalStartOfLine)
+ return false;
+ // [cpp.pre]/1.4
+ // export // not a preprocessing directive
+ // import foo; // preprocessing directive (ill-formed at phase
+ // 7)
+ if (TokAtPhysicalStartOfLine)
+ return false;
+ } else if (!TokAtPhysicalStartOfLine)
+ return false;
+
+ bool SavedParsingPreprocessorDirective =
+ CurPPLexer->ParsingPreprocessorDirective;
+ CurPPLexer->ParsingPreprocessorDirective = true;
+ // Peek next token.
+ auto NextTok = peekNextPPToken().value_or(Token{});
+ CurPPLexer->ParsingPreprocessorDirective = SavedParsingPreprocessorDirective;
+ if (Result.getIdentifierInfo()->isModulesImport() &&
+ NextTok.isOneOf(tok::raw_identifier, tok::less, tok::string_literal,
+ tok::colon)) {
+ Result.setKind(tok::kw_import);
+ ModuleImportLoc = Result.getLocation();
+ IsAtImport = false;
+ return true;
+ }
+ if (Result.getIdentifierInfo()->isModulesDeclaration() &&
+ NextTok.isOneOf(tok::raw_identifier, tok::colon, tok::semi)) {
+ Result.setKind(tok::kw_module);
+ ModuleDeclLoc = Result.getLocation();
+ return true;
+ }
+
+ // Ok, it's an identifier.
+ return false;
+}
+
/// Collect the tokens of a C++20 pp-import-suffix.
void Preprocessor::CollectPpImportSuffix(SmallVectorImpl<Token> &Toks) {
// FIXME: For error recovery, consider recognizing attribute syntax here
// and terminating / diagnosing a missing semicolon if we find anything
// else? (Can we leave that to the parser?)
- unsigned BracketDepth = 0;
while (true) {
Toks.emplace_back();
Lex(Toks.back());
switch (Toks.back().getKind()) {
- case tok::l_paren: case tok::l_square: case tok::l_brace:
- ++BracketDepth;
- break;
-
- case tok::r_paren: case tok::r_square: case tok::r_brace:
- if (BracketDepth == 0)
- return;
- --BracketDepth;
- break;
-
case tok::semi:
- if (BracketDepth == 0)
- return;
- break;
-
+ case tok::eod:
case tok::eof:
return;
-
default:
break;
}
}
}
+// Allocate a holding buffer for a sequence of tokens and introduce it into
+// the token stream.
+void Preprocessor::EnterModuleSuffixTokenStream(ArrayRef<Token> Toks) {
+ if (Toks.empty())
+ return;
+ 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 a token following the 'import' contextual keyword.
+// Lex a token following the 'import' contextual keyword.
///
/// pp-import: [C++20]
/// import header-name pp-import-suffix[opt] ;
@@ -1153,186 +1279,42 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
// Figure out what kind of lexer we actually have.
recomputeCurLexerKind();
- // Lex the next token. The header-name lexing rules are used at the start of
- // a pp-import.
- //
- // For now, we only support header-name imports in C++20 mode.
- // FIXME: Should we allow this in all language modes that support an import
- // declaration as an extension?
- 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.emplace_back(Result.getLocation(),
- getIdentifierInfo(Name));
- 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;
- 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;
- }
-
- // 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::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;
- }
-
- EnterTokens(Suffix);
+ SmallVector<IdentifierLoc, 2> Path;
+ Lex(Result);
+ if (LexModuleNameContinue(Result, ModuleImportLoc, Path))
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.emplace_back(Result.getLocation(),
- Result.getIdentifierInfo());
- ModuleImportExpectsIdentifier = false;
- CurLexerCallback = CLK_LexAfterModuleImport;
- return 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;
- return true;
- }
-
- // If we didn't recognize a module name at all, this is not a (valid) import.
- if (NamedModuleImportPath.empty() || Result.is(tok::eof))
- return true;
+ Suffix.push_back(ModuleNameLoc::CreateAnnotToken(*this, Path));
+ 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 (Result.isNot(tok::semi)) {
- Suffix.push_back(Result);
- CollectPpImportSuffix(Suffix);
+ SourceLocation SemiLoc = Suffix.back().getLocation();
+ if (Suffix.back().isNot(tok::semi)) {
+ if (Result.isNot(tok::eof))
+ CollectPpImportSuffix(Suffix);
if (Suffix.back().isNot(tok::semi)) {
// This is not an import after all.
- EnterTokens(Suffix);
+ EnterModuleSuffixTokenStream(Suffix);
return false;
}
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.getIdentifierInfo()->getName();
- }
- SourceLocation FirstPathLoc = NamedModuleImportPath[0].getLoc();
- NamedModuleImportPath.clear();
- NamedModuleImportPath.emplace_back(FirstPathLoc,
- getIdentifierInfo(FlatModuleName));
- }
-
Module *Imported = nullptr;
- // We don't/shouldn't load the standard c++20 modules when preprocessing.
- if (getLangOpts().Modules && !isInImportingCXXNamedModules()) {
- Imported = TheModuleLoader.loadModule(ModuleImportLoc,
- NamedModuleImportPath,
- Module::Hidden,
+ if (getLangOpts().Modules) {
+ Imported = TheModuleLoader.loadModule(ModuleImportLoc, Path, Module::Hidden,
/*IsInclusionDirective=*/false);
if (Imported)
makeModuleVisible(Imported, SemiLoc);
}
if (Callbacks)
- Callbacks->moduleImport(ModuleImportLoc, NamedModuleImportPath, Imported);
+ Callbacks->moduleImport(ModuleImportLoc, Path, Imported);
if (!Suffix.empty()) {
- EnterTokens(Suffix);
+ EnterModuleSuffixTokenStream(Suffix);
return false;
}
return true;
diff --git a/clang/lib/Lex/TokenConcatenation.cpp b/clang/lib/Lex/TokenConcatenation.cpp
index 05f4203bd722b..f94caee24dc11 100644
--- a/clang/lib/Lex/TokenConcatenation.cpp
+++ b/clang/lib/Lex/TokenConcatenation.cpp
@@ -161,7 +161,8 @@ bool TokenConcatenation::AvoidConcat(const Token &PrevPrevTok,
const Token &PrevTok,
const Token &Tok) const {
// No space is required between header unit name in quote and semi.
- if (PrevTok.is(tok::annot_header_unit) && Tok.is(tok::semi))
+ if (PrevTok.isOneOf(tok::annot_header_unit, tok::annot_module_name) &&
+ Tok.is(tok::semi))
return false;
// Conservatively assume that every annotation token that has a printable
@@ -197,11 +198,12 @@ bool TokenConcatenation::AvoidConcat(const Token &PrevPrevTok,
if (Tok.isAnnotation()) {
// Modules annotation can show up when generated automatically for includes.
assert(Tok.isOneOf(tok::annot_module_include, tok::annot_module_begin,
- tok::annot_module_end, tok::annot_embed) &&
+ tok::annot_module_end, tok::annot_embed,
+ tok::annot_module_name) &&
"unexpected annotation in AvoidConcat");
ConcatInfo = 0;
- if (Tok.is(tok::annot_embed))
+ if (Tok.isOneOf(tok::annot_embed, tok::annot_module_name))
return true;
}
diff --git a/clang/lib/Lex/TokenLexer.cpp b/clang/lib/Lex/TokenLexer.cpp
index fbb8c4262d6da..968654405203d 100644
--- a/clang/lib/Lex/TokenLexer.cpp
+++ b/clang/lib/Lex/TokenLexer.cpp
@@ -699,7 +699,9 @@ bool TokenLexer::Lex(Token &Tok) {
HasLeadingSpace = false;
// Handle recursive expansion!
- if (!Tok.isAnnotation() && Tok.getIdentifierInfo() != nullptr) {
+ if (!Tok.isAnnotation() && Tok.getIdentifierInfo() != nullptr &&
+ (!PP.getLangOpts().CPlusPlusModules ||
+ !Tok.isModuleContextualKeyword())) {
// Change the kind of this identifier to the appropriate token kind, e.g.
// turning "for" into a keyword.
IdentifierInfo *II = Tok.getIdentifierInfo();
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index db65c05cc114a..508a4ce0e5ff8 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -517,8 +517,6 @@ void Parser::Initialize() {
Ident_trivially_relocatable_if_eligible = nullptr;
Ident_replaceable_if_eligible = nullptr;
Ident_GNU_final = nullptr;
- Ident_import = nullptr;
- Ident_module = nullptr;
Ident_super = &PP.getIdentifierTable().get("super");
@@ -574,11 +572,6 @@ void Parser::Initialize() {
PP.SetPoisonReason(Ident_AbnormalTermination,diag::err_seh___finally_block);
}
- if (getLangOpts().CPlusPlusModules) {
- Ident_import = PP.getIdentifierInfo("import");
- Ident_module = PP.getIdentifierInfo("module");
- }
-
Actions.Initialize();
// Prime the lexer look-ahead.
@@ -626,24 +619,8 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result,
switch (NextToken().getKind()) {
case tok::kw_module:
goto module_decl;
-
- // Note: no need to handle kw_import here. We only form kw_import under
- // the Standard C++ Modules, and in that case 'export import' is parsed as
- // an export-declaration containing an import-declaration.
-
- // Recognize context-sensitive C++20 'export module' and 'export import'
- // declarations.
- case tok::identifier: {
- IdentifierInfo *II = NextToken().getIdentifierInfo();
- if ((II == Ident_module || II == Ident_import) &&
- GetLookAheadToken(2).isNot(tok::coloncolon)) {
- if (II == Ident_module)
- goto module_decl;
- else
- goto import_decl;
- }
- break;
- }
+ case tok::kw_import:
+ goto import_decl;
default:
break;
@@ -713,21 +690,6 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result,
//else don't tell Sema that we ended parsing: more input might come.
return true;
- case tok::identifier:
- // C++2a [basic.link]p3:
- // A token sequence beginning with 'export[opt] module' or
- // 'export[opt] import' and not immediately followed by '::'
- // is never interpreted as the declaration of a top-level-declaration.
- if ((Tok.getIdentifierInfo() == Ident_module ||
- Tok.getIdentifierInfo() == Ident_import) &&
- NextToken().isNot(tok::coloncolon)) {
- if (Tok.getIdentifierInfo() == Ident_module)
- goto module_decl;
- else
- goto import_decl;
- }
- break;
-
default:
break;
}
@@ -920,8 +882,9 @@ Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
case tok::kw_import: {
Sema::ModuleImportState IS = Sema::ModuleImportState::NotACXX20Module;
if (getLangOpts().CPlusPlusModules) {
- llvm_unreachable("not expecting a c++20 import here");
- ProhibitAttributes(Attrs);
+ Diag(Tok, diag::err_unexpected_module_import_decl) << /*IsImport*/ true;
+ SkipUntil(tok::semi);
+ return nullptr;
}
SingleDecl = ParseModuleImport(SourceLocation(), IS);
} break;
@@ -1013,7 +976,7 @@ Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
return nullptr;
case tok::kw_module:
- Diag(Tok, diag::err_unexpected_module_decl);
+ Diag(Tok, diag::err_unexpected_module_import_decl) << /*IsImport*/ false;
SkipUntil(tok::semi);
return nullptr;
@@ -2231,6 +2194,11 @@ void Parser::CodeCompleteNaturalLanguage() {
Actions.CodeCompletion().CodeCompleteNaturalLanguage();
}
+void Parser::CodeCompleteModuleImport(SourceLocation ImportLoc,
+ ModuleIdPath Path) {
+ Actions.CodeCompletion().CodeCompleteModuleImport(ImportLoc, Path);
+}
+
bool Parser::ParseMicrosoftIfExistsCondition(IfExistsCondition& Result) {
assert((Tok.is(tok::kw___if_exists) || Tok.is(tok::kw___if_not_exists)) &&
"Expected '__if_exists' or '__if_not_exists'");
@@ -2384,20 +2352,20 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
return Actions.ActOnPrivateModuleFragmentDecl(ModuleLoc, PrivateLoc);
}
- SmallVector<IdentifierLoc, 2> Path;
- if (ParseModuleName(ModuleLoc, Path, /*IsImport*/ false))
+ ModuleNameLoc *Path = nullptr;
+ if (Tok.isNot(tok::annot_module_name))
return nullptr;
+ Path = Tok.getAnnotationValueAs<ModuleNameLoc *>();
+ ConsumeAnnotationToken();
// Parse the optional module-partition.
- SmallVector<IdentifierLoc, 2> Partition;
+ ModuleNameLoc *Partition = nullptr;
if (Tok.is(tok::colon)) {
- SourceLocation ColonLoc = ConsumeToken();
- if (!getLangOpts().CPlusPlusModules)
- Diag(ColonLoc, diag::err_unsupported_module_partition)
- << SourceRange(ColonLoc, Partition.back().getLoc());
- // Recover by ignoring the partition name.
- else if (ParseModuleName(ModuleLoc, Partition, /*IsImport*/ false))
+ ConsumeToken();
+ if (Tok.isNot(tok::annot_module_name))
return nullptr;
+ Partition = Tok.getAnnotationValueAs<ModuleNameLoc *>();
+ ConsumeAnnotationToken();
}
// We don't support any module attributes yet; just parse them and diagnose.
@@ -2428,7 +2396,7 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
SourceLocation ImportLoc = ConsumeToken();
// For C++20 modules, we can have "name" or ":Partition name" as valid input.
- SmallVector<IdentifierLoc, 2> Path;
+ ModuleNameLoc *Path = nullptr;
bool IsPartition = false;
Module *HeaderUnit = nullptr;
if (Tok.is(tok::header_name)) {
@@ -2441,18 +2409,17 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
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().getLoc());
- // Recover by leaving partition empty.
- else if (ParseModuleName(ColonLoc, Path, /*IsImport*/ true))
+ ConsumeToken();
+ if (Tok.isNot(tok::annot_module_name))
return nullptr;
- else
- IsPartition = true;
+ IsPartition = true;
+ Path = Tok.getAnnotationValueAs<ModuleNameLoc *>();
+ ConsumeAnnotationToken();
} else {
- if (ParseModuleName(ImportLoc, Path, /*IsImport*/ true))
+ if (Tok.isNot(tok::annot_module_name))
return nullptr;
+ Path = Tok.getAnnotationValueAs<ModuleNameLoc *>();
+ ConsumeAnnotationToken();
}
ParsedAttributes Attrs(AttrFactory);
@@ -2520,7 +2487,7 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
if (HeaderUnit)
Import =
Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, HeaderUnit);
- else if (!Path.empty())
+ else if (Path)
Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Path,
IsPartition);
if (Import.isInvalid())
diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp
index 6c4df0aa35af5..98c60bab409be 100644
--- a/clang/lib/Sema/SemaModule.cpp
+++ b/clang/lib/Sema/SemaModule.cpp
@@ -57,23 +57,6 @@ static void checkModuleImportContext(Sema &S, Module *M,
}
}
-// We represent the primary and partition names as 'Paths' which are sections
-// of the hierarchical access path for a clang module. However for C++20
-// the periods in a name are just another character, and we will need to
-// flatten them into a string.
-static std::string stringFromPath(ModuleIdPath Path) {
- std::string Name;
- if (Path.empty())
- return Name;
-
- for (auto &Piece : Path) {
- if (!Name.empty())
- Name += ".";
- Name += Piece.getIdentifierInfo()->getName();
- }
- return Name;
-}
-
/// Helper function for makeTransitiveImportsVisible to decide whether
/// the \param Imported module unit is in the same module with the \param
/// CurrentModule.
@@ -255,10 +238,12 @@ static bool DiagReservedModuleName(Sema &S, const IdentifierInfo *II,
llvm_unreachable("fell off a fully covered switch");
}
-Sema::DeclGroupPtrTy
-Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
- ModuleDeclKind MDK, ModuleIdPath Path,
- ModuleIdPath Partition, ModuleImportState &ImportState) {
+Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
+ SourceLocation ModuleLoc,
+ ModuleDeclKind MDK,
+ ModuleNameLoc *PathLoc,
+ ModuleNameLoc *PartitionLoc,
+ ModuleImportState &ImportState) {
assert(getLangOpts().CPlusPlusModules &&
"should only have module decl in standard C++ modules");
@@ -268,8 +253,7 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
// module state;
ImportState = ModuleImportState::NotACXX20Module;
- bool IsPartition = !Partition.empty();
- if (IsPartition)
+ if (PartitionLoc)
switch (MDK) {
case ModuleDeclKind::Implementation:
MDK = ModuleDeclKind::PartitionImplementation;
@@ -351,17 +335,18 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
// Test the first part of the path to see if it's std[0-9]+ but allow the
// name in a system header.
- StringRef FirstComponentName = Path[0].getIdentifierInfo()->getName();
- if (!getSourceManager().isInSystemHeader(Path[0].getLoc()) &&
+ StringRef FirstComponentName =
+ PathLoc->getModuleIdPath()[0].getIdentifierInfo()->getName();
+ if (!getSourceManager().isInSystemHeader(PathLoc->getBeginLoc()) &&
(FirstComponentName == "std" ||
(FirstComponentName.starts_with("std") &&
llvm::all_of(FirstComponentName.drop_front(3), &llvm::isDigit))))
- Diag(Path[0].getLoc(), diag::warn_reserved_module_name)
- << Path[0].getIdentifierInfo();
+ Diag(PathLoc->getBeginLoc(), diag::warn_reserved_module_name)
+ << PathLoc->getModuleIdPath()[0].getIdentifierInfo();
// Then test all of the components in the path to see if any of them are
// using another kind of reserved or invalid identifier.
- for (auto Part : Path) {
+ for (auto Part : PathLoc->getModuleIdPath()) {
if (DiagReservedModuleName(*this, Part.getIdentifierInfo(), Part.getLoc()))
return nullptr;
}
@@ -369,20 +354,17 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
// Flatten the dots in a module name. Unlike Clang's hierarchical module map
// modules, the dots here are just another character that can appear in a
// module name.
- std::string ModuleName = stringFromPath(Path);
- if (IsPartition) {
+ std::string ModuleName = PathLoc->str();
+ if (PartitionLoc) {
ModuleName += ":";
- ModuleName += stringFromPath(Partition);
+ ModuleName += PartitionLoc->str();
}
// If a module name was explicitly specified on the command line, it must be
// correct.
if (!getLangOpts().CurrentModule.empty() &&
getLangOpts().CurrentModule != ModuleName) {
- Diag(Path.front().getLoc(), diag::err_current_module_name_mismatch)
- << SourceRange(Path.front().getLoc(), IsPartition
- ? Partition.back().getLoc()
- : Path.back().getLoc())
- << getLangOpts().CurrentModule;
+ Diag(PathLoc->getBeginLoc(), diag::err_current_module_name_mismatch)
+ << PathLoc->getRange() << getLangOpts().CurrentModule;
return nullptr;
}
const_cast<LangOptions&>(getLangOpts()).CurrentModule = ModuleName;
@@ -396,7 +378,7 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
// We can't have parsed or imported a definition of this module or parsed a
// module map defining it already.
if (auto *M = Map.findOrLoadModule(ModuleName)) {
- Diag(Path[0].getLoc(), diag::err_module_redefinition) << ModuleName;
+ Diag(PathLoc->getBeginLoc(), diag::err_module_redefinition) << ModuleName;
if (M->DefinitionLoc.isValid())
Diag(M->DefinitionLoc, diag::note_prev_module_definition);
else if (OptionalFileEntryRef FE = M->getASTFile())
@@ -419,7 +401,7 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
// keyword nor a module-partition implicitly imports the primary
// module interface unit of the module as if by a module-import-
// declaration.
- IdentifierLoc ModuleNameLoc(Path[0].getLoc(),
+ IdentifierLoc ModuleNameLoc(PathLoc->getBeginLoc(),
PP.getIdentifierInfo(ModuleName));
// The module loader will assume we're trying to import the module that
@@ -492,7 +474,7 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
// Make the import decl for the interface in the impl module.
ImportDecl *Import = ImportDecl::Create(Context, CurContext, ModuleLoc,
- Interface, Path[0].getLoc());
+ Interface, PathLoc->getBeginLoc());
CurContext->addDecl(Import);
// Sequence initialization of the imported module before that of the current
@@ -574,8 +556,8 @@ Sema::ActOnPrivateModuleFragmentDecl(SourceLocation ModuleLoc,
DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
SourceLocation ExportLoc,
- SourceLocation ImportLoc, ModuleIdPath Path,
- bool IsPartition) {
+ SourceLocation ImportLoc,
+ ModuleNameLoc *PathLoc, bool IsPartition) {
assert((!IsPartition || getLangOpts().CPlusPlusModules) &&
"partition seen in non-C++20 code?");
@@ -584,6 +566,7 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
IdentifierLoc ModuleNameLoc;
std::string ModuleName;
+ ModuleIdPath Path;
if (IsPartition) {
// We already checked that we are in a module purview in the parser.
assert(!ModuleScopes.empty() && "in a module purview, but no module?");
@@ -592,15 +575,17 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
// otherwise, the name of the importing named module.
ModuleName = NamedMod->getPrimaryModuleInterfaceName().str();
ModuleName += ":";
- ModuleName += stringFromPath(Path);
+ ModuleName += PathLoc->str();
ModuleNameLoc =
- IdentifierLoc(Path[0].getLoc(), PP.getIdentifierInfo(ModuleName));
+ IdentifierLoc(PathLoc->getBeginLoc(), PP.getIdentifierInfo(ModuleName));
Path = ModuleIdPath(ModuleNameLoc);
} else if (getLangOpts().CPlusPlusModules) {
- ModuleName = stringFromPath(Path);
+ ModuleName = PathLoc->str();
ModuleNameLoc =
- IdentifierLoc(Path[0].getLoc(), PP.getIdentifierInfo(ModuleName));
+ IdentifierLoc(PathLoc->getBeginLoc(), PP.getIdentifierInfo(ModuleName));
Path = ModuleIdPath(ModuleNameLoc);
+ } else {
+ Path = PathLoc->getModuleIdPath();
}
// Diagnose self-import before attempting a load.
diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
index 774bfd725d0a5..f772cefe6111b 100644
--- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
@@ -654,7 +654,7 @@ void ModuleDepCollectorPP::InclusionDirective(
void ModuleDepCollectorPP::moduleImport(SourceLocation ImportLoc,
ModuleIdPath Path,
const Module *Imported) {
- if (MDC.ScanInstance.getPreprocessor().isInImportingCXXNamedModules()) {
+ if (MDC.ScanInstance.getPreprocessor().isImportingCXXNamedModules()) {
P1689ModuleInfo RequiredModule;
RequiredModule.ModuleName = Path[0].getIdentifierInfo()->getName().str();
RequiredModule.Type = P1689ModuleInfo::ModuleType::NamedCXXModule;
diff --git a/clang/test/CXX/basic/basic.link/p1.cpp b/clang/test/CXX/basic/basic.link/p1.cpp
index c6a119aa7f47c..b8f37d152b0e0 100644
--- a/clang/test/CXX/basic/basic.link/p1.cpp
+++ b/clang/test/CXX/basic/basic.link/p1.cpp
@@ -1,57 +1,130 @@
-// RUN: %clang_cc1 -std=c++2a -verify %s
-// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG %s
-// RUN: %clang_cc1 -std=c++2a -verify -DNO_MODULE_DECL %s
-// RUN: %clang_cc1 -std=c++2a -verify -DNO_PRIVATE_FRAG %s
-// RUN: %clang_cc1 -std=c++2a -verify -DNO_MODULE_DECL -DNO_PRIVATE_FRAG %s
-// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_PRIVATE_FRAG %s
-// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_MODULE_DECL %s
-// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_MODULE_DECL -DNO_PRIVATE_FRAG %s
-// RUN: %clang_cc1 -std=c++2a -verify -DEXPORT_FRAGS %s
-
-#ifndef NO_GLOBAL_FRAG
-#ifdef EXPORT_FRAGS
-export // expected-error {{global module fragment cannot be exported}}
-#endif
-module;
-#ifdef NO_MODULE_DECL
-// expected-error at -2 {{missing 'module' declaration at end of global module fragment introduced here}}
-#endif
-#endif
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++2a -verify %t/M.cppm
+// RUN: %clang_cc1 -std=c++2a -verify %t/NoGlobalFrag.cppm
+// RUN: %clang_cc1 -std=c++2a -verify %t/NoModuleDecl.cppm
+// RUN: %clang_cc1 -std=c++2a -verify %t/NoPrivateFrag.cppm
+// RUN: %clang_cc1 -std=c++2a -verify %t/NoModuleDeclAndNoPrivateFrag.cppm
+// RUN: %clang_cc1 -std=c++2a -verify %t/NoGlobalFragAndNoPrivateFrag.cppm
+// RUN: %clang_cc1 -std=c++2a -verify %t/NoGlobalFragAndNoModuleDecl.cppm
+// RUN: %clang_cc1 -std=c++2a -verify %t/NoGlobalFragAndNoModuleDeclAndNoPrivateFrag.cppm
+// RUN: %clang_cc1 -std=c++2a -verify %t/ExportFrags.cppm
+//--- M.cppm
+module;
extern int a; // #a1
+export module Foo;
+
+int a; // expected-error {{declaration of 'a' in module Foo follows declaration in the global module}}
+ // expected-note@#a1 {{previous decl}}
+extern int b;
+
+module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
+module :private; // #priv-frag
+int b; // ok
+module :private; // expected-error {{private module fragment redefined}}
+ // expected-note@#priv-frag {{previous definition is here}}
-#ifndef NO_MODULE_DECL
+//--- NoGlobalFrag.cppm
+
+extern int a; // #a1
export module Foo;
-#ifdef NO_GLOBAL_FRAG
+
// expected-error at -2 {{module declaration must occur at the start of the translation unit}}
// expected-note at 1 {{add 'module;' to the start of the file to introduce a global module fragment}}
-#endif
// expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}}
// expected-note@#a1 {{previous decl}}
-#endif
+int a; // #a2
+extern int b;
+module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
+module :private; // #priv-frag
+int b; // ok
+module :private; // expected-error {{private module fragment redefined}}
+// expected-note@#priv-frag {{previous definition is here}}
+
+//--- NoModuleDecl.cppm
+module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
+extern int a; // #a1
+int a; // #a2
+extern int b;
+module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
+module :private; // expected-error {{private module fragment declaration with no preceding module declaration}}
+int b; // ok
+
+//--- NoPrivateFrag.cppm
+module;
+extern int a; // #a1
+export module Foo;
+
+// expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}}
+// expected-note@#a1 {{previous decl}}
int a; // #a2
extern int b;
module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
+int b; // ok
+
-#ifndef NO_PRIVATE_FRAG
-#ifdef EXPORT_FRAGS
-export // expected-error {{private module fragment cannot be exported}}
-#endif
+//--- NoModuleDeclAndNoPrivateFrag.cppm
+module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
+extern int a; // #a1
+int a; // #a2
+extern int b;
+
+module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
+
+int b; // ok
+
+//--- NoGlobalFragAndNoPrivateFrag.cppm
+extern int a; // #a1
+export module Foo; // expected-error {{module declaration must occur at the start of the translation unit}}
+// expected-note at 1 {{add 'module;' to the start of the file to introduce a global module fragment}}
+
+// expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}}
+// expected-note@#a1 {{previous decl}}
+
+int a; // #a2
+extern int b;
+
+module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
+
+int b; // ok
+
+//--- NoGlobalFragAndNoModuleDecl.cppm
+extern int a; // #a1
+int a; // #a2
+extern int b;
+module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
module :private; // #priv-frag
-#ifdef NO_MODULE_DECL
-// expected-error at -2 {{private module fragment declaration with no preceding module declaration}}
-#endif
-#endif
+// expected-error at -1 {{private module fragment declaration with no preceding module declaration}}
+int b; // ok
+
+
+//--- NoGlobalFragAndNoModuleDeclAndNoPrivateFrag.cppm
+extern int a; // #a1
+int a; // #a2
+extern int b;
+module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
int b; // ok
+//--- ExportFrags.cppm
+export module; // expected-error {{global module fragment cannot be exported}}
+extern int a; // #a1
+export module Foo;
+// expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}}
+// expected-note@#a1 {{previous decl}}
+
+int a; // #a2
+extern int b;
+
+module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
+
+module :private; // #priv-frag
-#ifndef NO_PRIVATE_FRAG
-#ifndef NO_MODULE_DECL
+int b; // ok
module :private; // expected-error {{private module fragment redefined}}
-// expected-note@#priv-frag {{previous definition is here}}
-#endif
-#endif
+ // expected-note@#priv-frag {{previous definition is here}}
diff --git a/clang/test/CXX/basic/basic.link/p3.cpp b/clang/test/CXX/basic/basic.link/p3.cpp
index 01202264d2591..1cf2b750a8a81 100644
--- a/clang/test/CXX/basic/basic.link/p3.cpp
+++ b/clang/test/CXX/basic/basic.link/p3.cpp
@@ -1,35 +1,18 @@
-// RUN: %clang_cc1 -std=c++2a -verify %s
-// RUN: %clang_cc1 -std=c++2a -verify %s -DIMPORT_ERROR=1
-// RUN: %clang_cc1 -std=c++2a -verify %s -DIMPORT_ERROR=2
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: %clang_cc1 -std=c++2a -verify %t/M.cppm
+// RUN: %clang_cc1 -std=c++2a -verify %t/ImportError1.cppm
+// RUN: %clang_cc1 -std=c++2a -verify %t/ImportError2.cppm
+
+//--- M.cppm
module;
-#if IMPORT_ERROR != 2
struct import { struct inner {}; };
-#endif
struct module { struct inner {}; };
-
constexpr int n = 123;
export module m; // #1
-
-// Import errors are fatal, so we test them in isolation.
-#if IMPORT_ERROR == 1
-import x = {}; // expected-error {{expected ';' after module name}}
- // expected-error at -1 {{module 'x' not found}}
-
-#elif IMPORT_ERROR == 2
-struct X;
-template<int> struct import;
-template<> struct import<n> {
- static X y;
-};
-
-// This is not valid because the 'import <n>' is a pp-import, even though it
-// grammatically can't possibly be an import declaration.
-struct X {} import<n>::y; // expected-error {{'n' file not found}}
-
-#else
module y = {}; // expected-error {{multiple module declarations}} expected-error 2{{}}
// expected-note@#1 {{previous module declaration}}
@@ -40,8 +23,8 @@ import::inner xi = {};
module::inner yi = {};
namespace N {
- module a;
- import b;
+ module a; // expected-error {{module declaration can only appear at the top level}}
+ import b; // expected-error {{import declaration can only appear at the top level}}
}
extern "C++" module cxxm;
@@ -51,4 +34,34 @@ template<typename T> module module_var_template;
// This is a variable named 'import' that shadows the type 'import' above.
struct X {} import;
-#endif
+
+//--- ImportError1.cppm
+module;
+
+struct import { struct inner {}; };
+struct module { struct inner {}; };
+
+constexpr int n = 123;
+
+export module m; // #1
+
+import x = {}; // expected-error {{expected ';' after module name}}
+ // expected-error at -1 {{module 'x' not found}}
+
+//--- ImportError2.cppm
+// expected-no-diagnostics
+module;
+
+struct module { struct inner {}; };
+
+constexpr int n = 123;
+
+export module m; // #1
+
+struct X;
+template<int> struct import;
+template<> struct import<n> {
+ static X y;
+};
+
+struct X {} import<n>::y;
diff --git a/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp b/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp
index d70eb7de22c6a..a57919f48afdd 100644
--- a/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp
+++ b/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp
@@ -1,14 +1,16 @@
// RUN: rm -rf %t
-// RUN: mkdir -p %t
-// RUN: echo '#ifndef FOO_H' > %t/foo.h
-// RUN: echo '#define FOO_H' >> %t/foo.h
-// RUN: echo 'extern int in_header;' >> %t/foo.h
-// RUN: echo '#endif' >> %t/foo.h
-// RUN: %clang_cc1 -std=c++2a -I%t -emit-module-interface -DINTERFACE %s -o %t.pcm
-// RUN: %clang_cc1 -std=c++2a -I%t -fmodule-file=A=%t.pcm -DIMPLEMENTATION %s -verify -fno-modules-error-recovery
-// RUN: %clang_cc1 -std=c++2a -I%t -fmodule-file=A=%t.pcm %s -verify -fno-modules-error-recovery
-
-#ifdef INTERFACE
+// RUN: split-file %s %t
+// RUN: %clang_cc1 -std=c++2a -I%t -emit-module-interface %t/interface.cppm -o %t.pcm
+// RUN: %clang_cc1 -std=c++2a -I%t -fmodule-file=A=%t.pcm %t/implA.cppm -verify -fno-modules-error-recovery
+// RUN: %clang_cc1 -std=c++2a -I%t -fmodule-file=A=%t.pcm %t/implB.cppm -verify -fno-modules-error-recovery
+
+//--- foo.h
+#ifndef FOO_H
+#define FOO_H
+extern int in_header;
+#endif
+
+//--- interface.cppm
module;
#include "foo.h"
// FIXME: The following need to be moved to a header file. The global module
@@ -22,11 +24,9 @@ static int internal;
module :private;
int not_exported_private;
static int internal_private;
-#else
-#ifdef IMPLEMENTATION
+//--- implA.cppm
module;
-#endif
void test_early() {
in_header = 1; // expected-error {{use of undeclared identifier 'in_header'}}
@@ -46,11 +46,7 @@ void test_early() {
internal_private = 1; // expected-error {{undeclared identifier}}
}
-#ifdef IMPLEMENTATION
module A;
-#else
-import A;
-#endif
void test_late() {
in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}}
@@ -61,20 +57,54 @@ void test_late() {
exported = 1;
not_exported = 1;
-#ifndef IMPLEMENTATION
- // expected-error at -2 {{use of undeclared identifier 'not_exported'; did you mean 'exported'?}}
- // expected-note at p2.cpp:18 {{'exported' declared here}}
-#endif
internal = 1; // expected-error {{use of undeclared identifier 'internal'}}
not_exported_private = 1;
-#ifndef IMPLEMENTATION
- // FIXME: should not be visible here
- // expected-error at -3 {{undeclared identifier}}
-#endif
internal_private = 1; // expected-error {{use of undeclared identifier 'internal_private'}}
}
-#endif
+//--- implB.cppm
+module;
+
+void test_early() {
+ in_header = 1; // expected-error {{use of undeclared identifier 'in_header'}}
+ // expected-note@* {{not visible}}
+
+ global_module_fragment = 1; // expected-error {{use of undeclared identifier 'global_module_fragment'}}
+
+ exported = 1; // expected-error {{use of undeclared identifier 'exported'}}
+
+ not_exported = 1; // expected-error {{use of undeclared identifier 'not_exported'}}
+
+ // FIXME: We need better diagnostic message for static variable.
+ internal = 1; // expected-error {{use of undeclared identifier 'internal'}}
+
+ not_exported_private = 1; // expected-error {{undeclared identifier}}
+
+ internal_private = 1; // expected-error {{undeclared identifier}}
+}
+
+export module B;
+import A;
+
+void test_late() {
+ in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}}
+ // expected-note@* {{not visible}}
+
+ global_module_fragment = 1; // expected-error {{missing '#include'; 'global_module_fragment' must be declared before it is used}}
+
+ exported = 1;
+
+ not_exported = 1; // expected-error {{use of undeclared identifier 'not_exported'; did you mean 'exported'?}}
+ // expected-note@* {{'exported' declared here}}
+
+ internal = 1; // expected-error {{use of undeclared identifier 'internal'}}
+
+ not_exported_private = 1;
+ // FIXME: should not be visible here
+ // expected-error at -2 {{undeclared identifier}}
+
+ internal_private = 1; // expected-error {{use of undeclared identifier 'internal_private'}}
+}
diff --git a/clang/test/CXX/lex/lex.pptoken/p3-2a.cpp b/clang/test/CXX/lex/lex.pptoken/p3-2a.cpp
index 0e0e5fec6e9d8..f51066806947f 100644
--- a/clang/test/CXX/lex/lex.pptoken/p3-2a.cpp
+++ b/clang/test/CXX/lex/lex.pptoken/p3-2a.cpp
@@ -15,7 +15,7 @@ import <foo bar>;
// CHECK: import <foo bar>;
import <foo bar>;
-// CHECK: foo; import <foo bar>;
+// CHECK: foo; import <foo bar>;
foo; import <foo bar>;
// CHECK: foo import <foo bar>;
@@ -45,7 +45,7 @@ export export import <foo bar>;
import <foo bar>;
UNBALANCED_PAREN
-// CHECK: import <foo bar>;
+// CHECK: import <foo bar>;
import <foo bar>;
)
@@ -57,14 +57,19 @@ import <foo bar>;
// CHECK: import <foo bar>;
import HEADER;
-// CHECK: import <foo bar>;
+// CHECK: {{^}}foo{{$}}
+// CHECK-NEXT: {{^}} bar{{$}}
+// CHECK-NEXT: {{^}}>;{{$}}
import <
foo
bar
>;
// CHECK: import{{$}}
-// CHECK: {{^}}<foo bar>;
+// CHECK-NEXT: {{^}}<{{$}}
+// CHECK-NEXT: {{^}}foo{{$}}
+// CHECK-NEXT: {{^}} bar{{$}}
+// CHECK-NEXT: {{^}}>;{{$}}
import
<
foo
@@ -72,7 +77,7 @@ foo
>;
// CHECK: import{{$}}
-// CHECK: {{^}}<foo bar>;
+// CHECK: {{^}}<foo bar>;
import
<foo bar>;
diff --git a/clang/test/CXX/module/basic/basic.def.odr/p6.cppm b/clang/test/CXX/module/basic/basic.def.odr/p6.cppm
index 8e7917dc63ea5..c532e7ad40a10 100644
--- a/clang/test/CXX/module/basic/basic.def.odr/p6.cppm
+++ b/clang/test/CXX/module/basic/basic.def.odr/p6.cppm
@@ -3,29 +3,28 @@
// RUN: split-file %s %t
//
// RUN: %clang_cc1 -std=c++20 -verify %t/global-vs-module.cppm
-// RUN: %clang_cc1 -std=c++20 -verify %t/global-vs-module.cppm -DEXPORT
-// RUN: %clang_cc1 -std=c++20 -verify %t/global-vs-module.cppm -DUSING
+// RUN: %clang_cc1 -std=c++20 -verify %t/global-vs-module-export.cppm
+// RUN: %clang_cc1 -std=c++20 -verify %t/global-vs-module-using.cppm
//
-// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/global-vs-module.cppm -o %t/M.pcm -DNO_GLOBAL -DEXPORT
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/M.cppm -o %t/M.pcm
// RUN: %clang_cc1 -std=c++20 -verify %t/module-vs-global.cpp -fmodule-file=M=%t/M.pcm
//
// Some of the following tests intentionally have no -verify in their RUN
// lines; we are testing that those cases do not produce errors.
//
-// RUN: %clang_cc1 -std=c++20 %t/module-vs-module.cpp -fmodule-file=M=%t/M.pcm -DMODULE_INTERFACE -verify
-// RUN: %clang_cc1 -std=c++20 %t/module-vs-module.cpp -fmodule-file=M=%t/M.pcm -DMODULE_INTERFACE -DNO_IMPORT
+// RUN: %clang_cc1 -std=c++20 %t/module-vs-module-interface.cpp -fmodule-file=M=%t/M.pcm -verify
+// RUN: %clang_cc1 -std=c++20 %t/module-vs-module-interface.cpp -fmodule-file=M=%t/M.pcm -DNO_IMPORT
//
-// RUN: %clang_cc1 -std=c++20 %t/module-vs-module.cpp -fmodule-file=M=%t/M.pcm -emit-module-interface -o %t/N.pcm -DMODULE_INTERFACE -DNO_ERRORS
-// RUN: %clang_cc1 -std=c++20 %t/module-vs-module.cpp -fmodule-file=M=%t/M.pcm -fmodule-file=N=%t/N.pcm -verify
+// RUN: %clang_cc1 -std=c++20 %t/module-vs-module-interface.cpp -fmodule-file=M=%t/M.pcm -emit-module-interface -o %t/N.pcm -DNO_ERRORS
+// RUN: %clang_cc1 -std=c++20 %t/module-vs-module-impl.cpp -fmodule-file=M=%t/M.pcm -fmodule-file=N=%t/N.pcm -verify
//
-// RUN: %clang_cc1 -std=c++20 %t/module-vs-module.cpp -fmodule-file=M=%t/M.pcm -fmodule-file=N=%t/N.pcm -DNO_IMPORT -verify
+// RUN: %clang_cc1 -std=c++20 %t/module-vs-module-impl.cpp -fmodule-file=M=%t/M.pcm -fmodule-file=N=%t/N.pcm -DNO_IMPORT -verify
//
-// RUN: %clang_cc1 -std=c++20 %t/module-vs-module.cpp -fmodule-file=M=%t/M.pcm -emit-module-interface -o %t/N-no-M.pcm -DMODULE_INTERFACE -DNO_ERRORS -DNO_IMPORT
-// RUN: %clang_cc1 -std=c++20 %t/module-vs-module.cpp -fmodule-file=M=%t/M.pcm -fmodule-file=N=%t/N-no-M.pcm -verify
-// RUN: %clang_cc1 -std=c++20 %t/module-vs-module.cpp -fmodule-file=N=%t/N-no-M.pcm -DNO_IMPORT
+// RUN: %clang_cc1 -std=c++20 %t/module-vs-module-interface.cpp -fmodule-file=M=%t/M.pcm -emit-module-interface -o %t/N-no-M.pcm -DNO_ERRORS -DNO_IMPORT
+// RUN: %clang_cc1 -std=c++20 %t/module-vs-module-impl.cpp -fmodule-file=M=%t/M.pcm -fmodule-file=N=%t/N-no-M.pcm -verify
+// RUN: %clang_cc1 -std=c++20 %t/module-vs-module-impl.cpp -fmodule-file=N=%t/N-no-M.pcm -DNO_IMPORT
//--- global-vs-module.cppm
-#ifndef NO_GLOBAL
module;
extern int var; // expected-note {{previous declaration is here}}
int func(); // expected-note {{previous declaration is here}}
@@ -40,11 +39,75 @@ template<typename> using type_tpl = int; // expected-note {{previous declaration
typedef int type;
namespace ns { using ::func; }
namespace ns_alias = ns;
-#endif
export module M;
-#ifdef USING
+extern int var; // expected-error {{declaration of 'var' in module M follows declaration in the global module}}
+int func(); // expected-error {{declaration of 'func' in module M follows declaration in the global module}}
+struct str; // expected-error {{declaration of 'str' in module M follows declaration in the global module}}
+using type = int;
+
+template<typename> extern int var_tpl; // expected-error {{declaration of 'var_tpl' in module M follows declaration in the global module}}
+template<typename> int func_tpl(); // expected-error {{declaration of 'func_tpl' in module M follows declaration in the global module}}
+template<typename> struct str_tpl; // expected-error {{declaration of 'str_tpl' in module M follows declaration in the global module}}
+template<typename> using type_tpl = int; // expected-error {{declaration of 'type_tpl' in module M follows declaration in the global module}}
+
+typedef int type;
+namespace ns { using ::func; }
+namespace ns_alias = ns;
+
+//--- global-vs-module-export.cppm
+module;
+extern int var; // expected-note {{previous declaration is here}}
+int func(); // expected-note {{previous declaration is here}}
+struct str; // expected-note {{previous declaration is here}}
+using type = int;
+
+template<typename> extern int var_tpl; // expected-note {{previous declaration is here}}
+template<typename> int func_tpl(); // expected-note {{previous declaration is here}}
+template<typename> struct str_tpl; // expected-note {{previous declaration is here}}
+template<typename> using type_tpl = int; // expected-note {{previous declaration is here}}
+
+typedef int type;
+namespace ns { using ::func; }
+namespace ns_alias = ns;
+
+export module M;
+
+export {
+extern int var; // expected-error {{declaration of 'var' in module M follows declaration in the global module}}
+int func(); // expected-error {{declaration of 'func' in module M follows declaration in the global module}}
+struct str; // expected-error {{declaration of 'str' in module M follows declaration in the global module}}
+using type = int;
+
+template<typename> extern int var_tpl; // expected-error {{declaration of 'var_tpl' in module M follows declaration in the global module}}
+template<typename> int func_tpl(); // expected-error {{declaration of 'func_tpl' in module M follows declaration in the global module}}
+template<typename> struct str_tpl; // expected-error {{declaration of 'str_tpl' in module M follows declaration in the global module}}
+template<typename> using type_tpl = int; // expected-error {{declaration of 'type_tpl' in module M follows declaration in the global module}}
+
+typedef int type;
+namespace ns { using ::func; }
+namespace ns_alias = ns;
+}
+
+//--- global-vs-module-using.cppm
+module;
+extern int var; // expected-note {{previous declaration is here}}
+int func(); // expected-note {{previous declaration is here}}
+struct str; // expected-note {{previous declaration is here}}
+using type = int;
+
+template<typename> extern int var_tpl; // expected-note {{previous declaration is here}}
+template<typename> int func_tpl(); // expected-note {{previous declaration is here}}
+template<typename> struct str_tpl; // expected-note {{previous declaration is here}}
+template<typename> using type_tpl = int; // expected-note {{previous declaration is here}}
+
+typedef int type;
+namespace ns { using ::func; }
+namespace ns_alias = ns;
+
+export module M;
+
using ::var;
using ::func;
using ::str;
@@ -53,11 +116,6 @@ using ::var_tpl;
using ::func_tpl;
using ::str_tpl;
using ::type_tpl;
-#endif
-
-#ifdef EXPORT
-export {
-#endif
extern int var; // expected-error {{declaration of 'var' in module M follows declaration in the global module}}
int func(); // expected-error {{declaration of 'func' in module M follows declaration in the global module}}
@@ -73,51 +131,87 @@ typedef int type;
namespace ns { using ::func; }
namespace ns_alias = ns;
-#ifdef EXPORT
+//--- M.cppm
+export module M;
+
+export {
+extern int var; // expected-error {{declaration of 'var' in module M follows declaration in the global module}}
+int func(); // expected-error {{declaration of 'func' in module M follows declaration in the global module}}
+struct str; // expected-error {{declaration of 'str' in module M follows declaration in the global module}}
+using type = int;
+
+template<typename> extern int var_tpl; // expected-error {{declaration of 'var_tpl' in module M follows declaration in the global module}}
+template<typename> int func_tpl(); // expected-error {{declaration of 'func_tpl' in module M follows declaration in the global module}}
+template<typename> struct str_tpl; // expected-error {{declaration of 'str_tpl' in module M follows declaration in the global module}}
+template<typename> using type_tpl = int; // expected-error {{declaration of 'type_tpl' in module M follows declaration in the global module}}
+
+typedef int type;
+namespace ns { using ::func; }
+namespace ns_alias = ns;
}
-#endif
//--- module-vs-global.cpp
+module;
import M;
-extern int var; // expected-error {{declaration of 'var' in the global module follows declaration in module M}} expected-note at global-vs-module.cppm:35 {{previous}}
-int func(); // expected-error {{declaration of 'func' in the global module follows declaration in module M}} expected-note at global-vs-module.cppm:36 {{previous}}
-struct str; // expected-error {{declaration of 'str' in the global module follows declaration in module M}} expected-note at global-vs-module.cppm:37 {{previous}}
+extern int var; // expected-error {{declaration of 'var' in the global module follows declaration in module M}} expected-note at M.cppm:4 {{previous}}
+int func(); // expected-error {{declaration of 'func' in the global module follows declaration in module M}} expected-note at M.cppm:5 {{previous}}
+struct str; // expected-error {{declaration of 'str' in the global module follows declaration in module M}} expected-note at M.cppm:6 {{previous}}
using type = int;
-template<typename> extern int var_tpl; // expected-error {{declaration of 'var_tpl' in the global module follows declaration in module M}} expected-note at global-vs-module.cppm:40 {{previous}}
-template<typename> int func_tpl(); // expected-error {{declaration of 'func_tpl' in the global module follows declaration in module M}} expected-note at global-vs-module.cppm:41 {{previous}}
-template<typename> struct str_tpl; // expected-error {{declaration of 'str_tpl' in the global module follows declaration in module M}} expected-note at global-vs-module.cppm:42 {{previous}}
-template<typename> using type_tpl = int; // expected-error {{declaration of 'type_tpl' in the global module follows declaration in module M}} expected-note at global-vs-module.cppm:43 {{previous}}
+template<typename> extern int var_tpl; // expected-error {{declaration of 'var_tpl' in the global module follows declaration in module M}} expected-note at M.cppm:9 {{previous}}
+template<typename> int func_tpl(); // expected-error {{declaration of 'func_tpl' in the global module follows declaration in module M}} expected-note at M.cppm:10 {{previous}}
+template<typename> struct str_tpl; // expected-error {{declaration of 'str_tpl' in the global module follows declaration in module M}} expected-note at M.cppm:11 {{previous}}
+template<typename> using type_tpl = int; // expected-error {{declaration of 'type_tpl' in the global module follows declaration in module M}} expected-note at M.cppm:12 {{previous}}
typedef int type;
namespace ns { using ::func; }
namespace ns_alias = ns;
-//--- module-vs-module.cpp
-#ifdef MODULE_INTERFACE
export module N;
-#else
-module N;
-#endif
+
+//--- module-vs-module-interface.cpp
+export module N;
#ifndef NO_IMPORT
import M;
#endif
#ifndef NO_ERRORS
-extern int var; // expected-error {{declaration of 'var' in module N follows declaration in module M}} expected-note at global-vs-module.cppm:35 {{previous}}
-int func(); // expected-error {{declaration of 'func' in module N follows declaration in module M}} expected-note at global-vs-module.cppm:36 {{previous}}
-struct str; // expected-error {{declaration of 'str' in module N follows declaration in module M}} expected-note at global-vs-module.cppm:37 {{previous}}
+extern int var; // expected-error {{declaration of 'var' in module N follows declaration in module M}} expected-note at M.cppm:4 {{previous}}
+int func(); // expected-error {{declaration of 'func' in module N follows declaration in module M}} expected-note at M.cppm:5 {{previous}}
+struct str; // expected-error {{declaration of 'str' in module N follows declaration in module M}} expected-note at M.cppm:6 {{previous}}
using type = int;
-template<typename> extern int var_tpl; // expected-error {{declaration of 'var_tpl' in module N follows declaration in module M}} expected-note at global-vs-module.cppm:40 {{previous}}
-template<typename> int func_tpl(); // expected-error {{declaration of 'func_tpl' in module N follows declaration in module M}} expected-note at global-vs-module.cppm:41 {{previous}}
-template<typename> struct str_tpl; // expected-error {{declaration of 'str_tpl' in module N follows declaration in module M}} expected-note at global-vs-module.cppm:42 {{previous}}
-template<typename> using type_tpl = int; // expected-error {{declaration of 'type_tpl' in module N follows declaration in module M}} expected-note at global-vs-module.cppm:43 {{previous}}
+template<typename> extern int var_tpl; // expected-error {{declaration of 'var_tpl' in module N follows declaration in module M}} expected-note at M.cppm:9 {{previous}}
+template<typename> int func_tpl(); // expected-error {{declaration of 'func_tpl' in module N follows declaration in module M}} expected-note at M.cppm:10 {{previous}}
+template<typename> struct str_tpl; // expected-error {{declaration of 'str_tpl' in module N follows declaration in module M}} expected-note at M.cppm:11 {{previous}}
+template<typename> using type_tpl = int; // expected-error {{declaration of 'type_tpl' in module N follows declaration in module M}} expected-note at M.cppm:12 {{previous}}
typedef int type;
namespace ns { using ::func; }
namespace ns_alias = ns;
#endif
+//--- module-vs-module-impl.cpp
+module N;
+
+#ifndef NO_IMPORT
+import M;
+#endif
+
+#ifndef NO_ERRORS
+extern int var; // expected-error {{declaration of 'var' in module N follows declaration in module M}} expected-note at M.cppm:4 {{previous}}
+int func(); // expected-error {{declaration of 'func' in module N follows declaration in module M}} expected-note at M.cppm:5 {{previous}}
+struct str; // expected-error {{declaration of 'str' in module N follows declaration in module M}} expected-note at M.cppm:6 {{previous}}
+using type = int;
+
+template<typename> extern int var_tpl; // expected-error {{declaration of 'var_tpl' in module N follows declaration in module M}} expected-note at M.cppm:9 {{previous}}
+template<typename> int func_tpl(); // expected-error {{declaration of 'func_tpl' in module N follows declaration in module M}} expected-note at M.cppm:10 {{previous}}
+template<typename> struct str_tpl; // expected-error {{declaration of 'str_tpl' in module N follows declaration in module M}} expected-note at M.cppm:11 {{previous}}
+template<typename> using type_tpl = int; // expected-error {{declaration of 'type_tpl' in module N follows declaration in module M}} expected-note at M.cppm:12 {{previous}}
+
+typedef int type;
+namespace ns { using ::func; }
+namespace ns_alias = ns;
+#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..4bdcc9e5f278e 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 -DEXPORT= -DMODULE_NAME=z
-// 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: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify -x c++ %t/z_impl.cppm
+// 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/x_impl.cppm
//
// 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: %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: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/z_interface.cppm
+// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/x_interface.cppm
//
// 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: %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: %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: %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: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/invalid_module_name.cppm
+// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/empty_attribute.cppm
+// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/fancy_attribute.cppm
+// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/maybe_unused_attribute.cppm
//--- x.cppm
export module x;
@@ -38,17 +30,31 @@ int a, b;
export module x.y;
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}}
-#elif TEST == 9
-// expected-warning at -4 {{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}}
-#else
+//--- z_impl.cppm
+module z; // expected-error {{module 'z' not found}}
+
+//--- x_impl.cppm
+// expected-no-diagnostics
+module x;
+
+//--- z_interface.cppm
// expected-no-diagnostics
-#endif
+export module z;
+
+//--- x_interface.cppm
+// expected-no-diagnostics
+export module x;
+
+//--- invalid_module_name.cppm
+export module z elderberry; // expected-error {{expected ';'}} \
+ // expected-error {{a type specifier is required}}
+
+//--- empty_attribute.cppm
+// expected-no-diagnostics
+export module z [[]];
+
+//--- fancy_attribute.cppm
+export module z [[fancy]]; // expected-warning {{unknown attribute 'fancy' ignored}}
+
+//--- maybe_unused_attribute.cppm
+export module z [[maybe_unused]]; // expected-error-re {{'maybe_unused' attribute cannot be applied to a module{{$}}}}
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..afc8c9a5b3be2 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,10 @@
// 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 \
-// RUN: -fmodule-file=a.b=%t/a.b.pcm -verify %t/test.cpp -DMODULE_NAME=a.b
+// RUN: -verify %t/test.interface.cpp
+// 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.implementation.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
@@ -33,12 +33,27 @@ int use_2 = b; // ok
// There is no relation between module x and module x.y.
int use_3 = c; // expected-error {{use of undeclared identifier 'c'}}
-//--- test.cpp
-#ifdef INTERFACE
-export module MODULE_NAME;
-#else
-module MODULE_NAME;
-#endif
+//--- test.interface.cpp
+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 identifier after '.' in module name}}
+import .x; // expected-error {{unknown type name 'import'}} \
+ // expected-error {{cannot use dot operator on a type}}
+
+import blarg; // expected-error {{module 'blarg' not found}}
+
+int use_4 = c; // ok
+
+//--- test.implementation.cpp
+module a.b;
import x;
@@ -48,8 +63,9 @@ import x [[noreturn]]; // expected-error {{'noreturn' attribute cannot be applie
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'}}
+import x.; // expected-error {{expected identifier after '.' in module name}}
+import .x; // expected-error {{unknown type name 'import'}} \
+ // expected-error {{cannot use dot operator on a type}}
import blarg; // expected-error {{module 'blarg' not found}}
diff --git a/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.interface/p1.cppm b/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.interface/p1.cppm
index 84ef85126c369..2158d7fa84b86 100644
--- a/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.interface/p1.cppm
+++ b/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.interface/p1.cppm
@@ -1,29 +1,26 @@
-// RUN: %clang_cc1 -std=c++20 %s -verify -emit-module-interface -o /dev/null
-// RUN: %clang_cc1 -std=c++20 %s -DINTERFACE -verify -emit-module-interface -o %t
-// RUN: %clang_cc1 -std=c++20 %s -DIMPLEMENTATION -verify -fmodule-file=A=%t -o /dev/null
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++20 %t/ExportDeclNotInModulePurview.cppm -verify -emit-module-interface -o /dev/null
+// RUN: %clang_cc1 -std=c++20 %t/A.cppm -verify -emit-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 %t/AddExport.cppm -verify -fmodule-file=A=%t/A.pcm -o /dev/null
//
-// RUN: %clang_cc1 -std=c++20 %s -DBUILT_AS_INTERFACE -emit-module-interface -verify -o /dev/null
-// RUN: %clang_cc1 -std=c++20 %s -DINTERFACE -DBUILT_AS_INTERFACE -emit-module-interface -verify -o /dev/null
-// RUN: %clang_cc1 -std=c++20 %s -DIMPLEMENTATION -DBUILT_AS_INTERFACE -emit-module-interface -verify -o /dev/null
+// RUN: %clang_cc1 -std=c++20 %t/AddExport2.cppm -emit-module-interface -verify -o /dev/null
-#if INTERFACE
+//--- ExportDeclNotInModulePurview.cppm
+// expected-error@* {{missing 'export module' declaration in module interface unit}}
+export int b; // expected-error {{export declaration can only be used within a module purview}}
+
+//--- A.cppm
// expected-no-diagnostics
export module A;
-#elif IMPLEMENTATION
-module A; // #module-decl
- #ifdef BUILT_AS_INTERFACE
- // expected-error at -2 {{missing 'export' specifier in module declaration while building module interface}}
- #define INTERFACE
- #endif
-#else // Not in a module
-// expected-error@* {{missing 'export module' declaration in module interface unit}}
-#endif
+export int a;
-#ifndef INTERFACE
+//--- AddExport.cppm
+module A; // #module-decl
export int b; // expected-error {{export declaration can only be used within a module purview}}
-#ifdef IMPLEMENTATION
// expected-note@#module-decl {{add 'export' here}}
-#endif
-#else
+
+//--- AddExport2.cppm
+module A; // expected-error {{missing 'export' specifier in module declaration while building module interface}}
export int a;
-#endif
diff --git a/clang/test/CXX/module/dcl.dcl/dcl.module/p1.cpp b/clang/test/CXX/module/dcl.dcl/dcl.module/p1.cpp
index db86b5dd34c38..95d087e0f6c78 100644
--- a/clang/test/CXX/module/dcl.dcl/dcl.module/p1.cpp
+++ b/clang/test/CXX/module/dcl.dcl/dcl.module/p1.cpp
@@ -1,14 +1,30 @@
-// RUN: %clang_cc1 -std=c++20 -verify %s -DFOO=export -DBAR=export
-// RUN: %clang_cc1 -std=c++20 -verify %s -DFOO=export -DBAR=
-// RUN: %clang_cc1 -std=c++20 %s -DFOO=export -emit-module-interface -o %t
-// RUN: %clang_cc1 -std=c++20 %s -fmodule-file=foo=%t -DFOO=
-// RUN: %clang_cc1 -std=c++20 %s -fmodule-file=foo=%t -DBAR=export
-// RUN: %clang_cc1 -std=c++20 -verify %s -fmodule-file=foo=%t -DFOO= -DBAR=export
-
-#ifdef FOO
-FOO module foo; // expected-note {{previous module declaration is here}}
-#endif
-
-#ifdef BAR
-BAR module bar; // expected-error {{translation unit contains multiple module declarations}}
-#endif
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++20 -verify %t/A.cppm
+// RUN: %clang_cc1 -std=c++20 -verify %t/B.cppm
+// RUN: %clang_cc1 -std=c++20 %t/C.cppm -emit-module-interface -o %t/C.pcm
+// RUN: %clang_cc1 -std=c++20 %t/D.cppm -fmodule-file=foo=%t/C.pcm
+// RUN: %clang_cc1 -std=c++20 %t/E.cppm -fmodule-file=foo=%t/C.pcm
+// RUN: %clang_cc1 -std=c++20 -verify %t/F.cppm -fmodule-file=foo=%t/C.pcm
+
+//--- A.cppm
+export module foo; // expected-note {{previous module declaration is here}}
+export module bar; // expected-error {{translation unit contains multiple module declarations}}
+
+//--- B.cppm
+export module foo; // expected-note {{previous module declaration is here}}
+module bar; // expected-error {{translation unit contains multiple module declarations}}
+
+//--- C.cppm
+export module foo;
+
+//--- D.cppm
+module foo;
+
+//--- E.cppm
+export module bar;
+
+//--- F.cppm
+module foo; // expected-note {{previous module declaration is here}}
+export module bar; // expected-error {{translation unit contains multiple module declarations}}
diff --git a/clang/test/CXX/module/dcl.dcl/dcl.module/p5.cpp b/clang/test/CXX/module/dcl.dcl/dcl.module/p5.cpp
index ca100443a4c67..a0d30233809f9 100644
--- a/clang/test/CXX/module/dcl.dcl/dcl.module/p5.cpp
+++ b/clang/test/CXX/module/dcl.dcl/dcl.module/p5.cpp
@@ -1,22 +1,47 @@
// RUN: rm -rf %t
-// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -o %t -DINTERFACE
-// RUN: %clang_cc1 -std=c++20 -fmodule-file=Foo=%t %s -verify -DIMPLEMENTATION
-// RUN: %clang_cc1 -std=c++20 -fmodule-file=Foo=%t %s -verify -DEARLY_IMPLEMENTATION
-// RUN: %clang_cc1 -std=c++20 -fmodule-file=Foo=%t %s -verify -DUSER
+// RUN: split-file %s %t
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/interface.cppm -o %t/interface.pcm
+// RUN: %clang_cc1 -std=c++20 -fmodule-file=Foo=%t/interface.pcm %t/implementation.cppm -verify -DIMPLEMENTATION
+// RUN: %clang_cc1 -std=c++20 -fmodule-file=Foo=%t/interface.pcm %t/early_impl.cppm -verify -DEARLY_IMPLEMENTATION
+// RUN: %clang_cc1 -std=c++20 -fmodule-file=Foo=%t/interface.pcm %t/user.cppm -verify -DUSER
+
+//--- interface.cppm
// expected-no-diagnostics
+module;
-#if defined(INTERFACE) || defined(EARLY_IMPLEMENTATION) || defined(IMPLEMENTATION)
+template<typename T> struct type_template {
+ typedef T type;
+ void f(type);
+};
+
+template<typename T> void type_template<T>::f(type) {}
+
+template<int = 0, typename = int, template<typename> class = type_template>
+struct default_template_args {};
+
+export module Foo;
+
+//--- implementation.cppm
+// expected-no-diagnostics
module;
-#endif
-#ifdef USER
-import Foo;
-#endif
+template<typename T> struct type_template {
+ typedef T type;
+ void f(type);
+};
+
+template<typename T> void type_template<T>::f(type) {}
+
+template<int = 0, typename = int, template<typename> class = type_template>
+struct default_template_args {};
-#ifdef EARLY_IMPLEMENTATION
module Foo;
-#endif
+
+//--- early_impl.cppm
+// expected-no-diagnostics
+module;
+module Foo;
template<typename T> struct type_template {
typedef T type;
@@ -28,10 +53,16 @@ template<typename T> void type_template<T>::f(type) {}
template<int = 0, typename = int, template<typename> class = type_template>
struct default_template_args {};
-#ifdef INTERFACE
-export module Foo;
-#endif
+//--- user.cppm
+// expected-no-diagnostics
+import Foo;
-#ifdef IMPLEMENTATION
-module Foo;
-#endif
+template<typename T> struct type_template {
+ typedef T type;
+ void f(type);
+};
+
+template<typename T> void type_template<T>::f(type) {}
+
+template<int = 0, typename = int, template<typename> class = type_template>
+struct default_template_args {};
diff --git a/clang/test/CXX/module/module.interface/p1.cpp b/clang/test/CXX/module/module.interface/p1.cpp
index 54a201e502323..9b3ec1348f9cf 100644
--- a/clang/test/CXX/module/module.interface/p1.cpp
+++ b/clang/test/CXX/module/module.interface/p1.cpp
@@ -1,28 +1,19 @@
-// RUN: %clang_cc1 -std=c++2a %s -DERRORS -verify
-// RUN: %clang_cc1 -std=c++2a %s -emit-module-interface -o %t.pcm
-// RUN: %clang_cc1 -std=c++2a %s -fmodule-file=M=%t.pcm -DIMPLEMENTATION -verify -Db=b2 -Dc=c2
+// RUN: rm -rf %t
+// RUN: split-file %s %t
-module;
+// RUN: %clang_cc1 -std=c++2a %t/errors.cppm -verify
+// RUN: %clang_cc1 -std=c++2a %t/M.cppm -emit-module-interface -o %t/M.pcm
+// RUN: %clang_cc1 -std=c++2a %t/impl.cppm -fmodule-file=M=%t/M.pcm -verify
-#ifdef ERRORS
+//--- errors.cppm
+module;
export int a; // expected-error {{export declaration can only be used within a module purview}}
-#endif
-
-#ifndef IMPLEMENTATION
-export
-#else
-// expected-error@#1 {{export declaration can only be used within a module purview}}
-// expected-error@#2 {{export declaration can only be used within a module purview}}
-// expected-note at +2 1+{{add 'export'}}
-#endif
-module M;
-
+export module M;
export int b; // #1
namespace N {
export int c; // #2
}
-#ifdef ERRORS
namespace { // expected-note 2{{anonymous namespace begins here}}
export int d1; // expected-error {{export declaration appears within anonymous namespace}}
namespace X {
@@ -35,4 +26,19 @@ export { export int f; } // expected-error {{within another export declaration}}
module :private; // expected-note {{private module fragment begins here}}
export int priv; // expected-error {{export declaration cannot be used in a private module fragment}}
-#endif
+
+//--- M.cppm
+export module M;
+export int b;
+namespace N {
+ export int c;
+}
+
+//--- impl.cppm
+module M; // #M
+
+export int b2; // expected-error {{export declaration can only be used within a module purview}}
+namespace N {
+ export int c2; // expected-error {{export declaration can only be used within a module purview}}
+}
+// expected-note@#M 2+{{add 'export'}}
diff --git a/clang/test/CXX/module/module.interface/p2.cpp b/clang/test/CXX/module/module.interface/p2.cpp
index 4f06b9f386869..8221c400ecd62 100644
--- a/clang/test/CXX/module/module.interface/p2.cpp
+++ b/clang/test/CXX/module/module.interface/p2.cpp
@@ -1,24 +1,26 @@
// RUN: rm -rf %t
// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
// RUN: %clang_cc1 -std=c++20 -x c++-header %S/Inputs/header.h -emit-header-unit -o %t/h.pcm
-// RUN: %clang_cc1 -std=c++20 %s -DX_INTERFACE -emit-module-interface -o %t/x.pcm
-// RUN: %clang_cc1 -std=c++20 %s -DY_INTERFACE -emit-module-interface -o %t/y.pcm
-// RUN: %clang_cc1 -std=c++20 %s -DINTERFACE -fmodule-file=X=%t/x.pcm -fmodule-file=Y=%t/y.pcm -emit-module-interface -o %t/m.pcm
-// RUN: %clang_cc1 -std=c++20 %s -DIMPLEMENTATION -I%S/Inputs -fmodule-file=%t/h.pcm \
+// RUN: %clang_cc1 -std=c++20 %t/x.cppm -emit-module-interface -o %t/x.pcm
+// RUN: %clang_cc1 -std=c++20 %t/y.cppm -emit-module-interface -o %t/y.pcm
+// RUN: %clang_cc1 -std=c++20 %t/interface.cppm -fmodule-file=X=%t/x.pcm -fmodule-file=Y=%t/y.pcm -emit-module-interface -o %t/m.pcm
+// RUN: %clang_cc1 -std=c++20 %t/impl.cppm -I%S/Inputs -fmodule-file=%t/h.pcm \
// RUN: -fmodule-file=X=%t/x.pcm -fmodule-file=Y=%t/y.pcm -fmodule-file=p2=%t/m.pcm -verify \
// RUN: -Wno-experimental-header-units
-// RUN: %clang_cc1 -std=c++20 %s -DUSER -I%S/Inputs -fmodule-file=%t/h.pcm -fmodule-file=p2=%t/m.pcm \
+// RUN: %clang_cc1 -std=c++20 %t/user.cppm -I%S/Inputs -fmodule-file=%t/h.pcm -fmodule-file=p2=%t/m.pcm \
// RUN: -fmodule-file=X=%t/x.pcm -fmodule-file=Y=%t/y.pcm -Wno-experimental-header-units -verify
-#if defined(X_INTERFACE)
+//--- x.cppm
export module X;
export int x;
-#elif defined(Y_INTERFACE)
+//--- y.cppm
export module Y;
export int y;
-#elif defined(INTERFACE)
+//--- interface.cppm
export module p2;
export import X;
import Y; // not exported
@@ -39,7 +41,7 @@ namespace C {}
namespace D { int f(); }
export namespace D {}
-#elif defined(IMPLEMENTATION)
+//--- impl.cppm
module p2;
import "header.h";
@@ -66,7 +68,7 @@ void use() {
int use_header() { return foo + bar::baz(); }
-#elif defined(USER)
+//--- user.cppm
import p2;
import "header.h";
@@ -96,7 +98,3 @@ void use() {
}
int use_header() { return foo + bar::baz(); }
-
-#else
-#error unknown mode
-#endif
diff --git a/clang/test/CXX/module/module.unit/p8.cpp b/clang/test/CXX/module/module.unit/p8.cpp
index a5c01c493558e..fb190257d3a23 100644
--- a/clang/test/CXX/module/module.unit/p8.cpp
+++ b/clang/test/CXX/module/module.unit/p8.cpp
@@ -1,37 +1,45 @@
-// RUN: echo 'export module foo; export int n;' > %t.cppm
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+// RUN: echo 'export module foo;' > %t.cppm
+// RUN: echo 'export int n;' >> %t.cppm
// RUN: %clang_cc1 -std=c++2a %t.cppm -emit-module-interface -o %t.pcm
-// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=0 %s
-// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=1 %s
-// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=2 %s
-// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=3 %s
-// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=4 %s
-// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=5 %s
-
-#if MODE == 0
+// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=0 %t/A.cppm
+// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=1 %t/B.cppm
+// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=2 %t/C.cppm
+// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=3 %t/D.cppm
+// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=4 %t/E.cppm
+// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=5 %t/F.cppm
+
+//--- A.cppm
// no module declaration
+// expected-no-diagnostics
-#elif MODE == 1
+//--- B.cppm
// expected-no-diagnostics
module foo; // Implementation, implicitly imports foo.
#define IMPORTED
-#elif MODE == 2
+int k = n;
+
+//--- C.cppm
export module foo;
-#elif MODE == 3
+int k = n; // expected-error {{use of undeclared identifier 'n'}}
+
+//--- D.cppm
export module bar; // A different module
-#elif MODE == 4
+int k = n; // expected-error {{use of undeclared identifier 'n'}}
+
+//--- E.cppm
module foo:bar; // Partition implementation
//#define IMPORTED (we don't import foo here)
-#elif MODE == 5
+int k = n; // expected-error {{use of undeclared identifier 'n'}}
+
+//--- F.cppm
export module foo:bar; // Partition interface
//#define IMPORTED (we don't import foo here)
-#endif
-
-int k = n;
-#ifndef IMPORTED
-// expected-error at -2 {{use of undeclared identifier 'n'}}
-#endif
+int k = n; // expected-error {{use of undeclared identifier 'n'}}
diff --git a/clang/test/Driver/modules.cpp b/clang/test/Driver/modules.cpp
index b0d1f2280d254..088a73230f81e 100644
--- a/clang/test/Driver/modules.cpp
+++ b/clang/test/Driver/modules.cpp
@@ -1,43 +1,48 @@
// RUN: rm -rf %t
// RUN: mkdir %t
+// RUN: split-file %s %t
// Check compiling a module interface to a .pcm file.
//
-// RUN: %clang -std=c++2a -x c++-module --precompile %s -o %t/module.pcm -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE
-// RUN: %clang -std=gnu++2a -x c++-module --precompile %s -o %t/module-gnu.pcm -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE
+// RUN: %clang -std=c++2a -x c++-module --precompile %t/foo.cpp -o %t/foo.pcm -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE
+// RUN: %clang -std=gnu++2a -x c++-module --precompile %t/foo.cpp -o %t/foo-gnu.pcm -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE
//
// CHECK-PRECOMPILE: -cc1 {{.*}} -emit-module-interface
// CHECK-PRECOMPILE-SAME: -o {{.*}}.pcm
// CHECK-PRECOMPILE-SAME: -x c++
-// CHECK-PRECOMPILE-SAME: modules.cpp
+// CHECK-PRECOMPILE-SAME: foo.cpp
// Check compiling a .pcm file to a .o file.
//
-// RUN: %clang -std=c++2a %t/module.pcm -S -o %t/module.pcm.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-COMPILE
+// RUN: %clang -std=c++2a %t/foo.pcm -S -o %t/foo.pcm.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-COMPILE
//
// CHECK-COMPILE: -cc1 {{.*}} {{-emit-obj|-S}}
-// CHECK-COMPILE-SAME: -o {{.*}}module{{2*}}.pcm.o
+// CHECK-COMPILE-SAME: -o {{.*}}foo{{2*}}.pcm.o
// CHECK-COMPILE-SAME: -x pcm
// CHECK-COMPILE-SAME: {{.*}}.pcm
// Check use of a .pcm file in another compilation.
//
-// RUN: %clang -std=c++2a -fmodule-file=%t/module.pcm -Dexport= %s -S -o %t/module.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-USE
-// RUN: %clang -std=c++20 -fmodule-file=%t/module.pcm -Dexport= %s -S -o %t/module.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-USE
-// RUN: %clang -std=gnu++20 -fmodule-file=%t/module-gnu.pcm -Dexport= %s -S -o %t/module.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-USE
+// RUN: %clang -std=c++2a -fmodule-file=foo=%t/foo.pcm %t/foo_impl.cpp -S -o %t/module.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-USE
+// RUN: %clang -std=c++20 -fmodule-file=foo=%t/foo.pcm %t/foo_impl.cpp -S -o %t/module.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-USE
+// RUN: %clang -std=gnu++20 -fmodule-file=foo=%t/foo-gnu.pcm %t/foo_impl.cpp -S -o %t/module.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-USE
//
// CHECK-USE: -cc1 {{.*}} {{-emit-obj|-S}}
-// CHECK-USE-SAME: -fmodule-file={{.*}}.pcm
+// CHECK-USE-SAME: -fmodule-file=foo={{.*}}.pcm
// CHECK-USE-SAME: -o {{.*}}.{{o|s}}{{"?}} {{.*}}-x c++
-// CHECK-USE-SAME: modules.cpp
+// CHECK-USE-SAME: foo_impl.cpp
// Check combining precompile and compile steps works.
//
-// RUN: %clang -std=c++2a -x c++-module %s -S -o %t/module2.pcm.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE --check-prefix=CHECK-COMPILE
+// RUN: %clang -std=c++2a -x c++-module %t/foo.cpp -S -o %t/foo2.pcm.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE --check-prefix=CHECK-COMPILE
// Check that .cppm is treated as a module implicitly.
//
-// RUN: cp %s %t/module.cppm
-// RUN: %clang -std=c++2a --precompile %t/module.cppm -o %t/module.pcm -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE
+// RUN: cp %t/foo.cpp %t/foo.cppm
+// RUN: %clang -std=c++2a --precompile %t/foo.cppm -o %t/foo.pcm -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE
+//--- foo.cpp
export module foo;
+
+//--- foo_impl.cpp
+module foo;
diff --git a/clang/test/Modules/pr121066.cpp b/clang/test/Modules/pr121066.cpp
index e92a81c53d683..849488e938d50 100644
--- a/clang/test/Modules/pr121066.cpp
+++ b/clang/test/Modules/pr121066.cpp
@@ -1,4 +1,3 @@
// RUN: %clang_cc1 -std=c++20 -fsyntax-only %s -verify
-import mod // expected-error {{expected ';' after module name}}
- // expected-error at -1 {{module 'mod' not found}}
+import mod // expected-error {{'import' directive must end with a ';' on the same line}}
diff --git a/clang/test/SemaCXX/type-aware-new-delete-transparent-contexts.cpp b/clang/test/SemaCXX/type-aware-new-delete-transparent-contexts.cpp
index 7c0b967a3c03f..30fea464a8dc5 100644
--- a/clang/test/SemaCXX/type-aware-new-delete-transparent-contexts.cpp
+++ b/clang/test/SemaCXX/type-aware-new-delete-transparent-contexts.cpp
@@ -1,12 +1,22 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -DTRANSPARENT_DECL=0
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -DTRANSPARENT_DECL=1
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -DTRANSPARENT_DECL=2
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+// RUN: %clang_cc1 -fsyntax-only -verify %t/testing.cpp -std=c++26 -fexceptions -DTRANSPARENT_DECL=0
+// RUN: %clang_cc1 -fsyntax-only -verify %t/testing.cpp -std=c++26 -fexceptions -DTRANSPARENT_DECL=1
+// RUN: %clang_cc1 -fsyntax-only -verify %t/module_testing.cppm -std=c++26 -fexceptions -DTRANSPARENT_DECL=2
+
+//--- module_testing.cppm
// expected-no-diagnostics
-#if TRANSPARENT_DECL==2
export module Testing;
-#endif
+#include "testing.inc"
+
+//--- testing.cpp
+// expected-no-diagnostics
+#include "testing.inc"
+
+//--- testing.inc
namespace std {
template <class T> struct type_identity {};
using size_t = __SIZE_TYPE__;
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
index 07450a0c59ec6..042e8bd80a50b 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -193,7 +193,8 @@ TEST_P(ASTMatchersTest, ExportDecl) {
if (!GetParam().isCXX20OrLater()) {
return;
}
- const std::string moduleHeader = "module;export module ast_matcher_test;";
+ const std::string moduleHeader =
+ "module;\n export module ast_matcher_test;\n";
EXPECT_TRUE(matches(moduleHeader + "export void foo();",
exportDecl(has(functionDecl()))));
EXPECT_TRUE(matches(moduleHeader + "export { void foo(); int v; }",
diff --git a/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp b/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp
index bdb5e23510118..49c005b245d20 100644
--- a/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp
+++ b/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp
@@ -986,11 +986,11 @@ ort \
ASSERT_FALSE(
minimizeSourceToDependencyDirectives(Source, Out, Tokens, Directives));
EXPECT_STREQ("#include \"textual-header.h\"\nexport module m;"
- "exp\\\nort import:l[[rename]];"
- "import<<=3;import a b d e d e f e;"
- "import foo[[no_unique_address]];import foo();"
- "import f(:sefse);import f(->a=3);"
- "<TokBeforeEOF>\n",
+ "\nexp\\\nort import:l[[rename]];"
+ "\nimport<<=3;\nimport a b d e d e f e;"
+ "\nimport foo[[no_unique_address]];\nimport foo();"
+ "\nimport f(:sefse);\nimport f(->a=3);"
+ "\n<TokBeforeEOF>\n",
Out.data());
ASSERT_EQ(Directives.size(), 11u);
EXPECT_EQ(Directives[0].Kind, pp_include);
diff --git a/clang/unittests/Lex/ModuleDeclStateTest.cpp b/clang/unittests/Lex/ModuleDeclStateTest.cpp
index 6ecba4de3187c..052dccd6fa8d5 100644
--- a/clang/unittests/Lex/ModuleDeclStateTest.cpp
+++ b/clang/unittests/Lex/ModuleDeclStateTest.cpp
@@ -40,7 +40,7 @@ class CheckNamedModuleImportingCB : public PPCallbacks {
void moduleImport(SourceLocation ImportLoc, ModuleIdPath Path,
const Module *Imported) override {
ASSERT_TRUE(NextCheckingIndex < IsImportingNamedModulesAssertions.size());
- EXPECT_EQ(PP.isInImportingCXXNamedModules(),
+ EXPECT_EQ(PP.isImportingCXXNamedModules(),
IsImportingNamedModulesAssertions[NextCheckingIndex]);
NextCheckingIndex++;
>From 8529a7fc297b6474ee85c3f7312b11aa362c76c3 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 2 Jun 2025 23:31:44 +0800
Subject: [PATCH 03/22] =?UTF-8?q?P3034R1=20Module=20Declarations=20Shouldn?=
=?UTF-8?q?=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/lib/Lex/PPDirectives.cpp | 8 +++++++-
clang/lib/Lex/Preprocessor.cpp | 12 ++++++------
2 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index f95b9d582c5cc..a0a6c8ceb299e 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -4249,7 +4249,13 @@ void Preprocessor::HandleCXXModuleDirective(Token ModuleTok) {
DirToks.push_back(Tok);
break;
case tok::identifier: {
- if (LexModuleNameContinue(Tok, UseLoc, Path)) {
+ // 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, Path,
+ /*AllowMacroExpansion=*/false)) {
if (Tok.isNot(tok::eod))
CheckEndOfDirective(ModuleTok.getIdentifierInfo()->getName());
return;
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index f1d94561f365c..d9237e710e7f9 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -1168,12 +1168,12 @@ bool Preprocessor::LexModuleNameContinue(Token &Tok, SourceLocation UseLoc,
///
/// At the start of phase 4 an import or module token is treated as starting a
/// directive and are converted to their respective keywords iff:
-/// • After skipping horizontal whitespace are
-/// • at the start of a logical line, or
-/// • preceded by an 'export' at the start of the logical line.
-/// • Are followed by an identifier pp token (before macro expansion), or
-/// • <, ", or : (but not ::) pp tokens for 'import', or
-/// • ; for 'module'
+/// - After skipping horizontal whitespace are
+/// - at the start of a logical line, or
+/// - preceded by an 'export' at the start of the logical line.
+/// - Are followed by an identifier pp token (before macro expansion), or
+/// - <, ", or : (but not ::) pp tokens for 'import', or
+/// - ; for 'module'
/// Otherwise the token is treated as an identifier.
bool Preprocessor::HandleModuleContextualKeyword(
Token &Result, bool TokAtPhysicalStartOfLine) {
>From ece6382d037522f82be0112a1a34f87b17d29d84 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Sun, 8 Jun 2025 23:33:04 +0800
Subject: [PATCH 04/22] [clang-scan-deps] Only check eod token in C++ modules
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/lib/Lex/DependencyDirectivesScanner.cpp | 25 +++++++++++++++----
1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/clang/lib/Lex/DependencyDirectivesScanner.cpp b/clang/lib/Lex/DependencyDirectivesScanner.cpp
index 8cd9c8a0473b6..1cca0351a9f62 100644
--- a/clang/lib/Lex/DependencyDirectivesScanner.cpp
+++ b/clang/lib/Lex/DependencyDirectivesScanner.cpp
@@ -505,7 +505,7 @@ bool Scanner::lexModuleDirectiveBody(DirectiveKind Kind, const char *&First,
First = Previous;
return false;
}
- if (Tok.isOneOf(tok::eof, tok::eod))
+ if (Tok.is(tok::eof) || (LangOpts.CPlusPlusModules && Tok.is(tok::eod)))
return reportError(
DirectiveLoc,
diag::err_dep_source_scanner_missing_semi_after_at_import);
@@ -865,10 +865,6 @@ bool Scanner::lexPPLine(const char *&First, const char *const End) {
if (*First == '@')
return lexAt(First, End);
- // Handle module directives for C++20 modules.
- if (*First == 'i' || *First == 'e' || *First == 'm')
- return lexModule(First, End);
-
if (*First == '_') {
if (isNextIdentifierOrSkipLine("_Pragma", First, End))
return lex_Pragma(First, End);
@@ -880,8 +876,27 @@ bool Scanner::lexPPLine(const char *&First, const char *const End) {
TheLexer.setParsingPreprocessorDirective(true);
auto ScEx2 = make_scope_exit(
[&]() { TheLexer.setParsingPreprocessorDirective(false); });
+
+ // Since P1857R3, the standard handling C++ module/import as a directive:
+ //
+ // [cpp.pre]p1:
+ // A preprocessing directive consists of a sequence of preprocessing tokens
+ // that satisfies the following constraints: At the start of translation
+ // phase 4, the first preprocessing token in the sequence, referred to as a
+ // directive-introducing token, begins with the first character in the source
+ // file (optionally after whitespace containing no new-line characters) or
+ // follows whitespace containing at least one new-line character, and is
+ // - a # preprocessing token, or
+ // - an import preprocessing token immediately followed on the same logical
+ // source line by a header-name, <, identifier, string-literal, or :
+ // preprocessing token, or
+ // - a module preprocessing token immediately followed on the same logical
+ // source line by an identifier, :, or ; preprocessing token, or
+ // - an export preprocessing token immediately followed on the same logical
+ // source line by one of the two preceding forms.
if (*First == 'i' || *First == 'e' || *First == 'm')
return lexModule(First, End);
+
// Lex '#'.
const dependency_directives_scan::Token &HashTok = lexToken(First, End);
if (HashTok.is(tok::hashhash)) {
>From 008844c7c2542554b4b3c3d2c782558ee74f9133 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 12 Jun 2025 19:34:39 +0800
Subject: [PATCH 05/22] A module directive may only appear as the first
preprocessing tokens in a file
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/include/clang/Lex/Lexer.h | 3 +
clang/include/clang/Lex/Preprocessor.h | 32 ++++++--
clang/include/clang/Lex/Token.h | 5 ++
clang/include/clang/Sema/Sema.h | 6 +-
clang/lib/Lex/Lexer.cpp | 11 +++
clang/lib/Lex/PPDirectives.cpp | 32 ++++----
clang/lib/Lex/Preprocessor.cpp | 5 +-
clang/lib/Parse/Parser.cpp | 12 ++-
clang/lib/Sema/SemaModule.cpp | 55 +++++++-------
clang/test/CXX/basic/basic.link/p2.cpp | 26 +++----
clang/test/Modules/named-modules-adl-3.cppm | 1 +
clang/test/Modules/reserved-names-1.cppm | 10 +++
.../reserved-names-system-header-1.cpp | 1 +
.../reserved-names-system-header-2.cpp | 1 +
clang/test/SemaCXX/modules.cppm | 75 ++++++++++++-------
15 files changed, 177 insertions(+), 98 deletions(-)
diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h
index a595cda1eaa77..7a178b5b5c85b 100644
--- a/clang/include/clang/Lex/Lexer.h
+++ b/clang/include/clang/Lex/Lexer.h
@@ -143,6 +143,9 @@ class Lexer : public PreprocessorLexer {
/// True if this is the first time we're lexing the input file.
bool IsFirstTimeLexingFile;
+ /// True if current lexing token is the first pp-token.
+ bool IsFirstPPToken;
+
// NewLinePtr - A pointer to new line character '\n' being lexed. For '\r\n',
// it also points to '\n.'
const char *NewLinePtr;
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 8bf4a3e3a3fb1..1a10336a7390c 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -137,6 +137,24 @@ struct CXXStandardLibraryVersionInfo {
std::uint64_t Version;
};
+class ExportContextualKeywordInfo {
+ Token ExportTok;
+ bool AtPhysicalStartOfLine = false;
+
+public:
+ ExportContextualKeywordInfo() = default;
+ ExportContextualKeywordInfo(const Token &Tok, bool AtPhysicalStartOfLine)
+ : ExportTok(Tok), AtPhysicalStartOfLine(AtPhysicalStartOfLine) {}
+
+ bool isValid() const { return ExportTok.is(tok::kw_export); }
+ bool isAtPhysicalStartOfLine() const { return AtPhysicalStartOfLine; }
+ Token getExportTok() const { return ExportTok; }
+ void reset() {
+ ExportTok.startToken();
+ AtPhysicalStartOfLine = false;
+ }
+};
+
/// Engages in a tight little dance with the lexer to efficiently
/// preprocess tokens.
///
@@ -359,14 +377,11 @@ class Preprocessor {
/// Whether we're declaring a standard C++20 named Modules.
bool DeclaringCXXNamedModules = false;
- struct ExportContextualKeywordInfo {
- Token ExportTok;
- bool TokAtPhysicalStartOfLine;
- };
-
/// Whether the last token we lexed was an 'export' keyword.
- std::optional<ExportContextualKeywordInfo> LastTokenWasExportKeyword =
- std::nullopt;
+ ExportContextualKeywordInfo LastTokenWasExportKeyword;
+
+ /// First pp-token in current translation unit.
+ Token FirstPPToken;
/// A position within a C++20 import-seq.
class StdCXXImportSeq {
@@ -1774,6 +1789,9 @@ class Preprocessor {
void HandleCXXImportDirective(Token Import);
void HandleCXXModuleDirective(Token Module);
+ void setFirstPPToken(const Token &Tok) { FirstPPToken = Tok; }
+ Token getFirstPPToken() const { return FirstPPToken; }
+
/// Callback invoked when the lexer sees one of export, import or module token
/// at the start of a line.
///
diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h
index 8e81207ddf8d7..3984d5d007f67 100644
--- a/clang/include/clang/Lex/Token.h
+++ b/clang/include/clang/Lex/Token.h
@@ -89,6 +89,8 @@ class Token {
IsReinjected = 0x800, // A phase 4 token that was produced before and
// re-added, e.g. via EnterTokenStream. Annotation
// tokens are *not* reinjected.
+ FirstPPToken = 0x1000, // This token is the first pp token in the
+ // translation unit.
};
tok::TokenKind getKind() const { return Kind; }
@@ -325,6 +327,9 @@ class Token {
/// represented as characters between '<#' and '#>' in the source code. The
/// lexer uses identifier tokens to represent placeholders.
bool isEditorPlaceholder() const { return getFlag(IsEditorPlaceholder); }
+
+ /// Returns true if this token is the first pp-token.
+ bool isFirstPPToken() const { return getFlag(FirstPPToken); }
};
/// Information about the conditional stack (\#if directives)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 47afe61571826..1e2b2e260b72f 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9937,9 +9937,9 @@ class Sema final : public SemaBase {
/// of a module interface or implementation.
DeclGroupPtrTy ActOnModuleDecl(SourceLocation StartLoc,
SourceLocation ModuleLoc, ModuleDeclKind MDK,
- ModuleNameLoc *PathLoc,
- ModuleNameLoc *PartitionLoc,
- ModuleImportState &ImportState);
+ ModuleIdPath Path, ModuleIdPath Partition,
+ ModuleImportState &ImportState,
+ bool AtStartOfTU);
/// The parser has processed a global-module-fragment declaration that begins
/// the definition of the global module fragment of the current module unit.
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index df9e0f1350903..7acdc5500c743 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -187,6 +187,8 @@ void Lexer::InitLexer(const char *BufStart, const char *BufPtr,
ExtendedTokenMode = 0;
NewLinePtr = nullptr;
+
+ IsFirstPPToken = true;
}
/// Lexer constructor - Create a new lexer object for the specified buffer
@@ -3739,11 +3741,20 @@ bool Lexer::Lex(Token &Result) {
HasLeadingEmptyMacro = false;
}
+ if (IsFirstPPToken) {
+ Result.setFlag(Token::FirstPPToken);
+ IsFirstPPToken = false;
+ }
+
bool atPhysicalStartOfLine = IsAtPhysicalStartOfLine;
IsAtPhysicalStartOfLine = false;
bool isRawLex = isLexingRawMode();
(void) isRawLex;
bool returnedToken = LexTokenInternal(Result, atPhysicalStartOfLine);
+
+ if (returnedToken && Result.isFirstPPToken() && PP)
+ PP->setFirstPPToken(Result);
+
// (After the LexTokenInternal call, the lexer might be destroyed.)
assert((returnedToken || !isRawLex) && "Raw lex must succeed");
return returnedToken;
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index a0a6c8ceb299e..de6df604a47e1 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -4089,7 +4089,7 @@ void Preprocessor::HandleCXXImportDirective(Token ImportTok) {
this->ImportingCXXNamedModules);
ImportingCXXNamedModules = true;
- if (LastTokenWasExportKeyword)
+ if (LastTokenWasExportKeyword.isValid())
LastTokenWasExportKeyword.reset();
Token Tok;
@@ -4216,13 +4216,26 @@ void Preprocessor::HandleCXXImportDirective(Token ImportTok) {
void Preprocessor::HandleCXXModuleDirective(Token ModuleTok) {
assert(getLangOpts().CPlusPlusModules && ModuleTok.is(tok::kw_module));
- SourceLocation StartLoc = ModuleTok.getLocation();
- if (LastTokenWasExportKeyword) {
- StartLoc = LastTokenWasExportKeyword->ExportTok.getLocation();
+ Token Introducer = ModuleTok;
+ if (LastTokenWasExportKeyword.isValid()) {
+ Introducer = LastTokenWasExportKeyword.getExportTok();
LastTokenWasExportKeyword.reset();
}
- bool IsInHeaderInclusion = !IncludeMacroStack.empty();
- bool IsInConditionBlock = CurPPLexer->getConditionalStackDepth() != 0;
+
+ SourceLocation StartLoc = Introducer.getLocation();
+ if (!IncludeMacroStack.empty()) {
+ SourceLocation End = DiscardUntilEndOfDirective().getEnd();
+ Diag(StartLoc, diag::err_module_decl_in_header)
+ << SourceRange(StartLoc, End);
+ return;
+ }
+
+ if (CurPPLexer->getConditionalStackDepth() != 0) {
+ SourceLocation End = DiscardUntilEndOfDirective().getEnd();
+ Diag(StartLoc, diag::err_pp_cond_span_module_decl)
+ << SourceRange(StartLoc, End);
+ return;
+ }
Token Tok;
SourceLocation UseLoc = ModuleTok.getLocation();
@@ -4305,13 +4318,6 @@ void Preprocessor::HandleCXXModuleDirective(Token ModuleTok) {
End = CheckEndOfDirective(ModuleTok.getIdentifierInfo()->getName());
else
End = DirToks.pop_back_val().getLocation();
- if (IsInHeaderInclusion)
- Diag(StartLoc, diag::err_module_decl_in_header)
- << SourceRange(StartLoc, End);
-
- if (IsInConditionBlock)
- Diag(StartLoc, diag::err_pp_cond_span_module_decl)
- << SourceRange(StartLoc, End);
EnterModuleSuffixTokenStream(DirToks);
}
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index d9237e710e7f9..a535df53c62ab 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -1185,11 +1185,10 @@ bool Preprocessor::HandleModuleContextualKeyword(
return false;
}
- if (LastTokenWasExportKeyword) {
- auto Export = *LastTokenWasExportKeyword;
+ if (LastTokenWasExportKeyword.isValid()) {
// The export keyword was not at the start of line, it's not a
// directive-introducing token.
- if (!Export.TokAtPhysicalStartOfLine)
+ if (!LastTokenWasExportKeyword.isAtPhysicalStartOfLine())
return false;
// [cpp.pre]/1.4
// export // not a preprocessing directive
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 508a4ce0e5ff8..48677fc88ff1c 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -17,6 +17,7 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/DiagnosticParse.h"
#include "clang/Basic/StackExhaustionHandler.h"
+#include "clang/Lex/ModuleLoader.h"
#include "clang/Parse/RAIIObjectsForParser.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
@@ -2303,7 +2304,8 @@ void Parser::ParseMicrosoftIfExistsExternalDeclaration() {
Parser::DeclGroupPtrTy
Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
- SourceLocation StartLoc = Tok.getLocation();
+ Token Introducer = Tok;
+ SourceLocation StartLoc = Introducer.getLocation();
Sema::ModuleDeclKind MDK = TryConsumeToken(tok::kw_export)
? Sema::ModuleDeclKind::Interface
@@ -2322,7 +2324,7 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
// Parse a global-module-fragment, if present.
if (getLangOpts().CPlusPlusModules && Tok.is(tok::semi)) {
SourceLocation SemiLoc = ConsumeToken();
- if (ImportState != Sema::ModuleImportState::FirstDecl) {
+ if (!Introducer.isFirstPPToken()) {
Diag(StartLoc, diag::err_global_module_introducer_not_at_start)
<< SourceRange(StartLoc, SemiLoc);
return nullptr;
@@ -2378,8 +2380,10 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
ExpectAndConsumeSemi(diag::err_module_expected_semi);
- return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, Partition,
- ImportState);
+ return Actions.ActOnModuleDecl(
+ StartLoc, ModuleLoc, MDK, Path->getModuleIdPath(),
+ Partition ? Partition->getModuleIdPath() : ModuleIdPath{}, ImportState,
+ Introducer.isFirstPPToken());
}
Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp
index 98c60bab409be..708c7f142841b 100644
--- a/clang/lib/Sema/SemaModule.cpp
+++ b/clang/lib/Sema/SemaModule.cpp
@@ -13,7 +13,9 @@
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTMutationListener.h"
+#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/HeaderSearch.h"
+#include "clang/Lex/ModuleLoader.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/ParsedAttr.h"
#include "clang/Sema/SemaInternal.h"
@@ -238,22 +240,21 @@ static bool DiagReservedModuleName(Sema &S, const IdentifierInfo *II,
llvm_unreachable("fell off a fully covered switch");
}
-Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
- SourceLocation ModuleLoc,
- ModuleDeclKind MDK,
- ModuleNameLoc *PathLoc,
- ModuleNameLoc *PartitionLoc,
- ModuleImportState &ImportState) {
+Sema::DeclGroupPtrTy
+Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
+ ModuleDeclKind MDK, ModuleIdPath Path,
+ ModuleIdPath Partition, ModuleImportState &ImportState,
+ bool AtStartOfTU) {
assert(getLangOpts().CPlusPlusModules &&
"should only have module decl in standard C++ modules");
- bool IsFirstDecl = ImportState == ModuleImportState::FirstDecl;
bool SeenGMF = ImportState == ModuleImportState::GlobalFragment;
// If any of the steps here fail, we count that as invalidating C++20
// module state;
ImportState = ModuleImportState::NotACXX20Module;
- if (PartitionLoc)
+ bool IsPartition = !Partition.empty();
+ if (IsPartition)
switch (MDK) {
case ModuleDeclKind::Implementation:
MDK = ModuleDeclKind::PartitionImplementation;
@@ -282,7 +283,7 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
// We were asked to compile a module interface unit but this is a module
// implementation unit.
Diag(ModuleLoc, diag::err_module_interface_implementation_mismatch)
- << FixItHint::CreateInsertion(ModuleLoc, "export ");
+ << FixItHint::CreateInsertion(ModuleLoc, "export ");
MDK = ModuleDeclKind::Interface;
break;
@@ -314,7 +315,7 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
// In C++20, the module-declaration must be the first declaration if there
// is no global module fragment.
- if (getLangOpts().CPlusPlusModules && !IsFirstDecl && !SeenGMF) {
+ if (getLangOpts().CPlusPlusModules && !AtStartOfTU && !SeenGMF) {
Diag(ModuleLoc, diag::err_module_decl_not_at_start);
SourceLocation BeginLoc =
ModuleScopes.empty()
@@ -335,18 +336,17 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
// Test the first part of the path to see if it's std[0-9]+ but allow the
// name in a system header.
- StringRef FirstComponentName =
- PathLoc->getModuleIdPath()[0].getIdentifierInfo()->getName();
- if (!getSourceManager().isInSystemHeader(PathLoc->getBeginLoc()) &&
+ StringRef FirstComponentName = Path[0].getIdentifierInfo()->getName();
+ if (!getSourceManager().isInSystemHeader(Path[0].getLoc()) &&
(FirstComponentName == "std" ||
(FirstComponentName.starts_with("std") &&
llvm::all_of(FirstComponentName.drop_front(3), &llvm::isDigit))))
- Diag(PathLoc->getBeginLoc(), diag::warn_reserved_module_name)
- << PathLoc->getModuleIdPath()[0].getIdentifierInfo();
+ Diag(Path[0].getLoc(), diag::warn_reserved_module_name)
+ << Path[0].getIdentifierInfo();
// Then test all of the components in the path to see if any of them are
// using another kind of reserved or invalid identifier.
- for (auto Part : PathLoc->getModuleIdPath()) {
+ for (auto Part : Path) {
if (DiagReservedModuleName(*this, Part.getIdentifierInfo(), Part.getLoc()))
return nullptr;
}
@@ -354,20 +354,23 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
// Flatten the dots in a module name. Unlike Clang's hierarchical module map
// modules, the dots here are just another character that can appear in a
// module name.
- std::string ModuleName = PathLoc->str();
- if (PartitionLoc) {
+ std::string ModuleName = ModuleNameLoc::stringFromModuleIdPath(Path);
+ if (IsPartition) {
ModuleName += ":";
- ModuleName += PartitionLoc->str();
+ ModuleName += ModuleNameLoc::stringFromModuleIdPath(Partition);
}
// If a module name was explicitly specified on the command line, it must be
// correct.
if (!getLangOpts().CurrentModule.empty() &&
getLangOpts().CurrentModule != ModuleName) {
- Diag(PathLoc->getBeginLoc(), diag::err_current_module_name_mismatch)
- << PathLoc->getRange() << getLangOpts().CurrentModule;
+ Diag(Path.front().getLoc(), diag::err_current_module_name_mismatch)
+ << SourceRange(Path.front().getLoc(), IsPartition
+ ? Partition.back().getLoc()
+ : Path.back().getLoc())
+ << getLangOpts().CurrentModule;
return nullptr;
}
- const_cast<LangOptions&>(getLangOpts()).CurrentModule = ModuleName;
+ const_cast<LangOptions &>(getLangOpts()).CurrentModule = ModuleName;
auto &Map = PP.getHeaderSearchInfo().getModuleMap();
Module *Mod; // The module we are creating.
@@ -378,7 +381,7 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
// We can't have parsed or imported a definition of this module or parsed a
// module map defining it already.
if (auto *M = Map.findOrLoadModule(ModuleName)) {
- Diag(PathLoc->getBeginLoc(), diag::err_module_redefinition) << ModuleName;
+ Diag(Path[0].getLoc(), diag::err_module_redefinition) << ModuleName;
if (M->DefinitionLoc.isValid())
Diag(M->DefinitionLoc, diag::note_prev_module_definition);
else if (OptionalFileEntryRef FE = M->getASTFile())
@@ -401,7 +404,7 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
// keyword nor a module-partition implicitly imports the primary
// module interface unit of the module as if by a module-import-
// declaration.
- IdentifierLoc ModuleNameLoc(PathLoc->getBeginLoc(),
+ IdentifierLoc ModuleNameLoc(Path[0].getLoc(),
PP.getIdentifierInfo(ModuleName));
// The module loader will assume we're trying to import the module that
@@ -412,7 +415,7 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
Interface = getModuleLoader().loadModule(ModuleLoc, {ModuleNameLoc},
Module::AllVisible,
/*IsInclusionDirective=*/false);
- const_cast<LangOptions&>(getLangOpts()).CurrentModule = ModuleName;
+ const_cast<LangOptions &>(getLangOpts()).CurrentModule = ModuleName;
if (!Interface) {
Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName;
@@ -474,7 +477,7 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
// Make the import decl for the interface in the impl module.
ImportDecl *Import = ImportDecl::Create(Context, CurContext, ModuleLoc,
- Interface, PathLoc->getBeginLoc());
+ Interface, Path[0].getLoc());
CurContext->addDecl(Import);
// Sequence initialization of the imported module before that of the current
diff --git a/clang/test/CXX/basic/basic.link/p2.cpp b/clang/test/CXX/basic/basic.link/p2.cpp
index ccad42022ee80..94cbc62490b2f 100644
--- a/clang/test/CXX/basic/basic.link/p2.cpp
+++ b/clang/test/CXX/basic/basic.link/p2.cpp
@@ -1,16 +1,16 @@
-// RUN: %clang_cc1 -std=c++2a -DEXPORT %s -verify
-// RUN: %clang_cc1 -std=c++2a -DEXPORT %s -emit-module-interface -o %t.pcm
-// RUN: %clang_cc1 -std=c++2a -UEXPORT %s -verify -fmodule-file=M=%t.pcm
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++2a %t/pmf_in_interface.cpp -verify
+// RUN: %clang_cc1 -std=c++2a %t/pmf_in_interface.cpp -emit-module-interface -o %t.pcm
+// RUN: %clang_cc1 -std=c++2a %t/pmf_in_implementation.cpp -verify -fmodule-file=M=%t.pcm
-#ifdef EXPORT
-// expected-no-diagnostics
-export
-#else
-// expected-note at +2 {{add 'export' here}}
-#endif
-module M;
-#ifndef EXPORT
-// expected-error at +2 {{private module fragment in module implementation unit}}
-#endif
+//--- pmf_in_interface.cpp
+// expected-no-diagnostics
+export module M;
module :private;
+
+//--- pmf_in_implementation.cpp
+module M; // expected-note {{add 'export' here}}
+module :private; // expected-error {{private module fragment in module implementation unit}}
diff --git a/clang/test/Modules/named-modules-adl-3.cppm b/clang/test/Modules/named-modules-adl-3.cppm
index d70946fa068b3..a3644b45a5347 100644
--- a/clang/test/Modules/named-modules-adl-3.cppm
+++ b/clang/test/Modules/named-modules-adl-3.cppm
@@ -58,6 +58,7 @@ void b(T x) {
}
//--- c.cppm
+module;
#ifdef EXPORT_OPERATOR
// expected-no-diagnostics
#endif
diff --git a/clang/test/Modules/reserved-names-1.cppm b/clang/test/Modules/reserved-names-1.cppm
index e780f1e35b3b7..35b264bcb573b 100644
--- a/clang/test/Modules/reserved-names-1.cppm
+++ b/clang/test/Modules/reserved-names-1.cppm
@@ -88,12 +88,14 @@ export module module; // expected-error {{'module' is an invalid name for a modu
export module import; // expected-error {{'import' is an invalid name for a module}}
//--- _Test.cppm
+module;
#ifdef NODIAGNOSTICS
// expected-no-diagnostics
#endif
export module _Test; // loud-warning {{'_Test' is a reserved name for a module}}
//--- __test.cppm
+module;
#ifdef NODIAGNOSTICS
// expected-no-diagnostics
#endif
@@ -101,6 +103,7 @@ export module __test; // loud-warning {{'__test' is a reserved name for a module
export int a = 43;
//--- te__st.cppm
+module;
#ifdef NODIAGNOSTICS
// expected-no-diagnostics
#endif
@@ -108,6 +111,7 @@ export module te__st; // loud-warning {{'te__st' is a reserved name for a module
export int a = 43;
//--- std.cppm
+module;
#ifdef NODIAGNOSTICS
// expected-no-diagnostics
#endif
@@ -116,36 +120,42 @@ export module std; // loud-warning {{'std' is a reserved name for a module}}
export int a = 43;
//--- std.foo.cppm
+module;
#ifdef NODIAGNOSTICS
// expected-no-diagnostics
#endif
export module std.foo;// loud-warning {{'std' is a reserved name for a module}}
//--- std0.cppm
+module;
#ifdef NODIAGNOSTICS
// expected-no-diagnostics
#endif
export module std0; // loud-warning {{'std0' is a reserved name for a module}}
//--- std1000000.cppm
+module;
#ifdef NODIAGNOSTICS
// expected-no-diagnostics
#endif
export module std1000000; // loud-warning {{'std1000000' is a reserved name for a module}}
//--- should_diag._Test.cppm
+module;
#ifdef NODIAGNOSTICS
// expected-no-diagnostics
#endif
export module should_diag._Test; // loud-warning {{'_Test' is a reserved name for a module}}
//--- system-module.cppm
+module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
// Show that being in a system header doesn't save you from diagnostics about
// use of an invalid module-name identifier.
# 34 "reserved-names-1.cpp" 1 3
export module module; // expected-error {{'module' is an invalid name for a module}}
//--- system._Test.import.cppm
+module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
# 34 "reserved-names-1.cpp" 1 3
export module _Test.import; // expected-error {{'import' is an invalid name for a module}}
diff --git a/clang/test/Modules/reserved-names-system-header-1.cpp b/clang/test/Modules/reserved-names-system-header-1.cpp
index 2db4c08add1d9..fa869483980f6 100644
--- a/clang/test/Modules/reserved-names-system-header-1.cpp
+++ b/clang/test/Modules/reserved-names-system-header-1.cpp
@@ -1,6 +1,7 @@
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
// expected-no-diagnostics
+module;
// Show that we suppress the reserved identifier diagnostic in a system header.
# 100 "file.cpp" 1 3 // Enter a system header
export module std;
diff --git a/clang/test/Modules/reserved-names-system-header-2.cpp b/clang/test/Modules/reserved-names-system-header-2.cpp
index 2087f487721cb..d429e58dc1714 100644
--- a/clang/test/Modules/reserved-names-system-header-2.cpp
+++ b/clang/test/Modules/reserved-names-system-header-2.cpp
@@ -1,6 +1,7 @@
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
// expected-no-diagnostics
+module;
// Show that we suppress the reserved identifier diagnostic in a system header.
# 100 "file.cpp" 1 3 // Enter a system header
export module __test;
diff --git a/clang/test/SemaCXX/modules.cppm b/clang/test/SemaCXX/modules.cppm
index 41204be76eafa..5d0d6da44a2ed 100644
--- a/clang/test/SemaCXX/modules.cppm
+++ b/clang/test/SemaCXX/modules.cppm
@@ -1,19 +1,20 @@
-// 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
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/test0.cpp -o %t/test0.pcm -verify
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/test1.cpp -o %t/test1.pcm -verify
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/test2.cpp -fmodule-file=foo=%t/test0.pcm -o %t/test2.pcm -verify
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/test3.cpp -fmodule-file=foo=%t/test0.pcm -o %t/test3.pcm -verify
+//--- test0.cpp
+// expected-no-diagnostics
export module foo;
static int m;
int n;
-#if TEST == 0
export {
int a;
int b;
@@ -27,33 +28,18 @@ export void f() {}
export struct T {
} t;
-#elif TEST == 3
-int use_a = a; // expected-error {{use of undeclared identifier 'a'}}
-#undef foo
-import foo; // expected-error {{imports must immediately follow the module declaration}}
-
-export {}
-export {
- ; // No diagnostic after P2615R1 DR
-}
-export {
- static_assert(true); // No diagnostic after P2615R1 DR
-}
+//--- test1.cpp
+export module foo;
-int use_b = b; // expected-error{{use of undeclared identifier 'b'}}
-int use_n = n; // FIXME: this should not be visible, because it is not exported
+static int m;
-extern int n;
-static_assert(&n != p); // expected-error{{use of undeclared identifier 'p'}}
-#endif
+int n;
-#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
// FIXME: Exports of declarations without external linkage are disallowed.
// Exports of declarations with non-external-linkage types are disallowed.
@@ -61,7 +47,6 @@ struct S {
// 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 {
@@ -71,4 +56,36 @@ export { // expected-note {{export block begins here}}
} // namespace NestedExport
}
}
-#endif
+
+//--- test2.cpp
+// expected-no-diagnostics
+export module foo;
+
+static int m;
+
+int n;
+
+//--- test3.cpp
+export module bar;
+
+static int m;
+
+int n;
+
+int use_a = a; // expected-error {{use of undeclared identifier 'a'}}
+
+import foo; // expected-error {{imports must immediately follow the module declaration}}
+
+export {}
+export {
+ ; // No diagnostic after P2615R1 DR
+}
+export {
+ static_assert(true); // No diagnostic after P2615R1 DR
+}
+
+int use_b = b; // expected-error{{use of undeclared identifier 'b'}}
+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'}}
>From db17bdaf94cfa5342fced1c15288d5d31864fd4e Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 12 Jun 2025 19:43:09 +0800
Subject: [PATCH 06/22] Format code
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/include/clang/Lex/Token.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h
index 3984d5d007f67..2b30f52a0780e 100644
--- a/clang/include/clang/Lex/Token.h
+++ b/clang/include/clang/Lex/Token.h
@@ -89,7 +89,7 @@ class Token {
IsReinjected = 0x800, // A phase 4 token that was produced before and
// re-added, e.g. via EnterTokenStream. Annotation
// tokens are *not* reinjected.
- FirstPPToken = 0x1000, // This token is the first pp token in the
+ FirstPPToken = 0x1000, // This token is the first pp token in the
// translation unit.
};
>From 1fa309028fa282535d5bbe76f35b251f5e6e88ac Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Sun, 22 Jun 2025 01:02:02 +0800
Subject: [PATCH 07/22] Resolve merge conflicts
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/include/clang/Lex/Preprocessor.h | 3 ---
clang/lib/Lex/Lexer.cpp | 3 ---
.../dcl.module/dcl.module.import/p1.cppm | 20 +------------------
3 files changed, 1 insertion(+), 25 deletions(-)
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 26c60e0494fc3..b2f4826b8c0a3 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -1789,9 +1789,6 @@ class Preprocessor {
void HandleCXXImportDirective(Token Import);
void HandleCXXModuleDirective(Token Module);
- void setFirstPPToken(const Token &Tok) { FirstPPToken = Tok; }
- Token getFirstPPToken() const { return FirstPPToken; }
-
/// Callback invoked when the lexer sees one of export, import or module token
/// at the start of a line.
///
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index f210c24847f21..f866f203de4e5 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -3752,9 +3752,6 @@ bool Lexer::Lex(Token &Result) {
(void) isRawLex;
bool returnedToken = LexTokenInternal(Result, atPhysicalStartOfLine);
- if (returnedToken && Result.isFirstPPToken() && PP)
- PP->setFirstPPToken(Result);
-
// (After the LexTokenInternal call, the lexer might be destroyed.)
assert((returnedToken || !isRawLex) && "Raw lex must succeed");
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 6ab02b94a6354..53fd3ea29eccb 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
@@ -43,24 +43,6 @@ 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-re {{unknown attribute 'blarg::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'}}
-
-import blarg; // expected-error {{module 'blarg' not found}}
-
-int use_4 = c; // ok
-
-//--- test.implementation.cpp
-module a.b;
-
-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-re {{unknown attribute 'blarg::noreturn' ignored{{.*}}}}
-
import x.y;
import x.; // expected-error {{expected identifier after '.' in module name}}
import .x; // expected-error {{unknown type name 'import'}} \
@@ -78,7 +60,7 @@ 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 [[blarg::noreturn]]; // expected-warning-re {{unknown attribute 'blarg::noreturn' ignored{{.*}}}}
import x.y;
import x.; // expected-error {{expected identifier after '.' in module name}}
>From 687cdeed3bcb00158314ab41913acc8d637952a8 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 24 Jun 2025 19:47:32 +0800
Subject: [PATCH 08/22] Use isNextPPTokenOneOf instead of peekNextPPToken
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/lib/Lex/Preprocessor.cpp | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 4d291c3f5c618..e7630f6ed7aae 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -54,6 +54,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Capacity.h"
@@ -1204,19 +1205,22 @@ bool Preprocessor::HandleModuleContextualKeyword(
bool SavedParsingPreprocessorDirective =
CurPPLexer->ParsingPreprocessorDirective;
CurPPLexer->ParsingPreprocessorDirective = true;
- // Peek next token.
- auto NextTok = peekNextPPToken().value_or(Token{});
- CurPPLexer->ParsingPreprocessorDirective = SavedParsingPreprocessorDirective;
+ auto _ = llvm::make_scope_exit([&]() {
+ CurPPLexer->ParsingPreprocessorDirective =
+ SavedParsingPreprocessorDirective;
+ });
+
if (Result.getIdentifierInfo()->isModulesImport() &&
- NextTok.isOneOf(tok::raw_identifier, tok::less, tok::string_literal,
- tok::colon)) {
+ isNextPPTokenOneOf<tok::raw_identifier, tok::less, tok::string_literal,
+ tok::colon>()) {
Result.setKind(tok::kw_import);
ModuleImportLoc = Result.getLocation();
IsAtImport = false;
return true;
}
+
if (Result.getIdentifierInfo()->isModulesDeclaration() &&
- NextTok.isOneOf(tok::raw_identifier, tok::colon, tok::semi)) {
+ isNextPPTokenOneOf<tok::raw_identifier, tok::colon, tok::semi>()) {
Result.setKind(tok::kw_module);
ModuleDeclLoc = Result.getLocation();
return true;
>From 8ce88a0c7314e9b6db467f8b7c880aa81de9187d Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 26 Jun 2025 01:42:21 +0800
Subject: [PATCH 09/22] Follow isNextPPTokenOneOf's change
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/lib/Lex/Preprocessor.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 5b7c5b1a2ea5f..57651fd47e1b1 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -1211,8 +1211,8 @@ bool Preprocessor::HandleModuleContextualKeyword(
});
if (Result.getIdentifierInfo()->isModulesImport() &&
- isNextPPTokenOneOf<tok::raw_identifier, tok::less, tok::string_literal,
- tok::colon>()) {
+ isNextPPTokenOneOf(tok::raw_identifier, tok::less, tok::string_literal,
+ tok::colon)) {
Result.setKind(tok::kw_import);
ModuleImportLoc = Result.getLocation();
IsAtImport = false;
@@ -1220,7 +1220,7 @@ bool Preprocessor::HandleModuleContextualKeyword(
}
if (Result.getIdentifierInfo()->isModulesDeclaration() &&
- isNextPPTokenOneOf<tok::raw_identifier, tok::colon, tok::semi>()) {
+ isNextPPTokenOneOf(tok::raw_identifier, tok::colon, tok::semi)) {
Result.setKind(tok::kw_module);
ModuleDeclLoc = Result.getLocation();
return true;
>From 21cb5ecd43c5116af1154ce18a12e1c744dfcf5c Mon Sep 17 00:00:00 2001
From: "Wang, Yihan" <yronglin777 at gmail.com>
Date: Tue, 8 Jul 2025 21:57:13 +0800
Subject: [PATCH 10/22] Follow getTrailingObjects changes
Signed-off-by: Wang, Yihan <yronglin777 at gmail.com>
---
clang/include/clang/Lex/Preprocessor.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index f6fc425f004d1..e7319c5808f6f 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -3190,7 +3190,7 @@ class ModuleNameLoc final
}
ModuleNameLoc(ModuleIdPath Path) : NumIdentifierLocs(Path.size()) {
- (void)llvm::copy(Path, getTrailingObjects<IdentifierLoc>());
+ (void)llvm::copy(Path, getTrailingObjectsNonStrict<IdentifierLoc>());
}
public:
@@ -3199,7 +3199,8 @@ class ModuleNameLoc final
static Token CreateAnnotToken(Preprocessor &PP, ModuleIdPath Path);
unsigned getNumIdentifierLocs() const { return NumIdentifierLocs; }
ModuleIdPath getModuleIdPath() const {
- return {getTrailingObjects<IdentifierLoc>(), getNumIdentifierLocs()};
+ return {getTrailingObjectsNonStrict<IdentifierLoc>(),
+ getNumIdentifierLocs()};
}
SourceLocation getBeginLoc() const {
>From 27ab41f0bd79cf42b3bc8dc00ed2289da42eabca Mon Sep 17 00:00:00 2001
From: "Wang, Yihan" <yronglin777 at gmail.com>
Date: Tue, 8 Jul 2025 23:50:26 +0800
Subject: [PATCH 11/22] Revert unnecessary changes and refine code
Signed-off-by: Wang, Yihan <yronglin777 at gmail.com>
---
clang/include/clang/Lex/Preprocessor.h | 4 ----
clang/lib/Basic/IdentifierTable.cpp | 5 ++++-
clang/lib/Lex/Lexer.cpp | 21 ++++++++++++---------
clang/lib/Lex/PPDirectives.cpp | 9 +++++----
4 files changed, 21 insertions(+), 18 deletions(-)
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index e7319c5808f6f..43c54ba222b85 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -2740,10 +2740,6 @@ class Preprocessor {
void removeCachedMacroExpandedTokensOfLastLexer();
- /// Peek the next token. If so, return the token, if not, this
- /// method should have no observable side-effect on the lexed tokens.
- std::optional<Token> peekNextPPToken();
-
/// After reading "MACRO(", this method is invoked to read all of the formal
/// arguments specified for the macro invocation. Returns null on error.
MacroArgs *ReadMacroCallArgumentList(Token &MacroName, MacroInfo *MI,
diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp
index 8b740da54c305..5f6d34bd3b7ec 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -465,6 +465,8 @@ tok::PPKeywordKind IdentifierInfo::getPPKeywordID() const {
unsigned Len = getLength();
if (Len < 2) return tok::pp_not_keyword;
const char *Name = getNameStart();
+
+ // clang-format off
switch (HASH(Len, Name[0], Name[2])) {
default: return tok::pp_not_keyword;
CASE( 2, 'i', '\0', if);
@@ -483,7 +485,7 @@ tok::PPKeywordKind IdentifierInfo::getPPKeywordID() const {
CASE( 6, 'd', 'f', define);
CASE( 6, 'i', 'n', ifndef);
CASE( 6, 'i', 'p', import);
- CASE(6, 'm', 'd', module);
+ CASE( 6, 'm', 'd', module);
CASE( 6, 'p', 'a', pragma);
CASE( 7, 'd', 'f', defined);
@@ -503,6 +505,7 @@ tok::PPKeywordKind IdentifierInfo::getPPKeywordID() const {
#undef CASE
#undef HASH
}
+ // clang-format on
}
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index d3b9be017ab09..66168b57c3055 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -3748,7 +3748,6 @@ bool Lexer::Lex(Token &Result) {
bool isRawLex = isLexingRawMode();
(void) isRawLex;
bool returnedToken = LexTokenInternal(Result, atPhysicalStartOfLine);
-
// (After the LexTokenInternal call, the lexer might be destroyed.)
assert((returnedToken || !isRawLex) && "Raw lex must succeed");
return returnedToken;
@@ -4046,8 +4045,8 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
// Notify MIOpt that we read a non-whitespace/non-comment token.
MIOpt.ReadToken();
bool returnedToken = LexIdentifierContinue(Result, CurPtr);
- if (returnedToken && Result.isModuleContextualKeyword() &&
- LangOpts.CPlusPlusModules &&
+ if (returnedToken && LangOpts.CPlusPlusModules &&
+ Result.isModuleContextualKeyword() &&
PP->HandleModuleContextualKeyword(Result, TokAtPhysicalStartOfLine) &&
!LexingRawMode && !Is_PragmaLexer)
goto HandleDirective;
@@ -4255,8 +4254,12 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
// it's actually the start of a preprocessing directive. Callback to
// the preprocessor to handle it.
// TODO: -fpreprocessed mode??
- if (TokAtPhysicalStartOfLine && !LexingRawMode && !Is_PragmaLexer)
+ if (TokAtPhysicalStartOfLine && !LexingRawMode && !Is_PragmaLexer) {
+ // We parsed a # character and it's the start of a preprocessing
+ // directive.
+ FormTokenWithChars(Result, CurPtr, tok::hash);
goto HandleDirective;
+ }
Kind = tok::hash;
}
@@ -4443,8 +4446,12 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
// it's actually the start of a preprocessing directive. Callback to
// the preprocessor to handle it.
// TODO: -fpreprocessed mode??
- if (TokAtPhysicalStartOfLine && !LexingRawMode && !Is_PragmaLexer)
+ if (TokAtPhysicalStartOfLine && !LexingRawMode && !Is_PragmaLexer) {
+ // We parsed a # character and it's the start of a preprocessing
+ // directive.
+ FormTokenWithChars(Result, CurPtr, tok::hash);
goto HandleDirective;
+ }
Kind = tok::hash;
}
@@ -4534,9 +4541,6 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
return true;
HandleDirective:
- // We parsed a # character and it's the start of a preprocessing directive.
- if (!Result.isOneOf(tok::kw_import, tok::kw_module))
- FormTokenWithChars(Result, CurPtr, tok::hash);
PP->HandleDirective(Result);
if (PP->hadModuleLoaderFatalFailure())
@@ -4632,7 +4636,6 @@ bool Lexer::LexDependencyDirectiveToken(Token &Result) {
}
if (Result.isLiteral())
return true;
-
if (Result.is(tok::colon)) {
// Convert consecutive colons to 'tok::coloncolon'.
if (*BufferPtr == ':') {
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 1c3a05f8d04dc..c3a78c20f2788 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -43,6 +43,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SaveAndRestore.h"
@@ -1252,13 +1253,13 @@ void Preprocessor::HandleDirective(Token &Result) {
// Save the '#' token in case we need to return it later.
Token SavedHash = Result;
- bool IsCXX20ImportOrModuleDirective =
+ bool IsCXXImportOrModule =
getLangOpts().CPlusPlusModules &&
Result.isModuleContextualKeyword(/*AllowExport=*/false);
// Read the next token, the directive flavor. This isn't expanded due to
// C99 6.10.3p8.
- if (!IsCXX20ImportOrModuleDirective)
+ if (LLVM_LIKELY(!IsCXXImportOrModule))
LexUnexpandedToken(Result);
// C99 6.10.3p11: Is this preprocessor directive in macro invocation? e.g.:
@@ -1280,7 +1281,7 @@ void Preprocessor::HandleDirective(Token &Result) {
case tok::pp_embed:
case tok::pp_module:
Diag(Result, diag::err_embedded_directive)
- << IsCXX20ImportOrModuleDirective << II->getName();
+ << IsCXXImportOrModule << II->getName();
Diag(*ArgMacro, diag::note_macro_expansion_here)
<< ArgMacro->getIdentifierInfo();
DiscardUntilEndOfDirective();
@@ -1375,7 +1376,7 @@ void Preprocessor::HandleDirective(Token &Result) {
return HandleCXXModuleDirective(Result);
// GNU Extensions.
case tok::pp_import:
- if (IsCXX20ImportOrModuleDirective)
+ if (IsCXXImportOrModule)
return HandleCXXImportDirective(Result);
return HandleImportDirective(SavedHash.getLocation(), Result);
case tok::pp_include_next:
>From 63a62aebbfe8a9c5b5fb7f932a2f91fd46c366aa Mon Sep 17 00:00:00 2001
From: "Wang, Yihan" <yronglin777 at gmail.com>
Date: Wed, 9 Jul 2025 23:53:39 +0800
Subject: [PATCH 12/22] Update cxx status
Signed-off-by: Wang, Yihan <yronglin777 at gmail.com>
---
clang/www/cxx_status.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index bb7144b827c3c..f3c39718be55d 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -931,7 +931,7 @@ <h2 id="cxx20">C++20 implementation status</h2>
</tr>
<tr>
<td><a href="https://wg21.link/p1857r3">P1857R3</a></td>
- <td class="none" align="center">No</td>
+ <td class="full" align="center">Clang 21</td>
</tr>
<tr>
<td><a href="https://wg21.link/p2115r0">P2115R0</a></td>
>From 1171f7f351df6cfa81e303e367597b9532fe0f1c Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 15 Jul 2025 23:46:04 +0800
Subject: [PATCH 13/22] Fix crash
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/lib/Lex/Lexer.cpp | 8 ++++----
clang/lib/Lex/PPDirectives.cpp | 9 ++++-----
2 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index b47555699f1aa..c1dbf4d901007 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -4046,10 +4046,10 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
// Notify MIOpt that we read a non-whitespace/non-comment token.
MIOpt.ReadToken();
bool returnedToken = LexIdentifierContinue(Result, CurPtr);
- if (returnedToken && LangOpts.CPlusPlusModules &&
- Result.isModuleContextualKeyword() &&
- PP->HandleModuleContextualKeyword(Result, TokAtPhysicalStartOfLine) &&
- !LexingRawMode && !Is_PragmaLexer)
+ if (returnedToken && Result.isModuleContextualKeyword() &&
+ LangOpts.CPlusPlusModules && !LexingRawMode && !Is_PragmaLexer &&
+ !ParsingPreprocessorDirective && PP &&
+ PP->HandleModuleContextualKeyword(Result, TokAtPhysicalStartOfLine))
goto HandleDirective;
return returnedToken;
}
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index c3a78c20f2788..1c3a05f8d04dc 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -43,7 +43,6 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
-#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SaveAndRestore.h"
@@ -1253,13 +1252,13 @@ void Preprocessor::HandleDirective(Token &Result) {
// Save the '#' token in case we need to return it later.
Token SavedHash = Result;
- bool IsCXXImportOrModule =
+ bool IsCXX20ImportOrModuleDirective =
getLangOpts().CPlusPlusModules &&
Result.isModuleContextualKeyword(/*AllowExport=*/false);
// Read the next token, the directive flavor. This isn't expanded due to
// C99 6.10.3p8.
- if (LLVM_LIKELY(!IsCXXImportOrModule))
+ if (!IsCXX20ImportOrModuleDirective)
LexUnexpandedToken(Result);
// C99 6.10.3p11: Is this preprocessor directive in macro invocation? e.g.:
@@ -1281,7 +1280,7 @@ void Preprocessor::HandleDirective(Token &Result) {
case tok::pp_embed:
case tok::pp_module:
Diag(Result, diag::err_embedded_directive)
- << IsCXXImportOrModule << II->getName();
+ << IsCXX20ImportOrModuleDirective << II->getName();
Diag(*ArgMacro, diag::note_macro_expansion_here)
<< ArgMacro->getIdentifierInfo();
DiscardUntilEndOfDirective();
@@ -1376,7 +1375,7 @@ void Preprocessor::HandleDirective(Token &Result) {
return HandleCXXModuleDirective(Result);
// GNU Extensions.
case tok::pp_import:
- if (IsCXXImportOrModule)
+ if (IsCXX20ImportOrModuleDirective)
return HandleCXXImportDirective(Result);
return HandleImportDirective(SavedHash.getLocation(), Result);
case tok::pp_include_next:
>From eabc2a412bc4f11e1df4235d89763f6939a4d84b Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 17 Jul 2025 01:13:57 +0800
Subject: [PATCH 14/22] Fix lit test
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/test/CXX/module/module.interface/p1.cpp | 4 +---
clang/www/cxx_status.html | 4 ++--
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/clang/test/CXX/module/module.interface/p1.cpp b/clang/test/CXX/module/module.interface/p1.cpp
index e055d565bb9b8..04c29d927d1d4 100644
--- a/clang/test/CXX/module/module.interface/p1.cpp
+++ b/clang/test/CXX/module/module.interface/p1.cpp
@@ -35,7 +35,5 @@ namespace N {
}
//--- impl.cpp
-module M; // #M
-
+module M; // expected-note {{add 'export'}}
export int b2; // expected-error {{export declaration can only be used within a module purview}}
-// expected-note@#M 2+{{add 'export'}}
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index f3c39718be55d..ed310310ff187 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -915,7 +915,7 @@ <h2 id="cxx20">C++20 implementation status</h2>
</tr>
<tr>
<td><a href="https://wg21.link/p1703r1">P1703R1</a></td>
- <td class="none" align="center">Subsumed by P1857</td>
+ <td class="unreleased" align="center">Subsumed by P1857</td>
</tr>
<tr> <!-- from Belfast -->
<td><a href="https://wg21.link/p1874r1">P1874R1</a></td>
@@ -931,7 +931,7 @@ <h2 id="cxx20">C++20 implementation status</h2>
</tr>
<tr>
<td><a href="https://wg21.link/p1857r3">P1857R3</a></td>
- <td class="full" align="center">Clang 21</td>
+ <td class="unreleased" align="center">Clang 21</td>
</tr>
<tr>
<td><a href="https://wg21.link/p2115r0">P2115R0</a></td>
>From b53318407649c1a4d73ca2f856b7385b4267f987 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 31 Jul 2025 01:53:33 +0800
Subject: [PATCH 15/22] Rebase and fix conflict
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/lib/Lex/DependencyDirectivesScanner.cpp | 36 ++++++++++++-------
clang/lib/Parse/Parser.cpp | 1 -
.../Lex/DependencyDirectivesScannerTest.cpp | 11 +++---
3 files changed, 29 insertions(+), 19 deletions(-)
diff --git a/clang/lib/Lex/DependencyDirectivesScanner.cpp b/clang/lib/Lex/DependencyDirectivesScanner.cpp
index 720beac234f9f..4e55e49b34a60 100644
--- a/clang/lib/Lex/DependencyDirectivesScanner.cpp
+++ b/clang/lib/Lex/DependencyDirectivesScanner.cpp
@@ -542,6 +542,12 @@ static void skipWhitespace(const char *&First, const char *const End) {
bool Scanner::lexModuleDirectiveBody(DirectiveKind Kind, const char *&First,
const char *const End) {
+ assert(Kind == DirectiveKind::cxx_export_import_decl ||
+ Kind == DirectiveKind::cxx_export_module_decl ||
+ Kind == DirectiveKind::cxx_import_decl ||
+ Kind == DirectiveKind::cxx_module_decl ||
+ Kind == DirectiveKind::decl_at_import);
+
const char *DirectiveLoc = Input.data() + CurDirToks.front().Offset;
for (;;) {
// Keep a copy of the First char incase it needs to be reset.
@@ -553,7 +559,7 @@ bool Scanner::lexModuleDirectiveBody(DirectiveKind Kind, const char *&First,
First = Previous;
return false;
}
- if (Tok.is(tok::eof) || (LangOpts.CPlusPlusModules && Tok.is(tok::eod)))
+ if (Tok.isOneOf(tok::eof, tok::eod))
return reportError(
DirectiveLoc,
diag::err_dep_source_scanner_missing_semi_after_at_import);
@@ -561,21 +567,24 @@ bool Scanner::lexModuleDirectiveBody(DirectiveKind Kind, const char *&First,
break;
}
- // Skip extra tokens after semi in C++20 Modules directive.
bool IsCXXModules = Kind == DirectiveKind::cxx_export_import_decl ||
Kind == DirectiveKind::cxx_export_module_decl ||
Kind == DirectiveKind::cxx_import_decl ||
Kind == DirectiveKind::cxx_module_decl;
- if (IsCXXModules)
+ if (IsCXXModules) {
lexPPDirectiveBody(First, End);
-
- if (!IsCXXModules) {
- if (!isVerticalWhitespace(*First))
- return reportError(
- DirectiveLoc,
- diag::err_dep_source_scanner_unexpected_tokens_at_import);
- skipNewline(First, End);
+ pushDirective(Kind);
+ return false;
}
+
+ pushDirective(Kind);
+ skipWhitespace(First, End);
+ if (First == End)
+ return false;
+ if (!isVerticalWhitespace(*First))
+ return reportError(
+ DirectiveLoc, diag::err_dep_source_scanner_unexpected_tokens_at_import);
+ skipNewline(First, End);
return false;
}
@@ -920,6 +929,10 @@ 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);
+
if (*First == '_') {
if (isNextIdentifierOrSkipLine("_Pragma", First, End))
return lex_Pragma(First, End);
@@ -932,9 +945,6 @@ bool Scanner::lexPPLine(const char *&First, const char *const End) {
auto ScEx2 = make_scope_exit(
[&]() { TheLexer.setParsingPreprocessorDirective(false); });
- if (*First == '@')
- return lexAt(First, End);
-
// Handle module directives for C++20 modules.
if (*First == 'i' || *First == 'e' || *First == 'm')
return lexModule(First, End);
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 6e8fdbb29f122..1639183d65b82 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -2489,7 +2489,6 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
break;
}
ExpectAndConsumeSemi(diag::err_module_expected_semi);
- TryConsumeToken(tok::eod);
if (SeenError)
return nullptr;
diff --git a/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp b/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp
index ddc87921ea084..1e1702c0c8d43 100644
--- a/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp
+++ b/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp
@@ -640,7 +640,7 @@ TEST(MinimizeSourceToDependencyDirectivesTest, AtImport) {
EXPECT_STREQ("@import A;\n", Out.data());
ASSERT_FALSE(minimizeSourceToDependencyDirectives("@import A\n;", Out));
- EXPECT_STREQ("@import A\n;\n", Out.data());
+ EXPECT_STREQ("@import A;\n", Out.data());
ASSERT_FALSE(minimizeSourceToDependencyDirectives("@import A.B;\n", Out));
EXPECT_STREQ("@import A.B;\n", Out.data());
@@ -685,18 +685,19 @@ TEST(MinimizeSourceToDependencyDirectivesTest, ImportFailures) {
minimizeSourceToDependencyDirectives("@import MACRO(A);\n", Out));
ASSERT_FALSE(minimizeSourceToDependencyDirectives("@import \" \";\n", Out));
- ASSERT_FALSE(minimizeSourceToDependencyDirectives("import <Foo.h>\n"
+ ASSERT_FALSE(minimizeSourceToDependencyDirectives("import <Foo.h>;\n"
"@import Foo;",
Out));
- EXPECT_STREQ("@import Foo;\n", Out.data());
+ EXPECT_STREQ("import<Foo.h>;\n at import Foo;\n", Out.data());
ASSERT_FALSE(
- minimizeSourceToDependencyDirectives("import <Foo.h>\n"
+ minimizeSourceToDependencyDirectives("import <Foo.h>;\n"
"#import <Foo.h>\n"
"@;\n"
"#pragma clang module import Foo",
Out));
- EXPECT_STREQ("#import <Foo.h>\n"
+ EXPECT_STREQ("import<Foo.h>;\n"
+ "#import <Foo.h>\n"
"#pragma clang module import Foo\n",
Out.data());
}
>From 0164d0d0b88d653a2f7f1d9113d8cdedcbd1b820 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Fri, 8 Aug 2025 15:44:37 +0800
Subject: [PATCH 16/22] Stash
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/lib/Lex/Preprocessor.cpp | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 555a5d4eaeccd..f3b6746e2eac9 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -876,7 +876,10 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
// Likewise if this is the standard C++ import keyword.
if (((LastTokenWasAt && II.isModulesImport()) ||
Identifier.is(tok::kw_import)) &&
- !InMacroArgs && !DisableMacroExpansion &&
+ // FIXME: Can we just ignore DisableMacroExpansion here?
+ // https://github.com/llvm/llvm-project/pull/137665 disable
+ // macro expansion when current input file is preprocessed.
+ !InMacroArgs && /*!DisableMacroExpansion &&*/
CurLexerCallback != CLK_CachingLexer) {
ModuleImportLoc = Identifier.getLocation();
IsAtImport = true;
>From 0588245b2d6e8d285a298d840bb1da5bf3b7dfb7 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Fri, 22 Aug 2025 20:02:04 +0800
Subject: [PATCH 17/22] Handle @import in preprocessor when -frewrite-imports
enabled
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/lib/Lex/Preprocessor.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 2f70c69347bf8..b04018bfcae6f 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -881,7 +881,8 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
// FIXME: Can we just ignore DisableMacroExpansion here?
// https://github.com/llvm/llvm-project/pull/137665 disable
// macro expansion when current input file is preprocessed.
- !InMacroArgs && /*!DisableMacroExpansion &&*/
+ !InMacroArgs &&
+ (!DisableMacroExpansion || MacroExpansionInDirectivesOverride) &&
CurLexerCallback != CLK_CachingLexer) {
ModuleImportLoc = Identifier.getLocation();
IsAtImport = true;
>From 49281ec661b311aa43e37c514eb595e612d75ffd Mon Sep 17 00:00:00 2001
From: "Wang, Yihan" <yronglin777 at gmail.com>
Date: Wed, 27 Aug 2025 23:12:38 +0800
Subject: [PATCH 18/22] Don't use ModuleNameLoc* as ActOnModuleImport
Signed-off-by: Wang, Yihan <yronglin777 at gmail.com>
---
clang/include/clang/Sema/Sema.h | 2 +-
clang/lib/Parse/Parser.cpp | 4 ++--
clang/lib/Sema/SemaModule.cpp | 15 ++++++---------
3 files changed, 9 insertions(+), 12 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5635a336e0db5..03a5349b1c31f 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9870,7 +9870,7 @@ class Sema final : public SemaBase {
/// \param IsPartition If the name is for a partition.
DeclResult ActOnModuleImport(SourceLocation StartLoc,
SourceLocation ExportLoc,
- SourceLocation ImportLoc, ModuleNameLoc *PathLoc,
+ SourceLocation ImportLoc, ModuleIdPath Path,
bool IsPartition = false);
DeclResult ActOnModuleImport(SourceLocation StartLoc,
SourceLocation ExportLoc,
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 3c2d9342f0407..9595cccd93a89 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -2501,8 +2501,8 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
Import =
Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, HeaderUnit);
else if (Path)
- Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Path,
- IsPartition);
+ Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc,
+ Path->getModuleIdPath(), IsPartition);
if (Import.isInvalid())
return nullptr;
diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp
index 6b9a815d35997..e06371ed56300 100644
--- a/clang/lib/Sema/SemaModule.cpp
+++ b/clang/lib/Sema/SemaModule.cpp
@@ -563,8 +563,8 @@ Sema::ActOnPrivateModuleFragmentDecl(SourceLocation ModuleLoc,
DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
SourceLocation ExportLoc,
- SourceLocation ImportLoc,
- ModuleNameLoc *PathLoc, bool IsPartition) {
+ SourceLocation ImportLoc, ModuleIdPath Path,
+ bool IsPartition) {
assert((!IsPartition || getLangOpts().CPlusPlusModules) &&
"partition seen in non-C++20 code?");
@@ -573,7 +573,6 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
IdentifierLoc ModuleNameLoc;
std::string ModuleName;
- ModuleIdPath Path;
if (IsPartition) {
// We already checked that we are in a module purview in the parser.
assert(!ModuleScopes.empty() && "in a module purview, but no module?");
@@ -582,17 +581,15 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
// otherwise, the name of the importing named module.
ModuleName = NamedMod->getPrimaryModuleInterfaceName().str();
ModuleName += ":";
- ModuleName += PathLoc->str();
+ ModuleName += ModuleNameLoc::stringFromModuleIdPath(Path);
ModuleNameLoc =
- IdentifierLoc(PathLoc->getBeginLoc(), PP.getIdentifierInfo(ModuleName));
+ IdentifierLoc(Path[0].getLoc(), PP.getIdentifierInfo(ModuleName));
Path = ModuleIdPath(ModuleNameLoc);
} else if (getLangOpts().CPlusPlusModules) {
- ModuleName = PathLoc->str();
+ ModuleName = ModuleNameLoc::stringFromModuleIdPath(Path);
ModuleNameLoc =
- IdentifierLoc(PathLoc->getBeginLoc(), PP.getIdentifierInfo(ModuleName));
+ IdentifierLoc(Path[0].getLoc(), PP.getIdentifierInfo(ModuleName));
Path = ModuleIdPath(ModuleNameLoc);
- } else {
- Path = PathLoc->getModuleIdPath();
}
// Diagnose self-import before attempting a load.
>From 1c8b28f361ce4cda23d5d46b43a3e1bb1abfd54f Mon Sep 17 00:00:00 2001
From: "Wang, Yihan" <yronglin777 at gmail.com>
Date: Fri, 29 Aug 2025 02:15:44 +0800
Subject: [PATCH 19/22] Remove tok::annot_module_name and added error recovery
to continue parsing the ill-formed module/import directive which was not
correctly recognized in preprocessor
Signed-off-by: Wang, Yihan <yronglin777 at gmail.com>
---
.../include/clang/Basic/DiagnosticLexKinds.td | 9 --
.../clang/Basic/DiagnosticParseKinds.td | 72 ++++++----
clang/include/clang/Basic/TokenKinds.def | 3 -
clang/include/clang/Basic/TokenKinds.h | 4 +
clang/include/clang/Lex/ModuleLoader.h | 1 +
clang/include/clang/Lex/Preprocessor.h | 86 ++++--------
clang/include/clang/Parse/Parser.h | 7 +-
clang/include/clang/Sema/Sema.h | 1 -
clang/lib/Basic/TokenKinds.cpp | 12 ++
.../lib/Frontend/PrintPreprocessedOutput.cpp | 8 +-
clang/lib/Lex/PPDirectives.cpp | 110 +++++++--------
clang/lib/Lex/Preprocessor.cpp | 81 ++++-------
clang/lib/Lex/TokenConcatenation.cpp | 8 +-
clang/lib/Parse/Parser.cpp | 129 +++++++++++++-----
clang/lib/Sema/SemaModule.cpp | 8 +-
clang/test/CXX/basic/basic.link/p3.cpp | 2 +-
.../basic/basic.link/module-declaration.cpp | 2 +-
.../dcl.module/dcl.module.import/p1.cppm | 6 +-
clang/test/Modules/pr121066.cpp | 3 +
19 files changed, 282 insertions(+), 270 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 7ad5858c33597..4b0411ed66d77 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -989,19 +989,10 @@ def warn_module_conflict : Warning<
InGroup<ModuleConflict>;
// C++20 modules
-def err_pp_expected_module_name_or_header_name
- : Error<"expected module name or header name">;
-def err_pp_expected_semi_after_module_or_import
- : Error<"'%select{module|import}0' directive must end with a ';' on the "
- "same line">;
def err_module_decl_in_header
: Error<"module declaration must not come from an #include directive">;
def err_pp_cond_span_module_decl
: Error<"preprocessor conditionals shall not span a module declaration">;
-def err_pp_module_expected_ident
- : Error<"expected a module name after '%select{module|import}0'">;
-def err_pp_unsupported_module_partition
- : Error<"module partitions are only supported for C++20 onwards">;
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 cf43447c53f13..d579fbdc80828 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1775,36 +1775,48 @@ def ext_bit_int : Extension<
} // end of Parse Issue category.
let CategoryName = "Modules Issue" in {
- def err_unexpected_module_import_decl
- : Error<"%select{module|import}0 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_attribute_not_module_attr
- : Error<"%0 attribute cannot be applied to a module">;
- def err_keyword_not_module_attr : Error<"%0 cannot be applied to a module">;
- def err_attribute_not_import_attr
- : Error<"%0 attribute cannot be applied to a module import">;
- def err_keyword_not_import_attr
- : Error<"%0 cannot be applied to a module import">;
- def err_module_expected_semi : Error<"expected ';' after module name">;
- def err_global_module_introducer_not_at_start
- : Error<"'module;' introducing a global module fragment can appear only "
- "at the start of the translation unit">;
- def err_module_fragment_exported
- : Error<"%select{global|private}0 module fragment cannot be exported">;
- def err_private_module_fragment_expected_semi
- : Error<"expected ';' after private module fragment declaration">;
- def err_missing_before_module_end : Error<"expected %0 at end of module">;
- def err_import_not_allowed_here
- : Error<"imports must immediately follow the module declaration">;
- def err_partition_import_outside_module
- : Error<"module partition imports must be within a module purview">;
- def err_import_in_wrong_fragment
- : Error<"module%select{| partition}0 imports cannot be in the "
- "%select{global|private}1 module fragment">;
-
- def err_export_empty : Error<"export declaration cannot be empty">;
+def err_invalid_module_or_import_directive : Error<
+ "the %select{module|import}0 directive is ill-formed, "
+ "%select{module contextual keyword must be followed by an identifier "
+ "or a ';' after being at the start of a line, or preceded by "
+ "an export keyword at the start of a line|"
+ "import contextual keyword must be followed by an identifier, "
+ "'<', '\"', or ':', but not '::', after being at the start of a "
+ "line or preceded by an export at the start of the line}0">;
+def err_unexpected_module_or_import_decl : Error<
+ "%select{module|import}0 declaration can only appear at the top level">;
+def err_module_expected_ident : Error<
+ "expected %select{identifier after '.' in |}0module name">;
+def err_attribute_not_module_attr : Error<
+ "%0 attribute cannot be applied to a module">;
+def err_keyword_not_module_attr : Error<
+ "%0 cannot be applied to a module">;
+def err_attribute_not_import_attr : Error<
+ "%0 attribute cannot be applied to a module import">;
+def err_keyword_not_import_attr : Error<
+ "%0 cannot be applied to a module import">;
+def err_module_expected_semi : Error<
+ "expected ';' after module name">;
+def err_expected_semi_after_module_or_import
+ : Error<"'%0' directive must end with a ';' on the same line">;
+def err_global_module_introducer_not_at_start : Error<
+ "'module;' introducing a global module fragment can appear only "
+ "at the start of the translation unit">;
+def err_module_fragment_exported : Error<
+ "%select{global|private}0 module fragment cannot be exported">;
+def err_private_module_fragment_expected_semi : Error<
+ "expected ';' after private module fragment declaration">;
+def err_missing_before_module_end : Error<"expected %0 at end of module">;
+def err_unsupported_module_partition : Error<
+ "module partitions are only supported for C++20 onwards">;
+def err_import_not_allowed_here : Error<
+ "imports must immediately follow the module declaration">;
+def err_partition_import_outside_module : Error<
+ "module partition imports must be within a module purview">;
+def err_import_in_wrong_fragment : Error<
+ "module%select{| partition}0 imports cannot be in the %select{global|private}1 module fragment">;
+
+def err_export_empty : Error<"export declaration cannot be empty">;
}
let CategoryName = "Generics Issue" in {
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 7750c84dbef78..9df539b7eea09 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -1026,9 +1026,6 @@ ANNOTATION(module_include)
ANNOTATION(module_begin)
ANNOTATION(module_end)
-// Annotations for C++, Clang and Objective-C named modules.
-ANNOTATION(module_name)
-
// Annotation for a header_name token that has been looked up and transformed
// into the name of a header unit.
ANNOTATION(header_unit)
diff --git a/clang/include/clang/Basic/TokenKinds.h b/clang/include/clang/Basic/TokenKinds.h
index d84f3598cbf33..f0d11d43bdf97 100644
--- a/clang/include/clang/Basic/TokenKinds.h
+++ b/clang/include/clang/Basic/TokenKinds.h
@@ -76,6 +76,10 @@ const char *getPunctuatorSpelling(TokenKind Kind) LLVM_READNONE;
/// tokens like 'int' and 'dynamic_cast'. Returns NULL for other token kinds.
const char *getKeywordSpelling(TokenKind Kind) LLVM_READNONE;
+/// Determines the spelling of simple Objective-C keyword tokens like '@import'.
+/// Returns NULL for other token kinds.
+const char *getObjCKeywordSpelling(ObjCKeywordKind Kind) LLVM_READNONE;
+
/// Returns the spelling of preprocessor keywords, such as "else".
const char *getPPKeywordSpelling(PPKeywordKind Kind) LLVM_READNONE;
diff --git a/clang/include/clang/Lex/ModuleLoader.h b/clang/include/clang/Lex/ModuleLoader.h
index a58407200c41c..042a5ab1f4a57 100644
--- a/clang/include/clang/Lex/ModuleLoader.h
+++ b/clang/include/clang/Lex/ModuleLoader.h
@@ -159,6 +159,7 @@ class ModuleLoader {
/// \returns Returns true if any modules with that symbol found.
virtual bool lookupMissingImports(StringRef Name,
SourceLocation TriggerLoc) = 0;
+ static std::string getFlatNameFromPath(ModuleIdPath Path);
bool HadFatalFailure = false;
};
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 727d2000f3d95..47c62ae9c36d5 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -83,7 +83,6 @@ class PreprocessorLexer;
class PreprocessorOptions;
class ScratchBuffer;
class TargetInfo;
-class ModuleNameLoc;
class NoTrivialPPDirectiveTracer;
namespace Builtin {
@@ -592,7 +591,12 @@ class Preprocessor {
reset();
}
- void handleModuleName(ModuleNameLoc *Path);
+ void handleIdentifier(IdentifierInfo *Identifier) {
+ if (isModuleCandidate() && Identifier)
+ Name += Identifier->getName().str();
+ else if (!isNamedModule())
+ reset();
+ }
void handleColon() {
if (isModuleCandidate())
@@ -601,6 +605,13 @@ class Preprocessor {
reset();
}
+ void handlePeriod() {
+ if (isModuleCandidate())
+ Name += ".";
+ else if (!isNamedModule())
+ reset();
+ }
+
void handleSemi() {
if (!Name.empty() && isModuleCandidate()) {
if (State == InterfaceCandidate)
@@ -1791,6 +1802,7 @@ class Preprocessor {
std::optional<LexEmbedParametersResult> LexEmbedParameters(Token &Current,
bool ForHasEmbed);
bool LexModuleNameContinue(Token &Tok, SourceLocation UseLoc,
+ SmallVectorImpl<Token> &Suffix,
SmallVectorImpl<IdentifierLoc> &Path,
bool AllowMacroExpansion = true);
void EnterModuleSuffixTokenStream(ArrayRef<Token> Toks);
@@ -1814,7 +1826,10 @@ class Preprocessor {
}
bool LexAfterModuleImport(Token &Result);
- void CollectPpImportSuffix(SmallVectorImpl<Token> &Toks);
+ void CollectPPImportSuffix(SmallVectorImpl<Token> &Toks,
+ bool StopUntilEOD = false);
+ bool CollectPPImportSuffixAndEnterStream(SmallVectorImpl<Token> &Toks,
+ bool StopUntilEOD = false);
void makeModuleVisible(Module *M, SourceLocation Loc,
bool IncludeExports = true);
@@ -2430,20 +2445,27 @@ class Preprocessor {
/// If \p EnableMacros is true, then we consider macros that expand to zero
/// tokens as being ok.
///
+ /// If \p ExtraToks not null, the extra tokens will be saved in this
+ /// container.
+ ///
/// \return The location of the end of the directive (the terminating
/// newline).
- SourceLocation CheckEndOfDirective(StringRef DirType,
- bool EnableMacros = false);
+ SourceLocation
+ CheckEndOfDirective(StringRef DirType, bool EnableMacros = false,
+ SmallVectorImpl<Token> *ExtraToks = nullptr);
/// Read and discard all tokens remaining on the current line until
/// the tok::eod token is found. Returns the range of the skipped tokens.
- SourceRange DiscardUntilEndOfDirective() {
+ SourceRange
+ DiscardUntilEndOfDirective(SmallVectorImpl<Token> *DiscardedToks = nullptr) {
Token Tmp;
- return DiscardUntilEndOfDirective(Tmp);
+ return DiscardUntilEndOfDirective(Tmp, DiscardedToks);
}
/// Same as above except retains the token that was found.
- SourceRange DiscardUntilEndOfDirective(Token &Tok);
+ SourceRange
+ DiscardUntilEndOfDirective(Token &Tok,
+ SmallVectorImpl<Token> *DiscardedToks = nullptr);
/// Returns true if the preprocessor has seen a use of
/// __DATE__ or __TIME__ in the file so far.
@@ -3182,54 +3204,6 @@ struct EmbedAnnotationData {
StringRef FileName;
};
-/// Represents module name annotation data.
-///
-/// module-name:
-/// module-name-qualifier[opt] identifier
-///
-/// partition-name: [C++20]
-/// : module-name-qualifier[opt] identifier
-///
-/// module-name-qualifier
-/// module-name-qualifier[opt] identifier .
-class ModuleNameLoc final
- : llvm::TrailingObjects<ModuleNameLoc, IdentifierLoc> {
- friend TrailingObjects;
- unsigned NumIdentifierLocs;
-
- unsigned numTrailingObjects(OverloadToken<IdentifierLoc>) const {
- return getNumIdentifierLocs();
- }
-
- ModuleNameLoc(ModuleIdPath Path) : NumIdentifierLocs(Path.size()) {
- (void)llvm::copy(Path, getTrailingObjectsNonStrict<IdentifierLoc>());
- }
-
-public:
- static std::string stringFromModuleIdPath(ModuleIdPath Path);
- static ModuleNameLoc *Create(Preprocessor &PP, ModuleIdPath Path);
- static Token CreateAnnotToken(Preprocessor &PP, ModuleIdPath Path);
- unsigned getNumIdentifierLocs() const { return NumIdentifierLocs; }
- ModuleIdPath getModuleIdPath() const {
- return {getTrailingObjectsNonStrict<IdentifierLoc>(),
- getNumIdentifierLocs()};
- }
-
- SourceLocation getBeginLoc() const {
- return getModuleIdPath().front().getLoc();
- }
- SourceLocation getEndLoc() const {
- auto &Last = getModuleIdPath().back();
- return Last.getLoc().getLocWithOffset(
- Last.getIdentifierInfo()->getLength());
- }
- SourceRange getRange() const { return {getBeginLoc(), getEndLoc()}; }
-
- std::string str() const;
- void print(llvm::raw_ostream &OS) const;
- void dump() const { print(llvm::errs()); }
-};
-
/// Registry of pragma handlers added by plugins
using PragmaHandlerRegistry = llvm::Registry<PragmaHandler>;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index a50d35148869f..7ff72d1662c8c 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -566,10 +566,6 @@ class Parser : public CodeCompletionHandler {
/// Contextual keywords for Microsoft extensions.
IdentifierInfo *Ident__except;
- // C++2a contextual keywords.
- mutable IdentifierInfo *Ident_import;
- mutable IdentifierInfo *Ident_module;
-
std::unique_ptr<CommentHandler> CommentSemaHandler;
/// Gets set to true after calling ProduceSignatureHelp, it is for a
@@ -1081,6 +1077,9 @@ class Parser : public CodeCompletionHandler {
bool ParseModuleName(SourceLocation UseLoc,
SmallVectorImpl<IdentifierLoc> &Path, bool IsImport);
+ void DiagnoseInvalidCXXModuleDecl(const Sema::ModuleImportState &ImportState);
+ void DiagnoseInvalidCXXModuleImport();
+
//===--------------------------------------------------------------------===//
// Preprocessor code-completion pass-through
void CodeCompleteDirective(bool InConditional) override;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 03a5349b1c31f..12112eb1ad225 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -146,7 +146,6 @@ class MangleNumberingContext;
typedef ArrayRef<IdentifierLoc> ModuleIdPath;
class ModuleLoader;
class MultiLevelTemplateArgumentList;
-class ModuleNameLoc;
struct NormalizedConstraint;
class ObjCInterfaceDecl;
class ObjCMethodDecl;
diff --git a/clang/lib/Basic/TokenKinds.cpp b/clang/lib/Basic/TokenKinds.cpp
index c300175ce90ba..a5b8c998d9b8e 100644
--- a/clang/lib/Basic/TokenKinds.cpp
+++ b/clang/lib/Basic/TokenKinds.cpp
@@ -46,6 +46,18 @@ const char *tok::getKeywordSpelling(TokenKind Kind) {
return nullptr;
}
+const char *tok::getObjCKeywordSpelling(ObjCKeywordKind Kind) {
+ switch (Kind) {
+#define OBJC_AT_KEYWORD(X) \
+ case objc_##X: \
+ return "@" #X;
+#include "clang/Basic/TokenKinds.def"
+ default:
+ break;
+ }
+ return nullptr;
+}
+
const char *tok::getPPKeywordSpelling(tok::PPKeywordKind Kind) {
switch (Kind) {
#define PPKEYWORD(x) case tok::pp_##x: return #x;
diff --git a/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp
index 68f736ddffd67..9e046633328d7 100644
--- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp
+++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp
@@ -758,8 +758,7 @@ void PrintPPOutputPPCallbacks::HandleWhitespaceBeforeTok(const Token &Tok,
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.is(tok::annot_embed) &&
- !Tok.is(tok::annot_module_name)))
+ !Tok.is(tok::annot_repl_input_end) && !Tok.is(tok::annot_embed)))
return;
// EmittedDirectiveOnThisLine takes priority over RequireSameLine.
@@ -979,11 +978,6 @@ static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok,
*Callbacks->OS << static_cast<int>(Byte);
PrintComma = true;
}
- } else if (Tok.is(tok::annot_module_name)) {
- Tok.getAnnotationValueAs<ModuleNameLoc *>()->print(*Callbacks->OS);
- PP.Lex(Tok);
- IsStartOfLine = true;
- continue;
} else if (Tok.isAnnotation()) {
// Ignore annotation tokens created by pragmas - the pragmas themselves
// will be reproduced in the preprocessed output.
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 6a3c2f067c043..ab239fc01ac1a 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -82,14 +82,19 @@ Preprocessor::AllocateVisibilityMacroDirective(SourceLocation Loc,
/// Read and discard all tokens remaining on the current line until
/// the tok::eod token is found.
-SourceRange Preprocessor::DiscardUntilEndOfDirective(Token &Tmp) {
+SourceRange Preprocessor::DiscardUntilEndOfDirective(
+ Token &Tmp, SmallVectorImpl<Token> *DiscardedToks) {
SourceRange Res;
-
- LexUnexpandedToken(Tmp);
+ auto ReadNextTok = [&]() {
+ LexUnexpandedToken(Tmp);
+ if (DiscardedToks && Tmp.isNot(tok::eod))
+ DiscardedToks->push_back(Tmp);
+ };
+ ReadNextTok();
Res.setBegin(Tmp.getLocation());
while (Tmp.isNot(tok::eod)) {
assert(Tmp.isNot(tok::eof) && "EOF seen while discarding directive tokens");
- LexUnexpandedToken(Tmp);
+ ReadNextTok();
}
Res.setEnd(Tmp.getLocation());
return Res;
@@ -439,21 +444,27 @@ void Preprocessor::ReadMacroName(Token &MacroNameTok, MacroUse isDefineUndef,
/// true, then we consider macros that expand to zero tokens as being ok.
///
/// Returns the location of the end of the directive.
-SourceLocation Preprocessor::CheckEndOfDirective(StringRef DirType,
- bool EnableMacros) {
+SourceLocation
+Preprocessor::CheckEndOfDirective(StringRef DirType, bool EnableMacros,
+ SmallVectorImpl<Token> *ExtraToks) {
Token Tmp;
+ auto ReadNextTok = [this, ExtraToks, &Tmp](auto &&LexFn) {
+ std::invoke(LexFn, this, Tmp);
+ if (ExtraToks && Tmp.isNot(tok::eod))
+ ExtraToks->push_back(Tmp);
+ };
// Lex unexpanded tokens for most directives: macros might expand to zero
// tokens, causing us to miss diagnosing invalid lines. Some directives (like
// #line) allow empty macros.
if (EnableMacros)
- Lex(Tmp);
+ ReadNextTok(&Preprocessor::Lex);
else
- LexUnexpandedToken(Tmp);
+ ReadNextTok(&Preprocessor::LexUnexpandedToken);
// There should be no tokens after the directive, but we allow them as an
// extension.
while (Tmp.is(tok::comment)) // Skip comments in -C mode.
- LexUnexpandedToken(Tmp);
+ ReadNextTok(&Preprocessor::LexUnexpandedToken);
if (Tmp.is(tok::eod))
return Tmp.getLocation();
@@ -4104,9 +4115,11 @@ void Preprocessor::HandleCXXImportDirective(Token ImportTok) {
Lex(Tok);
[[fallthrough]];
case tok::identifier: {
- if (LexModuleNameContinue(Tok, UseLoc, Path)) {
+ if (LexModuleNameContinue(Tok, UseLoc, DirToks, Path)) {
if (Tok.isNot(tok::eod))
- CheckEndOfDirective(ImportTok.getIdentifierInfo()->getName());
+ CheckEndOfDirective(ImportTok.getIdentifierInfo()->getName(),
+ /*EnableMacros=*/false, &DirToks);
+ EnterModuleSuffixTokenStream(DirToks);
return;
}
@@ -4118,7 +4131,7 @@ void Preprocessor::HandleCXXImportDirective(Token ImportTok) {
FlatName += ":";
}
- FlatName += ModuleNameLoc::stringFromModuleIdPath(Path);
+ FlatName += ModuleLoader::getFlatNameFromPath(Path);
SourceLocation StartLoc = IsPartition ? UseLoc : Path[0].getLoc();
IdentifierLoc FlatNameLoc(StartLoc, getIdentifierInfo(FlatName));
@@ -4128,42 +4141,37 @@ void Preprocessor::HandleCXXImportDirective(Token ImportTok) {
ModuleIdPath(FlatNameLoc),
/*Imported=*/nullptr);
}
- DirToks.push_back(ModuleNameLoc::CreateAnnotToken(*this, Path));
- DirToks.push_back(Tok);
break;
}
default:
- Diag(ImportTok, diag::err_pp_expected_module_name_or_header_name);
+ DirToks.push_back(Tok);
break;
}
// 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);
-
- // This is not a pp-import after all.
- if (DirToks.back().isNot(tok::semi)) {
- Diag(DirToks.back(), diag::err_pp_expected_semi_after_module_or_import)
- << /*IsImport*/ true
- << FixItHint::CreateInsertion(DirToks.back().getLocation(),
- tok::getPunctuatorSpelling(tok::semi));
- return;
- }
+ CollectPPImportSuffix(DirToks);
if (DirToks.back().isNot(tok::eod))
CheckEndOfDirective(ImportTok.getIdentifierInfo()->getName());
else
DirToks.pop_back();
- // C++2a [cpp.module]p1:
- // The ';' preprocessing-token terminating a pp-import shall not have
- // been produced by macro replacement.
- SourceLocation SemiLoc = DirToks.back().getLocation();
- if (SemiLoc.isMacroID())
- Diag(SemiLoc, diag::err_header_import_semi_in_macro);
+ // This is not a pp-import after all.
+ if (DirToks.back().isNot(tok::semi)) {
+ EnterModuleSuffixTokenStream(DirToks);
+ return;
+ }
if (ImportingHeader) {
+ // C++2a [cpp.module]p1:
+ // The ';' preprocessing-token terminating a pp-import shall not have
+ // been produced by macro replacement.
+ SourceLocation SemiLoc = DirToks.back().getLocation();
+ if (SemiLoc.isMacroID())
+ Diag(SemiLoc, diag::err_header_import_semi_in_macro);
+
auto Action = HandleHeaderIncludeOrImport(
/*HashLoc*/ SourceLocation(), ImportTok, Tok, SemiLoc);
switch (Action.Kind) {
@@ -4239,11 +4247,10 @@ void Preprocessor::HandleCXXModuleDirective(Token ModuleTok) {
DirToks.push_back(Tok);
LexUnexpandedToken(Tok);
if (Tok.isNot(tok::kw_private)) {
- Diag(DirToks.back().getLocation(), diag::err_pp_module_expected_ident)
- << /*IsImport=*/false
- << FixItHint::CreateReplacement(
- {Tok.getLocation(), Tok.getEndLoc()},
- tok::getKeywordSpelling(tok::kw_private));
+ if (Tok.isNot(tok::eod))
+ CheckEndOfDirective(ModuleTok.getIdentifierInfo()->getName(),
+ /*EnableMacros=*/false, &DirToks);
+ EnterModuleSuffixTokenStream(DirToks);
return;
}
DirToks.push_back(Tok);
@@ -4254,57 +4261,46 @@ void Preprocessor::HandleCXXModuleDirective(Token ModuleTok) {
// normal text.
//
// P3034R1 Module Declarations Shouldn’t be Macros.
- if (LexModuleNameContinue(Tok, UseLoc, Path,
+ if (LexModuleNameContinue(Tok, UseLoc, DirToks, Path,
/*AllowMacroExpansion=*/false)) {
if (Tok.isNot(tok::eod))
- CheckEndOfDirective(ModuleTok.getIdentifierInfo()->getName());
+ CheckEndOfDirective(ModuleTok.getIdentifierInfo()->getName(),
+ /*EnableMacros=*/false, &DirToks);
+ EnterModuleSuffixTokenStream(DirToks);
return;
}
- DirToks.push_back(ModuleNameLoc::CreateAnnotToken(*this, Path));
-
// 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)) {
- DirToks.push_back(Tok);
LexUnexpandedToken(Tok);
- if (LexModuleNameContinue(Tok, UseLoc, Partition)) {
+ if (LexModuleNameContinue(Tok, UseLoc, DirToks, Partition)) {
if (Tok.isNot(tok::eod))
CheckEndOfDirective(ModuleTok.getIdentifierInfo()->getName());
+ EnterModuleSuffixTokenStream(DirToks);
return;
}
- DirToks.push_back(ModuleNameLoc::CreateAnnotToken(*this, Partition));
}
- DirToks.push_back(Tok);
break;
}
default:
+ DirToks.push_back(Tok);
break;
- ;
}
// Consume the pp-import-suffix and expand any macros in it now, if we're not
// at the semicolon already.
SourceLocation End = DirToks.back().getLocation();
if (!DirToks.back().isOneOf(tok::semi, tok::eod)) {
- CollectPpImportSuffix(DirToks);
+ CollectPPImportSuffix(DirToks);
End = DirToks.back().getLocation();
}
- // This is not a pp-import after all.
- if (DirToks.back().isNot(tok::semi)) {
- Diag(DirToks.back(), diag::err_pp_expected_semi_after_module_or_import)
- << /*IsImport*/ false
- << FixItHint::CreateInsertion(DirToks.back().getLocation(),
- tok::getPunctuatorSpelling(tok::semi));
- return;
- }
-
if (DirToks.back().isNot(tok::eod))
- End = CheckEndOfDirective(ModuleTok.getIdentifierInfo()->getName());
+ End = CheckEndOfDirective(ModuleTok.getIdentifierInfo()->getName(),
+ /*EnableMacros=*/false, &DirToks);
else
End = DirToks.pop_back_val().getLocation();
-
EnterModuleSuffixTokenStream(DirToks);
}
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index b04018bfcae6f..95eaa633d6d9f 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -891,13 +891,6 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
return true;
}
-void Preprocessor::ModuleDeclSeq::handleModuleName(ModuleNameLoc *Path) {
- if (isModuleCandidate() && Path)
- Name += Path->str();
- else if (!isNamedModule())
- reset();
-}
-
void Preprocessor::Lex(Token &Result) {
++LexLevel;
@@ -961,6 +954,9 @@ void Preprocessor::Lex(Token &Result) {
case tok::colon:
ModuleDeclState.handleColon();
break;
+ case tok::period:
+ ModuleDeclState.handlePeriod();
+ break;
case tok::kw_import:
if (StdCXXImportSeqState.atTopLevel()) {
TrackGMFState.handleImport(StdCXXImportSeqState.afterTopLevelSeq());
@@ -975,9 +971,8 @@ void Preprocessor::Lex(Token &Result) {
ModuleDeclState.handleModule();
}
break;
- case tok::annot_module_name:
- ModuleDeclState.handleModuleName(
- Result.getAnnotationValueAs<ModuleNameLoc *>());
+ case tok::identifier:
+ ModuleDeclState.handleIdentifier(Result.getIdentifierInfo());
if (ModuleDeclState.isModuleCandidate())
break;
[[fallthrough]];
@@ -1120,28 +1115,11 @@ bool Preprocessor::LexHeaderName(Token &FilenameTok, bool AllowMacroExpansion) {
return false;
}
-ModuleNameLoc *ModuleNameLoc::Create(Preprocessor &PP, ModuleIdPath Path) {
- assert(!Path.empty() && "expect at least one identifier in a module name");
- void *Mem = PP.getPreprocessorAllocator().Allocate(
- totalSizeToAlloc<IdentifierLoc>(Path.size()), alignof(ModuleNameLoc));
- return new (Mem) ModuleNameLoc(Path);
-}
-
-Token ModuleNameLoc::CreateAnnotToken(Preprocessor &PP, ModuleIdPath Path) {
- auto *NameLoc = Create(PP, Path);
- Token ModuleNameTok;
- ModuleNameTok.startToken();
- ModuleNameTok.setKind(tok::annot_module_name);
- ModuleNameTok.setAnnotationRange(NameLoc->getRange());
- ModuleNameTok.setAnnotationValue(static_cast<void *>(NameLoc));
- return ModuleNameTok;
-}
-
// We represent the primary and partition names as 'Paths' which are sections
// of the hierarchical access path for a clang module. However for C++20
// the periods in a name are just another character, and we will need to
// flatten them into a string.
-std::string ModuleNameLoc::stringFromModuleIdPath(ModuleIdPath Path) {
+std::string ModuleLoader::getFlatNameFromPath(ModuleIdPath Path) {
std::string Name;
if (Path.empty())
return Name;
@@ -1155,31 +1133,22 @@ std::string ModuleNameLoc::stringFromModuleIdPath(ModuleIdPath Path) {
return Name;
}
-std::string ModuleNameLoc::str() const {
- return stringFromModuleIdPath(getModuleIdPath());
-}
-
-void ModuleNameLoc::print(llvm::raw_ostream &OS) const { OS << str(); }
-
bool Preprocessor::LexModuleNameContinue(Token &Tok, SourceLocation UseLoc,
+ SmallVectorImpl<Token> &Suffix,
SmallVectorImpl<IdentifierLoc> &Path,
bool AllowMacroExpansion) {
auto ConsumeToken = [&]() {
- return AllowMacroExpansion ? Lex(Tok) : LexUnexpandedToken(Tok);
+ if (AllowMacroExpansion)
+ Lex(Tok);
+ else
+ LexUnexpandedToken(Tok);
+ Suffix.push_back(Tok);
};
+ Suffix.push_back(Tok);
while (true) {
- if (Tok.isNot(tok::identifier)) {
- if (Tok.is(tok::code_completion)) {
- CurLexer->cutOffLexing();
- Tok.setKind(tok::eof);
- this->getCodeCompletionHandler()->CodeCompleteModuleImport(UseLoc,
- Path);
- }
- Diag(Tok.getLocation(), diag::err_pp_expected_module_name)
- << Path.empty();
+ if (Tok.isNot(tok::identifier))
return true;
- }
// Record this part of the module path.
Path.emplace_back(Tok.getLocation(), Tok.getIdentifierInfo());
@@ -1255,8 +1224,16 @@ bool Preprocessor::HandleModuleContextualKeyword(
return false;
}
+bool Preprocessor::CollectPPImportSuffixAndEnterStream(
+ SmallVectorImpl<Token> &Toks, bool StopUntilEOD) {
+ CollectPPImportSuffix(Toks);
+ EnterModuleSuffixTokenStream(Toks);
+ return false;
+}
+
/// Collect the tokens of a C++20 pp-import-suffix.
-void Preprocessor::CollectPpImportSuffix(SmallVectorImpl<Token> &Toks) {
+void Preprocessor::CollectPPImportSuffix(SmallVectorImpl<Token> &Toks,
+ bool StopUntilEOD) {
// FIXME: For error recovery, consider recognizing attribute syntax here
// and terminating / diagnosing a missing semicolon if we find anything
// else? (Can we leave that to the parser?)
@@ -1266,6 +1243,9 @@ void Preprocessor::CollectPpImportSuffix(SmallVectorImpl<Token> &Toks) {
switch (Toks.back().getKind()) {
case tok::semi:
+ if (!StopUntilEOD)
+ return;
+ [[fallthrough]];
case tok::eod:
case tok::eof:
return;
@@ -1312,18 +1292,15 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
SmallVector<Token, 32> Suffix;
SmallVector<IdentifierLoc, 2> Path;
Lex(Result);
- if (LexModuleNameContinue(Result, ModuleImportLoc, Path))
- return false;
-
- Suffix.push_back(ModuleNameLoc::CreateAnnotToken(*this, Path));
- Suffix.push_back(Result);
+ if (LexModuleNameContinue(Result, ModuleImportLoc, Suffix, Path))
+ return CollectPPImportSuffixAndEnterStream(Suffix);
// Consume the pp-import-suffix and expand any macros in it now, if we're not
// at the semicolon already.
SourceLocation SemiLoc = Suffix.back().getLocation();
if (Suffix.back().isNot(tok::semi)) {
if (Result.isNot(tok::eof))
- CollectPpImportSuffix(Suffix);
+ CollectPPImportSuffix(Suffix);
if (Suffix.back().isNot(tok::semi)) {
// This is not an import after all.
EnterModuleSuffixTokenStream(Suffix);
diff --git a/clang/lib/Lex/TokenConcatenation.cpp b/clang/lib/Lex/TokenConcatenation.cpp
index f94caee24dc11..05f4203bd722b 100644
--- a/clang/lib/Lex/TokenConcatenation.cpp
+++ b/clang/lib/Lex/TokenConcatenation.cpp
@@ -161,8 +161,7 @@ bool TokenConcatenation::AvoidConcat(const Token &PrevPrevTok,
const Token &PrevTok,
const Token &Tok) const {
// No space is required between header unit name in quote and semi.
- if (PrevTok.isOneOf(tok::annot_header_unit, tok::annot_module_name) &&
- Tok.is(tok::semi))
+ if (PrevTok.is(tok::annot_header_unit) && Tok.is(tok::semi))
return false;
// Conservatively assume that every annotation token that has a printable
@@ -198,12 +197,11 @@ bool TokenConcatenation::AvoidConcat(const Token &PrevPrevTok,
if (Tok.isAnnotation()) {
// Modules annotation can show up when generated automatically for includes.
assert(Tok.isOneOf(tok::annot_module_include, tok::annot_module_begin,
- tok::annot_module_end, tok::annot_embed,
- tok::annot_module_name) &&
+ tok::annot_module_end, tok::annot_embed) &&
"unexpected annotation in AvoidConcat");
ConcatInfo = 0;
- if (Tok.isOneOf(tok::annot_embed, tok::annot_module_name))
+ if (Tok.is(tok::annot_embed))
return true;
}
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 9595cccd93a89..0156967186931 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -17,6 +17,7 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/DiagnosticParse.h"
#include "clang/Basic/StackExhaustionHandler.h"
+#include "clang/Basic/TokenKinds.h"
#include "clang/Lex/ModuleLoader.h"
#include "clang/Parse/RAIIObjectsForParser.h"
#include "clang/Sema/DeclSpec.h"
@@ -25,6 +26,7 @@
#include "clang/Sema/Scope.h"
#include "clang/Sema/SemaCodeCompletion.h"
#include "llvm/ADT/STLForwardCompat.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TimeProfiler.h"
using namespace clang;
@@ -622,7 +624,25 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result,
goto module_decl;
case tok::kw_import:
goto import_decl;
-
+ // Error recovery and recognize context-sensitive C++20 'export module' and
+ // 'export import' declarations. If the module/import directive is
+ // well-formed, it should be converted to a keyword in preprocessor, but not
+ // an identifier we saw here.
+ //
+ // FIXME: We should generate better diagnostic information here to explain
+ // why the module/import directive is ill-formed.
+ case tok::identifier: {
+ if (getLangOpts().CPlusPlusModules &&
+ NextToken().isModuleContextualKeyword() &&
+ GetLookAheadToken(2).isNot(tok::coloncolon)) {
+ if (NextToken().getIdentifierInfo()->isStr(
+ tok::getKeywordSpelling(tok::kw_module)))
+ goto module_decl;
+ else
+ goto import_decl;
+ }
+ break;
+ }
default:
break;
}
@@ -690,7 +710,25 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result,
Actions.ActOnEndOfTranslationUnit();
//else don't tell Sema that we ended parsing: more input might come.
return true;
-
+ case tok::identifier:
+ // C++20 [basic.link]p3:
+ // A token sequence beginning with 'export[opt] module' or
+ // 'export[opt] import' and not immediately followed by '::'
+ // is never interpreted as the declaration of a top-level-declaration.
+ //
+ // Error recovery and recognize context-sensitive C++20 'export module' and
+ // 'export import' declarations. If the module/import directive is
+ // well-formed, it should be converted to a keyword in preprocessor, but not
+ // an identifier we saw here.
+ if (getLangOpts().CPlusPlusModules && Tok.isModuleContextualKeyword() &&
+ NextToken().isNot(tok::coloncolon)) {
+ if (Tok.getIdentifierInfo()->isStr(
+ tok::getKeywordSpelling(tok::kw_module)))
+ goto module_decl;
+ else
+ goto import_decl;
+ }
+ break;
default:
break;
}
@@ -883,7 +921,8 @@ Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
case tok::kw_import: {
Sema::ModuleImportState IS = Sema::ModuleImportState::NotACXX20Module;
if (getLangOpts().CPlusPlusModules) {
- Diag(Tok, diag::err_unexpected_module_import_decl) << /*IsImport*/ true;
+ Diag(Tok, diag::err_unexpected_module_or_import_decl)
+ << /*IsImport*/ true;
SkipUntil(tok::semi);
return nullptr;
}
@@ -977,7 +1016,7 @@ Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
return nullptr;
case tok::kw_module:
- Diag(Tok, diag::err_unexpected_module_import_decl) << /*IsImport*/ false;
+ Diag(Tok, diag::err_unexpected_module_or_import_decl) << /*IsImport*/ false;
SkipUntil(tok::semi);
return nullptr;
@@ -2319,10 +2358,16 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
? Sema::ModuleDeclKind::Interface
: Sema::ModuleDeclKind::Implementation;
- assert(
- (Tok.is(tok::kw_module) ||
- (Tok.is(tok::identifier) && Tok.getIdentifierInfo() == Ident_module)) &&
- "not a module declaration");
+ assert((Tok.is(tok::kw_module) ||
+ (Tok.is(tok::identifier) &&
+ Tok.getIdentifierInfo()->isStr(
+ tok::getKeywordSpelling(tok::kw_module)))) &&
+ "not a module declaration");
+
+ if (getLangOpts().CPlusPlusModules && Tok.is(tok::identifier))
+ Diag(StartLoc, diag::err_invalid_module_or_import_directive)
+ << /*IsImport=*/false;
+
SourceLocation ModuleLoc = ConsumeToken();
// Attributes appear after the module name, not before.
@@ -2363,20 +2408,20 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
return Actions.ActOnPrivateModuleFragmentDecl(ModuleLoc, PrivateLoc);
}
- ModuleNameLoc *Path = nullptr;
- if (Tok.isNot(tok::annot_module_name))
+ SmallVector<IdentifierLoc, 2> Path;
+ if (ParseModuleName(ModuleLoc, Path, /*IsImport*/ false))
return nullptr;
- Path = Tok.getAnnotationValueAs<ModuleNameLoc *>();
- ConsumeAnnotationToken();
// Parse the optional module-partition.
- ModuleNameLoc *Partition = nullptr;
+ SmallVector<IdentifierLoc, 2> Partition;
if (Tok.is(tok::colon)) {
- ConsumeToken();
- if (Tok.isNot(tok::annot_module_name))
+ SourceLocation ColonLoc = ConsumeToken();
+ if (!getLangOpts().CPlusPlusModules)
+ Diag(ColonLoc, diag::err_unsupported_module_partition)
+ << SourceRange(ColonLoc, Partition.back().getLoc());
+ // Recover by ignoring the partition name.
+ else if (ParseModuleName(ModuleLoc, Partition, /*IsImport*/ false))
return nullptr;
- Partition = Tok.getAnnotationValueAs<ModuleNameLoc *>();
- ConsumeAnnotationToken();
}
// We don't support any module attributes yet; just parse them and diagnose.
@@ -2387,12 +2432,12 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
/*DiagnoseEmptyAttrs=*/false,
/*WarnOnUnknownAttrs=*/true);
- ExpectAndConsumeSemi(diag::err_module_expected_semi);
+ ExpectAndConsumeSemi(diag::err_expected_semi_after_module_or_import,
+ tok::getKeywordSpelling(tok::kw_module));
- return Actions.ActOnModuleDecl(
- StartLoc, ModuleLoc, MDK, Path->getModuleIdPath(),
- Partition ? Partition->getModuleIdPath() : ModuleIdPath{}, ImportState,
- Introducer.hasSeenNoTrivialPPDirective());
+ return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, Partition,
+ ImportState,
+ Introducer.hasSeenNoTrivialPPDirective());
}
Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
@@ -2406,10 +2451,16 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
: Tok.isObjCAtKeyword(tok::objc_import)) &&
"Improper start to module import");
bool IsObjCAtImport = Tok.isObjCAtKeyword(tok::objc_import);
+ if (getLangOpts().CPlusPlusModules && !IsObjCAtImport &&
+ Tok.is(tok::identifier)) {
+ Diag(StartLoc, diag::err_invalid_module_or_import_directive)
+ << /*IsImport=*/true;
+ }
+
SourceLocation ImportLoc = ConsumeToken();
// For C++20 modules, we can have "name" or ":Partition name" as valid input.
- ModuleNameLoc *Path = nullptr;
+ SmallVector<IdentifierLoc, 2> Path;
bool IsPartition = false;
Module *HeaderUnit = nullptr;
if (Tok.is(tok::header_name)) {
@@ -2422,17 +2473,18 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
HeaderUnit = reinterpret_cast<Module *>(Tok.getAnnotationValue());
ConsumeAnnotationToken();
} else if (Tok.is(tok::colon)) {
- ConsumeToken();
- if (Tok.isNot(tok::annot_module_name))
+ SourceLocation ColonLoc = ConsumeToken();
+ if (!getLangOpts().CPlusPlusModules)
+ Diag(ColonLoc, diag::err_unsupported_module_partition)
+ << SourceRange(ColonLoc, Path.back().getLoc());
+ // Recover by leaving partition empty.
+ else if (ParseModuleName(ColonLoc, Path, /*IsImport=*/true))
return nullptr;
- IsPartition = true;
- Path = Tok.getAnnotationValueAs<ModuleNameLoc *>();
- ConsumeAnnotationToken();
+ else
+ IsPartition = true;
} else {
- if (Tok.isNot(tok::annot_module_name))
+ if (ParseModuleName(ImportLoc, Path, /*IsImport=*/true))
return nullptr;
- Path = Tok.getAnnotationValueAs<ModuleNameLoc *>();
- ConsumeAnnotationToken();
}
ParsedAttributes Attrs(AttrFactory);
@@ -2491,7 +2543,12 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
SeenError = false;
break;
}
- ExpectAndConsumeSemi(diag::err_module_expected_semi);
+
+ if (getLangOpts().CPlusPlusModules)
+ ExpectAndConsumeSemi(diag::err_expected_semi_after_module_or_import,
+ tok::getKeywordSpelling(tok::kw_import));
+ else
+ ExpectAndConsumeSemi(diag::err_module_expected_semi);
if (SeenError)
return nullptr;
@@ -2500,9 +2557,9 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
if (HeaderUnit)
Import =
Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, HeaderUnit);
- else if (Path)
- Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc,
- Path->getModuleIdPath(), IsPartition);
+ else if (!Path.empty())
+ Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Path,
+ IsPartition);
if (Import.isInvalid())
return nullptr;
@@ -2531,7 +2588,7 @@ bool Parser::ParseModuleName(SourceLocation UseLoc,
return true;
}
- Diag(Tok, diag::err_module_expected_ident) << IsImport;
+ Diag(Tok, diag::err_module_expected_ident) << Path.empty();
SkipUntil(tok::semi);
return true;
}
diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp
index e06371ed56300..1940f3eb6e746 100644
--- a/clang/lib/Sema/SemaModule.cpp
+++ b/clang/lib/Sema/SemaModule.cpp
@@ -357,10 +357,10 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
// Flatten the dots in a module name. Unlike Clang's hierarchical module map
// modules, the dots here are just another character that can appear in a
// module name.
- std::string ModuleName = ModuleNameLoc::stringFromModuleIdPath(Path);
+ std::string ModuleName = ModuleLoader::getFlatNameFromPath(Path);
if (IsPartition) {
ModuleName += ":";
- ModuleName += ModuleNameLoc::stringFromModuleIdPath(Partition);
+ ModuleName += ModuleLoader::getFlatNameFromPath(Partition);
}
// If a module name was explicitly specified on the command line, it must be
// correct.
@@ -581,12 +581,12 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
// otherwise, the name of the importing named module.
ModuleName = NamedMod->getPrimaryModuleInterfaceName().str();
ModuleName += ":";
- ModuleName += ModuleNameLoc::stringFromModuleIdPath(Path);
+ ModuleName += ModuleLoader::getFlatNameFromPath(Path);
ModuleNameLoc =
IdentifierLoc(Path[0].getLoc(), PP.getIdentifierInfo(ModuleName));
Path = ModuleIdPath(ModuleNameLoc);
} else if (getLangOpts().CPlusPlusModules) {
- ModuleName = ModuleNameLoc::stringFromModuleIdPath(Path);
+ ModuleName = ModuleLoader::getFlatNameFromPath(Path);
ModuleNameLoc =
IdentifierLoc(Path[0].getLoc(), PP.getIdentifierInfo(ModuleName));
Path = ModuleIdPath(ModuleNameLoc);
diff --git a/clang/test/CXX/basic/basic.link/p3.cpp b/clang/test/CXX/basic/basic.link/p3.cpp
index 5661738455a62..f389b07d39883 100644
--- a/clang/test/CXX/basic/basic.link/p3.cpp
+++ b/clang/test/CXX/basic/basic.link/p3.cpp
@@ -45,7 +45,7 @@ constexpr int n = 123;
export module m; // #1
-import x = {}; // expected-error {{expected ';' after module name}}
+import x = {}; // expected-error {{'import' directive must end with a ';' on the same line}}
// expected-error at -1 {{module 'x' not found}}
//--- ImportError2.cpp
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 4bdcc9e5f278e..10254cd064de0 100644
--- a/clang/test/CXX/module/basic/basic.link/module-declaration.cpp
+++ b/clang/test/CXX/module/basic/basic.link/module-declaration.cpp
@@ -46,7 +46,7 @@ export module z;
export module x;
//--- invalid_module_name.cppm
-export module z elderberry; // expected-error {{expected ';'}} \
+export module z elderberry; // expected-error {{'module' directive must end with a ';' on the same line}} \
// expected-error {{a type specifier is required}}
//--- empty_attribute.cppm
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 53fd3ea29eccb..c4842d2642f41 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
@@ -45,8 +45,7 @@ import x [[blarg::noreturn]]; // expected-warning-re {{unknown attribute 'blarg:
import x.y;
import x.; // expected-error {{expected identifier after '.' in module name}}
-import .x; // expected-error {{unknown type name 'import'}} \
- // expected-error {{cannot use dot operator on a type}}
+import .x; // expected-error-re {{the import directive is ill-formed{{.*}}}} expected-error {{expected module name}}
import blarg; // expected-error {{module 'blarg' not found}}
@@ -64,8 +63,7 @@ import x [[blarg::noreturn]]; // expected-warning-re {{unknown attribute 'blarg:
import x.y;
import x.; // expected-error {{expected identifier after '.' in module name}}
-import .x; // expected-error {{unknown type name 'import'}} \
- // expected-error {{cannot use dot operator on a type}}
+import .x; // expected-error-re {{the import directive is ill-formed{{.*}}}} expected-error {{expected module name}}
import blarg; // expected-error {{module 'blarg' not found}}
diff --git a/clang/test/Modules/pr121066.cpp b/clang/test/Modules/pr121066.cpp
index 849488e938d50..33bedd22ac487 100644
--- a/clang/test/Modules/pr121066.cpp
+++ b/clang/test/Modules/pr121066.cpp
@@ -1,3 +1,6 @@
// RUN: %clang_cc1 -std=c++20 -fsyntax-only %s -verify
+// This import directive is ill-formed, it's missing an ';' after
+// module name, but we try to recovery from error and import the module.
import mod // expected-error {{'import' directive must end with a ';' on the same line}}
+ // expected-error at -1 {{module 'mod' not found}}
>From 14ba684320993c3c7895ef6454794ac4b24aa63d Mon Sep 17 00:00:00 2001
From: "Wang, Yihan" <yronglin777 at gmail.com>
Date: Sun, 31 Aug 2025 16:00:29 +0800
Subject: [PATCH 20/22] Add test for pp-module and pp-import
Signed-off-by: Wang, Yihan <yronglin777 at gmail.com>
---
.../clang/Basic/DiagnosticParseKinds.td | 10 ++-
clang/test/CXX/module/cpp.pre/p1.cpp | 75 +++++++++++++++++++
2 files changed, 81 insertions(+), 4 deletions(-)
create mode 100644 clang/test/CXX/module/cpp.pre/p1.cpp
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index d579fbdc80828..529e948912330 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1777,12 +1777,14 @@ def ext_bit_int : Extension<
let CategoryName = "Modules Issue" in {
def err_invalid_module_or_import_directive : Error<
"the %select{module|import}0 directive is ill-formed, "
- "%select{module contextual keyword must be followed by an identifier "
+ "%select{module contextual keyword must be immediately "
+ "followed on the same line by an identifier, "
"or a ';' after being at the start of a line, or preceded by "
"an export keyword at the start of a line|"
- "import contextual keyword must be followed by an identifier, "
- "'<', '\"', or ':', but not '::', after being at the start of a "
- "line or preceded by an export at the start of the line}0">;
+ "import contextual keyword must be immediately followed "
+ "on the same line by an identifier, '<', '\"', or ':', but not '::', "
+ "after being at the start of a line or preceded by an export at "
+ "the start of the line}0">;
def err_unexpected_module_or_import_decl : Error<
"%select{module|import}0 declaration can only appear at the top level">;
def err_module_expected_ident : Error<
diff --git a/clang/test/CXX/module/cpp.pre/p1.cpp b/clang/test/CXX/module/cpp.pre/p1.cpp
new file mode 100644
index 0000000000000..a3928f3753e16
--- /dev/null
+++ b/clang/test/CXX/module/cpp.pre/p1.cpp
@@ -0,0 +1,75 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++20 %t/hash.cpp -fsyntax-only -verify
+// RUN: %clang_cc1 -std=c++20 %t/module.cpp -fsyntax-only -verify
+
+// RUN: %clang_cc1 -std=c++20 %t/rightpad.cppm -emit-module-interface -o %t/rightpad.pcm
+// RUN: %clang_cc1 -std=c++20 %t/M_part.cppm -emit-module-interface -o %t/M_part.pcm
+// RUN: %clang_cc1 -std=c++20 -xc++-system-header %t/string -emit-header-unit -o %t/string.pcm
+// RUN: %clang_cc1 -std=c++20 -xc++-user-header %t/squee -emit-header-unit -o %t/squee.pcm
+// RUN: %clang_cc1 -std=c++20 %t/import.cpp -isystem %t \
+// RUN: -fmodule-file=rightpad=%t/rightpad.pcm \
+// RUN: -fmodule-file=M:part=%t/M_part.pcm \
+// RUN: -fmodule-file=%t/string.pcm \
+// RUN: -fmodule-file=%t/squee.pcm \
+// RUN: -fsyntax-only -verify
+
+// RUN: %clang_cc1 -std=c++20 %t/module_decl_not_in_same_line.cpp -fsyntax-only -verify
+// RUN: %clang_cc1 -std=c++20 %t/foo.cppm -emit-module-interface -o %t/foo.pcm
+// RUN: %clang_cc1 -std=c++20 %t/import_decl_not_in_same_line.cpp -fmodule-file=foo=%t/foo.pcm -fsyntax-only -verify
+// RUN: %clang_cc1 -std=c++20 %t/not_import.cpp -fsyntax-only -verify
+
+//--- hash.cpp
+// expected-no-diagnostics
+# // preprocessing directive
+
+//--- module.cpp
+// expected-no-diagnostics
+module ; // preprocessing directive
+export module leftpad; // preprocessing directive
+
+//--- string
+#ifndef STRING_H
+#define STRING_H
+#endif // STRING_H
+
+//--- squee
+#ifndef SQUEE_H
+#define SQUEE_H
+#endif
+
+//--- rightpad.cppm
+export module rightpad;
+
+//--- M_part.cppm
+export module M:part;
+
+//--- import.cpp
+export module M;
+import <string>; // expected-warning {{the implementation of header units is in an experimental phase}}
+export import "squee"; // expected-warning {{the implementation of header units is in an experimental phase}}
+import rightpad; // preprocessing directive
+import :part; // preprocessing directive
+
+//--- module_decl_not_in_same_line.cpp
+module // expected-error {{the module directive is ill-formed, module contextual keyword must be immediately followed on the same line by an identifier, or a ';' after being at the start of a line, or preceded by an export keyword at the start of a line}}
+;export module M; // expected-error {{the module directive is ill-formed, module contextual keyword must be immediately followed on the same line by an identifier, or a ';' after being at the start of a line, or preceded by an export keyword at the start of a line}}
+
+//--- foo.cppm
+export module foo;
+
+//--- import_decl_not_in_same_line.cpp
+export module M;
+export // expected-error {{the import directive is ill-formed, import contextual keyword must be immediately followed on the same line by an identifier, '<', '"', or ':', but not '::', after being at the start of a line or preceded by an export at the start of the line}}
+import
+foo;
+
+export // expected-error {{the import directive is ill-formed, import contextual keyword must be immediately followed on the same line by an identifier, '<', '"', or ':', but not '::', after being at the start of a line or preceded by an export at the start of the line}}
+import foo;
+
+//--- not_import.cpp
+export module M;
+import :: // expected-error {{use of undeclared identifier 'import'}}
+import -> // expected-error {{cannot use arrow operator on a type}}
>From 5dc3c9a9735568df3e990695412a26a725a2e3ac Mon Sep 17 00:00:00 2001
From: "Wang, Yihan" <yronglin777 at gmail.com>
Date: Tue, 2 Sep 2025 01:16:17 +0800
Subject: [PATCH 21/22] Refine isModuleContextualKeyword and
isImportingCXXNamedModules, remove unecessary changes
Signed-off-by: Wang, Yihan <yronglin777 at gmail.com>
---
.../AnnotateFunctions/AnnotateFunctions.cpp | 2 +-
clang/include/clang/Lex/Preprocessor.h | 10 ++--
clang/include/clang/Lex/Token.h | 6 +--
clang/lib/Basic/IdentifierTable.cpp | 2 +-
clang/lib/Lex/Lexer.cpp | 13 +++--
clang/lib/Lex/PPDirectives.cpp | 49 ++++++++++---------
clang/lib/Lex/Preprocessor.cpp | 11 ++---
clang/lib/Lex/TokenLexer.cpp | 2 +-
clang/lib/Parse/Parser.cpp | 6 +--
clang/lib/Sema/SemaModule.cpp | 2 -
.../DependencyScanning/ModuleDepCollector.cpp | 3 +-
11 files changed, 55 insertions(+), 51 deletions(-)
diff --git a/clang/examples/AnnotateFunctions/AnnotateFunctions.cpp b/clang/examples/AnnotateFunctions/AnnotateFunctions.cpp
index 22a3eb97f938b..d872020c2d8a3 100644
--- a/clang/examples/AnnotateFunctions/AnnotateFunctions.cpp
+++ b/clang/examples/AnnotateFunctions/AnnotateFunctions.cpp
@@ -65,7 +65,7 @@ class PragmaAnnotateHandler : public PragmaHandler {
Token Tok;
PP.LexUnexpandedToken(Tok);
if (Tok.isNot(tok::eod))
- PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "#pragma";
+ PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
if (HandledDecl) {
DiagnosticsEngine &D = PP.getDiagnostics();
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 47c62ae9c36d5..362a5c429a26a 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -1812,7 +1812,7 @@ class Preprocessor {
/// Callback invoked when the lexer sees one of export, import or module token
/// at the start of a line.
///
- /// This consumes the import, module directive, modifies the
+ /// This consumes the import/module directive, modifies the
/// lexer/preprocessor state, and advances the lexer(s) so that the next token
/// read is the correct one.
bool HandleModuleContextualKeyword(Token &Result,
@@ -2537,11 +2537,15 @@ class Preprocessor {
/// If we're importing a standard C++20 Named Modules.
bool isImportingCXXNamedModules() const {
- return getLangOpts().CPlusPlusModules && ImportingCXXNamedModules;
+ assert(getLangOpts().CPlusPlusModules &&
+ "Import C++ named modules are only valid for C++20 modules");
+ return ImportingCXXNamedModules;
}
bool isDeclaringCXXNamedModules() const {
- return getLangOpts().CPlusPlusModules && DeclaringCXXNamedModules;
+ assert(getLangOpts().CPlusPlusModules &&
+ "Declare C++ named modules are only valid for C++20 modules");
+ return DeclaringCXXNamedModules;
}
/// Allocate a new MacroInfo object with the provided SourceLocation.
diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h
index e50b5438042c7..ae9ab99cf00b1 100644
--- a/clang/include/clang/Lex/Token.h
+++ b/clang/include/clang/Lex/Token.h
@@ -233,9 +233,6 @@ class Token {
PtrData = const_cast<char*>(Ptr);
}
- template <class T> T getAnnotationValueAs() const {
- return static_cast<T>(getAnnotationValue());
- }
void *getAnnotationValue() const {
assert(isAnnotation() && "Used AnnotVal on non-annotation token");
return PtrData;
@@ -296,7 +293,8 @@ class Token {
/// Return true if we have an C++20 Modules contextual keyword(export, import
/// or module).
- bool isModuleContextualKeyword(bool AllowExport = true) const;
+ bool isModuleContextualKeyword(const LangOptions &LangOpts,
+ bool AllowExport = true) const;
bool isSimpleTypeSpecifier(const LangOptions &LangOpts) const;
diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp
index 5f6d34bd3b7ec..3a4a3d379e021 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -349,7 +349,7 @@ void IdentifierTable::AddKeywords(const LangOptions &LangOpts) {
if (LangOpts.IEEE128)
AddKeyword("__ieee128", tok::kw___float128, KEYALL, LangOpts, *this);
- // Add the 'import' and 'module' contextual keyword.
+ // Add the 'import' and 'module' contextual keywords.
get("import").setModulesImport(true);
get("module").setModulesDeclaration(true);
}
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index a4d9440447429..4e550b672d245 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -75,7 +75,10 @@ tok::ObjCKeywordKind Token::getObjCKeywordID() const {
/// Return true if we have an C++20 Modules contextual keyword(export, import
/// or module).
-bool Token::isModuleContextualKeyword(bool AllowExport) const {
+bool Token::isModuleContextualKeyword(const LangOptions &LangOpts,
+ bool AllowExport) const {
+ if (!LangOpts.CPlusPlusModules)
+ return false;
if (AllowExport && is(tok::kw_export))
return true;
if (isOneOf(tok::kw_import, tok::kw_module))
@@ -4037,9 +4040,9 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
// Notify MIOpt that we read a non-whitespace/non-comment token.
MIOpt.ReadToken();
bool returnedToken = LexIdentifierContinue(Result, CurPtr);
- if (returnedToken && Result.isModuleContextualKeyword() &&
- LangOpts.CPlusPlusModules && !LexingRawMode && !Is_PragmaLexer &&
- !ParsingPreprocessorDirective && PP &&
+ if (returnedToken && Result.isModuleContextualKeyword(LangOpts) &&
+ !LexingRawMode && !Is_PragmaLexer && !ParsingPreprocessorDirective &&
+ PP &&
PP->HandleModuleContextualKeyword(Result, TokAtPhysicalStartOfLine))
goto HandleDirective;
return returnedToken;
@@ -4616,7 +4619,7 @@ bool Lexer::LexDependencyDirectiveToken(Token &Result) {
Result.setRawIdentifierData(TokPtr);
if (!isLexingRawMode()) {
const IdentifierInfo *II = PP->LookUpIdentifierInfo(Result);
- if (Result.isModuleContextualKeyword() &&
+ if (Result.isModuleContextualKeyword(LangOpts) &&
PP->HandleModuleContextualKeyword(Result, Result.isAtStartOfLine())) {
PP->HandleDirective(Result);
return false;
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index ab239fc01ac1a..cd553181563a5 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -1260,16 +1260,13 @@ void Preprocessor::HandleDirective(Token &Result) {
// pp-directive.
bool ReadAnyTokensBeforeDirective =CurPPLexer->MIOpt.getHasReadAnyTokensVal();
- // Save the '#' token in case we need to return it later.
- Token SavedHash = Result;
-
- bool IsCXX20ImportOrModuleDirective =
- getLangOpts().CPlusPlusModules &&
- Result.isModuleContextualKeyword(/*AllowExport=*/false);
+ // Save the directive-introducing token('#' and 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 (!IsCXX20ImportOrModuleDirective)
+ if (Introducer.is(tok::hash))
LexUnexpandedToken(Result);
// C99 6.10.3p11: Is this preprocessor directive in macro invocation? e.g.:
@@ -1291,7 +1288,9 @@ void Preprocessor::HandleDirective(Token &Result) {
case tok::pp_embed:
case tok::pp_module:
Diag(Result, diag::err_embedded_directive)
- << IsCXX20ImportOrModuleDirective << II->getName();
+ << Introducer.isModuleContextualKeyword(getLangOpts(),
+ /*AllowExport=*/false)
+ << II->getName();
Diag(*ArgMacro, diag::note_macro_expansion_here)
<< ArgMacro->getIdentifierInfo();
DiscardUntilEndOfDirective();
@@ -1308,7 +1307,8 @@ void Preprocessor::HandleDirective(Token &Result) {
ResetMacroExpansionHelper helper(this);
if (SkippingUntilPCHThroughHeader || SkippingUntilPragmaHdrStop)
- return HandleSkippedDirectiveWhileUsingPCH(Result, SavedHash.getLocation());
+ return HandleSkippedDirectiveWhileUsingPCH(Result,
+ Introducer.getLocation());
switch (Result.getKind()) {
case tok::eod:
@@ -1328,7 +1328,7 @@ void Preprocessor::HandleDirective(Token &Result) {
// directive. However do permit it in the predefines file, as we use line
// markers to mark the builtin macros as being in a system header.
if (getLangOpts().AsmPreprocessor &&
- SourceMgr.getFileID(SavedHash.getLocation()) != getPredefinesFileID())
+ SourceMgr.getFileID(Introducer.getLocation()) != getPredefinesFileID())
break;
return HandleDigitDirective(Result);
default:
@@ -1340,30 +1340,32 @@ void Preprocessor::HandleDirective(Token &Result) {
default: break;
// C99 6.10.1 - Conditional Inclusion.
case tok::pp_if:
- return HandleIfDirective(Result, SavedHash, ReadAnyTokensBeforeDirective);
+ return HandleIfDirective(Result, Introducer,
+ ReadAnyTokensBeforeDirective);
case tok::pp_ifdef:
- return HandleIfdefDirective(Result, SavedHash, false,
+ return HandleIfdefDirective(Result, Introducer, false,
true /*not valid for miopt*/);
case tok::pp_ifndef:
- return HandleIfdefDirective(Result, SavedHash, true,
+ return HandleIfdefDirective(Result, Introducer, true,
ReadAnyTokensBeforeDirective);
case tok::pp_elif:
case tok::pp_elifdef:
case tok::pp_elifndef:
- return HandleElifFamilyDirective(Result, SavedHash, II->getPPKeywordID());
+ return HandleElifFamilyDirective(Result, Introducer,
+ II->getPPKeywordID());
case tok::pp_else:
- return HandleElseDirective(Result, SavedHash);
+ return HandleElseDirective(Result, Introducer);
case tok::pp_endif:
return HandleEndifDirective(Result);
// C99 6.10.2 - Source File Inclusion.
case tok::pp_include:
// Handle #include.
- return HandleIncludeDirective(SavedHash.getLocation(), Result);
+ return HandleIncludeDirective(Introducer.getLocation(), Result);
case tok::pp___include_macros:
// Handle -imacros.
- return HandleIncludeMacrosDirective(SavedHash.getLocation(), Result);
+ return HandleIncludeMacrosDirective(Introducer.getLocation(), Result);
// C99 6.10.3 - Macro Replacement.
case tok::pp_define:
@@ -1381,16 +1383,17 @@ void Preprocessor::HandleDirective(Token &Result) {
// C99 6.10.6 - Pragma Directive.
case tok::pp_pragma:
- return HandlePragmaDirective({PIK_HashPragma, SavedHash.getLocation()});
+ return HandlePragmaDirective({PIK_HashPragma, Introducer.getLocation()});
case tok::pp_module:
return HandleCXXModuleDirective(Result);
// GNU Extensions.
case tok::pp_import:
- if (IsCXX20ImportOrModuleDirective)
+ if (Introducer.isModuleContextualKeyword(getLangOpts(),
+ /*AllowExport=*/false))
return HandleCXXImportDirective(Result);
- return HandleImportDirective(SavedHash.getLocation(), Result);
+ return HandleImportDirective(Introducer.getLocation(), Result);
case tok::pp_include_next:
- return HandleIncludeNextDirective(SavedHash.getLocation(), Result);
+ return HandleIncludeNextDirective(Introducer.getLocation(), Result);
case tok::pp_warning:
if (LangOpts.CPlusPlus)
@@ -1409,7 +1412,7 @@ void Preprocessor::HandleDirective(Token &Result) {
case tok::pp_sccs:
return HandleIdentSCCSDirective(Result);
case tok::pp_embed:
- return HandleEmbedDirective(SavedHash.getLocation(), Result,
+ return HandleEmbedDirective(Introducer.getLocation(), Result,
getCurrentFileLexer()
? *getCurrentFileLexer()->getFileEntry()
: static_cast<FileEntry *>(nullptr));
@@ -1440,7 +1443,7 @@ void Preprocessor::HandleDirective(Token &Result) {
if (getLangOpts().AsmPreprocessor) {
auto Toks = std::make_unique<Token[]>(2);
// Return the # and the token after it.
- Toks[0] = SavedHash;
+ Toks[0] = Introducer;
Toks[1] = Result;
// If the second token is a hashhash token, then we need to translate it to
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 95eaa633d6d9f..7d5c9cbfc33b5 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -1174,7 +1174,7 @@ bool Preprocessor::LexModuleNameContinue(Token &Tok, SourceLocation UseLoc,
/// Otherwise the token is treated as an identifier.
bool Preprocessor::HandleModuleContextualKeyword(
Token &Result, bool TokAtPhysicalStartOfLine) {
- if (!getLangOpts().CPlusPlusModules || !Result.isModuleContextualKeyword())
+ if (!Result.isModuleContextualKeyword(getLangOpts()))
return false;
if (Result.is(tok::kw_export)) {
@@ -1188,9 +1188,8 @@ bool Preprocessor::HandleModuleContextualKeyword(
if (!LastTokenWasExportKeyword.isAtPhysicalStartOfLine())
return false;
// [cpp.pre]/1.4
- // export // not a preprocessing directive
- // import foo; // preprocessing directive (ill-formed at phase
- // 7)
+ // export // not a preprocessing directive
+ // import foo; // preprocessing directive (ill-formed at phase7)
if (TokAtPhysicalStartOfLine)
return false;
} else if (!TokAtPhysicalStartOfLine)
@@ -1266,7 +1265,7 @@ void Preprocessor::EnterModuleSuffixTokenStream(ArrayRef<Token> Toks) {
/*DisableMacroExpansion*/ true, /*IsReinject*/ false);
}
-// Lex a token following the 'import' contextual keyword.
+/// Lex a token following the 'import' contextual keyword.
///
/// pp-import: [C++20]
/// import header-name pp-import-suffix[opt] ;
@@ -1290,7 +1289,7 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
recomputeCurLexerKind();
SmallVector<Token, 32> Suffix;
- SmallVector<IdentifierLoc, 2> Path;
+ SmallVector<IdentifierLoc, 3> Path;
Lex(Result);
if (LexModuleNameContinue(Result, ModuleImportLoc, Suffix, Path))
return CollectPPImportSuffixAndEnterStream(Suffix);
diff --git a/clang/lib/Lex/TokenLexer.cpp b/clang/lib/Lex/TokenLexer.cpp
index 676fbe6f98f44..958c8cf42c1f1 100644
--- a/clang/lib/Lex/TokenLexer.cpp
+++ b/clang/lib/Lex/TokenLexer.cpp
@@ -701,7 +701,7 @@ bool TokenLexer::Lex(Token &Tok) {
// Handle recursive expansion!
if (!Tok.isAnnotation() && Tok.getIdentifierInfo() != nullptr &&
(!PP.getLangOpts().CPlusPlusModules ||
- !Tok.isModuleContextualKeyword())) {
+ !Tok.isModuleContextualKeyword(PP.getLangOpts()))) {
// Change the kind of this identifier to the appropriate token kind, e.g.
// turning "for" into a keyword.
IdentifierInfo *II = Tok.getIdentifierInfo();
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 0156967186931..e0cc6bc7792ff 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -26,7 +26,6 @@
#include "clang/Sema/Scope.h"
#include "clang/Sema/SemaCodeCompletion.h"
#include "llvm/ADT/STLForwardCompat.h"
-#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TimeProfiler.h"
using namespace clang;
@@ -632,8 +631,7 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result,
// FIXME: We should generate better diagnostic information here to explain
// why the module/import directive is ill-formed.
case tok::identifier: {
- if (getLangOpts().CPlusPlusModules &&
- NextToken().isModuleContextualKeyword() &&
+ if (NextToken().isModuleContextualKeyword(getLangOpts()) &&
GetLookAheadToken(2).isNot(tok::coloncolon)) {
if (NextToken().getIdentifierInfo()->isStr(
tok::getKeywordSpelling(tok::kw_module)))
@@ -720,7 +718,7 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result,
// 'export import' declarations. If the module/import directive is
// well-formed, it should be converted to a keyword in preprocessor, but not
// an identifier we saw here.
- if (getLangOpts().CPlusPlusModules && Tok.isModuleContextualKeyword() &&
+ if (Tok.isModuleContextualKeyword(getLangOpts()) &&
NextToken().isNot(tok::coloncolon)) {
if (Tok.getIdentifierInfo()->isStr(
tok::getKeywordSpelling(tok::kw_module)))
diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp
index 1940f3eb6e746..529521e7185a4 100644
--- a/clang/lib/Sema/SemaModule.cpp
+++ b/clang/lib/Sema/SemaModule.cpp
@@ -14,9 +14,7 @@
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
-#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/HeaderSearch.h"
-#include "clang/Lex/ModuleLoader.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/ParsedAttr.h"
#include "clang/Sema/SemaInternal.h"
diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
index 9a3939c98ba12..23d89e88279ac 100644
--- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
@@ -631,7 +631,8 @@ void ModuleDepCollectorPP::InclusionDirective(
void ModuleDepCollectorPP::moduleImport(SourceLocation ImportLoc,
ModuleIdPath Path,
const Module *Imported) {
- if (MDC.ScanInstance.getPreprocessor().isImportingCXXNamedModules()) {
+ auto &PP = MDC.ScanInstance.getPreprocessor();
+ if (PP.getLangOpts().CPlusPlusModules && PP.isImportingCXXNamedModules()) {
P1689ModuleInfo RequiredModule;
RequiredModule.ModuleName = Path[0].getIdentifierInfo()->getName().str();
RequiredModule.Type = P1689ModuleInfo::ModuleType::NamedCXXModule;
>From 204dd212b4d1389f9b2efce919b8ef1bdef84776 Mon Sep 17 00:00:00 2001
From: "Wang, Yihan" <yronglin777 at gmail.com>
Date: Wed, 10 Sep 2025 01:41:02 +0800
Subject: [PATCH 22/22] [ObjC] Handle @import directive as a pp-directive
Signed-off-by: Wang, Yihan <yronglin777 at gmail.com>
---
.../AnnotateFunctions/AnnotateFunctions.cpp | 2 +-
.../include/clang/Basic/DiagnosticLexKinds.td | 5 +-
clang/include/clang/Lex/Preprocessor.h | 30 +----
clang/include/clang/Lex/Token.h | 8 +-
clang/lib/Lex/DependencyDirectivesScanner.cpp | 19 ++-
clang/lib/Lex/Lexer.cpp | 46 ++++---
clang/lib/Lex/PPDirectives.cpp | 105 +++++++++++++---
clang/lib/Lex/PPLexerChange.cpp | 13 +-
clang/lib/Lex/Pragma.cpp | 23 ++--
clang/lib/Lex/Preprocessor.cpp | 114 ++++--------------
clang/lib/Lex/TokenLexer.cpp | 3 +-
clang/test/Modules/no-stale-modtime.m | 3 +-
clang/test/Modules/pr62359.cppm | 2 +-
.../Lex/DependencyDirectivesScannerTest.cpp | 2 +-
14 files changed, 183 insertions(+), 192 deletions(-)
diff --git a/clang/examples/AnnotateFunctions/AnnotateFunctions.cpp b/clang/examples/AnnotateFunctions/AnnotateFunctions.cpp
index d872020c2d8a3..22a3eb97f938b 100644
--- a/clang/examples/AnnotateFunctions/AnnotateFunctions.cpp
+++ b/clang/examples/AnnotateFunctions/AnnotateFunctions.cpp
@@ -65,7 +65,7 @@ class PragmaAnnotateHandler : public PragmaHandler {
Token Tok;
PP.LexUnexpandedToken(Tok);
if (Tok.isNot(tok::eod))
- PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
+ PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "#pragma";
if (HandledDecl) {
DiagnosticsEngine &D = PP.getDiagnostics();
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index f5930f1badcbb..c8f04e408ad82 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -465,10 +465,7 @@ def err_pp_embed_device_file : Error<
"device files are not yet supported by '#embed' directive">;
def ext_pp_extra_tokens_at_eol : ExtWarn<
- "extra tokens at end of #%0 directive">, InGroup<ExtraTokens>;
-def ext_pp_extra_tokens_at_module_directive_eol
- : ExtWarn<"extra tokens at end of '%0' directive">,
- InGroup<ExtraTokens>;
+ "extra tokens at end of %0 directive">, InGroup<ExtraTokens>;
def ext_pp_comma_expr : Extension<"comma operator in operand of #if">;
def ext_pp_bad_vaargs_use : Extension<
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 362a5c429a26a..b95fa0a661594 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -137,24 +137,6 @@ struct CXXStandardLibraryVersionInfo {
std::uint64_t Version;
};
-class ExportContextualKeywordInfo {
- Token ExportTok;
- bool AtPhysicalStartOfLine = false;
-
-public:
- ExportContextualKeywordInfo() = default;
- ExportContextualKeywordInfo(const Token &Tok, bool AtPhysicalStartOfLine)
- : ExportTok(Tok), AtPhysicalStartOfLine(AtPhysicalStartOfLine) {}
-
- bool isValid() const { return ExportTok.is(tok::kw_export); }
- bool isAtPhysicalStartOfLine() const { return AtPhysicalStartOfLine; }
- Token getExportTok() const { return ExportTok; }
- void reset() {
- ExportTok.startToken();
- AtPhysicalStartOfLine = false;
- }
-};
-
/// Engages in a tight little dance with the lexer to efficiently
/// preprocess tokens.
///
@@ -378,7 +360,7 @@ class Preprocessor {
bool DeclaringCXXNamedModules = false;
/// Whether the last token we lexed was an 'export' keyword.
- ExportContextualKeywordInfo LastTokenWasExportKeyword;
+ std::optional<Token> ModuleLikeDirectiveIntroducer;
/// First pp-token source location in current translation unit.
SourceLocation FirstPPTokenLoc;
@@ -1808,6 +1790,7 @@ class Preprocessor {
void EnterModuleSuffixTokenStream(ArrayRef<Token> Toks);
void HandleCXXImportDirective(Token Import);
void HandleCXXModuleDirective(Token Module);
+ void HandleObjCAtImportDirective(Token &ImportTok);
/// Callback invoked when the lexer sees one of export, import or module token
/// at the start of a line.
@@ -1815,8 +1798,7 @@ class Preprocessor {
/// This consumes the import/module directive, modifies the
/// lexer/preprocessor state, and advances the lexer(s) so that the next token
/// read is the correct one.
- bool HandleModuleContextualKeyword(Token &Result,
- bool TokAtPhysicalStartOfLine);
+ bool HandleModuleContextualKeyword(Token &Result);
/// Get the start location of the first pp-token in main file.
SourceLocation getMainFileFirstPPTokenLoc() const {
@@ -1825,7 +1807,6 @@ class Preprocessor {
return FirstPPTokenLoc;
}
- bool LexAfterModuleImport(Token &Result);
void CollectPPImportSuffix(SmallVectorImpl<Token> &Toks,
bool StopUntilEOD = false);
bool CollectPPImportSuffixAndEnterStream(SmallVectorImpl<Token> &Toks,
@@ -2351,11 +2332,13 @@ class Preprocessor {
}
}
+
/// Check whether the next pp-token is one of the specificed token kind. this
/// method should have no observable side-effect on the lexed tokens.
template <typename... Ts> bool isNextPPTokenOneOf(Ts... Ks) {
static_assert(sizeof...(Ts) > 0,
"requires at least one tok::TokenKind specified");
+
// Do some quick tests for rejection cases.
std::optional<Token> Val;
if (CurLexer)
@@ -3175,9 +3158,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/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h
index ae9ab99cf00b1..91785bd14510d 100644
--- a/clang/include/clang/Lex/Token.h
+++ b/clang/include/clang/Lex/Token.h
@@ -90,8 +90,10 @@ class Token {
// re-added, e.g. via EnterTokenStream. Annotation
// tokens are *not* reinjected.
HasSeenNoTrivialPPDirective =
- 0x1000, // Whether we've seen any 'no-trivial' pp-directives before
+ 0x1000, // Whether we've seen any 'no-trivial' pp-directives before//
// current position.
+ PhysicalStartOfLine =
+ 0x2000, // At the physical start of line or only after whitespace.
};
tok::TokenKind getKind() const { return Kind; }
@@ -277,6 +279,10 @@ class Token {
///
bool isAtStartOfLine() const { return getFlag(StartOfLine); }
+ /// isAtPhysicalStartOfLine - Return true if this token is at the physical
+ /// start of a line.
+ bool isAtPhysicalStartOfLine() const { return getFlag(PhysicalStartOfLine); }
+
/// Return true if this token has whitespace before it.
///
bool hasLeadingSpace() const { return getFlag(LeadingSpace); }
diff --git a/clang/lib/Lex/DependencyDirectivesScanner.cpp b/clang/lib/Lex/DependencyDirectivesScanner.cpp
index 64a2d17451d1d..85ffc87017619 100644
--- a/clang/lib/Lex/DependencyDirectivesScanner.cpp
+++ b/clang/lib/Lex/DependencyDirectivesScanner.cpp
@@ -579,15 +579,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,
@@ -931,10 +928,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);
-
if (*First == '_') {
if (isNextIdentifierOrSkipLine("_Pragma", First, End))
return lex_Pragma(First, End);
@@ -947,6 +940,10 @@ bool Scanner::lexPPLine(const char *&First, const char *const End) {
auto ScEx2 = make_scope_exit(
[&]() { TheLexer.setParsingPreprocessorDirective(false); });
+ // FIXME: Shoule we handle @import as a preprocessing directive?
+ if (*First == '@')
+ return lexAt(First, End);
+
// Handle module directives for C++20 modules.
if (*First == 'i' || *First == 'e' || *First == 'm')
return lexModule(First, End);
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index 4e550b672d245..4ad2dbd63c830 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -77,16 +77,13 @@ tok::ObjCKeywordKind Token::getObjCKeywordID() const {
/// or module).
bool Token::isModuleContextualKeyword(const LangOptions &LangOpts,
bool AllowExport) const {
- if (!LangOpts.CPlusPlusModules)
+ if (!LangOpts.CPlusPlusModules || isAnnotation())
return false;
if (AllowExport && is(tok::kw_export))
return true;
- if (isOneOf(tok::kw_import, tok::kw_module))
- return true;
- if (isNot(tok::identifier))
- return false;
- const auto *II = getIdentifierInfo();
- return II->isModulesImport() || II->isModulesDeclaration();
+ if (const auto *II = getIdentifierInfo())
+ return II->isModulesImport() || II->isModulesDeclaration();
+ return false;
}
/// Determine whether the token kind starts a simple-type-specifier.
@@ -3758,6 +3755,9 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
assert(!Result.needsCleaning() && "Result needs cleaning");
assert(!Result.hasPtrData() && "Result has not been reset");
+ if (TokAtPhysicalStartOfLine)
+ Result.setFlag(Token::PhysicalStartOfLine);
+
// CurPtr - Cache BufferPtr in an automatic variable.
const char *CurPtr = BufferPtr;
@@ -4040,10 +4040,9 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
// Notify MIOpt that we read a non-whitespace/non-comment token.
MIOpt.ReadToken();
bool returnedToken = LexIdentifierContinue(Result, CurPtr);
- if (returnedToken && Result.isModuleContextualKeyword(LangOpts) &&
- !LexingRawMode && !Is_PragmaLexer && !ParsingPreprocessorDirective &&
- PP &&
- PP->HandleModuleContextualKeyword(Result, TokAtPhysicalStartOfLine))
+ if (returnedToken && Result.isNot(tok::eof) && !LexingRawMode &&
+ !Is_PragmaLexer && !ParsingPreprocessorDirective && PP &&
+ PP->HandleModuleContextualKeyword(Result))
goto HandleDirective;
return returnedToken;
}
@@ -4454,9 +4453,14 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
case '@':
// Objective C support.
- if (CurPtr[-1] == '@' && LangOpts.ObjC)
+ if (CurPtr[-1] == '@' && LangOpts.ObjC) {
+ if (TokAtPhysicalStartOfLine && !LexingRawMode && !Is_PragmaLexer) {
+ FormTokenWithChars(Result, CurPtr, tok::at);
+ (void)PP->HandleModuleContextualKeyword(Result);
+ return true;
+ }
Kind = tok::at;
- else
+ } else
Kind = tok::unknown;
break;
@@ -4538,10 +4542,6 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
HandleDirective:
PP->HandleDirective(Result);
- if (PP->hadModuleLoaderFatalFailure())
- // With a fatal failure in the module loader, we abort parsing.
- return true;
-
// We parsed the directive; lex a token with the new state.
return false;
@@ -4608,20 +4608,26 @@ bool Lexer::LexDependencyDirectiveToken(Token &Result) {
const char *TokPtr = convertDependencyDirectiveToken(DDTok, Result);
- if (Result.is(tok::hash) && Result.isAtStartOfLine()) {
+ if (Result.is(tok::hash) && Result.isAtStartOfLine() && !isLexingRawMode()) {
PP->HandleDirective(Result);
if (PP->hadModuleLoaderFatalFailure())
// With a fatal failure in the module loader, we abort parsing.
return true;
return false;
}
+ if (Result.is(tok::at) && Result.isAtStartOfLine() && !isLexingRawMode()) {
+ (void)PP->HandleModuleContextualKeyword(Result);
+ return false;
+ }
if (Result.is(tok::raw_identifier)) {
Result.setRawIdentifierData(TokPtr);
if (!isLexingRawMode()) {
const IdentifierInfo *II = PP->LookUpIdentifierInfo(Result);
- if (Result.isModuleContextualKeyword(LangOpts) &&
- PP->HandleModuleContextualKeyword(Result, Result.isAtStartOfLine())) {
+ if (PP->HandleModuleContextualKeyword(Result)) {
PP->HandleDirective(Result);
+ if (PP->hadModuleLoaderFatalFailure())
+ // With a fatal failure in the module loader, we abort parsing.
+ return true;
return false;
}
if (II->isHandleIdentifierCase())
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index cd553181563a5..de80f28f1e9f5 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -478,13 +478,15 @@ Preprocessor::CheckEndOfDirective(StringRef DirType, bool EnableMacros,
!CurTokenLexer)
Hint = FixItHint::CreateInsertion(Tmp.getLocation(),"//");
- unsigned DiagID = diag::ext_pp_extra_tokens_at_eol;
- // C++20 import or module directive has no '#' prefix.
if (getLangOpts().CPlusPlusModules &&
(DirType == "import" || DirType == "module"))
- DiagID = diag::ext_pp_extra_tokens_at_module_directive_eol;
-
- Diag(Tmp, DiagID) << DirType << Hint;
+ Diag(Tmp, diag::ext_pp_extra_tokens_at_eol) << DirType << Hint;
+ else if (IsAtImport && DirType == "import")
+ Diag(Tmp, diag::ext_pp_extra_tokens_at_eol)
+ << llvm::Twine("@").concat(DirType).str() << Hint;
+ else
+ Diag(Tmp, diag::ext_pp_extra_tokens_at_eol)
+ << llvm::Twine("#").concat(DirType).str() << Hint;
return DiscardUntilEndOfDirective().getEnd();
}
@@ -1388,10 +1390,18 @@ void Preprocessor::HandleDirective(Token &Result) {
return HandleCXXModuleDirective(Result);
// GNU Extensions.
case tok::pp_import:
- if (Introducer.isModuleContextualKeyword(getLangOpts(),
- /*AllowExport=*/false))
+ switch (Introducer.getKind()) {
+ case tok::kw_import:
+ if (ModuleLikeDirectiveIntroducer &&
+ ModuleLikeDirectiveIntroducer->is(tok::at))
+ return HandleObjCAtImportDirective(Result);
return HandleCXXImportDirective(Result);
- return HandleImportDirective(Introducer.getLocation(), Result);
+ case tok::hash:
+ return HandleImportDirective(Introducer.getLocation(), Result);
+ default:
+ llvm_unreachable("Not a valid import directive");
+ }
+ break;
case tok::pp_include_next:
return HandleIncludeNextDirective(Introducer.getLocation(), Result);
@@ -4084,14 +4094,80 @@ void Preprocessor::HandleEmbedDirective(SourceLocation HashLoc, Token &EmbedTok,
HandleEmbedDirectiveImpl(HashLoc, *Params, BinaryContents, FilenameToGo);
}
+/// Lex a token following the 'import' contextual keyword.
+///
+/// [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::HandleObjCAtImportDirective(Token &ImportTok) {
+ assert(ModuleLikeDirectiveIntroducer &&
+ ModuleLikeDirectiveIntroducer->is(tok::at) &&
+ "@ token must set during pervious lexing");
+ ModuleLikeDirectiveIntroducer.reset();
+ ModuleImportLoc = ImportTok.getLocation();
+ SmallVector<Token, 32> DirToks{ImportTok};
+ SmallVector<IdentifierLoc, 3> Path;
+ Token Tok;
+ Lex(Tok);
+ if (LexModuleNameContinue(Tok, ModuleImportLoc, DirToks, Path)) {
+ if (Tok.isNot(tok::eod))
+ CheckEndOfDirective(ImportTok.getIdentifierInfo()->getName(),
+ /*EnableMacros=*/false, &DirToks);
+ EnterModuleSuffixTokenStream(DirToks);
+ 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;
+ }
+
+ SourceLocation SemiLoc = DirToks.back().getLocation();
+ Module *Imported = nullptr;
+ if (getLangOpts().Modules) {
+ Imported = TheModuleLoader.loadModule(ImportTok.getLocation(), Path,
+ Module::Hidden,
+ /*IsInclusionDirective=*/false);
+ if (Imported)
+ makeModuleVisible(Imported, SemiLoc);
+
+ // We hit an error processing the import. Bail out.
+ if (hadModuleLoaderFatalFailure()) {
+ // With a fatal failure in the module loader, we abort parsing.
+ assert(CurLexer && "#include but no current lexer set!");
+ CurLexer->cutOffLexing();
+ }
+ }
+
+ if (Callbacks)
+ Callbacks->moduleImport(ModuleImportLoc, Path, Imported);
+ EnterModuleSuffixTokenStream(DirToks);
+}
+
void Preprocessor::HandleCXXImportDirective(Token ImportTok) {
assert(getLangOpts().CPlusPlusModules && ImportTok.is(tok::kw_import));
llvm::SaveAndRestore<bool> SaveImportingCXXModules(
this->ImportingCXXNamedModules);
ImportingCXXNamedModules = true;
- if (LastTokenWasExportKeyword.isValid())
- LastTokenWasExportKeyword.reset();
+ if (ModuleLikeDirectiveIntroducer)
+ ModuleLikeDirectiveIntroducer.reset();
Token Tok;
if (LexHeaderName(Tok)) {
@@ -4215,9 +4291,9 @@ void Preprocessor::HandleCXXImportDirective(Token ImportTok) {
void Preprocessor::HandleCXXModuleDirective(Token ModuleTok) {
assert(getLangOpts().CPlusPlusModules && ModuleTok.is(tok::kw_module));
Token Introducer = ModuleTok;
- if (LastTokenWasExportKeyword.isValid()) {
- Introducer = LastTokenWasExportKeyword.getExportTok();
- LastTokenWasExportKeyword.reset();
+ if (ModuleLikeDirectiveIntroducer) {
+ Introducer = *ModuleLikeDirectiveIntroducer;
+ ModuleLikeDirectiveIntroducer.reset();
}
SourceLocation StartLoc = Introducer.getLocation();
@@ -4280,7 +4356,8 @@ void Preprocessor::HandleCXXModuleDirective(Token ModuleTok) {
LexUnexpandedToken(Tok);
if (LexModuleNameContinue(Tok, UseLoc, DirToks, Partition)) {
if (Tok.isNot(tok::eod))
- CheckEndOfDirective(ModuleTok.getIdentifierInfo()->getName());
+ CheckEndOfDirective(ModuleTok.getIdentifierInfo()->getName(),
+ /*EnableMacros=*/false, &DirToks);
EnterModuleSuffixTokenStream(DirToks);
return;
}
diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp
index d8f61c02a9837..8bd3749dd8ca2 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/Pragma.cpp b/clang/lib/Lex/Pragma.cpp
index bba3c89bed38f..914974ef5f450 100644
--- a/clang/lib/Lex/Pragma.cpp
+++ b/clang/lib/Lex/Pragma.cpp
@@ -817,7 +817,7 @@ void Preprocessor::HandlePragmaModuleBuild(Token &Tok) {
LexUnexpandedToken(Tok);
if (Tok.isNot(tok::eod)) {
- Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
+ Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "#pragma";
DiscardUntilEndOfDirective();
}
@@ -898,7 +898,7 @@ void Preprocessor::HandlePragmaHdrstop(Token &Tok) {
}
if (Tok.isNot(tok::eod))
Diag(Tok.getLocation(), diag::ext_pp_extra_tokens_at_eol)
- << "pragma hdrstop";
+ << "#pragma hdrstop";
if (creatingPCHWithPragmaHdrStop() &&
SourceMgr.isInMainFile(Tok.getLocation())) {
@@ -1236,7 +1236,7 @@ struct PragmaDebugHandler : public PragmaHandler {
if (Tok.isNot(tok::eod)) {
PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol)
- << "pragma clang __debug captured";
+ << "#pragma clang __debug captured";
return;
}
@@ -1538,7 +1538,7 @@ struct PragmaWarningHandler : public PragmaHandler {
PP.Lex(Tok);
if (Tok.isNot(tok::eod))
- PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma warning";
+ PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "#pragma warning";
}
};
@@ -1602,7 +1602,8 @@ struct PragmaExecCharsetHandler : public PragmaHandler {
PP.Lex(Tok);
if (Tok.isNot(tok::eod))
- PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma execution_character_set";
+ PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol)
+ << "#pragma execution_character_set";
}
};
@@ -1719,7 +1720,7 @@ struct PragmaModuleImportHandler : public PragmaHandler {
return;
if (Tok.isNot(tok::eod))
- PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
+ PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "#pragma";
// If we have a non-empty module path, load the named module.
Module *Imported =
@@ -1755,7 +1756,7 @@ struct PragmaModuleBeginHandler : public PragmaHandler {
return;
if (Tok.isNot(tok::eod))
- PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
+ PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "#pragma";
// We can only enter submodules of the current module.
StringRef Current = PP.getLangOpts().CurrentModule;
@@ -1814,7 +1815,7 @@ struct PragmaModuleEndHandler : public PragmaHandler {
PP.LexUnexpandedToken(Tok);
if (Tok.isNot(tok::eod))
- PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
+ PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "#pragma";
Module *M = PP.LeaveSubmodule(/*ForPragma*/true);
if (M)
@@ -1848,7 +1849,7 @@ struct PragmaModuleLoadHandler : public PragmaHandler {
return;
if (Tok.isNot(tok::eod))
- PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
+ PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "#pragma";
// Load the module, don't make it visible.
PP.getModuleLoader().loadModule(Loc, ModuleName, Module::Hidden,
@@ -1905,7 +1906,7 @@ struct PragmaARCCFCodeAuditedHandler : public PragmaHandler {
// Verify that this is followed by EOD.
PP.LexUnexpandedToken(Tok);
if (Tok.isNot(tok::eod))
- PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
+ PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "#pragma";
// The start location of the active audit.
SourceLocation BeginLoc = PP.getPragmaARCCFCodeAuditedInfo().getLoc();
@@ -1960,7 +1961,7 @@ struct PragmaAssumeNonNullHandler : public PragmaHandler {
// Verify that this is followed by EOD.
PP.LexUnexpandedToken(Tok);
if (Tok.isNot(tok::eod))
- PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
+ PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "#pragma";
// The start location of the active audit.
SourceLocation BeginLoc = PP.getPragmaAssumeNonNullLoc();
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 7d5c9cbfc33b5..5436122da00e1 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -867,27 +867,6 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
// like "#define TY typeof", "TY(1) x".
if (II.isExtensionToken() && !DisableMacroExpansion)
Diag(Identifier, diag::ext_token_used);
-
- // 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.isModulesImport()) ||
- Identifier.is(tok::kw_import)) &&
- // FIXME: Can we just ignore DisableMacroExpansion here?
- // https://github.com/llvm/llvm-project/pull/137665 disable
- // macro expansion when current input file is preprocessed.
- !InMacroArgs &&
- (!DisableMacroExpansion || MacroExpansionInDirectivesOverride) &&
- CurLexerCallback != CLK_CachingLexer) {
- ModuleImportLoc = Identifier.getLocation();
- IsAtImport = true;
- CurLexerCallback = CLK_LexAfterModuleImport;
- }
return true;
}
@@ -990,8 +969,8 @@ void Preprocessor::Lex(Token &Result) {
}
LastTokenWasAt = Result.is(tok::at);
- if (Result.isNot(tok::kw_export))
- LastTokenWasExportKeyword.reset();
+ if (!Result.isOneOf(tok::at, tok::kw_export))
+ ModuleLikeDirectiveIntroducer.reset();
--LexLevel;
@@ -1172,27 +1151,37 @@ bool Preprocessor::LexModuleNameContinue(Token &Tok, SourceLocation UseLoc,
/// - <, ", or : (but not ::) pp tokens for 'import', or
/// - ; for 'module'
/// Otherwise the token is treated as an identifier.
-bool Preprocessor::HandleModuleContextualKeyword(
- Token &Result, bool TokAtPhysicalStartOfLine) {
- if (!Result.isModuleContextualKeyword(getLangOpts()))
+bool Preprocessor::HandleModuleContextualKeyword(Token &Result) {
+ if (!Result.isModuleContextualKeyword(getLangOpts()) &&
+ Result.isNot(tok::at) && !Result.isObjCAtKeyword(tok::objc_import))
return false;
- if (Result.is(tok::kw_export)) {
- LastTokenWasExportKeyword = {Result, TokAtPhysicalStartOfLine};
+ if (Result.isOneOf(tok::kw_export, tok::at)) {
+ ModuleLikeDirectiveIntroducer = Result;
return false;
}
- if (LastTokenWasExportKeyword.isValid()) {
+ if (ModuleLikeDirectiveIntroducer) {
// The export keyword was not at the start of line, it's not a
// directive-introducing token.
- if (!LastTokenWasExportKeyword.isAtPhysicalStartOfLine())
+ if (!ModuleLikeDirectiveIntroducer->isAtPhysicalStartOfLine())
return false;
// [cpp.pre]/1.4
// export // not a preprocessing directive
// import foo; // preprocessing directive (ill-formed at phase7)
- if (TokAtPhysicalStartOfLine)
+ if (Result.isAtPhysicalStartOfLine())
+ return false;
+
+ if (ModuleLikeDirectiveIntroducer->is(tok::at)) {
+ if (Result.isObjCAtKeyword(tok::objc_import)) {
+ Result.setKind(tok::kw_import);
+ ModuleImportLoc = Result.getLocation();
+ IsAtImport = true;
+ return true;
+ }
return false;
- } else if (!TokAtPhysicalStartOfLine)
+ }
+ } else if (!Result.isAtPhysicalStartOfLine())
return false;
bool SavedParsingPreprocessorDirective =
@@ -1265,67 +1254,6 @@ void Preprocessor::EnterModuleSuffixTokenStream(ArrayRef<Token> Toks) {
/*DisableMacroExpansion*/ true, /*IsReinject*/ false);
}
-/// 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);
-
- // Consume the pp-import-suffix and expand any macros in it now, if we're not
- // at the semicolon already.
- SourceLocation SemiLoc = Suffix.back().getLocation();
- if (Suffix.back().isNot(tok::semi)) {
- if (Result.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/lib/Lex/TokenLexer.cpp b/clang/lib/Lex/TokenLexer.cpp
index 958c8cf42c1f1..9846ffc3a5f86 100644
--- a/clang/lib/Lex/TokenLexer.cpp
+++ b/clang/lib/Lex/TokenLexer.cpp
@@ -701,7 +701,8 @@ bool TokenLexer::Lex(Token &Tok) {
// Handle recursive expansion!
if (!Tok.isAnnotation() && Tok.getIdentifierInfo() != nullptr &&
(!PP.getLangOpts().CPlusPlusModules ||
- !Tok.isModuleContextualKeyword(PP.getLangOpts()))) {
+ !Tok.isModuleContextualKeyword(PP.getLangOpts())) &&
+ (!PP.getLangOpts().ObjC || !Tok.isObjCAtKeyword(tok::objc_import))) {
// Change the kind of this identifier to the appropriate token kind, e.g.
// turning "for" into a keyword.
IdentifierInfo *II = Tok.getIdentifierInfo();
diff --git a/clang/test/Modules/no-stale-modtime.m b/clang/test/Modules/no-stale-modtime.m
index 92c18ac591add..6c62853db358e 100644
--- a/clang/test/Modules/no-stale-modtime.m
+++ b/clang/test/Modules/no-stale-modtime.m
@@ -4,7 +4,8 @@
// RUN: rm -rf %t
// RUN: mkdir -p %t
// This could be replaced by diamond_*, except we want to modify the top header
-// RUN: echo '@import l; @import r;' > %t/b.h
+// RUN: echo '@import l;' > %t/b.h
+// RUN: echo '@import r;' >> %t/b.h
// RUN: echo '@import t; // fromt l' > %t/l.h
// RUN: echo '@import t; // fromt r' > %t/r.h
diff --git a/clang/test/Modules/pr62359.cppm b/clang/test/Modules/pr62359.cppm
index fab0b7d03d814..8d9bd0a8c1376 100644
--- a/clang/test/Modules/pr62359.cppm
+++ b/clang/test/Modules/pr62359.cppm
@@ -56,4 +56,4 @@ int use2() {
}
// CHECK: OpenMP{{.*}}differs in precompiled file '{{.*}}Hello.pcm' vs. current file
-// CHECK: use of undeclared identifier 'pragma'
+// CHECK: use of undeclared identifier 'hello'
diff --git a/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp b/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp
index 1e1702c0c8d43..156de36b185ef 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_TRUE(minimizeSourceToDependencyDirectives("@import A\n;", Out));
EXPECT_STREQ("@import A;\n", Out.data());
ASSERT_FALSE(minimizeSourceToDependencyDirectives("@import A.B;\n", Out));
More information about the cfe-commits
mailing list