https://github.com/berkaysahiin created 
https://github.com/llvm/llvm-project/pull/219839

None

>From d0b3442f80e1884979ede26cb4366cfc31b713e1 Mon Sep 17 00:00:00 2001
From: Berkay Sahin <[email protected]>
Date: Thu, 27 Aug 2026 04:53:39 +0300
Subject: [PATCH] [clangd][modules] Support go-to-definition on module imports

---
 clang-tools-extra/clangd/XRefs.cpp            | 54 ++++++++++++++++++
 .../unittests/PrerequisiteModulesTest.cpp     | 56 +++++++++++++++++++
 2 files changed, 110 insertions(+)

diff --git a/clang-tools-extra/clangd/XRefs.cpp 
b/clang-tools-extra/clangd/XRefs.cpp
index 86528d806eab3..f51039f33992b 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -42,6 +42,7 @@
 #include "clang/AST/StmtVisitor.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/LLVM.h"
+#include "clang/Basic/Module.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TokenKinds.h"
@@ -235,6 +236,54 @@ std::optional<Location> makeLocation(const ASTContext 
&AST, SourceLocation Loc,
   return L;
 }
 
+std::optional<LocatedSymbol>
+locateModuleReferent(const syntax::Token &TouchedIdentifier, ParsedAST &AST,
+                     llvm::StringRef MainFilePath) {
+  const SourceManager &SM = AST.getSourceManager();
+  const ASTContext &Context = AST.getASTContext();
+
+  const Module *ResultModule = nullptr;
+
+  for (const ImportDecl *Import : Context.local_imports()) {
+    const Module *Imported = Import->getImportedModule();
+    ArrayRef<SourceLocation> IdentifierLocs = Import->getIdentifierLocs();
+    if (!Imported || !Imported->isNamedModule() || IdentifierLocs.empty())
+      continue;
+
+    std::string Name = Imported->getFullModuleName();
+    if (auto Colon = Name.find(':'); Colon != std::string::npos)
+      Name.erase(0, Colon + 1);
+    if (Name.empty())
+      continue;
+
+    const size_t NameSize = static_cast<int>(Name.size() - 1);
+
+    const SourceLocation NameBegin = SM.getSpellingLoc(IdentifierLocs.front());
+    const SourceLocation NameEnd = NameBegin.getLocWithOffset(NameSize);
+
+    if (SM.isPointWithin(TouchedIdentifier.location(), NameBegin, NameEnd)) {
+      ResultModule = Imported;
+      break;
+    }
+  }
+
+  if (!ResultModule)
+    return std::nullopt;
+
+  const SourceLocation DefinitionLoc =
+      SM.getSpellingLoc(ResultModule->DefinitionLoc);
+  auto Definition = makeLocation(Context, DefinitionLoc, MainFilePath);
+
+  if (!Definition)
+    return std::nullopt;
+
+  LocatedSymbol Result;
+  Result.Name = ResultModule->getFullModuleName();
+  Result.PreferredDeclaration = *Definition;
+  Result.Definition = *Definition;
+  return Result;
+}
+
 // Treat #included files as symbols, to enable go-to-definition on them.
 std::optional<LocatedSymbol> locateFileReferent(const Position &Pos,
                                                 ParsedAST &AST,
@@ -865,6 +914,11 @@ std::vector<LocatedSymbol> locateSymbolAt(ParsedAST &AST, 
Position Pos,
     }
   }
 
+  if (TouchedIdentifier)
+    if (auto Module =
+            locateModuleReferent(*TouchedIdentifier, AST, MainFilePath))
+      return {*std::move(Module)};
+
   ASTNodeKind NodeKind;
   auto ASTResults = locateASTReferent(*CurLoc, TouchedIdentifier, AST,
                                       MainFilePath, Index, NodeKind);
diff --git a/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp 
b/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp
index f10f3e4976cf9..8933a21108e0e 100644
--- a/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp
+++ b/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp
@@ -19,6 +19,7 @@
 #include "ProjectModules.h"
 #include "SemanticHighlighting.h"
 #include "TestTU.h"
+#include "XRefs.h"
 #include "support/Path.h"
 #include "support/ThreadsafeFS.h"
 #include "clang/Tooling/Tooling.h"
@@ -629,6 +630,61 @@ import A;
   EXPECT_TRUE(D.isFromASTFile());
 }
 
+TEST_F(PrerequisiteModulesTests, LocateImportedModule) {
+  MockDirectoryCompilationDatabase CDB(TestDir, FS);
+
+  Annotations Dep(R"cpp(
+export $decl[[module]] dep.one.two;
+)cpp");
+  CDB.addFile("Dep.cppm", Dep.code());
+
+  Annotations Part(R"cpp(
+export $decl[[module]] M:part.one;
+)cpp");
+  CDB.addFile("M-part.cppm", Part.code());
+
+  Annotations Use(R"cpp(
+export module M;
+import $dep0^dep.$dep1^one.$dep2^two;
+import :$part0^part.$part1^one;
+)cpp");
+  CDB.addFile("M.cppm", Use.code());
+
+  ModulesBuilder Builder(CDB);
+  auto Inputs = getInputs("M.cppm", CDB);
+  Inputs.ModulesManager = &Builder;
+  Inputs.Opts.SkipPreambleBuild = true;
+
+  auto CI = buildCompilerInvocation(Inputs, DiagConsumer);
+  ASSERT_TRUE(CI);
+  auto Preamble =
+      buildPreamble(getFullPath("M.cppm"), *CI, Inputs, /*InMemory=*/true,
+                    /*Callback=*/nullptr);
+  ASSERT_TRUE(Preamble);
+
+  auto AST = ParsedAST::build(getFullPath("M.cppm"), Inputs, std::move(CI), {},
+                              Preamble);
+  ASSERT_TRUE(AST);
+  ASSERT_TRUE(AST->getDiagnostics().empty());
+
+  auto Check = [&](llvm::StringRef Point, llvm::StringRef Name,
+                   llvm::StringRef File, Range TargetRange) {
+    auto Results = locateSymbolAt(*AST, Use.point(Point));
+    ASSERT_THAT(Results, testing::SizeIs(1));
+    EXPECT_EQ(Results.front().Name, Name);
+    Location Target{
+        URIForFile::canonicalize(getFullPath(File), getFullPath("M.cppm")),
+        TargetRange};
+    EXPECT_EQ(Results.front().PreferredDeclaration, Target);
+    EXPECT_EQ(Results.front().Definition, Target);
+  };
+
+  for (llvm::StringRef Point : {"dep0", "dep1", "dep2"})
+    Check(Point, "dep.one.two", "Dep.cppm", Dep.range("decl"));
+  for (llvm::StringRef Point : {"part0", "part1"})
+    Check(Point, "M:part.one", "M-part.cppm", Part.range("decl"));
+}
+
 // An end to end test for code complete in modules
 TEST_F(PrerequisiteModulesTests, CodeCompleteTest) {
   MockDirectoryCompilationDatabase CDB(TestDir, FS);

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to