https://github.com/ckandeler updated https://github.com/llvm/llvm-project/pull/220518
>From b025af6934d0b7621c8ffc01ae1e131de4a61edc Mon Sep 17 00:00:00 2001 From: Christian Kandeler <[email protected]> Date: Tue, 1 Sep 2026 18:44:14 +0200 Subject: [PATCH] [clangd] Include the operator name in documentHighlight Placing the cursor on an overloaded operator's declaration (e.g. `operator new`, `operator[]`) and requesting textDocument/document Highlight only highlighted the `operator` keyword itself, not the name or symbol that follows it (`new`, `[]`, etc.), even though that name is what's actually significant to the user. ReferenceFinder already splits some references into several spelled tokens (used for Objective-C's split selector syntax); reuse that mechanism for the operator name too, extracting the tokens spelled in the operator name's source range. This only applies to the declaration's own occurrence. Assisted-by: Claude --- clang-tools-extra/clangd/XRefs.cpp | 37 +++++++++++++++++++ .../clangd/unittests/XRefsTests.cpp | 26 +++++++++++++ 2 files changed, 63 insertions(+) diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index 86528d806eab3..c0af69df48da9 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -962,6 +962,30 @@ std::vector<DocumentLink> getDocumentLinks(ParsedAST &AST) { namespace { +/// Returns the locations of the spelled tokens overlapping [Range.getBegin(), +/// Range.getEnd()], in order. Both ends of \p Range must be file locations +/// in the same file. +llvm::SmallVector<SourceLocation, 4> +tokensSpelledInRange(const syntax::TokenBuffer &TB, const SourceManager &SM, + SourceRange Range) { + llvm::SmallVector<SourceLocation, 4> Locs; + if (Range.getBegin().isInvalid() || Range.getEnd().isInvalid()) + return Locs; + FileID FID = SM.getFileID(Range.getBegin()); + if (FID != SM.getFileID(Range.getEnd())) + return Locs; + unsigned EndOffset = SM.getFileOffset(Range.getEnd()); + llvm::ArrayRef<syntax::Token> Toks = TB.spelledTokens(FID); + auto It = llvm::partition_point(Toks, [&](const syntax::Token &Tok) { + return SM.getFileOffset(Tok.location()) < + SM.getFileOffset(Range.getBegin()); + }); + for (; It != Toks.end() && SM.getFileOffset(It->location()) <= EndOffset; + ++It) + Locs.push_back(It->location()); + return Locs; +} + /// Collects references to symbols within the main file. class ReferenceFinder : public index::IndexDataConsumer { public: @@ -1039,6 +1063,19 @@ class ReferenceFinder : public index::IndexDataConsumer { } else if (auto *OMD = llvm::dyn_cast_or_null<ObjCMethodDecl>(ASTNode.OrigD)) { OMD->getSelectorLocs(Locs); + } else if (auto *FD = llvm::dyn_cast_or_null<FunctionDecl>(D); + FD && FD->isOverloadedOperator() && + isInsideMainFile(FD->getNameInfo().getLoc(), SM)) { + // The operator name (e.g. `new`, `[]`, `<<`) is a separate token (or + // tokens) from the `operator` keyword itself; report both so the + // whole name gets highlighted, not just the keyword. Only do this + // when the declaration itself is in the main file: TB only has + // spelled tokens for the main file, and this is only useful anyway + // when we're looking at the occurrence at the declaration itself + // (checked below). + Locs.push_back(FD->getNameInfo().getLoc()); + auto OpNameRange = FD->getNameInfo().getCXXOperatorNameRange(); + llvm::append_range(Locs, tokensSpelledInRange(TB, SM, OpNameRange)); } // Sanity check: we expect the *first* token to match the reported loc. // Otherwise, maybe it was e.g. some other kind of reference to a Decl. diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp index dce033af73c1a..13192f2880eae 100644 --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -138,6 +138,32 @@ TEST(HighlightsTest, All) { return 1; } )cpp", + R"cpp(// Overloaded operator: the whole name, not just `operator`, is highlighted. + using size_t = decltype(sizeof(0)); + struct S { + static void *[[operator]] [[n^ew]](size_t); + static void operator delete(void *); + }; + )cpp", + R"cpp(// Same, with the cursor on the operator keyword itself. + using size_t = decltype(sizeof(0)); + struct S { + static void *[[^operator]] [[new]](size_t); + static void operator delete(void *); + }; + )cpp", + R"cpp(// Same, for operator delete. + using size_t = decltype(sizeof(0)); + struct S { + static void *operator new(size_t); + static void [[operator]] [[del^ete]](void *); + }; + )cpp", + R"cpp(// Overloaded operator spanning multiple tokens. + struct S { + void [[operator]] [[^(]][[)]](int); + }; + )cpp", }; for (const char *Test : Tests) { Annotations T(Test); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
