From b77d675694b8590666f1d7b473b5658d63de5342 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 9 Apr 2026 18:04:06 -0400
Subject: [PATCH 44/48] FIXME: WIP on "textDocument/documentHighlight"

---
 gcc/lsp/gcc-impl/server.cc       |  13 +++-
 gcc/lsp/gcc-impl/worker.cc       | 108 ++++++++++++++++++++++++++++++-
 gcc/lsp/gcc-impl/worker.h        |   4 ++
 gcc/lsp/lsp-spec.cc              |  75 +++++++++++++++++++++
 gcc/lsp/lsp-spec.h               |  57 ++++++++++++++--
 gcc/topics/ast-events-recorder.h |   8 +++
 6 files changed, 258 insertions(+), 7 deletions(-)

diff --git a/gcc/lsp/gcc-impl/server.cc b/gcc/lsp/gcc-impl/server.cc
index 9a19231a726..9d70d04f5ca 100644
--- a/gcc/lsp/gcc-impl/server.cc
+++ b/gcc/lsp/gcc-impl/server.cc
@@ -460,6 +460,12 @@ consume_client_request (json_rpc::request &&req)
 	(std::move (req));
       return;
     }
+  else if (0 == strcmp (method, "textDocument/documentHighlight"))
+    {
+      delegate_textDocument_request_to_worker<spec::DocumentHighlightParams>
+	(std::move (req));
+      return;
+    }
 #if 0
   else if (0 == strcmp (method, "textDocument/definition"))
     return dispatch_textDocument_definition (std::move (req));
@@ -706,11 +712,16 @@ lsp::gcc_impl::server::mediator::get_ServerCapabilities ()
 #endif
   
 #if 1
-  result.documentSymbolProvider.set_optional_value
+  result.documentHighlightProvider.set_optional_value
     (lsp::spec::boolean (true)); // FIXME
 #endif
 
 #if 1
+  result.documentSymbolProvider.set_optional_value
+    (lsp::spec::boolean (true)); // FIXME
+#endif
+
+#if 0
   result.semanticTokensProvider.set_optional_value
     (lsp::spec::SemanticTokensOptions ()); // FIXME
 #endif
diff --git a/gcc/lsp/gcc-impl/worker.cc b/gcc/lsp/gcc-impl/worker.cc
index fdeefc83561..6dd5378a58c 100644
--- a/gcc/lsp/gcc-impl/worker.cc
+++ b/gcc/lsp/gcc-impl/worker.cc
@@ -269,6 +269,27 @@ consume_json_rpc_request (json_rpc::request &&req)
 	 unmarshal_result.take_value ());
       return;
     }
+  else if (0 == strcmp (method, "textDocument/documentHighlight"))
+    {
+      auto unmarshal_result
+	= spec::DocumentHighlightParams::from_json (req.get_id (),
+						    *req.get_params ());
+      if (unmarshal_result.get_error ())
+	{
+	  gcc_unreachable (); // FIXME
+#if 0
+	  send_response_to_client
+	    (json_rpc::response::invalid_params (req,
+						 std::move (unmarshal_result)));
+#endif
+	  return;
+	}
+
+      on_request_textDocument_documentHighlight
+	(req.take_id (),
+	 unmarshal_result.take_value ());
+      return;
+    }
   else if (0 == strcmp (method, "textDocument/hover"))
     {
       auto unmarshal_result
@@ -422,6 +443,91 @@ on_request_textDocument_declaration (std::unique_ptr<json::value> req_id,
 				  json::make_null ()));
 }
 
