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