diff --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -63,6 +63,7 @@ IndexTests.cpp JSONTransportTests.cpp LSPClient.cpp + ModulesTests.cpp ParsedASTTests.cpp PathMappingTests.cpp PreambleTests.cpp diff --git a/clang-tools-extra/clangd/unittests/ModulesTests.cpp b/clang-tools-extra/clangd/unittests/ModulesTests.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/unittests/ModulesTests.cpp @@ -0,0 +1,44 @@ +//===-- 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 "Annotations.h" +#include "TestFS.h" +#include "TestTU.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=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(); +} + +} // namespace +} // namespace clangd +} // namespace clang diff --git a/clang/lib/Frontend/PrecompiledPreamble.cpp b/clang/lib/Frontend/PrecompiledPreamble.cpp --- a/clang/lib/Frontend/PrecompiledPreamble.cpp +++ b/clang/lib/Frontend/PrecompiledPreamble.cpp @@ -208,6 +208,11 @@ Callbacks.AfterPCHEmitted(Writer); } + bool BeginSourceFileAction(CompilerInstance &CI) override { + assert(CI.getLangOpts().CompilingPCH); + return ASTFrontendAction::BeginSourceFileAction(CI); + } + bool shouldEraseOutputFiles() override { return !hasEmittedPreamblePCH(); } bool hasCodeCompletionSupport() const override { return false; } bool hasASTFileSupport() const override { return false; } @@ -396,6 +401,8 @@ auto PreambleDepCollector = std::make_shared(); Clang->addDependencyCollector(PreambleDepCollector); + Clang->getLangOpts().CompilingPCH = true; + // Remap the main source file to the preamble buffer. StringRef MainFilePath = FrontendOpts.Inputs[0].getFile(); auto PreambleInputBuffer = llvm::MemoryBuffer::getMemBufferCopy( diff --git a/clang/unittests/Frontend/ASTUnitTest.cpp b/clang/unittests/Frontend/ASTUnitTest.cpp --- a/clang/unittests/Frontend/ASTUnitTest.cpp +++ b/clang/unittests/Frontend/ASTUnitTest.cpp @@ -13,6 +13,7 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/PCHContainerOperations.h" +#include "clang/Lex/HeaderSearch.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/ToolOutputFile.h" @@ -111,4 +112,41 @@ llvm::MemoryBuffer::MemoryBuffer_MMap); } +TEST_F(ASTUnitTest, ModuleTextualHeader) { + llvm::IntrusiveRefCntPtr InMemoryFs = + new llvm::vfs::InMemoryFileSystem(); + InMemoryFs->addFile("test.cpp", 0, llvm::MemoryBuffer::getMemBuffer(R"cpp( + #include "Textual.h" + void foo() {} + )cpp")); + InMemoryFs->addFile("m.modulemap", 0, llvm::MemoryBuffer::getMemBuffer(R"cpp( + module M { + module Textual { + textual header "Textual.h" + } + } + )cpp")); + InMemoryFs->addFile("Textual.h", 0, llvm::MemoryBuffer::getMemBuffer(R"cpp( + void foo(); + )cpp")); + + const char *Args[] = {"clang", "test.cpp", "-fmodule-map-file=m.modulemap", + "-fmodule-name=M"}; + Diags = CompilerInstance::createDiagnostics(new DiagnosticOptions()); + CInvok = createInvocationFromCommandLine(Args, Diags); + ASSERT_TRUE(CInvok); + + FileManager *FileMgr = new FileManager(FileSystemOptions(), InMemoryFs); + PCHContainerOps = std::make_shared(); + + auto AU = ASTUnit::LoadFromCompilerInvocation( + CInvok, PCHContainerOps, Diags, FileMgr, false, CaptureDiagsKind::None, 1, + TU_Complete, false, false, false); + auto File = AU->getFileManager().getFileRef("Textual.h", false, false); + assert(File); + // Verify that we do not crash here. + EXPECT_TRUE(AU->getPreprocessor().getHeaderSearchInfo().getExistingFileInfo( + &File->getFileEntry())); +} + } // anonymous namespace