[C++][Modules] A module directive may only appear as the first preprocessing tokens in a file (#144233)

This PR is 2nd part of
[P1857R3](https://github.com/llvm/llvm-project/pull/107168)
implementation, and mainly implement the restriction `A module directive
may only appear as the first preprocessing tokens in a file (excluding
the global module fragment.)`:
[cpp.pre](https://eel.is/c++draft/cpp.pre):
```
module-file:
    pp-global-module-fragment[opt] pp-module group[opt] pp-private-module-fragment[opt]
```

We also refine tests use `split-file` instead of conditional macro.

Signed-off-by: yronglin <yronglin777@gmail.com>
This commit is contained in:
yronglin 2025-06-21 18:58:56 +08:00 committed by GitHub
parent 1b5d6ec685
commit ea321392eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 738 additions and 329 deletions

View File

@ -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;

View File

@ -350,6 +350,9 @@ private:
/// Whether the last token we lexed was an '@'.
bool LastTokenWasAt = false;
/// First pp-token in current translation unit.
std::optional<Token> FirstPPToken;
/// A position within a C++20 import-seq.
class StdCXXImportSeq {
public:
@ -1766,6 +1769,20 @@ public:
std::optional<LexEmbedParametersResult> LexEmbedParameters(Token &Current,
bool ForHasEmbed);
/// Whether the preprocessor already seen the first pp-token in main file.
bool hasSeenMainFileFirstPPToken() const { return FirstPPToken.has_value(); }
/// Record first pp-token and check if it has a Token::FirstPPToken flag.
void HandleMainFileFirstPPToken(const Token &Tok) {
if (!hasSeenMainFileFirstPPToken() && Tok.isFirstPPToken() &&
SourceMgr.isWrittenInMainFile(Tok.getLocation()))
FirstPPToken = Tok;
}
Token getMainFileFirstPPToken() const {
assert(FirstPPToken && "First main file pp-token doesn't exists");
return *FirstPPToken;
}
bool LexAfterModuleImport(Token &Result);
void CollectPpImportSuffix(SmallVectorImpl<Token> &Toks);

View File

@ -86,9 +86,12 @@ public:
// macro stringizing or charizing operator.
CommaAfterElided = 0x200, // The comma following this token was elided (MS).
IsEditorPlaceholder = 0x400, // This identifier is a placeholder.
IsReinjected = 0x800, // A phase 4 token that was produced before and
// re-added, e.g. via EnterTokenStream. Annotation
// tokens are *not* reinjected.
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; }
@ -318,6 +321,9 @@ public:
/// 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)

View File

@ -9822,7 +9822,8 @@ public:
DeclGroupPtrTy ActOnModuleDecl(SourceLocation StartLoc,
SourceLocation ModuleLoc, ModuleDeclKind MDK,
ModuleIdPath Path, ModuleIdPath Partition,
ModuleImportState &ImportState);
ModuleImportState &ImportState,
bool IntroducerIsFirstPPToken);
/// The parser has processed a global-module-fragment declaration that begins
/// the definition of the global module fragment of the current module unit.

View File

@ -174,6 +174,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
@ -3725,6 +3727,11 @@ bool Lexer::Lex(Token &Result) {
HasLeadingEmptyMacro = false;
}
if (IsFirstPPToken) {
Result.setFlag(Token::FirstPPToken);
IsFirstPPToken = false;
}
bool atPhysicalStartOfLine = IsAtPhysicalStartOfLine;
IsAtPhysicalStartOfLine = false;
bool isRawLex = isLexingRawMode();
@ -3732,6 +3739,10 @@ bool Lexer::Lex(Token &Result) {
bool returnedToken = LexTokenInternal(Result, atPhysicalStartOfLine);
// (After the LexTokenInternal call, the lexer might be destroyed.)
assert((returnedToken || !isRawLex) && "Raw lex must succeed");
if (returnedToken && Result.isFirstPPToken() && PP &&
!PP->hasSeenMainFileFirstPPToken())
PP->HandleMainFileFirstPPToken(Result);
return returnedToken;
}
@ -4535,6 +4546,8 @@ const char *Lexer::convertDependencyDirectiveToken(
Result.setFlag((Token::TokenFlags)DDTok.Flags);
Result.setLength(DDTok.Length);
BufferPtr = TokPtr + DDTok.Length;
if (PP && !PP->hasSeenMainFileFirstPPToken() && Result.isFirstPPToken())
PP->HandleMainFileFirstPPToken(Result);
return TokPtr;
}

View File

@ -1242,6 +1242,9 @@ void Preprocessor::HandleDirective(Token &Result) {
// pp-directive.
bool ReadAnyTokensBeforeDirective =CurPPLexer->MIOpt.getHasReadAnyTokensVal();
if (!hasSeenMainFileFirstPPToken())
HandleMainFileFirstPPToken(Result);
// Save the '#' token in case we need to return it later.
Token SavedHash = Result;

View File

@ -469,6 +469,9 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier,
// to disable the optimization in this case.
if (CurPPLexer) CurPPLexer->MIOpt.ExpandedMacro();
if (!hasSeenMainFileFirstPPToken())
HandleMainFileFirstPPToken(Identifier);
// If this is a builtin macro, like __LINE__ or _Pragma, handle it specially.
if (MI->isBuiltinMacro()) {
if (Callbacks)

View File

@ -247,6 +247,8 @@ void Preprocessor::DumpToken(const Token &Tok, bool DumpFlags) const {
llvm::errs() << " [LeadingSpace]";
if (Tok.isExpandDisabled())
llvm::errs() << " [ExpandDisabled]";
if (Tok.isFirstPPToken())
llvm::errs() << " [First pp-token]";
if (Tok.needsCleaning()) {
const char *Start = SourceMgr.getCharacterData(Tok.getLocation());
llvm::errs() << " [UnClean='" << StringRef(Start, Tok.getLength())

View File

@ -2340,7 +2340,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
@ -2359,7 +2360,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;
@ -2416,7 +2417,7 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
ExpectAndConsumeSemi(diag::err_module_expected_semi);
return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, Partition,
ImportState);
ImportState, Introducer.isFirstPPToken());
}
Decl *Parser::ParseModuleImport(SourceLocation AtLoc,

View File

@ -263,11 +263,11 @@ static bool DiagReservedModuleName(Sema &S, const IdentifierInfo *II,
Sema::DeclGroupPtrTy
Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
ModuleDeclKind MDK, ModuleIdPath Path,
ModuleIdPath Partition, ModuleImportState &ImportState) {
ModuleIdPath Partition, ModuleImportState &ImportState,
bool IntroducerIsFirstPPToken) {
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;
@ -333,14 +333,11 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
SeenGMF == (bool)this->TheGlobalModuleFragment) &&
"mismatched global module state");
// In C++20, the module-declaration must be the first declaration if there
// is no global module fragment.
if (getLangOpts().CPlusPlusModules && !IsFirstDecl && !SeenGMF) {
// In C++20, A module directive may only appear as the first preprocessing
// tokens in a file (excluding the global module fragment.).
if (getLangOpts().CPlusPlusModules && !IntroducerIsFirstPPToken && !SeenGMF) {
Diag(ModuleLoc, diag::err_module_decl_not_at_start);
SourceLocation BeginLoc =
ModuleScopes.empty()
? SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID())
: ModuleScopes.back().BeginLoc;
SourceLocation BeginLoc = PP.getMainFileFirstPPToken().getLocation();
if (BeginLoc.isValid()) {
Diag(BeginLoc, diag::note_global_module_introducer_missing)
<< FixItHint::CreateInsertion(BeginLoc, "module;\n");

View File

@ -1,57 +1,128 @@
// 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
// RUN: rm -rf %t
// RUN: split-file %s %t
#ifndef NO_GLOBAL_FRAG
#ifdef EXPORT_FRAGS
export // expected-error {{global module fragment cannot be exported}}
#endif
// 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;
#ifdef NO_MODULE_DECL
// expected-error@-2 {{missing 'module' declaration at end of global module fragment introduced here}}
#endif
#endif
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}}
//--- NoGlobalFrag.cppm
extern int a; // #a1
#ifndef NO_MODULE_DECL
export module Foo;
#ifdef NO_GLOBAL_FRAG
// expected-error@-2 {{module declaration must occur at the start of the translation unit}}
// expected-note@1 {{add 'module;' to the start of the file to introduce a global module fragment}}
#endif
export module Foo; // expected-error {{module declaration must occur at the start of the translation unit}}
// expected-note@-2 {{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}}
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
//--- 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@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}}
#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}}
#ifndef NO_PRIVATE_FRAG
#ifdef EXPORT_FRAGS
export // expected-error {{private module fragment cannot be exported}}
#endif
module :private; // #priv-frag
#ifdef NO_MODULE_DECL
// expected-error@-2 {{private module fragment declaration with no preceding module declaration}}
#endif
#endif
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
// expected-error@-1 {{private module fragment declaration with no preceding module declaration}}
int b; // ok
#ifndef NO_PRIVATE_FRAG
#ifndef NO_MODULE_DECL
//--- 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
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}}

View File

@ -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
#ifdef EXPORT
// 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
//--- pmf_in_interface.cpp
// expected-no-diagnostics
export
#else
// expected-note@+2 {{add 'export' here}}
#endif
module M;
#ifndef EXPORT
// expected-error@+2 {{private module fragment in module implementation unit}}
#endif
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}}

