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, 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> HeaderInfo;
106};
107
108TEST_F(ModuleDeclStateTest, NamedModuleInterface) {
109 const char *source = R"(
110export 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
127TEST_F(ModuleDeclStateTest, NamedModuleImplementation) {
128 const char *source = R"(
129module 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
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 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
165TEST_F(ModuleDeclStateTest, ModuleInterfacePartition) {
166 const char *source = R"(
167export 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
184TEST_F(ModuleDeclStateTest, ModuleNameWithDot) {
185 const char *source = R"(
186export 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
203TEST_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
221TEST_F(ModuleDeclStateTest, ModuleWithGMF) {
222 const char *source = R"(
223module;
224#include "bar.h"
225#include <zoo.h>
226import "bar";
227import <zoo>;
228export module foo:part;
229import "HU";
230import M;
231import :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
248TEST_F(ModuleDeclStateTest, ModuleWithGMFWithClangNamedModule) {
249 const char *source = R"(
250module;
251#include "bar.h"
252#include <zoo.h>
253import "bar";
254import <zoo>;
255export module foo:part;
256import "HU";
257import M;
258import :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
275TEST_F(ModuleDeclStateTest, ImportsInNormalTU) {
276 const char *source = R"(
277#include "bar.h"
278#include <zoo.h>
279import "bar";
280import <zoo>;
281import "HU";
282import M;
283// We can't import a partition in non-module TU.
284import :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
300TEST_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
318TEST_F(ModuleDeclStateTest, ImportWixedForm) {
319 const char *source = R"(
320import "HU";
321@import anything;
322import M;
323@import another;
324import 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

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