Author: Chuanqi Xu Date: 2026-03-23T03:48:41Z New Revision: 95c906a31c451f273c2d4b2aca0ac4494c06ae98
URL: https://github.com/llvm/llvm-project/commit/95c906a31c451f273c2d4b2aca0ac4494c06ae98 DIFF: https://github.com/llvm/llvm-project/commit/95c906a31c451f273c2d4b2aca0ac4494c06ae98.diff LOG: [clangd] [C++ Modules] Fix handling of relative paths in prebuilt mod… (#187654) …ule files When compile_commands.json contains relative paths in -fmodule-file= arguments (as generated by CMake), clangd failed to find the BMI files because it was looking for them relative to the wrong working directory. This patch fixes the issue by converting relative paths to absolute paths based on the compilation directory (CompileCommand.Directory) before checking if the module file exists and is up to date. Added a unit test that verifies the fix works correctly. AI Assisted Added: Modified: clang-tools-extra/clangd/ModulesBuilder.cpp clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp Removed: ################################################################################ diff --git a/clang-tools-extra/clangd/ModulesBuilder.cpp b/clang-tools-extra/clangd/ModulesBuilder.cpp index 612c25b61a89c..f9581d51446ff 100644 --- a/clang-tools-extra/clangd/ModulesBuilder.cpp +++ b/clang-tools-extra/clangd/ModulesBuilder.cpp @@ -584,12 +584,20 @@ void ModulesBuilder::ModulesBuilderImpl::getPrebuiltModuleFile( if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName)) continue; - if (IsModuleFileUpToDate(ModuleFilePath, BuiltModuleFiles, + // Convert relative path to absolute path based on the compilation directory + llvm::SmallString<256> AbsoluteModuleFilePath; + if (llvm::sys::path::is_relative(ModuleFilePath)) { + AbsoluteModuleFilePath = Inputs.CompileCommand.Directory; + llvm::sys::path::append(AbsoluteModuleFilePath, ModuleFilePath); + } else + AbsoluteModuleFilePath = ModuleFilePath; + + if (IsModuleFileUpToDate(AbsoluteModuleFilePath, BuiltModuleFiles, TFS.view(std::nullopt))) { log("Reusing prebuilt module file {0} of module {1} for {2}", - ModuleFilePath, ModuleName, ModuleUnitFileName); + AbsoluteModuleFilePath, ModuleName, ModuleUnitFileName); BuiltModuleFiles.addModuleFile( - PrebuiltModuleFile::make(ModuleName, ModuleFilePath)); + PrebuiltModuleFile::make(ModuleName, AbsoluteModuleFilePath)); } } } diff --git a/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp b/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp index f072c299cad22..488d7d7e6323c 100644 --- a/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp +++ b/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp @@ -17,6 +17,7 @@ #include "ModulesBuilder.h" #include "ScanningProjectModules.h" #include "TestTU.h" +#include "support/Path.h" #include "support/ThreadsafeFS.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/FileSystem.h" @@ -745,6 +746,90 @@ import M; EXPECT_EQ(HS.PrebuiltModuleFiles, HS2.PrebuiltModuleFiles); } +// Test that prebuilt module files with relative paths are correctly resolved. +// This tests the fix for the issue where clangd couldn't find BMI files when +// the compilation database contained relative paths in -fmodule-file= +// arguments. +TEST_F(PrerequisiteModulesTests, PrebuiltModuleFileWithRelativePath) { + MockDirectoryCompilationDatabase CDB(TestDir, FS); + + CDB.addFile("M.cppm", R"cpp( +export module M; +export int m_value = 42; + )cpp"); + + CDB.addFile("U.cpp", R"cpp( +import M; +int use() { return m_value; } + )cpp"); + + // Step 1: Build the module file using ModulesBuilder + ModulesBuilder Builder(CDB); + auto ModuleInfo = + Builder.buildPrerequisiteModulesFor(getFullPath("U.cpp"), FS); + ASSERT_TRUE(ModuleInfo); + + HeaderSearchOptions HS(TestDir); + ModuleInfo->adjustHeaderSearchOptions(HS); + ASSERT_EQ(HS.PrebuiltModuleFiles.count("M"), 1u); + + // Get the absolute path of the built module file + std::string OriginalBMPath = HS.PrebuiltModuleFiles["M"]; + ASSERT_TRUE(llvm::sys::path::is_absolute(OriginalBMPath)); + ASSERT_TRUE(llvm::sys::fs::exists(OriginalBMPath)); + + // Step 2: Create a subdirectory in TestDir and copy the BMI there + SmallString<256> BMSubDir(TestDir); + llvm::sys::path::append(BMSubDir, "prebuilt_modules"); + ASSERT_FALSE(llvm::sys::fs::create_directories(BMSubDir)); + + SmallString<256> NewBMPath(BMSubDir); + llvm::sys::path::append(NewBMPath, "M.pcm"); + + // Copy the BMI file to the new location + ASSERT_FALSE(llvm::sys::fs::copy_file(OriginalBMPath, NewBMPath)); + ASSERT_TRUE(llvm::sys::fs::exists(NewBMPath)); + + // Step 3: Create a relative path from the new absolute path + std::string RelativeBMPath = + llvm::StringRef(NewBMPath).drop_front(TestDir.size() + 1).str(); + ASSERT_FALSE(RelativeBMPath.empty()); + ASSERT_TRUE(llvm::sys::path::is_relative(RelativeBMPath)); + + // Step 4: Create a new CDB with relative path in -fmodule-file= + MockDirectoryCompilationDatabase CDBWithRelativePath(TestDir, FS); + + CDBWithRelativePath.addFile("M.cppm", R"cpp( +export module M; +export int m_value = 42; + )cpp"); + + CDBWithRelativePath.addFile("U.cpp", R"cpp( +import M; +int use() { return m_value; } + )cpp"); + + // Use relative path in -fmodule-file= argument + CDBWithRelativePath.ExtraClangFlags.push_back("-fmodule-file=M=" + + RelativeBMPath); + + // Step 5: Verify that clangd can find and reuse the prebuilt module file + ModulesBuilder BuilderWithRelativePath(CDBWithRelativePath); + auto ModuleInfo2 = BuilderWithRelativePath.buildPrerequisiteModulesFor( + getFullPath("U.cpp"), FS); + ASSERT_TRUE(ModuleInfo2); + + HeaderSearchOptions HS2(TestDir); + ModuleInfo2->adjustHeaderSearchOptions(HS2); + + // The module file should be found and the paths should match + ASSERT_EQ(HS2.PrebuiltModuleFiles.count("M"), 1u); + EXPECT_EQ(HS2.PrebuiltModuleFiles["M"], std::string(NewBMPath)) + << "Expected absolute path: " << NewBMPath + << "\nGot: " << HS2.PrebuiltModuleFiles["M"] + << "\nRelative path used: " << RelativeBMPath; +} + } // namespace } // namespace clang::clangd _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
