1//===- unittests/Lex/ModuleDeclStateTest.cpp - PPCallbacks tests ------===//
2//
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
6//
7//===--------------------------------------------------------------===//
8
9#include "clang/Basic/Diagnostic.h"
10#include "clang/Basic/DiagnosticOptions.h"
11#include "clang/Basic/FileManager.h"
12#include "clang/Basic/LangOptions.h"
13#include "clang/Basic/SourceManager.h"
14#include "clang/Basic/TargetInfo.h"
15#include "clang/Basic/TargetOptions.h"
16#include "clang/Lex/HeaderSearch.h"
17#include "clang/Lex/HeaderSearchOptions.h"
18#include "clang/Lex/ModuleLoader.h"
19#include "clang/Lex/Preprocessor.h"
20#include "clang/Lex/PreprocessorOptions.h"
21#include "gtest/gtest.h"
22#include <cstddef>
23#include <initializer_list>
24
25using namespace clang;
26
27namespace {
28
29class CheckNamedModuleImportingCB : public PPCallbacks {
30 Preprocessor &PP;
31 std::vector<bool> IsImportingNamedModulesAssertions;
32 std::size_t NextCheckingIndex;
33
34public:
35 CheckNamedModuleImportingCB(Preprocessor &PP,
36 std::initializer_list<bool> lists)
37 : PP(PP), IsImportingNamedModulesAssertions(lists), NextCheckingIndex(0) {
38 }
39
40 void moduleImport(SourceLocation ImportLoc, ModuleIdPath Path,
41 const Module *Imported) override {
42 ASSERT_TRUE(NextCheckingIndex < IsImportingNamedModulesAssertions.size());
43 EXPECT_EQ(PP.isInImportingCXXNamedModules(),
44 IsImportingNamedModulesAssertions[NextCheckingIndex]);
45 NextCheckingIndex++;
46
47 ASSERT_EQ(Imported, nullptr);
48 }
49
50 // Currently, only the named module will be handled by `moduleImport`
51 // callback.
52 std::size_t importNamedModuleNum() { return NextCheckingIndex; }
53};
54class ModuleDeclStateTest : public ::testing::Test {
55protected:
56 ModuleDeclStateTest()
57 : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()),
58 Diags(DiagID, DiagOpts, new IgnoringDiagConsumer()),
59 SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions) {
60 TargetOpts->Triple = "x86_64-unknown-linux-gnu";
61 Target = TargetInfo::CreateTargetInfo(Diags, Opts&: *TargetOpts);
62 }
63
64 std::unique_ptr<Preprocessor> getPreprocessor(const char *source,
65 Language Lang) {
66 std::unique_ptr<llvm::MemoryBuffer> Buf =
67 llvm::MemoryBuffer::getMemBuffer(InputData: source);
68 SourceMgr.setMainFileID(SourceMgr.createFileID(Buffer: std::move(Buf)));
69
70 std::vector<std::string> Includes;
71 LangOptions::setLangDefaults(Opts&: LangOpts, Lang, T: Target->getTriple(), Includes,
72 LangStd: LangStandard::lang_cxx20);
73 LangOpts.CPlusPlusModules = true;
74 if (Lang != Language::CXX) {
75 LangOpts.Modules = true;
76 LangOpts.ImplicitModules = true;
77 }
78
79 HeaderInfo.emplace(args&: HSOpts, args&: SourceMgr, args&: Diags, args&: LangOpts, args: Target.get());
80
81 return std::make_unique<Preprocessor>(args&: PPOpts, args&: Diags, args&: LangOpts, args&: SourceMgr,
82 args&: *HeaderInfo, args&: ModLoader,
83 /*IILookup=*/args: nullptr,
84 /*OwnsHeaderSearch=*/args: false);
85 }
86
87 void preprocess(Preprocessor &PP, std::unique_ptr<PPCallbacks> C) {
88 PP.Initialize(Target: *Target);
89 PP.addPPCallbacks(C: std::move(C));
90 PP.EnterMainSourceFile();
91
92 PP.LexTokensUntilEOF();
93 }
94
95 FileSystemOptions FileMgrOpts;
96 FileManager FileMgr;
97 IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
98 DiagnosticOptions DiagOpts;
99 DiagnosticsEngine Diags;
100 SourceManager SourceMgr;
101 std::shared_ptr<TargetOptions> TargetOpts;
102 IntrusiveRefCntPtr<TargetInfo> Target;
103 LangOptions LangOpts;
104 TrivialModuleLoader ModLoader;
105 HeaderSearchOptions HSOpts;
106 std::optional<HeaderSearch> HeaderInfo;
107 PreprocessorOptions PPOpts;
108};
109
110TEST_F(ModuleDeclStateTest, NamedModuleInterface) {
111 const char *source = R"(
112export module foo;
113 )";
114 std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX);
115
116 std::initializer_list<bool> ImportKinds = {};
117 auto Callback =
118 std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds);
119 CheckNamedModuleImportingCB *CallbackPtr = Callback.get();
120 preprocess(PP&: *PP, C: std::move(Callback));
121 EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0);
122 EXPECT_TRUE(PP->isInNamedModule());
123 EXPECT_TRUE(PP->isInNamedInterfaceUnit());
124 EXPECT_FALSE(PP->isInImplementationUnit());
125 EXPECT_EQ(PP->getNamedModuleName(), "foo");
126}
127
128TEST_F(ModuleDeclStateTest, NamedModuleImplementation) {
129 const char *source = R"(
130module foo;
131 )";
132 std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX);
133
134 std::initializer_list<bool> ImportKinds = {};
135 auto Callback =
136 std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds);
137 CheckNamedModuleImportingCB *CallbackPtr = Callback.get();
138 preprocess(PP&: *PP, C: std::move(Callback));
139 EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0);
140 EXPECT_TRUE(PP->isInNamedModule());
141 EXPECT_FALSE(PP->isInNamedInterfaceUnit());
142 EXPECT_TRUE(PP->isInImplementationUnit());
143 EXPECT_EQ(PP->getNamedModuleName(), "foo");
144}
145
146TEST_F(ModuleDeclStateTest, ModuleImplementationPartition) {
147 const char *source = R"(
148module foo:part;
149 )";
150 std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX);
151
152 std::initializer_list<bool> ImportKinds = {};
153 auto Callback =
154 std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds);
155 CheckNamedModuleImportingCB *CallbackPtr = Callback.get();
156 preprocess(PP&: *PP, C: std::move(Callback));
157 EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0);
158 EXPECT_TRUE(PP->isInNamedModule());
159 EXPECT_FALSE(PP->isInNamedInterfaceUnit());
160 EXPECT_FALSE(PP->isInImplementationUnit());
161 EXPECT_EQ(PP->getNamedModuleName(), "foo:part");
162}
163
164TEST_F(ModuleDeclStateTest, ModuleInterfacePartition) {
165 const char *source = R"(
166export module foo:part;
167 )";
168 std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX);
169
170 std::initializer_list<bool> ImportKinds = {};
171 auto Callback =
172 std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds);
173 CheckNamedModuleImportingCB *CallbackPtr = Callback.get();
174 preprocess(PP&: *PP, C: std::move(Callback));
175 EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0);
176 EXPECT_TRUE(PP->isInNamedModule());
177 EXPECT_TRUE(PP->isInNamedInterfaceUnit());
178 EXPECT_FALSE(PP->isInImplementationUnit());
179 EXPECT_EQ(PP->getNamedModuleName(), "foo:part");
180}
181
182TEST_F(ModuleDeclStateTest, ModuleNameWithDot) {
183 const char *source = R"(
184export module foo.dot:part.dot;
185 )";
186 std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX);
187
188 std::initializer_list<bool> ImportKinds = {};
189 auto Callback =
190 std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds);
191 CheckNamedModuleImportingCB *CallbackPtr = Callback.get();
192 preprocess(PP&: *PP, C: std::move(Callback));
193 EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0);
194 EXPECT_TRUE(PP->isInNamedModule());
195 EXPECT_TRUE(PP->isInNamedInterfaceUnit());
196 EXPECT_FALSE(PP->isInImplementationUnit());
197 EXPECT_EQ(PP->getNamedModuleName(), "foo.dot:part.dot");
198}
199
200TEST_F(ModuleDeclStateTest, NotModule) {
201 const char *source = R"(
202// export module foo:part;
203 )";
204 std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX);
205
206 std::initializer_list<bool> ImportKinds = {};
207 auto Callback =
208 std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds);
209 CheckNamedModuleImportingCB *CallbackPtr = Callback.get();
210 preprocess(PP&: *PP, C: std::move(Callback));
211 EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0);
212 EXPECT_FALSE(PP->isInNamedModule());
213 EXPECT_FALSE(PP->isInNamedInterfaceUnit());
214 EXPECT_FALSE(PP->isInImplementationUnit());
215}
216
217TEST_F(ModuleDeclStateTest, ModuleWithGMF) {
218 const char *source = R"(
219module;
220#include "bar.h"
221#include <zoo.h>
222import "bar";
223import <zoo>;
224export module foo:part;
225import "HU";
226import M;
227import :another;
228 )";
229 std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX);
230
231 std::initializer_list<bool> ImportKinds = {true, true};
232 auto Callback =
233 std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds);
234 CheckNamedModuleImportingCB *CallbackPtr = Callback.get();
235 preprocess(PP&: *PP, C: std::move(Callback));
236 EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)2);
237 EXPECT_TRUE(PP->isInNamedModule());
238 EXPECT_TRUE(PP->isInNamedInterfaceUnit());
239 EXPECT_FALSE(PP->isInImplementationUnit());
240 EXPECT_EQ(PP->getNamedModuleName(), "foo:part");
241}
242
243TEST_F(ModuleDeclStateTest, ModuleWithGMFWithClangNamedModule) {
244 const char *source = R"(
245module;
246#include "bar.h"
247#include <zoo.h>
248import "bar";
249import <zoo>;
250export module foo:part;
251import "HU";
252import M;
253import :another;
254 )";
255 std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX);
256
257 std::initializer_list<bool> ImportKinds = {true, true};
258 auto Callback =
259 std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds);
260 CheckNamedModuleImportingCB *CallbackPtr = Callback.get();
261 preprocess(PP&: *PP, C: std::move(Callback));
262 EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)2);
263 EXPECT_TRUE(PP->isInNamedModule());
264 EXPECT_TRUE(PP->isInNamedInterfaceUnit());
265 EXPECT_FALSE(PP->isInImplementationUnit());
266 EXPECT_EQ(PP->getNamedModuleName(), "foo:part");
267}
268
269TEST_F(ModuleDeclStateTest, ImportsInNormalTU) {
270 const char *source = R"(
271#include "bar.h"
272#include <zoo.h>
273import "bar";
274import <zoo>;
275import "HU";
276import M;
277// We can't import a partition in non-module TU.
278import :another;
279 )";
280 std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX);
281
282 std::initializer_list<bool> ImportKinds = {true};
283 auto Callback =
284 std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds);
285 CheckNamedModuleImportingCB *CallbackPtr = Callback.get();
286 preprocess(PP&: *PP, C: std::move(Callback));
287 EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)1);
288 EXPECT_FALSE(PP->isInNamedModule());
289 EXPECT_FALSE(PP->isInNamedInterfaceUnit());
290 EXPECT_FALSE(PP->isInImplementationUnit());
291}
292
293TEST_F(ModuleDeclStateTest, ImportAClangNamedModule) {
294 const char *source = R"(
295@import anything;
296 )";
297 std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::ObjCXX);
298
299 std::initializer_list<bool> ImportKinds = {false};
300 auto Callback =
301 std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds);
302 CheckNamedModuleImportingCB *CallbackPtr = Callback.get();
303 preprocess(PP&: *PP, C: std::move(Callback));
304 EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)1);
305 EXPECT_FALSE(PP->isInNamedModule());
306 EXPECT_FALSE(PP->isInNamedInterfaceUnit());
307 EXPECT_FALSE(PP->isInImplementationUnit());
308}
309
310TEST_F(ModuleDeclStateTest, ImportWixedForm) {
311 const char *source = R"(
312import "HU";
313@import anything;
314import M;
315@import another;
316import M2;
317 )";
318 std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::ObjCXX);
319
320 std::initializer_list<bool> ImportKinds = {false, true, false, true};
321 auto Callback =
322 std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds);
323 CheckNamedModuleImportingCB *CallbackPtr = Callback.get();
324 preprocess(PP&: *PP, C: std::move(Callback));
325 EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)4);
326 EXPECT_FALSE(PP->isInNamedModule());
327 EXPECT_FALSE(PP->isInNamedInterfaceUnit());
328 EXPECT_FALSE(PP->isInImplementationUnit());
329}
330
331} // namespace
332

source code of clang/unittests/Lex/ModuleDeclStateTest.cpp