+struct decl_usage_visitor
+{
+  decl_usage_visitor (tree decl)
+  : m_decl (decl)
+  {
+    gcc_assert (m_decl);
+  }
+
+  void visit (ast_node &node)
+  {
+    if (node.m_expr == m_decl)
+      m_uses.push_back (&node);
+  }
+
+  tree m_decl;
+  std::vector<ast_node *> m_uses;
+};
+
+void
+lsp::gcc_impl::worker::mediator::
+on_request_textDocument_documentHighlight (std::unique_ptr<json::value> req_id,
+					   spec::DocumentHighlightParams &&p)
+{
+  auto logger = get_logger ();
+  LSP_LOG_SCOPE (logger);
+
+  gcc_assert (m_frontend);
+
+  if (auto ast_node = get_ast_node_at_position (p))
+    {
+      // FIXME:
+      if (1)
+	{
+	  fprintf (stderr, "\nnode at position: \n");
+	  visit_node (*ast_node);
+	}
+      if (ast_node->m_expr)
+	{
+	  if (DECL_P (ast_node->m_expr))
+	    {
+	      spec::DocumentHighlightKind text_kind;
+	      text_kind.m_val = spec::DocumentHighlightKind::values::Text;
+
+	      vector<spec::DocumentHighlight> results;
+
+	      /* Get declaration.  */
+	      {
+		spec::DocumentHighlight decl;
+		location_t loc = DECL_SOURCE_LOCATION (ast_node->m_expr);
+		decl.range = lsp::spec::Range::from_location_t (loc);
+		decl.kind.set_optional_value (text_kind);
+		results.push_back (decl);
+	      }
+
+	      /* Get uses.  */
+	      decl_usage_visitor visitor (ast_node->m_expr);
+	      m_recorder.m_root_node.accept (visitor);
+	      for (auto iter : visitor.m_uses)
+		{
+		  spec::DocumentHighlight use;
+		  use.range = range_from_ast_node (*iter);
+		  use.kind.set_optional_value (text_kind);
+		  results.push_back (use);
+		}
+
+	      send_response_to_server
+		(json_rpc::response::success (std::move (req_id),
+					      results.to_json ()));
+	      return;
+	    }
+	}
+      else
+	{
+	  if (logger)
+	    logger->log ("ast node %qs has no tree node", ast_node->m_kind);
+	}
+    }
+
+  if (logger)
+    logger->log ("ast node not found");
+  send_response_to_server
+    (json_rpc::response::success (std::move (req_id),
+				  json::make_null ()));
+}
+
 void
 lsp::gcc_impl::worker::mediator::
 on_request_textDocument_documentSymbol (std::unique_ptr<json::value> req_id,
@@ -450,7 +556,7 @@ on_request_textDocument_hover (std::unique_ptr<json::value> req_id,
   if (auto ast_node = get_ast_node_at_position (p))
     {
       // FIXME:
-      if (1)
+      if (0)
 	{
 	  fprintf (stderr, "\nnode at position: \n");
 	  visit_node (*ast_node);
diff --git a/gcc/lsp/gcc-impl/worker.h b/gcc/lsp/gcc-impl/worker.h
index ce3f693dfd8..7c7678c57af 100644
--- a/gcc/lsp/gcc-impl/worker.h
+++ b/gcc/lsp/gcc-impl/worker.h
@@ -109,6 +109,10 @@ public:
   on_request_textDocument_declaration (std::unique_ptr<json::value> req_id,
 				       spec::DeclarationParams &&p);
 
+  void
+  on_request_textDocument_documentHighlight (std::unique_ptr<json::value> req_id,
+					     spec::DocumentHighlightParams &&p);
+
   void
   on_request_textDocument_documentSymbol (std::unique_ptr<json::value> req_id,
 					  spec::DocumentSymbolParams &&p);
diff --git a/gcc/lsp/lsp-spec.cc b/gcc/lsp/lsp-spec.cc
index 3cd630b99c7..4eec37a0f71 100644
--- a/gcc/lsp/lsp-spec.cc
+++ b/gcc/lsp/lsp-spec.cc
@@ -480,6 +480,7 @@ BEGIN_DEFINE_FROM_JSON(ServerCapabilities)
   UNMARSHAL_OPTIONAL_PROPERTY (HoverOptions, hoverProvider);
   UNMARSHAL_OPTIONAL_PROPERTY (SignatureHelpOptions, signatureHelpProvider);
   UNMARSHAL_OPTIONAL_PROPERTY (boolean, declarationProvider);
+  UNMARSHAL_OPTIONAL_PROPERTY (boolean, documentHighlightProvider);
   UNMARSHAL_OPTIONAL_PROPERTY (boolean, documentSymbolProvider);
   UNMARSHAL_OPTIONAL_PROPERTY (SemanticTokensOptions, semanticTokensProvider);
 }
@@ -492,6 +493,7 @@ BEGIN_DEFINE_TO_JSON(ServerCapabilities)
   MARSHAL_OPTIONAL_PROPERTY (HoverOptions, hoverProvider);
   MARSHAL_OPTIONAL_PROPERTY (SignatureHelpOptions, signatureHelpProvider);
   MARSHAL_OPTIONAL_PROPERTY (boolean, declarationProvider);
+  MARSHAL_OPTIONAL_PROPERTY (boolean, documentHighlightProvider);
   MARSHAL_OPTIONAL_PROPERTY (boolean, documentSymbolProvider);
   MARSHAL_OPTIONAL_PROPERTY (SemanticTokensOptions, semanticTokensProvider);
 }
@@ -1062,6 +1064,79 @@ BEGIN_DEFINE_TO_JSON(Hover)
 }
 END_DEFINE_TO_JSON
 
+// struct DocumentHighlightParams : public TextDocumentPositionParams
+
+BEGIN_DEFINE_FROM_JSON(DocumentHighlightParams)
+{
+  // FIXME: how to share impl with base class?
+  UNMARSHAL_REQUIRED_PROPERTY (TextDocumentIdentifier, textDocument);
+  UNMARSHAL_REQUIRED_PROPERTY (Position, position);
+}
+END_DEFINE_FROM_JSON
+
+BEGIN_DEFINE_TO_JSON(DocumentHighlightParams)
+{
+  // FIXME: how to share impl with base class?
+  MARSHAL_REQUIRED_PROPERTY (TextDocumentIdentifier, textDocument);
+  MARSHAL_REQUIRED_PROPERTY (Position, position);
+}
+END_DEFINE_TO_JSON
+
+// struct DocumentHighlightKind
+
+// FIXME: add generic code for this
+// FIXME: check for known values?
+
+template <typename T>
+lsp::result<T>
+unmarshal_enum (const unmarshaller &ctxt,
+		const json::value &jv)
+{
+  const json::integer_number *js_int = jv.dyn_cast_integer_number ();
+  if (!js_int)
+    return not_an_integer<T> (ctxt, jv);
+
+  T unmarshalled_val;
+  unmarshalled_val.m_val = static_cast<typename T::values> (js_int->get ());
+  return unmarshalled_val;
+}
+
+lsp::result<lsp::spec::DocumentHighlightKind>
+lsp::spec::DocumentHighlightKind::from_json (const unmarshaller &ctxt,
+					     const json::value &jv)
+{
+  return unmarshal_enum<lsp::spec::DocumentHighlightKind> (ctxt, jv);
+}
+
+template <typename T>
+std::unique_ptr<json::value>
+marshal_enum (const T &t)
+{
+  return std::make_unique<json::integer_number> (static_cast<long> (t.m_val));
+}
+
+std::unique_ptr<json::value>
+lsp::spec::DocumentHighlightKind::to_json () const
+{
+  return marshal_enum<lsp::spec::DocumentHighlightKind> (*this);
+}
+
+// struct DocumentHighlight
+
+BEGIN_DEFINE_FROM_JSON(DocumentHighlight)
+{
+  UNMARSHAL_REQUIRED_PROPERTY (Range, range);
+  UNMARSHAL_OPTIONAL_PROPERTY (DocumentHighlightKind, kind);
+}
+END_DEFINE_FROM_JSON
+
+BEGIN_DEFINE_TO_JSON(DocumentHighlight)
+{
+  MARSHAL_REQUIRED_PROPERTY (Range, range);
+  MARSHAL_OPTIONAL_PROPERTY (DocumentHighlightKind, kind);
+}
+END_DEFINE_TO_JSON
+
 #if CHECKING_P
 
 namespace selftest {
diff --git a/gcc/lsp/lsp-spec.h b/gcc/lsp/lsp-spec.h
index 70dc217c998..a1eea5b67a8 100644
--- a/gcc/lsp/lsp-spec.h
+++ b/gcc/lsp/lsp-spec.h
@@ -137,6 +137,29 @@ public:
   : m_ptr (std::make_unique<Type> (std::move (t)))
   {}
 
+  optional (const optional<Type> &other)
+  {
+    if (other.m_ptr)
+      m_ptr = std::make_unique<Type> (*other.m_ptr);
+  }
+
+  optional<Type> &
+  operator= (const optional<Type> &other)
+  {
+    if (other.m_ptr)
+      m_ptr = std::make_unique<Type> (*other.m_ptr);
+    else
+      m_ptr.release ();
+    return *this;
+  }
+
+  optional<Type> &
+  operator= (optional<Type> &&other)
+  {
+    m_ptr = std::move (other.m_ptr);
+    return *this;
+  }
+
   Type *
   get_optional_value () { return m_ptr.get (); }
 
@@ -148,6 +171,11 @@ public:
   {
     m_ptr = std::make_unique<Type> (std::move (t));
   }
+  void
+  set_optional_value (const Type &t)
+  {
+    m_ptr = std::make_unique<Type> (t);
+  }
 
 private:
   std::unique_ptr<Type> m_ptr;
@@ -448,12 +476,8 @@ struct ServerCapabilities
 	 */
 	referencesProvider?: boolean | ReferenceOptions;
 
-	/**
-	 * The server provides document highlight support.
-	 */
-	documentHighlightProvider?: boolean | DocumentHighlightOptions;
 #endif
-
+  optional<boolean> documentHighlightProvider;
   optional<boolean> documentSymbolProvider;
 
 #if 0
@@ -948,6 +972,29 @@ struct Hover
   optional<Range> range;
 };
 
+struct DocumentHighlightParams : public TextDocumentPositionParams
+{
+  DECLARE_JSON_METHODS(DocumentHighlightParams)
+};
+
+struct DocumentHighlightKind
+{
+  DECLARE_JSON_METHODS(DocumentHighlightKind)
+  enum class values {
+    Text = 1,
+    Read = 2,
+    Write = 3
+  } m_val;
+};
+
+struct DocumentHighlight
+{
+  DECLARE_JSON_METHODS(DocumentHighlight)
+
+  Range range;
+  optional<DocumentHighlightKind> kind;
+};
+
 } // namespace lsp::spec
 
 /* A pp_element subclass for printing lsp::spec types, by printing
diff --git a/gcc/topics/ast-events-recorder.h b/gcc/topics/ast-events-recorder.h
index 0b37e6997bf..ff48cce234c 100644
--- a/gcc/topics/ast-events-recorder.h
+++ b/gcc/topics/ast-events-recorder.h
@@ -40,6 +40,14 @@ public:
       m_expr (nullptr)
     {}
 
+    template <typename NodeVisitor>
+    void accept (NodeVisitor &visitor)
+    {
+      visitor.visit (*this);
+      for (auto &iter : m_children)
+	iter->accept (visitor);
+    }
+
     const char *m_kind;
     location_t m_start_loc;
     location_t m_end_loc;
-- 
2.49.0

