https://github.com/JaagupAverin created 
https://github.com/llvm/llvm-project/pull/155105

Currently macro expansions are hard capped at 2048. This PR adds the CLI option 
`--limit-hover-contents` which is passed down in place of the hard coded value.
Resolves #153355.

>From 82c5665913eca5c63c952df93df27d9875596dea Mon Sep 17 00:00:00 2001
From: JaagupA <jaagup.ave...@gmail.com>
Date: Sat, 23 Aug 2025 16:14:35 +0300
Subject: [PATCH] [clangd] Add option: limit-hover-contents

Resolves #153355.
---
 clang-tools-extra/clangd/ClangdLSPServer.cpp  |  1 +
 clang-tools-extra/clangd/ClangdLSPServer.h    |  3 ++
 clang-tools-extra/clangd/ClangdServer.cpp     |  6 ++-
 clang-tools-extra/clangd/ClangdServer.h       |  2 +-
 clang-tools-extra/clangd/Hover.cpp            |  9 +++--
 clang-tools-extra/clangd/Hover.h              |  9 +++--
 clang-tools-extra/clangd/tool/Check.cpp       |  2 +-
 clang-tools-extra/clangd/tool/ClangdMain.cpp  | 10 +++++
 .../clangd/unittests/HoverTests.cpp           | 40 +++++++++++++++++++
 9 files changed, 71 insertions(+), 11 deletions(-)

diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp 
b/clang-tools-extra/clangd/ClangdLSPServer.cpp
index b445dcf2bbd2e..503935a0a4797 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -1268,6 +1268,7 @@ void ClangdLSPServer::onDocumentHighlight(
 void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params,
                               Callback<std::optional<Hover>> Reply) {
   Server->findHover(Params.textDocument.uri.file(), Params.position,
+                    Opts.HoverContentsLimit,
                     [Reply = std::move(Reply),
                      this](llvm::Expected<std::optional<HoverInfo>> H) mutable 
{
                       if (!H)
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h 
b/clang-tools-extra/clangd/ClangdLSPServer.h
index 6ada3fd9e6e47..7c2c44fea1b4a 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.h
+++ b/clang-tools-extra/clangd/ClangdLSPServer.h
@@ -64,6 +64,9 @@ class ClangdLSPServer : private ClangdServer::Callbacks,
     /// Limit the number of references returned (0 means no limit).
     size_t ReferencesLimit = 0;
 
+    /// Limit the number of characters returned when hovering a symbol.
+    size_t HoverContentsLimit = 0;
+
     /// Flag to hint the experimental modules support is enabled.
     bool EnableExperimentalModulesSupport = false;
   };
diff --git a/clang-tools-extra/clangd/ClangdServer.cpp 
b/clang-tools-extra/clangd/ClangdServer.cpp
index ac1e9aa5f0ff1..01ec164a56e59 100644
--- a/clang-tools-extra/clangd/ClangdServer.cpp
+++ b/clang-tools-extra/clangd/ClangdServer.cpp
@@ -843,14 +843,16 @@ void ClangdServer::findDocumentHighlights(
 }
 
 void ClangdServer::findHover(PathRef File, Position Pos,
+                             size_t HoverContentsLimit,
                              Callback<std::optional<HoverInfo>> CB) {
-  auto Action = [File = File.str(), Pos, CB = std::move(CB),
+  auto Action = [File = File.str(), Pos, HoverContentsLimit, CB = 
std::move(CB),
                  this](llvm::Expected<InputsAndAST> InpAST) mutable {
     if (!InpAST)
       return CB(InpAST.takeError());
     format::FormatStyle Style = getFormatStyleForFile(
         File, InpAST->Inputs.Contents, *InpAST->Inputs.TFS, false);
-    CB(clangd::getHover(InpAST->AST, Pos, std::move(Style), Index));
+    CB(clangd::getHover(InpAST->AST, Pos, std::move(Style), Index,
+                        HoverContentsLimit));
   };
 
   WorkScheduler->runWithAST("Hover", File, std::move(Action), Transient);
diff --git a/clang-tools-extra/clangd/ClangdServer.h 
b/clang-tools-extra/clangd/ClangdServer.h
index 4a1eae188f7eb..6a3fa55d1fcc3 100644
--- a/clang-tools-extra/clangd/ClangdServer.h
+++ b/clang-tools-extra/clangd/ClangdServer.h
@@ -270,7 +270,7 @@ class ClangdServer {
                               Callback<std::vector<DocumentHighlight>> CB);
 
   /// Get code hover for a given position.
-  void findHover(PathRef File, Position Pos,
+  void findHover(PathRef File, Position Pos, size_t HoverContentsLimit,
                  Callback<std::optional<HoverInfo>> CB);
 
   /// Get information about type hierarchy for a given position.
diff --git a/clang-tools-extra/clangd/Hover.cpp 
b/clang-tools-extra/clangd/Hover.cpp
index a7cf45c632827..f352a16c12ecb 100644
--- a/clang-tools-extra/clangd/Hover.cpp
+++ b/clang-tools-extra/clangd/Hover.cpp
@@ -755,7 +755,7 @@ HoverInfo evaluateMacroExpansion(unsigned int 
SpellingBeginOffset,
 
 /// Generate a \p Hover object given the macro \p MacroDecl.
 HoverInfo getHoverContents(const DefinedMacro &Macro, const syntax::Token &Tok,
-                           ParsedAST &AST) {
+                           ParsedAST &AST, size_t HoverContentsLimit) {
   HoverInfo HI;
   SourceManager &SM = AST.getSourceManager();
   HI.Name = std::string(Macro.Name);
@@ -795,7 +795,7 @@ HoverInfo getHoverContents(const DefinedMacro &Macro, const 
syntax::Token &Tok,
     for (const auto &ExpandedTok : Expansion->Expanded) {
       ExpansionText += ExpandedTok.text(SM);
       ExpansionText += " ";
-      if (ExpansionText.size() > 2048) {
+      if (HoverContentsLimit && ExpansionText.size() > HoverContentsLimit) {
         ExpansionText.clear();
         break;
       }
@@ -1250,7 +1250,8 @@ void maybeAddUsedSymbols(ParsedAST &AST, HoverInfo &HI, 
const Inclusion &Inc) {
 
 std::optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
                                   const format::FormatStyle &Style,
-                                  const SymbolIndex *Index) {
+                                  const SymbolIndex *Index,
+                                  size_t HoverContentsLimit) {
   static constexpr trace::Metric HoverCountMetric(
       "hover", trace::Metric::Counter, "case");
   PrintingPolicy PP =
@@ -1297,7 +1298,7 @@ std::optional<HoverInfo> getHover(ParsedAST &AST, 
Position Pos,
       HighlightRange = Tok.range(SM).toCharRange(SM);
       if (auto M = locateMacroAt(Tok, AST.getPreprocessor())) {
         HoverCountMetric.record(1, "macro");
-        HI = getHoverContents(*M, Tok, AST);
+        HI = getHoverContents(*M, Tok, AST, HoverContentsLimit);
         if (auto DefLoc = M->Info->getDefinitionLoc(); DefLoc.isValid()) {
           include_cleaner::Macro IncludeCleanerMacro{
               AST.getPreprocessor().getIdentifierInfo(Tok.text(SM)), DefLoc};
diff --git a/clang-tools-extra/clangd/Hover.h b/clang-tools-extra/clangd/Hover.h
index 614180a7b9846..4dbd7c050421d 100644
--- a/clang-tools-extra/clangd/Hover.h
+++ b/clang-tools-extra/clangd/Hover.h
@@ -163,10 +163,13 @@ inline bool operator==(const HoverInfo::Param &LHS,
          std::tie(RHS.Type, RHS.Name, RHS.Default);
 }
 
+constexpr size_t DefaultHoverContentsLimit = 2048;
+
 /// Get the hover information when hovering at \p Pos.
-std::optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
-                                  const format::FormatStyle &Style,
-                                  const SymbolIndex *Index);
+std::optional<HoverInfo>
+getHover(ParsedAST &AST, Position Pos, const format::FormatStyle &Style,
+         const SymbolIndex *Index,
+         size_t HoverContentsLimit = DefaultHoverContentsLimit);
 
 } // namespace clangd
 } // namespace clang
diff --git a/clang-tools-extra/clangd/tool/Check.cpp 
b/clang-tools-extra/clangd/tool/Check.cpp
index df8d075e80596..d103ef3f5aed3 100644
--- a/clang-tools-extra/clangd/tool/Check.cpp
+++ b/clang-tools-extra/clangd/tool/Check.cpp
@@ -441,7 +441,7 @@ class Checker {
       unsigned Definitions = locateSymbolAt(*AST, Pos, &Index).size();
       vlog("    definition: {0}", Definitions);
 
-      auto Hover = getHover(*AST, Pos, Style, &Index);
+      auto Hover = getHover(*AST, Pos, Style, &Index, Opts.HoverContentsLimit);
       vlog("    hover: {0}", Hover.has_value());
 
       unsigned DocHighlights = findDocumentHighlights(*AST, Pos).size();
diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp 
b/clang-tools-extra/clangd/tool/ClangdMain.cpp
index f287439f10cab..5e8755d643d7f 100644
--- a/clang-tools-extra/clangd/tool/ClangdMain.cpp
+++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp
@@ -328,6 +328,15 @@ opt<int> ReferencesLimit{
     init(1000),
 };
 
+opt<int> HoverContentsLimit{
+    "limit-hover-contents",
+    cat(Features),
+    desc("Limit the number of characters returned when hovering a symbol. "
+         "Texts longer than this value will be dropped (not truncated). "
+         "0 means no limit (default=2048)"),
+    init(DefaultHoverContentsLimit),
+};
+
 opt<int> RenameFileLimit{
     "rename-file-limit",
     cat(Features),
@@ -925,6 +934,7 @@ clangd accepts flags on the commandline, and in the 
CLANGD_FLAGS environment var
   Opts.BackgroundIndex = EnableBackgroundIndex;
   Opts.BackgroundIndexPriority = BackgroundIndexPriority;
   Opts.ReferencesLimit = ReferencesLimit;
+  Opts.HoverContentsLimit = HoverContentsLimit;
   Opts.Rename.LimitFiles = RenameFileLimit;
   auto PAI = createProjectAwareIndex(
       [SupportContainedRefs = Opts.EnableOutgoingCalls](
diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp 
b/clang-tools-extra/clangd/unittests/HoverTests.cpp
index 6407349d9af9b..fe0f4334746ca 100644
--- a/clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -4461,6 +4461,46 @@ constexpr u64 pow_with_mod(u64 a, u64 b, u64 p) {
   EXPECT_TRUE(H->Type);
 }
 
+TEST(Hover, HoverContentsLimit) {
+  const char *const Code =
+      R"cpp(
+          #define C(A) A##A // Concatenate
+          #define E(A) C(A) // Expand
+          #define Z0032 00000000000000000000000000000000
+          #define Z0064 E(Z0032)
+          #define Z0128 E(Z0064)
+          #define Z0256 E(Z0128)
+          #define Z0512 E(Z0256)
+          #define Z1024 E(Z0512)
+          #define Z2048 E(Z1024)
+          #define Z4096 E(Z2048) // 4096 zeroes
+          int main() { return [[^Z4096]]; }
+      )cpp";
+
+  struct {
+    size_t HoverContentsLimit;
+    const std::string ExpectedDefinition;
+  } Cases[] = {
+      // With a limit of 2048, the macro expansion should get dropped.
+      {2048, "#define Z4096 E(Z2048)"},
+      // With a limit of 8192, the macro expansion should be fully expanded.
+      {8192, std::string("#define Z4096 E(Z2048)\n\n") +
+                 std::string("// Expands to\n") + std::string(4096, '0')},
+  };
+  for (const auto &Case : Cases) {
+    SCOPED_TRACE(Code);
+
+    Annotations T(Code);
+    TestTU TU = TestTU::withCode(T.code());
+    auto AST = TU.build();
+    auto H = getHover(AST, T.point(), format::getLLVMStyle(), nullptr,
+                      Case.HoverContentsLimit);
+    ASSERT_TRUE(H);
+
+    EXPECT_EQ(H->Definition, Case.ExpectedDefinition);
+  }
+};
+
 TEST(Hover, FunctionParameters) {
   struct {
     const char *const Code;

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to