| 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 | |