diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp --- a/clang-tools-extra/clangd/Selection.cpp +++ b/clang-tools-extra/clangd/Selection.cpp @@ -732,6 +732,19 @@ } else if (const auto *CCI = N.get()) { // : [[b_]](42) return CCI->getMemberLocation(); + } else if (const auto *ME = N.get()) { + // In member expressions involving anonymous structs, there's an implicit + // reference to the container. Make sure all the tokens that belong to the + // member are claimed first to prevent this unspelled children from owning + // them. + // struct Foo { + // struct { int x; } + // void foo() { [[x]] = 2; } + // }; + if (auto *FD = llvm::dyn_cast(ME->getMemberDecl())) { + if (FD->getParent()->isAnonymousStructOrUnion()) + return SourceRange(ME->getMemberLoc(), ME->getEndLoc()); + } } return SourceRange(); } diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -365,6 +365,25 @@ ElementsAre(Sym("Forward", SymbolHeader.range("forward"), Test.range()))); } +TEST(LocateSymbol, AnonymousStructFields) { + auto Code = Annotations(R"cpp( + class Foo { + struct { + struct { + int $1[[x]]; + } $0[[y]]; + }; + void foo() { $0^y.$1^x = 2; } + }; + )cpp"); + TestTU TU = TestTU::withCode(Code.code()); + auto AST = TU.build(); + EXPECT_THAT(locateSymbolAt(AST, Code.point("0"), TU.index().get()), + UnorderedElementsAre(Sym("y", Code.range("0"), Code.range("0")))); + EXPECT_THAT(locateSymbolAt(AST, Code.point("1"), TU.index().get()), + UnorderedElementsAre(Sym("x", Code.range("1"), Code.range("1")))); +} + TEST(LocateSymbol, FindOverrides) { auto Code = Annotations(R"cpp( class Foo {