https://github.com/Anthony-Gaudino created 
https://github.com/llvm/llvm-project/pull/225404

## Summary

A comment block containing a section-divider line was reported as documentation 
for the next declaration. In practice this meant the title of a banner was 
shown on hover, e.g.:

```cpp
// Per-frame pump
// ========================================================================
void update(); // hover showed "Per-frame pump\n===..."
```

`getDeclComment` already filtered comments consisting solely of special 
characters (`looksLikeDocComment`), but a divider combined with a title line 
passed the filter and the title was shown as documentation.

## What this changes

- `looksLikeDocComment` (`CodeCompletionStrings.cpp`) now also rejects any 
comment containing a divider line: a line that, trimmed, is a run of 10+ 
repetitions of a single character. The check runs on the formatted text, so it 
applies regardless of comment style (`//`, `///`, `/* ... */`).
- The length threshold keeps short runs (markdown `---` rules and similar 
adornments) working as documentation; a regression test pins this boundary.

This affects hover and code-completion documentation, which share 
`getDeclComment`.

## Testing

- New cases in `TEST(Hover, Structured)`: titled `===` divider, same with a 
blank line before the declaration, `---` divider, `/* ### */` block banner, 
`+++` divider, and a short-run negative control.
- Verified the divider cases fail without the fix (hover showed the banner 
title) and pass with it.
- Full `ClangdTests` suite passes (1434 tests).

## Related

- Related to clangd/clangd#974 (divider banners matched across blank lines).

From 9738df685a6e7d9932298bcb08fd148dd4aaabbc Mon Sep 17 00:00:00 2001
From: Anthony Gaudino <[email protected]>
Date: Tue, 22 Sep 2026 14:44:33 +0100
Subject: [PATCH] [clangd] Ignore section-divider comments in hover
 documentation

Comments containing a divider line (a run of 10+ identical characters,
e.g. // ===... or /* ###... */ banners) are never documentation, even
when combined with a title line. Previously the title of such a block
was reported as documentation for the next declaration.

Related to clangd/clangd#974.
---
 .../clangd/CodeCompletionStrings.cpp          |  28 ++++-
 .../clangd/unittests/HoverTests.cpp           | 100 ++++++++++++++++++
 2 files changed, 127 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/CodeCompletionStrings.cpp 
b/clang-tools-extra/clangd/CodeCompletionStrings.cpp
index dc86be60a876f..61c86f7b1d82c 100644
--- a/clang-tools-extra/clangd/CodeCompletionStrings.cpp
+++ b/clang-tools-extra/clangd/CodeCompletionStrings.cpp
@@ -54,13 +54,39 @@ void appendOptionalChunk(const CodeCompletionString &CCS, 
std::string *Out) {
   }
 }
 
+/// A divider line is a long run of a single repeated character, as used in
+/// section banners. These are never documentation, even when combined with
+/// a title line, as in:
+///   // Per-frame pump
+///   // ====================================================================
+/// The length threshold keeps short runs (e.g. markdown `---` rules or RST
+/// adornments) working as documentation.
+bool isDividerLine(llvm::StringRef Line) {
+  constexpr unsigned MinDividerLength = 10;
+  Line = Line.trim(" \t\r\n");
+  if (Line.size() < MinDividerLength)
+    return false;
+  return Line.find_first_not_of(Line.front()) == llvm::StringRef::npos;
+}
+
 bool looksLikeDocComment(llvm::StringRef CommentText) {
   // We don't report comments that only contain "special" chars.
   // This avoids reporting various delimiters, like:
   //   =================
   //   -----------------
   //   *****************
-  return CommentText.find_first_not_of("/*-= \t\r\n") != llvm::StringRef::npos;
+  if (CommentText.find_first_not_of("/*-= \t\r\n") == llvm::StringRef::npos)
+    return false;
+  // Nor comments containing a section-divider line. Without this, the title
+  // of a divider block is reported as documentation for the next declaration.
+  llvm::StringRef Rest = CommentText;
+  while (!Rest.empty()) {
+    const auto Split = Rest.split('\n');
+    if (isDividerLine(Split.first))
+      return false;
+    Rest = Split.second;
+  }
+  return true;
 }
 
 // Determine whether the completion string should be patched
diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp 
b/clang-tools-extra/clangd/unittests/HoverTests.cpp
index 15b03e6bb6ece..0c2d667a45589 100644
--- a/clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -57,6 +57,106 @@ TEST(Hover, Structured) {
          HI.Type = "void ()";
          HI.Parameters.emplace();
        }},
+      // A section divider with a title is not documentation.
+      {R"cpp(
+          // Per-frame pump
+          // 
========================================================================
+          void [[fo^o]]() {}
+          )cpp",
+       [](HoverInfo &HI) {
+         HI.NamespaceScope = "";
+         HI.Name = "foo";
+         HI.Kind = index::SymbolKind::Function;
+         HI.Documentation = "";
+         HI.Definition = "void foo()";
+         HI.ReturnType = "void";
+         HI.Type = "void ()";
+         HI.Parameters.emplace();
+       }},
+      // Same, with a blank line between the divider and the declaration.
+      {R"cpp(
+          // Per-frame pump
+          // 
========================================================================
+
+          void [[fo^o]]() {}
+          )cpp",
+       [](HoverInfo &HI) {
+         HI.NamespaceScope = "";
+         HI.Name = "foo";
+         HI.Kind = index::SymbolKind::Function;
+         HI.Documentation = "";
+         HI.Definition = "void foo()";
+         HI.ReturnType = "void";
+         HI.Type = "void ()";
+         HI.Parameters.emplace();
+       }},
+      // Other divider styles are not documentation either.
+      {R"cpp(
+          // Appearance
+          // 
------------------------------------------------------------------------
+          void [[fo^o]]() {}
+          )cpp",
+       [](HoverInfo &HI) {
+         HI.NamespaceScope = "";
+         HI.Name = "foo";
+         HI.Kind = index::SymbolKind::Function;
+         HI.Documentation = "";
+         HI.Definition = "void foo()";
+         HI.ReturnType = "void";
+         HI.Type = "void ()";
+         HI.Parameters.emplace();
+       }},
+      // Block-comment banners are not documentation either.
+      {R"cpp(
+          /*
+          
##########################################################################
+          Private
+          
##########################################################################
+          */
+          void [[fo^o]]() {}
+          )cpp",
+       [](HoverInfo &HI) {
+         HI.NamespaceScope = "";
+         HI.Name = "foo";
+         HI.Kind = index::SymbolKind::Function;
+         HI.Documentation = "";
+         HI.Definition = "void foo()";
+         HI.ReturnType = "void";
+         HI.Type = "void ()";
+         HI.Parameters.emplace();
+       }},
+      // Plus-run dividers are not documentation either.
+      {R"cpp(
+          // Helpers
+          // 
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+          void [[fo^o]]() {}
+          )cpp",
+       [](HoverInfo &HI) {
+         HI.NamespaceScope = "";
+         HI.Name = "foo";
+         HI.Kind = index::SymbolKind::Function;
+         HI.Documentation = "";
+         HI.Definition = "void foo()";
+         HI.ReturnType = "void";
+         HI.Type = "void ()";
+         HI.Parameters.emplace();
+       }},
+      // Short runs (e.g. markdown rules) are still documentation.
+      {R"cpp(
+          // Best foo ever.
+          // ---
+          void [[fo^o]]() {}
+          )cpp",
+       [](HoverInfo &HI) {
+         HI.NamespaceScope = "";
+         HI.Name = "foo";
+         HI.Kind = index::SymbolKind::Function;
+         HI.Documentation = "Best foo ever.\n---";
+         HI.Definition = "void foo()";
+         HI.ReturnType = "void";
+         HI.Type = "void ()";
+         HI.Parameters.emplace();
+       }},
       {R"cpp(
           // Best foo ever.
           void [[fo^o]](auto x) {}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to