1 //== unittests/Serialization/LoadSpecLazily.cpp ----------------------========//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "clang/Frontend/CompilerInstance.h"
10 #include "clang/Frontend/FrontendAction.h"
11 #include "clang/Frontend/FrontendActions.h"
12 #include "clang/Parse/ParseAST.h"
13 #include "clang/Serialization/ASTDeserializationListener.h"
14 #include "clang/Tooling/Tooling.h"
15 #include "gtest/gtest.h"
18 using namespace clang
;
19 using namespace clang::tooling
;
23 class LoadSpecLazilyTest
: public ::testing::Test
{
24 void SetUp() override
{
26 sys::fs::createUniqueDirectory("load-spec-lazily-test", TestDir
));
29 void TearDown() override
{ sys::fs::remove_directories(TestDir
); }
32 SmallString
<256> TestDir
;
34 void addFile(StringRef Path
, StringRef Contents
) {
35 ASSERT_FALSE(sys::path::is_absolute(Path
));
37 SmallString
<256> AbsPath(TestDir
);
38 sys::path::append(AbsPath
, Path
);
41 sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath
)));
44 llvm::raw_fd_ostream
OS(AbsPath
, EC
);
49 std::string
GenerateModuleInterface(StringRef ModuleName
,
51 std::string FileName
= llvm::Twine(ModuleName
+ ".cppm").str();
52 addFile(FileName
, Contents
);
54 IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> VFS
=
55 llvm::vfs::createPhysicalFileSystem();
56 IntrusiveRefCntPtr
<DiagnosticsEngine
> Diags
=
57 CompilerInstance::createDiagnostics(*VFS
, new DiagnosticOptions());
58 CreateInvocationOptions CIOpts
;
62 std::string CacheBMIPath
=
63 llvm::Twine(TestDir
+ "/" + ModuleName
+ ".pcm").str();
64 std::string PrebuiltModulePath
=
65 "-fprebuilt-module-path=" + TestDir
.str().str();
66 const char *Args
[] = {"clang++",
69 PrebuiltModulePath
.c_str(),
76 CacheBMIPath
.c_str()};
77 std::shared_ptr
<CompilerInvocation
> Invocation
=
78 createInvocation(Args
, CIOpts
);
79 EXPECT_TRUE(Invocation
);
81 CompilerInstance Instance
;
82 Instance
.setDiagnostics(Diags
.get());
83 Instance
.setInvocation(Invocation
);
84 Instance
.getFrontendOpts().OutputFile
= CacheBMIPath
;
85 // Avoid memory leaks.
86 Instance
.getFrontendOpts().DisableFree
= false;
87 GenerateModuleInterfaceAction Action
;
88 EXPECT_TRUE(Instance
.ExecuteAction(Action
));
89 EXPECT_FALSE(Diags
->hasErrorOccurred());
95 enum class CheckingMode
{ Forbidden
, Required
};
97 class DeclsReaderListener
: public ASTDeserializationListener
{
98 StringRef SpeficiedName
;
101 bool ReadedSpecifiedName
= false;
104 void DeclRead(GlobalDeclID ID
, const Decl
*D
) override
{
105 auto *ND
= dyn_cast
<NamedDecl
>(D
);
109 ReadedSpecifiedName
|= ND
->getName().contains(SpeficiedName
);
110 if (Mode
== CheckingMode::Forbidden
) {
111 EXPECT_FALSE(ReadedSpecifiedName
);
115 DeclsReaderListener(StringRef SpeficiedName
, CheckingMode Mode
)
116 : SpeficiedName(SpeficiedName
), Mode(Mode
) {}
118 ~DeclsReaderListener() {
119 if (Mode
== CheckingMode::Required
) {
120 EXPECT_TRUE(ReadedSpecifiedName
);
125 class LoadSpecLazilyConsumer
: public ASTConsumer
{
126 DeclsReaderListener Listener
;
129 LoadSpecLazilyConsumer(StringRef SpecifiedName
, CheckingMode Mode
)
130 : Listener(SpecifiedName
, Mode
) {}
132 ASTDeserializationListener
*GetASTDeserializationListener() override
{
137 class CheckLoadSpecLazilyAction
: public ASTFrontendAction
{
138 StringRef SpecifiedName
;
142 std::unique_ptr
<ASTConsumer
>
143 CreateASTConsumer(CompilerInstance
&CI
, StringRef
/*Unused*/) override
{
144 return std::make_unique
<LoadSpecLazilyConsumer
>(SpecifiedName
, Mode
);
147 CheckLoadSpecLazilyAction(StringRef SpecifiedName
, CheckingMode Mode
)
148 : SpecifiedName(SpecifiedName
), Mode(Mode
) {}
151 TEST_F(LoadSpecLazilyTest
, BasicTest
) {
152 GenerateModuleInterface("M", R
"cpp(
154 export template <class T>
156 export class ShouldNotBeLoaded {};
158 A<ShouldNotBeLoaded> AS;
162 const char *test_file_contents
= R
"cpp(
166 std::string DepArg
= "-fprebuilt-module-path=" + TestDir
.str().str();
168 runToolOnCodeWithArgs(std::make_unique
<CheckLoadSpecLazilyAction
>(
169 "ShouldNotBeLoaded", CheckingMode::Forbidden
),
180 TEST_F(LoadSpecLazilyTest
, ChainedTest
) {
181 GenerateModuleInterface("M", R
"cpp(
183 export template <class T>
187 GenerateModuleInterface("N", R
"cpp(
190 export class ShouldNotBeLoaded {};
192 A<ShouldNotBeLoaded> AS;
196 const char *test_file_contents
= R
"cpp(
200 std::string DepArg
= "-fprebuilt-module-path=" + TestDir
.str().str();
202 runToolOnCodeWithArgs(std::make_unique
<CheckLoadSpecLazilyAction
>(
203 "ShouldNotBeLoaded", CheckingMode::Forbidden
),
214 /// Test that we won't crash due to we may invalidate the lazy specialization
215 /// lookup table during the loading process.
216 TEST_F(LoadSpecLazilyTest
, ChainedTest2
) {
217 GenerateModuleInterface("M", R
"cpp(
219 export template <class T>
229 GenerateModuleInterface("N", R
"cpp(
232 export class MayBeLoaded {};
238 export class ExportedClass {};
240 export template<> class A<ExportedClass> {
246 const char *test_file_contents
= R
"cpp(
251 std::string DepArg
= "-fprebuilt-module-path=" + TestDir
.str().str();
252 EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique
<CheckLoadSpecLazilyAction
>(
253 "MayBeLoaded", CheckingMode::Required
),