blob: ed694384da57cf1bf06fbcfd9c0427a81ac3e31b [file] [log] [blame]
//===- unittests/Lex/ModuleDeclStateTest.cpp - PPCallbacks tests ------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===--------------------------------------------------------------===//
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/HeaderSearchOptions.h"
#include "clang/Lex/ModuleLoader.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "gtest/gtest.h"
#include <cstddef>
#include <initializer_list>
using namespace clang;
namespace {
class CheckNamedModuleImportingCB : public PPCallbacks {
Preprocessor &PP;
std::vector<bool> IsImportingNamedModulesAssertions;
std::size_t NextCheckingIndex;
public:
CheckNamedModuleImportingCB(Preprocessor &PP,
std::initializer_list<bool> lists)
: PP(PP), IsImportingNamedModulesAssertions(lists), NextCheckingIndex(0) {
}
void moduleImport(SourceLocation ImportLoc, ModuleIdPath Path,
const Module *Imported) override {
ASSERT_TRUE(NextCheckingIndex < IsImportingNamedModulesAssertions.size());
EXPECT_EQ(PP.isInImportingCXXNamedModules(),
IsImportingNamedModulesAssertions[NextCheckingIndex]);
NextCheckingIndex++;
ASSERT_EQ(Imported, nullptr);
}
// Currently, only the named module will be handled by `moduleImport`
// callback.
std::size_t importNamedModuleNum() { return NextCheckingIndex; }
};
class ModuleDeclStateTest : public ::testing::Test {
protected:
ModuleDeclStateTest()
: FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()),
Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()),
SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions) {
TargetOpts->Triple = "x86_64-unknown-linux-gnu";
Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
}
std::unique_ptr<Preprocessor>
getPreprocessor(const char *source, Language Lang) {
std::unique_ptr<llvm::MemoryBuffer> Buf =
llvm::MemoryBuffer::getMemBuffer(source);
SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
std::vector<std::string> Includes;
LangOptions::setLangDefaults(LangOpts, Lang, Target->getTriple(), Includes, LangStandard::lang_cxx20);
LangOpts.CPlusPlusModules = true;
if (Lang != Language::CXX) {
LangOpts.Modules = true;
LangOpts.ImplicitModules = true;
}
HeaderInfo.emplace(std::make_shared<HeaderSearchOptions>(), SourceMgr,
Diags, LangOpts, Target.get());
return std::make_unique<Preprocessor>(
std::make_shared<PreprocessorOptions>(), Diags, LangOpts, SourceMgr,
*HeaderInfo, ModLoader,
/*IILookup =*/nullptr,
/*OwnsHeaderSearch =*/false);
}
void preprocess(Preprocessor &PP, std::unique_ptr<PPCallbacks> C) {
PP.Initialize(*Target);
PP.addPPCallbacks(std::move(C));
PP.EnterMainSourceFile();
while (1) {
Token tok;
PP.Lex(tok);
if (tok.is(tok::eof))
break;
}
}
FileSystemOptions FileMgrOpts;
FileManager FileMgr;
IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
DiagnosticsEngine Diags;
SourceManager SourceMgr;
std::shared_ptr<TargetOptions> TargetOpts;
IntrusiveRefCntPtr<TargetInfo> Target;
LangOptions LangOpts;
TrivialModuleLoader ModLoader;
std::optional<HeaderSearch> HeaderInfo;
};
TEST_F(ModuleDeclStateTest, NamedModuleInterface) {
const char *source = R"(
export module foo;
)";
std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX);
std::initializer_list<bool> ImportKinds = {};
preprocess(*PP,
std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds));
auto *Callback =
static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks());
EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0);
EXPECT_TRUE(PP->isInNamedModule());
EXPECT_TRUE(PP->isInNamedInterfaceUnit());
EXPECT_FALSE(PP->isInImplementationUnit());
EXPECT_EQ(PP->getNamedModuleName(), "foo");
}
TEST_F(ModuleDeclStateTest, NamedModuleImplementation) {
const char *source = R"(
module foo;
)";
std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX);
std::initializer_list<bool> ImportKinds = {};
preprocess(*PP,
std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds));
auto *Callback =
static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks());
EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0);
EXPECT_TRUE(PP->isInNamedModule());
EXPECT_FALSE(PP->isInNamedInterfaceUnit());
EXPECT_TRUE(PP->isInImplementationUnit());
EXPECT_EQ(PP->getNamedModuleName(), "foo");
}
TEST_F(ModuleDeclStateTest, ModuleImplementationPartition) {
const char *source = R"(
module foo:part;
)";
std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX);
std::initializer_list<bool> ImportKinds = {};
preprocess(*PP,
std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds));
auto *Callback =
static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks());
EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0);
EXPECT_TRUE(PP->isInNamedModule());
EXPECT_FALSE(PP->isInNamedInterfaceUnit());
EXPECT_FALSE(PP->isInImplementationUnit());
EXPECT_EQ(PP->getNamedModuleName(), "foo:part");
}
TEST_F(ModuleDeclStateTest, ModuleInterfacePartition) {
const char *source = R"(
export module foo:part;
)";
std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX);
std::initializer_list<bool> ImportKinds = {};
preprocess(*PP,
std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds));
auto *Callback =
static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks());
EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0);
EXPECT_TRUE(PP->isInNamedModule());
EXPECT_TRUE(PP->isInNamedInterfaceUnit());
EXPECT_FALSE(PP->isInImplementationUnit());
EXPECT_EQ(PP->getNamedModuleName(), "foo:part");
}
TEST_F(ModuleDeclStateTest, ModuleNameWithDot) {
const char *source = R"(
export module foo.dot:part.dot;
)";
std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX);
std::initializer_list<bool> ImportKinds = {};
preprocess(*PP,
std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds));
auto *Callback =
static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks());
EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0);
EXPECT_TRUE(PP->isInNamedModule());
EXPECT_TRUE(PP->isInNamedInterfaceUnit());
EXPECT_FALSE(PP->isInImplementationUnit());
EXPECT_EQ(PP->getNamedModuleName(), "foo.dot:part.dot");
}
TEST_F(ModuleDeclStateTest, NotModule) {
const char *source = R"(
// export module foo:part;
)";
std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX);
std::initializer_list<bool> ImportKinds = {};
preprocess(*PP,
std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds));
auto *Callback =
static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks());
EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0);
EXPECT_FALSE(PP->isInNamedModule());
EXPECT_FALSE(PP->isInNamedInterfaceUnit());
EXPECT_FALSE(PP->isInImplementationUnit());
}
TEST_F(ModuleDeclStateTest, ModuleWithGMF) {
const char *source = R"(
module;
#include "bar.h"
#include <zoo.h>
import "bar";
import <zoo>;
export module foo:part;
import "HU";
import M;
import :another;
)";
std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX);
std::initializer_list<bool> ImportKinds = {true, true};
preprocess(*PP,
std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds));
auto *Callback =
static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks());
EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)2);
EXPECT_TRUE(PP->isInNamedModule());
EXPECT_TRUE(PP->isInNamedInterfaceUnit());
EXPECT_FALSE(PP->isInImplementationUnit());
EXPECT_EQ(PP->getNamedModuleName(), "foo:part");
}
TEST_F(ModuleDeclStateTest, ModuleWithGMFWithClangNamedModule) {
const char *source = R"(
module;
#include "bar.h"
#include <zoo.h>
import "bar";
import <zoo>;
export module foo:part;
import "HU";
import M;
import :another;
)";
std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX);
std::initializer_list<bool> ImportKinds = {true, true};
preprocess(*PP,
std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds));
auto *Callback =
static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks());
EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)2);
EXPECT_TRUE(PP->isInNamedModule());
EXPECT_TRUE(PP->isInNamedInterfaceUnit());
EXPECT_FALSE(PP->isInImplementationUnit());
EXPECT_EQ(PP->getNamedModuleName(), "foo:part");
}
TEST_F(ModuleDeclStateTest, ImportsInNormalTU) {
const char *source = R"(
#include "bar.h"
#include <zoo.h>
import "bar";
import <zoo>;
import "HU";
import M;
// We can't import a partition in non-module TU.
import :another;
)";
std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX);
std::initializer_list<bool> ImportKinds = {true};
preprocess(*PP,
std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds));
auto *Callback =
static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks());
EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)1);
EXPECT_FALSE(PP->isInNamedModule());
EXPECT_FALSE(PP->isInNamedInterfaceUnit());
EXPECT_FALSE(PP->isInImplementationUnit());
}
TEST_F(ModuleDeclStateTest, ImportAClangNamedModule) {
const char *source = R"(
@import anything;
)";
std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::ObjCXX);
std::initializer_list<bool> ImportKinds = {false};
preprocess(*PP,
std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds));
auto *Callback =
static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks());
EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)1);
EXPECT_FALSE(PP->isInNamedModule());
EXPECT_FALSE(PP->isInNamedInterfaceUnit());
EXPECT_FALSE(PP->isInImplementationUnit());
}
TEST_F(ModuleDeclStateTest, ImportWixedForm) {
const char *source = R"(
import "HU";
@import anything;
import M;
@import another;
import M2;
)";
std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::ObjCXX);
std::initializer_list<bool> ImportKinds = {false, true, false, true};
preprocess(*PP,
std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds));
auto *Callback =
static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks());
EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)4);
EXPECT_FALSE(PP->isInNamedModule());
EXPECT_FALSE(PP->isInNamedInterfaceUnit());
EXPECT_FALSE(PP->isInImplementationUnit());
}
} // namespace