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 | |
25 | using namespace clang; |
26 | |
27 | namespace { |
28 | |
29 | class CheckNamedModuleImportingCB : public PPCallbacks { |
30 | Preprocessor &PP; |
31 | std::vector<bool> IsImportingNamedModulesAssertions; |
32 | std::size_t NextCheckingIndex; |
33 | |
34 | public: |
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 | }; |
54 | class ModuleDeclStateTest : public ::testing::Test { |
55 | protected: |
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> |
65 | getPreprocessor(const char *source, 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, LangStd: LangStandard::lang_cxx20); |
72 | LangOpts.CPlusPlusModules = true; |
73 | if (Lang != Language::CXX) { |
74 | LangOpts.Modules = true; |
75 | LangOpts.ImplicitModules = true; |
76 | } |
77 | |
78 | HeaderInfo.emplace(args&: HSOpts, args&: SourceMgr, args&: Diags, args&: LangOpts, args: Target.get()); |
79 | |
80 | return std::make_unique<Preprocessor>(args&: PPOpts, args&: Diags, args&: LangOpts, args&: SourceMgr, |
81 | args&: *HeaderInfo, args&: ModLoader, |
82 | /*IILookup=*/args: nullptr, |
83 | /*OwnsHeaderSearch=*/args: false); |
84 | } |
85 | |
86 | void preprocess(Preprocessor &PP, std::unique_ptr<PPCallbacks> C) { |
87 | PP.Initialize(Target: *Target); |
88 | PP.addPPCallbacks(C: std::move(C)); |
89 | PP.EnterMainSourceFile(); |
90 | |
91 | PP.LexTokensUntilEOF(); |
92 | } |
93 | |
94 | FileSystemOptions FileMgrOpts; |
95 | FileManager FileMgr; |
96 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID; |
97 | DiagnosticOptions DiagOpts; |
98 | DiagnosticsEngine Diags; |
99 | SourceManager SourceMgr; |
100 | std::shared_ptr<TargetOptions> TargetOpts; |
101 | IntrusiveRefCntPtr<TargetInfo> Target; |
102 | LangOptions LangOpts; |
103 | TrivialModuleLoader ModLoader; |
104 | HeaderSearchOptions HSOpts; |
105 | std::optional<HeaderSearch> ; |
106 | PreprocessorOptions PPOpts; |
107 | }; |
108 | |
109 | TEST_F(ModuleDeclStateTest, NamedModuleInterface) { |
110 | const char *source = R"( |
111 | export module foo; |
112 | )" ; |
113 | std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX); |
114 | |
115 | std::initializer_list<bool> ImportKinds = {}; |
116 | preprocess(PP&: *PP, |
117 | C: std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds)); |
118 | |
119 | auto *Callback = |
120 | static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); |
121 | EXPECT_EQ(Callback->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 | |
128 | TEST_F(ModuleDeclStateTest, NamedModuleImplementation) { |
129 | const char *source = R"( |
130 | module foo; |
131 | )" ; |
132 | std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX); |
133 | |
134 | std::initializer_list<bool> ImportKinds = {}; |
135 | preprocess(PP&: *PP, |
136 | C: std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds)); |
137 | |
138 | auto *Callback = |
139 | static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); |
140 | EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0); |
141 | EXPECT_TRUE(PP->isInNamedModule()); |
142 | EXPECT_FALSE(PP->isInNamedInterfaceUnit()); |
143 | EXPECT_TRUE(PP->isInImplementationUnit()); |
144 | EXPECT_EQ(PP->getNamedModuleName(), "foo" ); |
145 | } |
146 | |
147 | TEST_F(ModuleDeclStateTest, ModuleImplementationPartition) { |
148 | const char *source = R"( |
149 | module foo:part; |
150 | )" ; |
151 | std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX); |
152 | |
153 | std::initializer_list<bool> ImportKinds = {}; |
154 | preprocess(PP&: *PP, |
155 | C: std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds)); |
156 | |
157 | auto *Callback = |
158 | static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); |
159 | EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0); |
160 | EXPECT_TRUE(PP->isInNamedModule()); |
161 | EXPECT_FALSE(PP->isInNamedInterfaceUnit()); |
162 | EXPECT_FALSE(PP->isInImplementationUnit()); |
163 | EXPECT_EQ(PP->getNamedModuleName(), "foo:part" ); |
164 | } |
165 | |
166 | TEST_F(ModuleDeclStateTest, ModuleInterfacePartition) { |
167 | const char *source = R"( |
168 | export module foo:part; |
169 | )" ; |
170 | std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX); |
171 | |
172 | std::initializer_list<bool> ImportKinds = {}; |
173 | preprocess(PP&: *PP, |
174 | C: std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds)); |
175 | |
176 | auto *Callback = |
177 | static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); |
178 | EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0); |
179 | EXPECT_TRUE(PP->isInNamedModule()); |
180 | EXPECT_TRUE(PP->isInNamedInterfaceUnit()); |
181 | EXPECT_FALSE(PP->isInImplementationUnit()); |
182 | EXPECT_EQ(PP->getNamedModuleName(), "foo:part" ); |
183 | } |
184 | |
185 | TEST_F(ModuleDeclStateTest, ModuleNameWithDot) { |
186 | const char *source = R"( |
187 | export module foo.dot:part.dot; |
188 | )" ; |
189 | std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX); |
190 | |
191 | std::initializer_list<bool> ImportKinds = {}; |
192 | preprocess(PP&: *PP, |
193 | C: std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds)); |
194 | |
195 | auto *Callback = |
196 | static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); |
197 | EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0); |
198 | EXPECT_TRUE(PP->isInNamedModule()); |
199 | EXPECT_TRUE(PP->isInNamedInterfaceUnit()); |
200 | EXPECT_FALSE(PP->isInImplementationUnit()); |
201 | EXPECT_EQ(PP->getNamedModuleName(), "foo.dot:part.dot" ); |
202 | } |
203 | |
204 | TEST_F(ModuleDeclStateTest, NotModule) { |
205 | const char *source = R"( |
206 | // export module foo:part; |
207 | )" ; |
208 | std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX); |
209 | |
210 | std::initializer_list<bool> ImportKinds = {}; |
211 | preprocess(PP&: *PP, |
212 | C: std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds)); |
213 | |
214 | auto *Callback = |
215 | static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); |
216 | EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0); |
217 | EXPECT_FALSE(PP->isInNamedModule()); |
218 | EXPECT_FALSE(PP->isInNamedInterfaceUnit()); |
219 | EXPECT_FALSE(PP->isInImplementationUnit()); |
220 | } |
221 | |
222 | TEST_F(ModuleDeclStateTest, ModuleWithGMF) { |
223 | const char *source = R"( |
224 | module; |
225 | #include "bar.h" |
226 | #include <zoo.h> |
227 | import "bar"; |
228 | import <zoo>; |
229 | export module foo:part; |
230 | import "HU"; |
231 | import M; |
232 | import :another; |
233 | )" ; |
234 | std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX); |
235 | |
236 | std::initializer_list<bool> ImportKinds = {true, true}; |
237 | preprocess(PP&: *PP, |
238 | C: std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds)); |
239 | |
240 | auto *Callback = |
241 | static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); |
242 | EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)2); |
243 | EXPECT_TRUE(PP->isInNamedModule()); |
244 | EXPECT_TRUE(PP->isInNamedInterfaceUnit()); |
245 | EXPECT_FALSE(PP->isInImplementationUnit()); |
246 | EXPECT_EQ(PP->getNamedModuleName(), "foo:part" ); |
247 | } |
248 | |
249 | TEST_F(ModuleDeclStateTest, ModuleWithGMFWithClangNamedModule) { |
250 | const char *source = R"( |
251 | module; |
252 | #include "bar.h" |
253 | #include <zoo.h> |
254 | import "bar"; |
255 | import <zoo>; |
256 | export module foo:part; |
257 | import "HU"; |
258 | import M; |
259 | import :another; |
260 | )" ; |
261 | std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX); |
262 | |
263 | std::initializer_list<bool> ImportKinds = {true, true}; |
264 | preprocess(PP&: *PP, |
265 | C: std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds)); |
266 | |
267 | auto *Callback = |
268 | static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); |
269 | EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)2); |
270 | EXPECT_TRUE(PP->isInNamedModule()); |
271 | EXPECT_TRUE(PP->isInNamedInterfaceUnit()); |
272 | EXPECT_FALSE(PP->isInImplementationUnit()); |
273 | EXPECT_EQ(PP->getNamedModuleName(), "foo:part" ); |
274 | } |
275 | |
276 | TEST_F(ModuleDeclStateTest, ImportsInNormalTU) { |
277 | const char *source = R"( |
278 | #include "bar.h" |
279 | #include <zoo.h> |
280 | import "bar"; |
281 | import <zoo>; |
282 | import "HU"; |
283 | import M; |
284 | // We can't import a partition in non-module TU. |
285 | import :another; |
286 | )" ; |
287 | std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::CXX); |
288 | |
289 | std::initializer_list<bool> ImportKinds = {true}; |
290 | preprocess(PP&: *PP, |
291 | C: std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds)); |
292 | |
293 | auto *Callback = |
294 | static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); |
295 | EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)1); |
296 | EXPECT_FALSE(PP->isInNamedModule()); |
297 | EXPECT_FALSE(PP->isInNamedInterfaceUnit()); |
298 | EXPECT_FALSE(PP->isInImplementationUnit()); |
299 | } |
300 | |
301 | TEST_F(ModuleDeclStateTest, ImportAClangNamedModule) { |
302 | const char *source = R"( |
303 | @import anything; |
304 | )" ; |
305 | std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::ObjCXX); |
306 | |
307 | std::initializer_list<bool> ImportKinds = {false}; |
308 | preprocess(PP&: *PP, |
309 | C: std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds)); |
310 | |
311 | auto *Callback = |
312 | static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); |
313 | EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)1); |
314 | EXPECT_FALSE(PP->isInNamedModule()); |
315 | EXPECT_FALSE(PP->isInNamedInterfaceUnit()); |
316 | EXPECT_FALSE(PP->isInImplementationUnit()); |
317 | } |
318 | |
319 | TEST_F(ModuleDeclStateTest, ImportWixedForm) { |
320 | const char *source = R"( |
321 | import "HU"; |
322 | @import anything; |
323 | import M; |
324 | @import another; |
325 | import M2; |
326 | )" ; |
327 | std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Lang: Language::ObjCXX); |
328 | |
329 | std::initializer_list<bool> ImportKinds = {false, true, false, true}; |
330 | preprocess(PP&: *PP, |
331 | C: std::make_unique<CheckNamedModuleImportingCB>(args&: *PP, args&: ImportKinds)); |
332 | |
333 | auto *Callback = |
334 | static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); |
335 | EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)4); |
336 | EXPECT_FALSE(PP->isInNamedModule()); |
337 | EXPECT_FALSE(PP->isInNamedInterfaceUnit()); |
338 | EXPECT_FALSE(PP->isInImplementationUnit()); |
339 | } |
340 | |
341 | } // namespace |
342 | |