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

Reply via email to