xref: /llvm-project/clang/unittests/Serialization/LoadSpecLazilyTest.cpp (revision 7f4312015291a32d811a0f37e24b4d9736c524f7)
120e90495SChuanqi Xu //== unittests/Serialization/LoadSpecLazily.cpp ----------------------========//
220e90495SChuanqi Xu //
320e90495SChuanqi Xu // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
420e90495SChuanqi Xu // See https://llvm.org/LICENSE.txt for license information.
520e90495SChuanqi Xu // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
620e90495SChuanqi Xu //
720e90495SChuanqi Xu //===----------------------------------------------------------------------===//
820e90495SChuanqi Xu 
920e90495SChuanqi Xu #include "clang/Frontend/CompilerInstance.h"
1020e90495SChuanqi Xu #include "clang/Frontend/FrontendAction.h"
1120e90495SChuanqi Xu #include "clang/Frontend/FrontendActions.h"
1220e90495SChuanqi Xu #include "clang/Parse/ParseAST.h"
1320e90495SChuanqi Xu #include "clang/Serialization/ASTDeserializationListener.h"
1420e90495SChuanqi Xu #include "clang/Tooling/Tooling.h"
1520e90495SChuanqi Xu #include "gtest/gtest.h"
1620e90495SChuanqi Xu 
1720e90495SChuanqi Xu using namespace llvm;
1820e90495SChuanqi Xu using namespace clang;
1920e90495SChuanqi Xu using namespace clang::tooling;
2020e90495SChuanqi Xu 
2120e90495SChuanqi Xu namespace {
2220e90495SChuanqi Xu 
2320e90495SChuanqi Xu class LoadSpecLazilyTest : public ::testing::Test {
2420e90495SChuanqi Xu   void SetUp() override {
2520e90495SChuanqi Xu     ASSERT_FALSE(
2620e90495SChuanqi Xu         sys::fs::createUniqueDirectory("load-spec-lazily-test", TestDir));
2720e90495SChuanqi Xu   }
2820e90495SChuanqi Xu 
2920e90495SChuanqi Xu   void TearDown() override { sys::fs::remove_directories(TestDir); }
3020e90495SChuanqi Xu 
3120e90495SChuanqi Xu public:
3220e90495SChuanqi Xu   SmallString<256> TestDir;
3320e90495SChuanqi Xu 
3420e90495SChuanqi Xu   void addFile(StringRef Path, StringRef Contents) {
3520e90495SChuanqi Xu     ASSERT_FALSE(sys::path::is_absolute(Path));
3620e90495SChuanqi Xu 
3720e90495SChuanqi Xu     SmallString<256> AbsPath(TestDir);
3820e90495SChuanqi Xu     sys::path::append(AbsPath, Path);
3920e90495SChuanqi Xu 
4020e90495SChuanqi Xu     ASSERT_FALSE(
4120e90495SChuanqi Xu         sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
4220e90495SChuanqi Xu 
4320e90495SChuanqi Xu     std::error_code EC;
4420e90495SChuanqi Xu     llvm::raw_fd_ostream OS(AbsPath, EC);
4520e90495SChuanqi Xu     ASSERT_FALSE(EC);
4620e90495SChuanqi Xu     OS << Contents;
4720e90495SChuanqi Xu   }
4820e90495SChuanqi Xu 
4920e90495SChuanqi Xu   std::string GenerateModuleInterface(StringRef ModuleName,
5020e90495SChuanqi Xu                                       StringRef Contents) {
5120e90495SChuanqi Xu     std::string FileName = llvm::Twine(ModuleName + ".cppm").str();
5220e90495SChuanqi Xu     addFile(FileName, Contents);
5320e90495SChuanqi Xu 
5420e90495SChuanqi Xu     IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
5520e90495SChuanqi Xu         llvm::vfs::createPhysicalFileSystem();
5620e90495SChuanqi Xu     IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
5720e90495SChuanqi Xu         CompilerInstance::createDiagnostics(*VFS, new DiagnosticOptions());
5820e90495SChuanqi Xu     CreateInvocationOptions CIOpts;
5920e90495SChuanqi Xu     CIOpts.Diags = Diags;
6020e90495SChuanqi Xu     CIOpts.VFS = VFS;
6120e90495SChuanqi Xu 
6220e90495SChuanqi Xu     std::string CacheBMIPath =
6320e90495SChuanqi Xu         llvm::Twine(TestDir + "/" + ModuleName + ".pcm").str();
6420e90495SChuanqi Xu     std::string PrebuiltModulePath =
6520e90495SChuanqi Xu         "-fprebuilt-module-path=" + TestDir.str().str();
6620e90495SChuanqi Xu     const char *Args[] = {"clang++",
6720e90495SChuanqi Xu                           "-std=c++20",
6820e90495SChuanqi Xu                           "--precompile",
6920e90495SChuanqi Xu                           PrebuiltModulePath.c_str(),
7020e90495SChuanqi Xu                           "-working-directory",
7120e90495SChuanqi Xu                           TestDir.c_str(),
7220e90495SChuanqi Xu                           "-I",
7320e90495SChuanqi Xu                           TestDir.c_str(),
7420e90495SChuanqi Xu                           FileName.c_str(),
7520e90495SChuanqi Xu                           "-o",
7620e90495SChuanqi Xu                           CacheBMIPath.c_str()};
7720e90495SChuanqi Xu     std::shared_ptr<CompilerInvocation> Invocation =
7820e90495SChuanqi Xu         createInvocation(Args, CIOpts);
7920e90495SChuanqi Xu     EXPECT_TRUE(Invocation);
8020e90495SChuanqi Xu 
8120e90495SChuanqi Xu     CompilerInstance Instance;
8220e90495SChuanqi Xu     Instance.setDiagnostics(Diags.get());
8320e90495SChuanqi Xu     Instance.setInvocation(Invocation);
8420e90495SChuanqi Xu     Instance.getFrontendOpts().OutputFile = CacheBMIPath;
85*7f431201SIlya Biryukov     // Avoid memory leaks.
86*7f431201SIlya Biryukov     Instance.getFrontendOpts().DisableFree = false;
8720e90495SChuanqi Xu     GenerateModuleInterfaceAction Action;
8820e90495SChuanqi Xu     EXPECT_TRUE(Instance.ExecuteAction(Action));
8920e90495SChuanqi Xu     EXPECT_FALSE(Diags->hasErrorOccurred());
9020e90495SChuanqi Xu 
9120e90495SChuanqi Xu     return CacheBMIPath;
9220e90495SChuanqi Xu   }
9320e90495SChuanqi Xu };
9420e90495SChuanqi Xu 
9520e90495SChuanqi Xu enum class CheckingMode { Forbidden, Required };
9620e90495SChuanqi Xu 
9720e90495SChuanqi Xu class DeclsReaderListener : public ASTDeserializationListener {
9820e90495SChuanqi Xu   StringRef SpeficiedName;
9920e90495SChuanqi Xu   CheckingMode Mode;
10020e90495SChuanqi Xu 
10120e90495SChuanqi Xu   bool ReadedSpecifiedName = false;
10220e90495SChuanqi Xu 
10320e90495SChuanqi Xu public:
10420e90495SChuanqi Xu   void DeclRead(GlobalDeclID ID, const Decl *D) override {
10520e90495SChuanqi Xu     auto *ND = dyn_cast<NamedDecl>(D);
10620e90495SChuanqi Xu     if (!ND)
10720e90495SChuanqi Xu       return;
10820e90495SChuanqi Xu 
10920e90495SChuanqi Xu     ReadedSpecifiedName |= ND->getName().contains(SpeficiedName);
11020e90495SChuanqi Xu     if (Mode == CheckingMode::Forbidden) {
11120e90495SChuanqi Xu       EXPECT_FALSE(ReadedSpecifiedName);
11220e90495SChuanqi Xu     }
11320e90495SChuanqi Xu   }
11420e90495SChuanqi Xu 
11520e90495SChuanqi Xu   DeclsReaderListener(StringRef SpeficiedName, CheckingMode Mode)
11620e90495SChuanqi Xu       : SpeficiedName(SpeficiedName), Mode(Mode) {}
11720e90495SChuanqi Xu 
11820e90495SChuanqi Xu   ~DeclsReaderListener() {
11920e90495SChuanqi Xu     if (Mode == CheckingMode::Required) {
12020e90495SChuanqi Xu       EXPECT_TRUE(ReadedSpecifiedName);
12120e90495SChuanqi Xu     }
12220e90495SChuanqi Xu   }
12320e90495SChuanqi Xu };
12420e90495SChuanqi Xu 
12520e90495SChuanqi Xu class LoadSpecLazilyConsumer : public ASTConsumer {
12620e90495SChuanqi Xu   DeclsReaderListener Listener;
12720e90495SChuanqi Xu 
12820e90495SChuanqi Xu public:
12920e90495SChuanqi Xu   LoadSpecLazilyConsumer(StringRef SpecifiedName, CheckingMode Mode)
13020e90495SChuanqi Xu       : Listener(SpecifiedName, Mode) {}
13120e90495SChuanqi Xu 
13220e90495SChuanqi Xu   ASTDeserializationListener *GetASTDeserializationListener() override {
13320e90495SChuanqi Xu     return &Listener;
13420e90495SChuanqi Xu   }
13520e90495SChuanqi Xu };
13620e90495SChuanqi Xu 
13720e90495SChuanqi Xu class CheckLoadSpecLazilyAction : public ASTFrontendAction {
13820e90495SChuanqi Xu   StringRef SpecifiedName;
13920e90495SChuanqi Xu   CheckingMode Mode;
14020e90495SChuanqi Xu 
14120e90495SChuanqi Xu public:
14220e90495SChuanqi Xu   std::unique_ptr<ASTConsumer>
14320e90495SChuanqi Xu   CreateASTConsumer(CompilerInstance &CI, StringRef /*Unused*/) override {
14420e90495SChuanqi Xu     return std::make_unique<LoadSpecLazilyConsumer>(SpecifiedName, Mode);
14520e90495SChuanqi Xu   }
14620e90495SChuanqi Xu 
14720e90495SChuanqi Xu   CheckLoadSpecLazilyAction(StringRef SpecifiedName, CheckingMode Mode)
14820e90495SChuanqi Xu       : SpecifiedName(SpecifiedName), Mode(Mode) {}
14920e90495SChuanqi Xu };
15020e90495SChuanqi Xu 
15120e90495SChuanqi Xu TEST_F(LoadSpecLazilyTest, BasicTest) {
15220e90495SChuanqi Xu   GenerateModuleInterface("M", R"cpp(
15320e90495SChuanqi Xu export module M;
15420e90495SChuanqi Xu export template <class T>
15520e90495SChuanqi Xu class A {};
15620e90495SChuanqi Xu export class ShouldNotBeLoaded {};
15720e90495SChuanqi Xu export class Temp {
15820e90495SChuanqi Xu    A<ShouldNotBeLoaded> AS;
15920e90495SChuanqi Xu };
16020e90495SChuanqi Xu   )cpp");
16120e90495SChuanqi Xu 
16220e90495SChuanqi Xu   const char *test_file_contents = R"cpp(
16320e90495SChuanqi Xu import M;
16420e90495SChuanqi Xu A<int> a;
16520e90495SChuanqi Xu   )cpp";
16620e90495SChuanqi Xu   std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str();
16720e90495SChuanqi Xu   EXPECT_TRUE(
16820e90495SChuanqi Xu       runToolOnCodeWithArgs(std::make_unique<CheckLoadSpecLazilyAction>(
16920e90495SChuanqi Xu                                 "ShouldNotBeLoaded", CheckingMode::Forbidden),
17020e90495SChuanqi Xu                             test_file_contents,
17120e90495SChuanqi Xu                             {
17220e90495SChuanqi Xu                                 "-std=c++20",
17320e90495SChuanqi Xu                                 DepArg.c_str(),
17420e90495SChuanqi Xu                                 "-I",
17520e90495SChuanqi Xu                                 TestDir.c_str(),
17620e90495SChuanqi Xu                             },
17720e90495SChuanqi Xu                             "test.cpp"));
17820e90495SChuanqi Xu }
17920e90495SChuanqi Xu 
18020e90495SChuanqi Xu TEST_F(LoadSpecLazilyTest, ChainedTest) {
18120e90495SChuanqi Xu   GenerateModuleInterface("M", R"cpp(
18220e90495SChuanqi Xu export module M;
18320e90495SChuanqi Xu export template <class T>
18420e90495SChuanqi Xu class A {};
18520e90495SChuanqi Xu   )cpp");
18620e90495SChuanqi Xu 
18720e90495SChuanqi Xu   GenerateModuleInterface("N", R"cpp(
18820e90495SChuanqi Xu export module N;
18920e90495SChuanqi Xu export import M;
19020e90495SChuanqi Xu export class ShouldNotBeLoaded {};
19120e90495SChuanqi Xu export class Temp {
19220e90495SChuanqi Xu    A<ShouldNotBeLoaded> AS;
19320e90495SChuanqi Xu };
19420e90495SChuanqi Xu   )cpp");
19520e90495SChuanqi Xu 
19620e90495SChuanqi Xu   const char *test_file_contents = R"cpp(
19720e90495SChuanqi Xu import N;
19820e90495SChuanqi Xu A<int> a;
19920e90495SChuanqi Xu   )cpp";
20020e90495SChuanqi Xu   std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str();
20120e90495SChuanqi Xu   EXPECT_TRUE(
20220e90495SChuanqi Xu       runToolOnCodeWithArgs(std::make_unique<CheckLoadSpecLazilyAction>(
20320e90495SChuanqi Xu                                 "ShouldNotBeLoaded", CheckingMode::Forbidden),
20420e90495SChuanqi Xu                             test_file_contents,
20520e90495SChuanqi Xu                             {
20620e90495SChuanqi Xu                                 "-std=c++20",
20720e90495SChuanqi Xu                                 DepArg.c_str(),
20820e90495SChuanqi Xu                                 "-I",
20920e90495SChuanqi Xu                                 TestDir.c_str(),
21020e90495SChuanqi Xu                             },
21120e90495SChuanqi Xu                             "test.cpp"));
21220e90495SChuanqi Xu }
21320e90495SChuanqi Xu 
21420e90495SChuanqi Xu /// Test that we won't crash due to we may invalidate the lazy specialization
21520e90495SChuanqi Xu /// lookup table during the loading process.
21620e90495SChuanqi Xu TEST_F(LoadSpecLazilyTest, ChainedTest2) {
21720e90495SChuanqi Xu   GenerateModuleInterface("M", R"cpp(
21820e90495SChuanqi Xu export module M;
21920e90495SChuanqi Xu export template <class T>
22020e90495SChuanqi Xu class A {};
22120e90495SChuanqi Xu 
22220e90495SChuanqi Xu export class B {};
22320e90495SChuanqi Xu 
22420e90495SChuanqi Xu export class C {
22520e90495SChuanqi Xu   A<B> D;
22620e90495SChuanqi Xu };
22720e90495SChuanqi Xu   )cpp");
22820e90495SChuanqi Xu 
22920e90495SChuanqi Xu   GenerateModuleInterface("N", R"cpp(
23020e90495SChuanqi Xu export module N;
23120e90495SChuanqi Xu export import M;
23220e90495SChuanqi Xu export class MayBeLoaded {};
23320e90495SChuanqi Xu 
23420e90495SChuanqi Xu export class Temp {
23520e90495SChuanqi Xu    A<MayBeLoaded> AS;
23620e90495SChuanqi Xu };
23720e90495SChuanqi Xu 
23820e90495SChuanqi Xu export class ExportedClass {};
23920e90495SChuanqi Xu 
24020e90495SChuanqi Xu export template<> class A<ExportedClass> {
24120e90495SChuanqi Xu    A<MayBeLoaded> AS;
24220e90495SChuanqi Xu    A<B>           AB;
24320e90495SChuanqi Xu };
24420e90495SChuanqi Xu   )cpp");
24520e90495SChuanqi Xu 
24620e90495SChuanqi Xu   const char *test_file_contents = R"cpp(
24720e90495SChuanqi Xu import N;
24820e90495SChuanqi Xu Temp T;
24920e90495SChuanqi Xu A<ExportedClass> a;
25020e90495SChuanqi Xu   )cpp";
25120e90495SChuanqi Xu   std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str();
25220e90495SChuanqi Xu   EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique<CheckLoadSpecLazilyAction>(
25320e90495SChuanqi Xu                                         "MayBeLoaded", CheckingMode::Required),
25420e90495SChuanqi Xu                                     test_file_contents,
25520e90495SChuanqi Xu                                     {
25620e90495SChuanqi Xu                                         "-std=c++20",
25720e90495SChuanqi Xu                                         DepArg.c_str(),
25820e90495SChuanqi Xu                                         "-I",
25920e90495SChuanqi Xu                                         TestDir.c_str(),
26020e90495SChuanqi Xu                                     },
26120e90495SChuanqi Xu                                     "test.cpp"));
26220e90495SChuanqi Xu }
26320e90495SChuanqi Xu 
26420e90495SChuanqi Xu } // namespace
265