blob: eefa4be9fbee52276a8283908313a91bebc0f28b [file] [log] [blame] [edit]
//== unittests/Serialization/NamespaceLookupOptimizationTest.cpp =======//
//
// 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/Driver/CreateInvocationFromArgs.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Parse/ParseAST.h"
#include "clang/Serialization/ASTReader.h"
#include "clang/Tooling/Tooling.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace clang;
using namespace clang::tooling;
namespace {
class NamespaceLookupTest : public ::testing::Test {
void SetUp() override {
ASSERT_FALSE(
sys::fs::createUniqueDirectory("namespace-lookup-test", TestDir));
}
void TearDown() override { sys::fs::remove_directories(TestDir); }
public:
SmallString<256> TestDir;
void addFile(StringRef Path, StringRef Contents) {
ASSERT_FALSE(sys::path::is_absolute(Path));
SmallString<256> AbsPath(TestDir);
sys::path::append(AbsPath, Path);
ASSERT_FALSE(
sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
std::error_code EC;
llvm::raw_fd_ostream OS(AbsPath, EC);
ASSERT_FALSE(EC);
OS << Contents;
}
std::string GenerateModuleInterface(StringRef ModuleName,
StringRef Contents) {
std::string FileName = llvm::Twine(ModuleName + ".cppm").str();
addFile(FileName, Contents);
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
llvm::vfs::createPhysicalFileSystem();
DiagnosticOptions DiagOpts;
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
CompilerInstance::createDiagnostics(*VFS, DiagOpts);
CreateInvocationOptions CIOpts;
CIOpts.Diags = Diags;
CIOpts.VFS = VFS;
std::string CacheBMIPath =
llvm::Twine(TestDir + "/" + ModuleName + ".pcm").str();
std::string PrebuiltModulePath =
"-fprebuilt-module-path=" + TestDir.str().str();
const char *Args[] = {"clang++",
"-std=c++20",
"--precompile",
PrebuiltModulePath.c_str(),
"-working-directory",
TestDir.c_str(),
"-I",
TestDir.c_str(),
FileName.c_str(),
"-o",
CacheBMIPath.c_str()};
std::shared_ptr<CompilerInvocation> Invocation =
createInvocation(Args, CIOpts);
EXPECT_TRUE(Invocation);
CompilerInstance Instance(std::move(Invocation));
Instance.setDiagnostics(Diags);
Instance.getFrontendOpts().OutputFile = CacheBMIPath;
// Avoid memory leaks.
Instance.getFrontendOpts().DisableFree = false;
GenerateModuleInterfaceAction Action;
EXPECT_TRUE(Instance.ExecuteAction(Action));
EXPECT_FALSE(Diags->hasErrorOccurred());
return CacheBMIPath;
}
};
struct NamespaceLookupResult {
int NumLocalNamespaces = 0;
int NumExternalNamespaces = 0;
};
class NamespaceLookupConsumer : public ASTConsumer {
NamespaceLookupResult &Result;
public:
explicit NamespaceLookupConsumer(NamespaceLookupResult &Result)
: Result(Result) {}
void HandleTranslationUnit(ASTContext &Context) override {
TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
ASSERT_TRUE(TU);
ASTReader *Chain = dyn_cast_or_null<ASTReader>(Context.getExternalSource());
ASSERT_TRUE(Chain);
for (const Decl *D :
TU->lookup(DeclarationName(&Context.Idents.get("N")))) {
if (!isa<NamespaceDecl>(D))
continue;
if (!D->isFromASTFile()) {
++Result.NumLocalNamespaces;
} else {
++Result.NumExternalNamespaces;
EXPECT_EQ(D, Chain->getKeyDeclaration(D));
}
}
}
};
class NamespaceLookupAction : public ASTFrontendAction {
NamespaceLookupResult &Result;
public:
explicit NamespaceLookupAction(NamespaceLookupResult &Result)
: Result(Result) {}
std::unique_ptr<ASTConsumer>
CreateASTConsumer(CompilerInstance &CI, StringRef /*Unused*/) override {
return std::make_unique<NamespaceLookupConsumer>(Result);
}
};
TEST_F(NamespaceLookupTest, ExternalNamespacesOnly) {
GenerateModuleInterface("M1", R"cpp(
export module M1;
namespace N {}
)cpp");
GenerateModuleInterface("M2", R"cpp(
export module M2;
namespace N {}
)cpp");
GenerateModuleInterface("M3", R"cpp(
export module M3;
namespace N {}
)cpp");
const char *test_file_contents = R"cpp(
import M1;
import M2;
import M3;
)cpp";
std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str();
NamespaceLookupResult Result;
EXPECT_TRUE(runToolOnCodeWithArgs(
std::make_unique<NamespaceLookupAction>(Result), test_file_contents,
{
"-std=c++20",
DepArg.c_str(),
"-I",
TestDir.c_str(),
},
"main.cpp"));
EXPECT_EQ(0, Result.NumLocalNamespaces);
EXPECT_EQ(1, Result.NumExternalNamespaces);
}
TEST_F(NamespaceLookupTest, ExternalReplacedByLocal) {
GenerateModuleInterface("M1", R"cpp(
export module M1;
namespace N {}
)cpp");
GenerateModuleInterface("M2", R"cpp(
export module M2;
namespace N {}
)cpp");
GenerateModuleInterface("M3", R"cpp(
export module M3;
namespace N {}
)cpp");
const char *test_file_contents = R"cpp(
import M1;
import M2;
import M3;
namespace N {}
)cpp";
std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str();
NamespaceLookupResult Result;
EXPECT_TRUE(runToolOnCodeWithArgs(
std::make_unique<NamespaceLookupAction>(Result), test_file_contents,
{
"-std=c++20",
DepArg.c_str(),
"-I",
TestDir.c_str(),
},
"main.cpp"));
EXPECT_EQ(1, Result.NumLocalNamespaces);
EXPECT_EQ(0, Result.NumExternalNamespaces);
}
TEST_F(NamespaceLookupTest, LocalAndExternalInterleaved) {
GenerateModuleInterface("M1", R"cpp(
export module M1;
namespace N {}
)cpp");
GenerateModuleInterface("M2", R"cpp(
export module M2;
namespace N {}
)cpp");
GenerateModuleInterface("M3", R"cpp(
export module M3;
namespace N {}
)cpp");
const char *test_file_contents = R"cpp(
import M1;
namespace N {}
import M2;
import M3;
)cpp";
std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str();
NamespaceLookupResult Result;
EXPECT_TRUE(runToolOnCodeWithArgs(
std::make_unique<NamespaceLookupAction>(Result), test_file_contents,
{
"-std=c++20",
DepArg.c_str(),
"-I",
TestDir.c_str(),
},
"main.cpp"));
EXPECT_EQ(1, Result.NumLocalNamespaces);
EXPECT_EQ(1, Result.NumExternalNamespaces);
}
} // namespace