View File

@ -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
// 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
#ifdef INTERFACE
//--- 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@-2 {{use of undeclared identifier 'not_exported'; did you mean 'exported'?}}
// expected-note@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@-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@-2 {{undeclared identifier}}
internal_private = 1; // expected-error {{use of undeclared identifier 'internal_private'}}
}

View File

@ -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,25 +39,9 @@ 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
using ::var;
using ::func;
using ::str;
using ::type;
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}}
struct str; // expected-error {{declaration of 'str' in module M follows declaration in the global module}}
@ -73,51 +56,162 @@ typedef int type;
namespace ns { using ::func; }
namespace ns_alias = ns;
#ifdef EXPORT
}
#endif
//--- module-vs-global.cpp
import M;
extern int var; // expected-error {{declaration of 'var' in the global module follows declaration in module M}} expected-note@global-vs-module.cppm:35 {{previous}}
int func(); // expected-error {{declaration of 'func' in the global module follows declaration in module M}} expected-note@global-vs-module.cppm:36 {{previous}}
struct str; // expected-error {{declaration of 'str' in the global module follows declaration in module M}} expected-note@global-vs-module.cppm:37 {{previous}}
//--- 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-error {{declaration of 'var_tpl' in the global module follows declaration in module M}} expected-note@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@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@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@global-vs-module.cppm:43 {{previous}}
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;
using ::type;
using ::var_tpl;
using ::func_tpl;
using ::str_tpl;
using ::type_tpl;
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;
//--- 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;
}
//--- 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@M.cppm:4 {{previous}}
int func(); // expected-error {{declaration of 'func' in the global module follows declaration in module M}} expected-note@M.cppm:5 {{previous}}
struct str; // expected-error {{declaration of 'str' in the global module follows declaration in module M}} expected-note@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@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@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@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@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@global-vs-module.cppm:35 {{previous}}
int func(); // expected-error {{declaration of 'func' in module N follows declaration in module M}} expected-note@global-vs-module.cppm:36 {{previous}}
struct str; // expected-error {{declaration of 'str' in module N follows declaration in module M}} expected-note@global-vs-module.cppm:37 {{previous}}
extern int var; // expected-error {{declaration of 'var' in module N follows declaration in module M}} expected-note@M.cppm:4 {{previous}}
int func(); // expected-error {{declaration of 'func' in module N follows declaration in module M}} expected-note@M.cppm:5 {{previous}}
struct str; // expected-error {{declaration of 'str' in module N follows declaration in module M}} expected-note@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@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@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@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@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@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@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@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@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@M.cppm:4 {{previous}}
int func(); // expected-error {{declaration of 'func' in module N follows declaration in module M}} expected-note@M.cppm:5 {{previous}}
struct str; // expected-error {{declaration of 'str' in module N follows declaration in module M}} expected-note@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@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@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@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@M.cppm:12 {{previous}}
typedef int type;
namespace ns { using ::func; }
namespace ns_alias = ns;
#endif

