//===-- ModulesTests.cpp ---------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "TestFS.h" #include "TestTU.h" #include "llvm/ADT/StringRef.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include namespace clang { namespace clangd { namespace { TEST(Modules, TextualIncludeInPreamble) { TestTU TU = TestTU::withCode(R"cpp( #include "Textual.h" void foo() {} )cpp"); TU.ExtraArgs.push_back("-fmodule-name=M"); TU.ExtraArgs.push_back("-fmodule-map-file=" + testPath("m.modulemap")); TU.AdditionalFiles["Textual.h"] = "void foo();"; TU.AdditionalFiles["m.modulemap"] = R"modulemap( module M { module Textual { textual header "Textual.h" } } )modulemap"; // Test that we do not crash. TU.index(); } // Verify that visibility of AST nodes belonging to modules, but loaded from // preamble PCH, is restored. TEST(Modules, PreambleBuildVisibility) { TestTU TU = TestTU::withCode(R"cpp( #include "module.h" foo x; )cpp"); TU.OverlayRealFileSystemForModules = true; TU.ExtraArgs.push_back("-fmodules"); TU.ExtraArgs.push_back("-fmodules-strict-decluse"); TU.ExtraArgs.push_back("-Xclang"); TU.ExtraArgs.push_back("-fmodules-local-submodule-visibility"); TU.ExtraArgs.push_back("-fmodule-map-file=" + testPath("m.modulemap")); TU.AdditionalFiles["module.h"] = R"cpp( typedef int foo; )cpp"; TU.AdditionalFiles["m.modulemap"] = R"modulemap( module M { header "module.h" } )modulemap"; EXPECT_TRUE(TU.build().getDiagnostics().empty()); } TEST(Modules, Diagnostic) { // Produce a diagnostic while building an implicit module. Use // -fmodules-strict-decluse, but any non-silenced diagnostic will do. TestTU TU = TestTU::withCode(R"cpp( /*error-ok*/ #include "modular.h" void bar() {} )cpp"); TU.OverlayRealFileSystemForModules = true; TU.ExtraArgs.push_back("-fmodule-map-file=" + testPath("m.modulemap")); TU.ExtraArgs.push_back("-fmodules"); TU.ExtraArgs.push_back("-fimplicit-modules"); TU.ExtraArgs.push_back("-fmodules-strict-decluse"); TU.AdditionalFiles["modular.h"] = R"cpp( #include "non-modular.h" )cpp"; TU.AdditionalFiles["non-modular.h"] = ""; TU.AdditionalFiles["m.modulemap"] = R"modulemap( module M { header "modular.h" } )modulemap"; // Test that we do not crash. TU.build(); } // Unknown module formats are a fatal failure for clang. Ensure we don't crash. TEST(Modules, UnknownFormat) { TestTU TU = TestTU::withCode(R"(#include "modular.h")"); TU.OverlayRealFileSystemForModules = true; TU.ExtraArgs.push_back("-Xclang"); TU.ExtraArgs.push_back("-fmodule-format=obj"); TU.ExtraArgs.push_back("-fmodule-map-file=" + testPath("m.modulemap")); TU.ExtraArgs.push_back("-fmodules"); TU.ExtraArgs.push_back("-fimplicit-modules"); TU.AdditionalFiles["modular.h"] = ""; TU.AdditionalFiles["m.modulemap"] = R"modulemap( module M { header "modular.h" })modulemap"; // Test that we do not crash. TU.build(); } // Test that we can build and use a preamble for a module unit. TEST(Modules, ModulePreamble) { TestTU TU = TestTU::withCode(R"cpp( module; #define PREAMBLE_MACRO 1 export module foo; #define MODULE_MACRO 2 module :private; #define PRIVATE_MACRO 3 )cpp"); TU.ExtraArgs.push_back("-std=c++20"); TU.ExtraArgs.push_back("--precompile"); auto AST = TU.build(); auto &SM = AST.getSourceManager(); auto GetMacroFile = [&](llvm::StringRef Name) -> FileID { if (auto *MI = AST.getPreprocessor().getMacroInfo( &AST.getASTContext().Idents.get(Name))) return SM.getFileID(MI->getDefinitionLoc()); return {}; }; EXPECT_EQ(GetMacroFile("PREAMBLE_MACRO"), SM.getPreambleFileID()); EXPECT_EQ(GetMacroFile("MODULE_MACRO"), SM.getMainFileID()); EXPECT_EQ(GetMacroFile("PRIVATE_MACRO"), SM.getMainFileID()); } } // namespace } // namespace clangd } // namespace clang