https://github.com/Decodetalkers updated https://github.com/llvm/llvm-project/pull/200001
>From fd1b871b4af49a230e77334bd40b480b84d3ad4b Mon Sep 17 00:00:00 2001 From: ShootingStarDragons <[email protected]> Date: Thu, 28 May 2026 00:16:49 +0900 Subject: [PATCH] feat: add gcc scan rules this will make completion will work after using gcc to compile the whole project --- clang-tools-extra/clangd/ProjectModules.cpp | 201 ++++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/clang-tools-extra/clangd/ProjectModules.cpp b/clang-tools-extra/clangd/ProjectModules.cpp index d3727171bff12..d6e65fa9b015f 100644 --- a/clang-tools-extra/clangd/ProjectModules.cpp +++ b/clang-tools-extra/clangd/ProjectModules.cpp @@ -12,11 +12,16 @@ #include "clang/DependencyScanning/DependencyScanningService.h" #include "clang/Tooling/DependencyScanningTool.h" #include "clang/Tooling/Tooling.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/Regex.h" #include "llvm/TargetParser/Host.h" namespace clang::clangd { @@ -171,6 +176,8 @@ class ModuleDependencyScanner { /// Scanning the single file specified by \param FilePath. std::optional<ModuleDependencyInfo> scan(PathRef FilePath, const ProjectModules::CommandMangler &Mangler); + std::optional<ModuleDependencyInfo> scanGcc(tooling::CompileCommand Cmd, + PathRef MapFile); /// Scanning every source file in the current project to get the /// <module-name> to <module-unit-source> map. @@ -209,15 +216,209 @@ class ModuleDependencyScanner { llvm::StringMap<std::string> ModuleNameToSource; }; +namespace gcc { +static const llvm::Regex ImportRegex = + llvm::Regex("import: ([^ ]*) ([^ ]*.gcm)"); +static const llvm::Regex ModuleRegex = llvm::Regex("module: ([^ ]*)"); +static const llvm::Regex SourceRegex = llvm::Regex("source: ([^ ]*)"); +static const llvm::Regex CwdRegex = llvm::Regex("cwd: ([^ ]*)"); + +static const llvm::Regex ModmapRegex = llvm::Regex("([^ ^$^\n]*) ([^ ]*.gcm)"); + +struct RoadMapInfo { + std::string Name; + std::string Path; +}; + +struct ReadElfInfo { + std::string Source; + std::string ModuleName; + std::vector<std::string> Imports; + + static std::optional<ReadElfInfo> get(llvm::StringRef Source); +}; + +std::optional<ReadElfInfo> ReadElfInfo::get(llvm::StringRef Content) { + std::vector<std::string> Imports = {}; + std::string Source; + std::string ModuleName; + std::string Cwd; + { + llvm::StringRef CwdText = Content; + llvm::SmallVector<llvm::StringRef, 1> Matches; + std::string Error; + if (!CwdRegex.match(CwdText, &Matches, &Error)) { + return std::nullopt; + } + Cwd = Matches[1].trim().str(); + } + { + llvm::StringRef ImportText = Content; + while (!ImportText.empty()) { + llvm::SmallVector<llvm::StringRef, 2> Matches; + std::string Error; + if (!ImportRegex.match(ImportText, &Matches, &Error)) { + break; + } + + auto ImportModule = Matches[1].trim().str(); + Imports.push_back(ImportModule); + size_t Pos = ImportText.find(Matches[0]); + ImportText = ImportText.drop_front(Pos + Matches[0].size()); + } + } + + { + llvm::StringRef SourceText = Content; + llvm::SmallVector<llvm::StringRef, 1> Matches; + std::string Error; + if (!SourceRegex.match(SourceText, &Matches, &Error)) { + return std::nullopt; + } + + llvm::StringRef SourcePa = Matches[1].trim(); + if (llvm::sys::path::is_absolute(SourcePa)) { + Source = SourcePa.str(); + + } else { + llvm::StringRef PathRef = Cwd; + llvm::SmallString<128> CurrentPath = PathRef; + llvm::sys::path::append(CurrentPath, SourcePa); + Source = CurrentPath.str(); + } + } + { + llvm::StringRef ModuleText = Content; + llvm::SmallVector<llvm::StringRef, 1> Matches; + std::string Error; + if (!ModuleRegex.match(ModuleText, &Matches, &Error)) { + return std::nullopt; + } + ModuleName = Matches[1].trim().str(); + } + return ReadElfInfo{Source, ModuleName, Imports}; +} + +static bool fitGccModulePath(std::string Cmd) { + llvm::StringRef Arg = Cmd; + return Arg.starts_with("-fmodule-mapper=") && Arg.ends_with("modmap"); +} + +std::optional<ReadElfInfo> scanGcm(llvm::StringRef GCMPath) { + llvm::SmallString<64> OutputFile; + llvm::sys::fs::createTemporaryFile("readref", "", OutputFile); + llvm::FileRemover OutRemover(OutputFile); + std::optional<llvm::StringRef> Redirects[3] = { + /*Stdin*/ {""}, {OutputFile.str()}, {}}; + std::string ErrorMessage; + auto Readelf = llvm::sys::findProgramByName("readelf"); + if (!Readelf) { + return std::nullopt; + } + int Ret = llvm::sys::ExecuteAndWait( + *Readelf, {"readelf", "-p.gnu.c++.README", GCMPath}, std::nullopt, + Redirects, 10, 0, &ErrorMessage); + if (Ret != 0) { + return std::nullopt; + } + auto Buf = llvm::MemoryBuffer::getFile(OutputFile); + + if (!Buf) { + return std::nullopt; + } + llvm::StringRef Path = Buf->get()->getBuffer().trim(); + if (Path.empty()) { + return std::nullopt; + } + llvm::StringRef Text = Path; + return ReadElfInfo::get(Text); +} + +struct ModuleResult { + std::optional<std::string> ModuleName; + std::vector<std::string> RequiredModules; +}; + +} // namespace gcc +std::optional<ModuleDependencyScanner::ModuleDependencyInfo> +ModuleDependencyScanner::scanGcc(tooling::CompileCommand Cmd, PathRef MapFile) { + using namespace gcc; + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File = + llvm::MemoryBuffer::getFile(MapFile); + if (std::error_code Result = File.getError()) { + elog("Map File Not Found, {0}, If you are using gcc, you need to compile it first", MapFile); + return std::nullopt; + } + auto Content = (*File)->getBuffer(); + llvm::StringRef CurrentDir = Cmd.Directory; + std::vector<gcc::RoadMapInfo> RoadMapInfos = {}; + llvm::StringRef Text = Content; + while (!Text.empty()) { + llvm::SmallVector<llvm::StringRef, 2> Matches; + std::string Error; + if (!gcc::ModmapRegex.match(Text, &Matches, &Error)) { + break; + } + + auto Name = Matches[1].trim().str(); + + auto ReadPath = Matches[2].trim(); + std::string Path; + if (llvm::sys::path::is_absolute(ReadPath)) { + Path = ReadPath.str(); + } else { + llvm::SmallString<128> CurrentPath = CurrentDir; + llvm::sys::path::append(CurrentPath, ReadPath); + Path = CurrentPath.str(); + } + RoadMapInfos.push_back(RoadMapInfo{Name, Path}); + + size_t Pos = Text.find(Matches[0]); + Text = Text.drop_front(Pos + Matches[0].size()); + } + ModuleDependencyScanner::ModuleDependencyInfo Result; + for (const RoadMapInfo &Info : RoadMapInfos) { + auto GCMInfo = scanGcm(Info.Path); + if (!GCMInfo) { + continue; + } + + ModuleNameToSource.try_emplace(GCMInfo->ModuleName, GCMInfo->Source); + if (GCMInfo->Source == Cmd.Filename) { + Result.ModuleName = GCMInfo->ModuleName; + } + Result.RequiredModules.push_back(Info.Name); + } + return Result; +} + +// I need to read deeper here +// problem is here +// We can read the data from modmap +// But we cannot get the the required module +// it can be itself std::optional<ModuleDependencyScanner::ModuleDependencyInfo> ModuleDependencyScanner::scan(PathRef FilePath, const ProjectModules::CommandMangler &Mangler) { auto Cmd = getCompileCommandForFile(*CDB, FilePath, Mangler); + if (!Cmd) return std::nullopt; using namespace clang::tooling; + auto CmdLine = Cmd->CommandLine; + auto It = llvm::find_if(CmdLine, gcc::fitGccModulePath); + if (It != CmdLine.end()) { + llvm::StringRef Module = *It; + // TODO: is there anyway to get it is gcc? + if (Module.consume_front("-fmodule-mapper=")) { + llvm::StringRef Cwd = Cmd->Directory; + llvm::SmallString<128> MapFile = Cwd; + llvm::sys::path::append(MapFile, Module); + return scanGcc(*Cmd, MapFile); + } + } DependencyScanningTool ScanningTool(Service); std::string S; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