View File

@ -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
//--- z_impl.cppm
module z; // expected-error {{module 'z' not found}}
EXPORT module MODULE_NAME;
#if TEST == 7
// expected-error@-2 {{expected ';'}} expected-error@-2 {{a type specifier is required}}
#elif TEST == 9
// expected-warning@-4 {{unknown attribute 'fancy' ignored}}
#elif TEST == 10
// expected-error-re@-6 {{'maybe_unused' attribute cannot be applied to a module{{$}}}}
#elif TEST == 1
// expected-error@-8 {{module 'z' not found}}
#else
//--- x_impl.cppm
// expected-no-diagnostics
#endif
module x;
//--- z_interface.cppm
// expected-no-diagnostics
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{{$}}}}

View File

@ -0,0 +1,8 @@
// RUN: rm -rf %t
// RUN: mkdir -p %t
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -verify -o %t/M.pcm
// This is a comment
#define I32 int // expected-note {{add 'module;' to the start of the file to introduce a global module fragment}}
export module M; // expected-error {{module declaration must occur at the start of the translation unit}}
export I32 i32;

View File

@ -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,19 +33,33 @@ 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 'blarg::noreturn' ignored}}
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 a module name after 'import'}}

View File

@ -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: %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: rm -rf %t
// RUN: split-file %s %t
#if INTERFACE
// 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 %t/AddExport2.cppm -emit-module-interface -verify -o /dev/null
//--- 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@-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
#ifndef INTERFACE
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
export int a;
#endif
//--- AddExport.cppm
module A; // #module-decl
export int b; // expected-error {{export declaration can only be used within a module purview}}
// expected-note@#module-decl {{add 'export' here}}
//--- AddExport2.cppm
module A; // expected-error {{missing 'export' specifier in module declaration while building module interface}}
export int a;

