https://github.com/aotodev updated 
https://github.com/llvm/llvm-project/pull/234755

>From a1932bd189db86b634b47672db2f2271ede52513 Mon Sep 17 00:00:00 2001
From: Aoto <[email protected]>
Date: Thu, 22 Oct 2026 22:11:32 +0200
Subject: [PATCH 1/3] [clangd] Index symbols declared inside export
 declarations

---
 .../clangd/index/SymbolCollector.cpp          |  4 +-
 .../clangd/unittests/RenameTests.cpp          | 40 +++++++++++++++++++
 .../clangd/unittests/SymbolCollectorTests.cpp | 22 ++++++++++
 3 files changed, 64 insertions(+), 2 deletions(-)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index e53bd3265d96c4..ae753373635bcd 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -524,13 +524,13 @@ bool SymbolCollector::shouldCollectSymbol(const NamedDecl 
&ND,
 
   // We want most things but not "local" symbols such as symbols inside
   // FunctionDecl, BlockDecl, ObjCMethodDecl and OMPDeclareReductionDecl.
-  // FIXME: Need a matcher for ExportDecl in order to include symbols declared
-  // within an export.
   const auto *DeclCtx = ND.getDeclContext();
   switch (DeclCtx->getDeclKind()) {
   case Decl::TranslationUnit:
   case Decl::Namespace:
   case Decl::LinkageSpec:
+  // An export-declaration can only appear at namespace scope.
+  case Decl::Export:
   case Decl::Enum:
   case Decl::ObjCProtocol:
   case Decl::ObjCInterface:
diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp 
b/clang-tools-extra/clangd/unittests/RenameTests.cpp
index 42279b51230e7c..635f8e53f0a6a7 100644
--- a/clang-tools-extra/clangd/unittests/RenameTests.cpp
+++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp
@@ -1538,6 +1538,46 @@ TEST(RenameTest, ProtobufSymbolIsExcluded) {
               testing::HasSubstr("not a supported kind"));
 }
 
+TEST(RenameTest, ExportedSymbols) {
+  // Symbols whose parent is an ExportDecl must be indexable, or rename rejects
+  // them as "not eligible for indexing".
+  const char *Tests[] = {
+      R"cpp(
+        export module m;
+        export int [[f^un]]() { return 32; }
+        int x = [[fun]]();
+      )cpp",
+      R"cpp(
+        export module m;
+        export {
+          struct [[S^truct]] {};
+        }
+        [[Struct]] s;
+      )cpp",
+      R"cpp(
+        export module m;
+        namespace ns {
+        export int [[V^ar]];
+        }
+        int y = ns::[[Var]];
+      )cpp",
+  };
+
+  for (const char *Test : Tests) {
+    SCOPED_TRACE(Test);
+    Annotations Code(Test);
+    TestTU TU = TestTU::withCode(Code.code());
+    TU.Filename = "test.cppm";
+    TU.ExtraArgs = {"-std=c++20", "-xc++-module"};
+    auto AST = TU.build();
+    auto Results =
+        rename({Code.point(), "newName", AST, testPath(TU.Filename)});
+
+    ASSERT_TRUE(bool(Results)) << llvm::toString(Results.takeError());
+    EXPECT_EQ(Results->LocalChanges, Code.ranges());
+  }
+}
+
 TEST(RenameTest, PrepareRename) {
   Annotations FooH("void func();");
   Annotations FooCC(R"cpp(
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index a04756518ef23b..62f8cbb7b1ee00 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -2213,6 +2213,28 @@ TEST_F(SymbolCollectorTest, Concepts) {
                   qName("A"), hasKind(clang::index::SymbolKind::Concept))));
 }
 
