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