View File

@ -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
// RUN: rm -rf %t
// RUN: split-file %s %t
#ifdef FOO
FOO module foo; // expected-note {{previous module declaration is here}}
#endif
// 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
#ifdef BAR
BAR module bar; // expected-error {{translation unit contains multiple module declarations}}
#endif
//--- 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}}

View File

@ -1,22 +1,14 @@
// 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
#if defined(INTERFACE) || defined(EARLY_IMPLEMENTATION) || defined(IMPLEMENTATION)
module;
#endif
#ifdef USER
import Foo;
#endif
#ifdef EARLY_IMPLEMENTATION
module Foo;
#endif
template<typename T> struct type_template {
typedef T type;
@ -28,10 +20,49 @@ 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
#ifdef IMPLEMENTATION
//--- implementation.cppm
// expected-no-diagnostics
module;
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 {};
module Foo;
#endif
//--- early_impl.cppm
// expected-no-diagnostics
module;
module Foo;
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 {};
//--- user.cppm
// expected-no-diagnostics
import Foo;
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 {};

View File

@ -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

View File

@ -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
// 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
#if MODE == 0
//--- 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@-2 {{use of undeclared identifier 'n'}}
#endif
int k = n; // expected-error {{use of undeclared identifier 'n'}}

View File

@ -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;

View File

@ -58,6 +58,7 @@ void b(T x) {
}
//--- c.cppm
module;
#ifdef EXPORT_OPERATOR
// expected-no-diagnostics
#endif

View File

@ -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}}

View File

@ -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;

View File

@ -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;

View File

