Index: clangd/CMakeLists.txt =================================================================== --- clangd/CMakeLists.txt +++ clangd/CMakeLists.txt @@ -56,7 +56,6 @@ clangSerialization clangTooling clangToolingCore - clangToolingInclusions clangToolingRefactor ${LLVM_PTHREAD_LIB} ${CLANGD_ATOMIC_LIB} Index: clangd/Headers.h =================================================================== --- clangd/Headers.h +++ clangd/Headers.h @@ -17,7 +17,7 @@ #include "clang/Format/Format.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/PPCallbacks.h" -#include "clang/Tooling/Inclusions/HeaderIncludes.h" +#include "clang/Tooling/Core/HeaderIncludes.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/Error.h" Index: clangd/index/SymbolCollector.h =================================================================== --- clangd/index/SymbolCollector.h +++ clangd/index/SymbolCollector.h @@ -80,6 +80,12 @@ Options Opts; // Decls referenced from the current TU, flushed on finish(). llvm::DenseSet ReferencedDecls; + // Maps canonical declaration provided by clang to canonical declaration for + // an index symbol, if clangd prefers a different declaration than that + // provided by clang. For example, friend declaration might be considered + // canonical by clang but should not be considered canonical in the index + // unless it's a definition. + llvm::DenseMap CanonicalDecls; }; } // namespace clangd Index: clangd/index/SymbolCollector.cpp =================================================================== --- clangd/index/SymbolCollector.cpp +++ clangd/index/SymbolCollector.cpp @@ -290,6 +290,19 @@ index::IndexDataConsumer::ASTNodeInfo ASTNode) { assert(ASTCtx && PP.get() && "ASTContext and Preprocessor must be set."); assert(CompletionAllocator && CompletionTUInfo); + assert(ASTNode.OrigD); + // If OrigD is an declaration associated with a friend declaration and it's + // not a definition, skip it. Note that OrigD is the occurrence that the + // collector is currently visiting. + if ((ASTNode.OrigD->getFriendObjectKind() != + Decl::FriendObjectKind::FOK_None) && + !(Roles & static_cast(index::SymbolRole::Definition))) + return true; + // A declaration created for a friend declaration should not be used as the + // canonical declaration in the index. Use OrigD instead, unless we've already + // picked a replacement for D + if (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None) + D = CanonicalDecls.try_emplace(D, ASTNode.OrigD).first->second; const NamedDecl *ND = llvm::dyn_cast(D); if (!ND) return true; Index: unittests/clangd/SymbolCollectorTests.cpp =================================================================== --- unittests/clangd/SymbolCollectorTests.cpp +++ unittests/clangd/SymbolCollectorTests.cpp @@ -812,6 +812,48 @@ QName("nx::Kind"), QName("nx::Kind_Fine"))); } +TEST_F(SymbolCollectorTest, DoNotIndexSymbolsInFriendDecl) { + Annotations Header(R"( + namespace nx { + class $z[[Z]] {}; + class X { + friend class Y; + friend class Z; + friend void foo(); + friend void $bar[[bar]]() {} + }; + class $y[[Y]] {}; + void $foo[[foo]](); + } + )"); + runSymbolCollector(Header.code(), /*Main=*/""); + + EXPECT_THAT(Symbols, + UnorderedElementsAre( + QName("nx"), QName("nx::X"), + AllOf(QName("nx::Y"), DeclRange(Header.range("y"))), + AllOf(QName("nx::Z"), DeclRange(Header.range("z"))), + AllOf(QName("nx::foo"), DeclRange(Header.range("foo"))), + AllOf(QName("nx::bar"), DeclRange(Header.range("bar"))))); +} + +TEST_F(SymbolCollectorTest, ReferencesInFriendDecl) { + const std::string Header = R"( + class X; + class Y; + )"; + const std::string Main = R"( + class C { + friend ::X; + friend class Y; + }; + )"; + CollectorOpts.CountReferences = true; + runSymbolCollector(Header, Main); + EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), Refs(1)), + AllOf(QName("Y"), Refs(1)))); +} + } // namespace } // namespace clangd } // namespace clang