+TEST_F(SymbolCollectorTest, ExportedDeclarations) {
+  const char *Main = R"cpp(
+    export module m;
+    export void f();
+    export int v;
+    export {
+      void g();
+      struct S { void m(); };
+    }
+    namespace ns {
+    export void h();
+    void not_exported();
+    }
+  )cpp";
+
+  runSymbolCollector("", Main, {"-std=c++20", "-xc++-module"});
+  EXPECT_THAT(Symbols,
+              UnorderedElementsAre(qName("f"), qName("v"), qName("g"),
+                                   qName("S"), qName("S::m"), qName("ns"),
+                                   qName("ns::h"), qName("ns::not_exported")));
+}
+
 TEST_F(SymbolCollectorTest, IncludeHeaderForwardDecls) {
   CollectorOpts.CollectIncludePath = true;
   const std::string Header = R"cpp(#pragma once 

>From b0cce0b32c825149f8433b342d959c612ef8f36a Mon Sep 17 00:00:00 2001
From: Aoto <[email protected]>
Date: Fri, 23 Oct 2026 10:59:46 +0200
Subject: [PATCH 2/3] [clangd] Use the non-transparent context and fix code
 completion

---
 clang-tools-extra/clangd/CodeComplete.cpp     |  3 +-
 .../clangd/index/SymbolCollector.cpp          |  7 ++---
 .../clangd/unittests/SymbolCollectorTests.cpp | 31 +++++++++++++++++++
 3 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/clang-tools-extra/clangd/CodeComplete.cpp 
b/clang-tools-extra/clangd/CodeComplete.cpp
index 2c7a317d045a94..87c7fa15101f73 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -2414,10 +2414,9 @@ SignatureHelp signatureHelp(PathRef FileName, Position 
Pos,
 
 bool isIndexedForCodeCompletion(const NamedDecl &ND, ASTContext &ASTCtx) {
   auto InTopLevelScope = [](const NamedDecl &ND) {
-    switch (ND.getDeclContext()->getDeclKind()) {
+    switch (ND.getNonTransparentDeclContext()->getDeclKind()) {
     case Decl::TranslationUnit:
     case Decl::Namespace:
-    case Decl::LinkageSpec:
       return true;
     default:
       break;
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index ae753373635bcd..388aa0a5340755 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -524,13 +524,12 @@ bool SymbolCollector::shouldCollectSymbol(const NamedDecl 
&ND,
 
   // We want most things but not "local" symbols such as symbols inside
   // FunctionDecl, BlockDecl, ObjCMethodDecl and OMPDeclareReductionDecl.
-  const auto *DeclCtx = ND.getDeclContext();
+  // Transparent contexts (export, extern "C", unscoped enums) are skipped, as
+  // their declarations belong to the enclosing context.
+  const auto *DeclCtx = ND.getNonTransparentDeclContext();
   switch (DeclCtx->getDeclKind()) {
   case Decl::TranslationUnit:
   case Decl::Namespace:
-  case Decl::LinkageSpec:
-  // An export-declaration can only appear at namespace scope.
-  case Decl::Export:
   case Decl::Enum:
   case Decl::ObjCProtocol:
   case Decl::ObjCInterface:
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 62f8cbb7b1ee00..834b46f11a8df8 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -2235,6 +2235,37 @@ TEST_F(SymbolCollectorTest, ExportedDeclarations) {
                                    qName("ns::h"), qName("ns::not_exported")));
 }
 
+TEST_F(SymbolCollectorTest, ExportedDeclarationsForCodeCompletion) {
+  // Symbols declared in the main file are never used for code completion, so
+  // the exported declarations live in a header included into the purview.
+  InMemoryFileSystem->addFile(testPath("exports.h"), 0,
+                              llvm::MemoryBuffer::getMemBuffer(R"cpp(
+    export void f();
+    export {
+      struct S { void m(); };
+      enum E { A };
+    }
+    namespace ns {
+    export void h();
+    }
+  )cpp"));
+  const char *Main = R"cpp(
+    export module m;
+    #include "exports.h"
+  )cpp";
+
+  runSymbolCollector("", Main,
+                     {"-std=c++20", "-xc++-module", "-I", testRoot()});
+  EXPECT_THAT(Symbols, UnorderedElementsAre(
+                           AllOf(qName("f"), forCodeCompletion(true)),
+                           AllOf(qName("S"), forCodeCompletion(true)),
+                           AllOf(qName("S::m"), forCodeCompletion(false)),
+                           AllOf(qName("E"), forCodeCompletion(true)),
+                           AllOf(qName("A"), forCodeCompletion(true)),
+                           AllOf(qName("ns"), forCodeCompletion(true)),
+                           AllOf(qName("ns::h"), forCodeCompletion(true))));
+}
+
 TEST_F(SymbolCollectorTest, IncludeHeaderForwardDecls) {
   CollectorOpts.CollectIncludePath = true;
   const std::string Header = R"cpp(#pragma once 

>From b98f0adc865a94e6d7e9c01a43ff38b4ac22b446 Mon Sep 17 00:00:00 2001
From: Aoto <[email protected]>
Date: Fri, 23 Oct 2026 17:06:16 +0200
Subject: [PATCH 3/3] [clang][Index] Skip export declarations in USRs

---
 .../clangd/unittests/SymbolCollectorTests.cpp | 20 +++++++++++++++++++
 .../UnifiedSymbolResolution/USRGeneration.cpp |  2 +-
 clang/test/Index/USR/export.cpp               | 18 +++++++++++++++++
 3 files changed, 39 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/Index/USR/export.cpp

diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 834b46f11a8df8..00157a5d81abec 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -2266,6 +2266,26 @@ TEST_F(SymbolCollectorTest, 
ExportedDeclarationsForCodeCompletion) {
                            AllOf(qName("ns::h"), forCodeCompletion(true))));
 }
 
+TEST_F(SymbolCollectorTest, ExportedDeclarationsHaveDistinctIDs) {
+  // The enclosing namespace must be part of an exported symbol's USR, or these
+  // functions would share one SymbolID and be merged.
+  const char *Main = R"cpp(
+    export module m;
+    void f();
+    namespace a {
+    export void f();
+    }
+    namespace b {
+    export void f();
+    }
+  )cpp";
+
+  runSymbolCollector("", Main, {"-std=c++20", "-xc++-module"});
+  EXPECT_THAT(Symbols,
+              UnorderedElementsAre(qName("f"), qName("a"), qName("a::f"),
+                                   qName("b"), qName("b::f")));
+}
+
 TEST_F(SymbolCollectorTest, IncludeHeaderForwardDecls) {
   CollectorOpts.CollectIncludePath = true;
   const std::string Header = R"cpp(#pragma once 
diff --git a/clang/lib/UnifiedSymbolResolution/USRGeneration.cpp 
b/clang/lib/UnifiedSymbolResolution/USRGeneration.cpp
index b8c72dc6af4594..4481480069830f 100644
--- a/clang/lib/UnifiedSymbolResolution/USRGeneration.cpp
+++ b/clang/lib/UnifiedSymbolResolution/USRGeneration.cpp
@@ -228,7 +228,7 @@ bool USRGenerator::ShouldGenerateLocation(const NamedDecl 
*D) {
 void USRGenerator::VisitDeclContext(const DeclContext *DC) {
   if (const NamedDecl *D = dyn_cast<NamedDecl>(DC))
     Visit(D);
-  else if (isa<LinkageSpecDecl>(DC)) // Linkage specs are transparent in USRs.
+  else if (DC->isTransparentContext()) // e.g. linkage specs and exports.
     VisitDeclContext(DC->getParent());
 }
 
diff --git a/clang/test/Index/USR/export.cpp b/clang/test/Index/USR/export.cpp
new file mode 100644
index 00000000000000..1d1983c789bf65
--- /dev/null
+++ b/clang/test/Index/USR/export.cpp
@@ -0,0 +1,18 @@
+// RUN: c-index-test core -print-source-symbols -- -std=c++20 -xc++-module %s 
| FileCheck %s
+
+// Export declarations are skipped in USRs for enclosed items.
+export module m;
+
+namespace ns {
+export void f();
+// CHECK: [[@LINE-1]]:13 | function/C | f | c:@N@ns@F@f# |
+export {
+int X;
+// CHECK: [[@LINE-1]]:5 | variable/C | X | c:@N@ns@X |
+}
+export namespace inner {
+// CHECK: [[@LINE-1]]:18 | namespace/C++ | inner | c:@N@ns@N@inner |
+void g();
+// CHECK: [[@LINE-1]]:6 | function/C | g | c:@N@ns@N@inner@F@g# |
+}
+}

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

Reply via email to