@ -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
// 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
#endif
export module foo;
static int m;
int n;
#if TEST == 0
export {
int a;
int b;
@ -27,10 +28,52 @@ export void f() {}
export struct T {
} t;
#elif TEST == 3
//--- test1.cpp
export module foo;
static int m;
int n;
struct S {
export int n; // expected-error {{expected member name or ';'}}
export static int n; // expected-error {{expected member name or ';'}}
};
// FIXME: Exports of declarations without external linkage are disallowed.
// Exports of declarations with non-external-linkage types are disallowed.
// Cannot export within another export. This isn't precisely covered by the
// language rules right now, but (per personal correspondence between zygoloid
// and gdr) is the intent.
export { // expected-note {{export block begins here}}
extern "C++" {
namespace NestedExport {
export { // expected-error {{export declaration appears within another export declaration}}
int q;
}
} // namespace NestedExport
}
}
//--- 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'}}
#undef foo
import foo; // expected-error {{imports must immediately follow the module declaration}}
export {}
@ -46,29 +89,3 @@ int use_n = n; // FIXME: this should not be visible, because it is not exported
extern int n;
static_assert(&n != p); // expected-error{{use of undeclared identifier 'p'}}
#endif
#if TEST == 1
struct S {
export int n; // expected-error {{expected member name or ';'}}
export static int n; // expected-error {{expected member name or ';'}}
};
#endif
// FIXME: Exports of declarations without external linkage are disallowed.
// Exports of declarations with non-external-linkage types are disallowed.
// Cannot export within another export. This isn't precisely covered by the
// language rules right now, but (per personal correspondence between zygoloid
// and gdr) is the intent.
#if TEST == 1
export { // expected-note {{export block begins here}}
extern "C++" {
namespace NestedExport {
export { // expected-error {{export declaration appears within another export declaration}}
int q;
}
} // namespace NestedExport
}
}
#endif

View File

@ -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__;

View File

@ -49,7 +49,8 @@ protected:
}
std::unique_ptr<Preprocessor> CreatePP(StringRef Source,
TrivialModuleLoader &ModLoader) {
TrivialModuleLoader &ModLoader,
StringRef PreDefines = {}) {
std::unique_ptr<llvm::MemoryBuffer> Buf =
llvm::MemoryBuffer::getMemBuffer(Source);
SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
@ -61,6 +62,8 @@ protected:
PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader,
/*IILookup =*/nullptr,
/*OwnsHeaderSearch =*/false);
if (!PreDefines.empty())
PP->setPredefines(PreDefines.str());
PP->Initialize(*Target);
PP->EnterMainSourceFile();
return PP;
@ -769,4 +772,46 @@ TEST(LexerPreambleTest, PreambleBounds) {
}
}
TEST_F(LexerTest, CheckFirstPPToken) {
{
TrivialModuleLoader ModLoader;
auto PP = CreatePP("// This is a comment\n"
"int a;",
ModLoader);
Token Tok;
PP->Lex(Tok);
EXPECT_TRUE(Tok.is(tok::kw_int));
EXPECT_TRUE(PP->hasSeenMainFileFirstPPToken());
EXPECT_TRUE(PP->getMainFileFirstPPToken().isFirstPPToken());
EXPECT_TRUE(PP->getMainFileFirstPPToken().is(tok::kw_int));
}
{
TrivialModuleLoader ModLoader;
auto PP = CreatePP("// This is a comment\n"
"#define FOO int\n"
"FOO a;",
ModLoader);
Token Tok;
PP->Lex(Tok);
EXPECT_TRUE(Tok.is(tok::kw_int));
EXPECT_TRUE(PP->hasSeenMainFileFirstPPToken());
EXPECT_TRUE(PP->getMainFileFirstPPToken().isFirstPPToken());
EXPECT_TRUE(PP->getMainFileFirstPPToken().is(tok::hash));
}
{
TrivialModuleLoader ModLoader;
auto PP = CreatePP("// This is a comment\n"
"FOO a;",
ModLoader, "#define FOO int\n");
Token Tok;
PP->Lex(Tok);
EXPECT_TRUE(Tok.is(tok::kw_int));
EXPECT_TRUE(PP->hasSeenMainFileFirstPPToken());
EXPECT_TRUE(PP->getMainFileFirstPPToken().isFirstPPToken());
EXPECT_TRUE(PP->getMainFileFirstPPToken().is(tok::identifier));
EXPECT_TRUE(
PP->getMainFileFirstPPToken().getIdentifierInfo()->isStr("FOO"));
}
}
} // anonymous namespace