[clang-tools-extra] [clangd] Fix crash on hover over 64bit enums with MSB set (PR #173187)
Mythreya Kuricheti via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 9 20:59:47 PST 2026
https://github.com/MythreyaK updated https://github.com/llvm/llvm-project/pull/173187
>From 3e867e79f7ae623110e298e8072555412e4afce3 Mon Sep 17 00:00:00 2001
From: Mythreya <git at mythreya.dev>
Date: Sun, 21 Dec 2025 05:00:58 -0800
Subject: [PATCH 1/3] [clangd] Fix crash on hover over large `UL` enums
---
clang-tools-extra/clangd/Hover.cpp | 4 +-
.../clangd/unittests/HoverTests.cpp | 59 +++++++++++++++++++
2 files changed, 61 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp
index bcf72255423bc..096689bfb2fcc 100644
--- a/clang-tools-extra/clangd/Hover.cpp
+++ b/clang-tools-extra/clangd/Hover.cpp
@@ -454,7 +454,7 @@ std::optional<std::string> printExprValue(const Expr *E,
// Show enums symbolically, not numerically like APValue::printPretty().
if (T->isEnumeralType() && Constant.Val.isInt() &&
- Constant.Val.getInt().getSignificantBits() <= 64) {
+ Constant.Val.getInt().isRepresentableByInt64()) {
// Compare to int64_t to avoid bit-width match requirements.
int64_t Val = Constant.Val.getInt().getExtValue();
for (const EnumConstantDecl *ECD : T->castAsEnumDecl()->enumerators())
@@ -465,7 +465,7 @@ std::optional<std::string> printExprValue(const Expr *E,
}
// Show hex value of integers if they're at least 10 (or negative!)
if (T->isIntegralOrEnumerationType() && Constant.Val.isInt() &&
- Constant.Val.getInt().getSignificantBits() <= 64 &&
+ Constant.Val.getInt().isRepresentableByInt64() &&
Constant.Val.getInt().uge(10))
return llvm::formatv("{0} ({1})", Constant.Val.getAsString(Ctx, T),
printHex(Constant.Val.getInt()))
diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp
index eb858ff616e90..0c8423bd5fb2a 100644
--- a/clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -5015,6 +5015,65 @@ TEST(Hover, FunctionParameters) {
}
}
+TEST(Hover, GH2381) {
+ Annotations Code(R"cpp(
+ struct Foo {
+ enum Bar {
+ A = -42UL,
+ B = ~0UL,
+ C = 0xFFFFFFFFFFFFFFFFUL,
+ };
+ };
+ constexpr auto va$a^ = Foo::A;
+ constexpr auto vb$b^ = Foo::B;
+ constexpr auto vc$c^ = Foo::C;
+ )cpp");
+
+ TestTU TU = TestTU::withCode(Code.code());
+ auto AST = TU.build();
+
+ {
+ auto H = getHover(AST, Code.point("a"), format::getLLVMStyle(), nullptr);
+
+ ASSERT_TRUE(H);
+ EXPECT_EQ(H->Name, "va");
+ EXPECT_EQ(H->Kind, index::SymbolKind::Variable);
+ EXPECT_EQ(H->NamespaceScope, "");
+ EXPECT_EQ(H->LocalScope, "");
+ EXPECT_EQ(H->Type, "const Foo::Bar");
+ EXPECT_EQ(H->Definition, "constexpr auto va = Foo::A");
+ // FIXME: Should be "A (FFFFFFFFFFFFFFD6)
+ EXPECT_EQ(H->Value, "18446744073709551574");
+ }
+
+ {
+ auto H = getHover(AST, Code.point("b"), format::getLLVMStyle(), nullptr);
+
+ ASSERT_TRUE(H);
+ EXPECT_EQ(H->Name, "vb");
+ EXPECT_EQ(H->Kind, index::SymbolKind::Variable);
+ EXPECT_EQ(H->NamespaceScope, "");
+ EXPECT_EQ(H->LocalScope, "");
+ EXPECT_EQ(H->Type, "const Foo::Bar");
+ EXPECT_EQ(H->Definition, "constexpr auto vb = Foo::B");
+ // FIXME: Should be "B (0xFFFFFFFFFFFFFFFF)");
+ EXPECT_EQ(H->Value, "18446744073709551615");
+ }
+
+ {
+ auto H = getHover(AST, Code.point("c"), format::getLLVMStyle(), nullptr);
+
+ ASSERT_TRUE(H);
+ EXPECT_EQ(H->Name, "vc");
+ EXPECT_EQ(H->Kind, index::SymbolKind::Variable);
+ EXPECT_EQ(H->NamespaceScope, "");
+ EXPECT_EQ(H->LocalScope, "");
+ EXPECT_EQ(H->Type, "const Foo::Bar");
+ EXPECT_EQ(H->Definition, "constexpr auto vc = Foo::C");
+ // FIXME: Should be "C (0xFFFFFFFFFFFFFFFF)");
+ EXPECT_EQ(H->Value, "18446744073709551615");
+ }
+}
} // namespace
} // namespace clangd
} // namespace clang
>From df6a1c98c7bfe895d0f894a92d570ff8c7752826 Mon Sep 17 00:00:00 2001
From: Mythreya <git at mythreya.dev>
Date: Sun, 21 Dec 2025 05:30:12 -0800
Subject: [PATCH 2/3] update test
---
.../clangd/unittests/HoverTests.cpp | 86 ++++++++++---------
1 file changed, 46 insertions(+), 40 deletions(-)
diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp
index 0c8423bd5fb2a..725651a92d4fd 100644
--- a/clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -5018,10 +5018,10 @@ TEST(Hover, FunctionParameters) {
TEST(Hover, GH2381) {
Annotations Code(R"cpp(
struct Foo {
- enum Bar {
- A = -42UL,
- B = ~0UL,
- C = 0xFFFFFFFFFFFFFFFFUL,
+ enum Bar : unsigned long long int {
+ A = -42ULL,
+ B = ~0ULL,
+ C = 0xFFFFFFFFFFFFFFFFULL,
};
};
constexpr auto va$a^ = Foo::A;
@@ -5030,48 +5030,54 @@ TEST(Hover, GH2381) {
)cpp");
TestTU TU = TestTU::withCode(Code.code());
- auto AST = TU.build();
- {
- auto H = getHover(AST, Code.point("a"), format::getLLVMStyle(), nullptr);
+ for (const auto *Triplet :
+ {"--target=x86_64-pc-windows-msvc", "--target=x86_64-pc-linux-gnu"}) {
+ SCOPED_TRACE(Triplet);
+ TU.ExtraArgs.push_back(Triplet);
+ auto AST = TU.build();
- ASSERT_TRUE(H);
- EXPECT_EQ(H->Name, "va");
- EXPECT_EQ(H->Kind, index::SymbolKind::Variable);
- EXPECT_EQ(H->NamespaceScope, "");
- EXPECT_EQ(H->LocalScope, "");
- EXPECT_EQ(H->Type, "const Foo::Bar");
- EXPECT_EQ(H->Definition, "constexpr auto va = Foo::A");
- // FIXME: Should be "A (FFFFFFFFFFFFFFD6)
- EXPECT_EQ(H->Value, "18446744073709551574");
- }
+ {
+ auto H = getHover(AST, Code.point("a"), format::getLLVMStyle(), nullptr);
- {
- auto H = getHover(AST, Code.point("b"), format::getLLVMStyle(), nullptr);
+ ASSERT_TRUE(H);
+ EXPECT_EQ(H->Name, "va");
+ EXPECT_EQ(H->Kind, index::SymbolKind::Variable);
+ EXPECT_EQ(H->NamespaceScope, "");
+ EXPECT_EQ(H->LocalScope, "");
+ EXPECT_EQ(H->Type, "const Foo::Bar");
+ EXPECT_EQ(H->Definition, "constexpr auto va = Foo::A");
+ // FIXME: Should be "A (FFFFFFFFFFFFFFD6)
+ EXPECT_EQ(H->Value, "18446744073709551574");
+ }
- ASSERT_TRUE(H);
- EXPECT_EQ(H->Name, "vb");
- EXPECT_EQ(H->Kind, index::SymbolKind::Variable);
- EXPECT_EQ(H->NamespaceScope, "");
- EXPECT_EQ(H->LocalScope, "");
- EXPECT_EQ(H->Type, "const Foo::Bar");
- EXPECT_EQ(H->Definition, "constexpr auto vb = Foo::B");
- // FIXME: Should be "B (0xFFFFFFFFFFFFFFFF)");
- EXPECT_EQ(H->Value, "18446744073709551615");
- }
+ {
+ auto H = getHover(AST, Code.point("b"), format::getLLVMStyle(), nullptr);
- {
- auto H = getHover(AST, Code.point("c"), format::getLLVMStyle(), nullptr);
+ ASSERT_TRUE(H);
+ EXPECT_EQ(H->Name, "vb");
+ EXPECT_EQ(H->Kind, index::SymbolKind::Variable);
+ EXPECT_EQ(H->NamespaceScope, "");
+ EXPECT_EQ(H->LocalScope, "");
+ EXPECT_EQ(H->Type, "const Foo::Bar");
+ EXPECT_EQ(H->Definition, "constexpr auto vb = Foo::B");
+ // FIXME: Should be "B (0xFFFFFFFFFFFFFFFF)");
+ EXPECT_EQ(H->Value, "18446744073709551615");
+ }
- ASSERT_TRUE(H);
- EXPECT_EQ(H->Name, "vc");
- EXPECT_EQ(H->Kind, index::SymbolKind::Variable);
- EXPECT_EQ(H->NamespaceScope, "");
- EXPECT_EQ(H->LocalScope, "");
- EXPECT_EQ(H->Type, "const Foo::Bar");
- EXPECT_EQ(H->Definition, "constexpr auto vc = Foo::C");
- // FIXME: Should be "C (0xFFFFFFFFFFFFFFFF)");
- EXPECT_EQ(H->Value, "18446744073709551615");
+ {
+ auto H = getHover(AST, Code.point("c"), format::getLLVMStyle(), nullptr);
+
+ ASSERT_TRUE(H);
+ EXPECT_EQ(H->Name, "vc");
+ EXPECT_EQ(H->Kind, index::SymbolKind::Variable);
+ EXPECT_EQ(H->NamespaceScope, "");
+ EXPECT_EQ(H->LocalScope, "");
+ EXPECT_EQ(H->Type, "const Foo::Bar");
+ EXPECT_EQ(H->Definition, "constexpr auto vc = Foo::C");
+ // FIXME: Should be "C (0xFFFFFFFFFFFFFFFF)");
+ EXPECT_EQ(H->Value, "18446744073709551615");
+ }
}
}
} // namespace
>From e7689a9d67b1ae2f7d97e91e7e52507ac73a8ec9 Mon Sep 17 00:00:00 2001
From: Mythreya <git at mythreya.dev>
Date: Sun, 21 Dec 2025 06:08:53 -0800
Subject: [PATCH 3/3] Update handling for ULL enums
---
clang-tools-extra/clangd/Hover.cpp | 28 +++++++++++++------
.../clangd/unittests/HoverTests.cpp | 22 ++-------------
2 files changed, 21 insertions(+), 29 deletions(-)
diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp
index 096689bfb2fcc..006c306f64aa8 100644
--- a/clang-tools-extra/clangd/Hover.cpp
+++ b/clang-tools-extra/clangd/Hover.cpp
@@ -57,6 +57,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
@@ -453,15 +454,24 @@ std::optional<std::string> printExprValue(const Expr *E,
return std::nullopt;
// Show enums symbolically, not numerically like APValue::printPretty().
- if (T->isEnumeralType() && Constant.Val.isInt() &&
- Constant.Val.getInt().isRepresentableByInt64()) {
- // Compare to int64_t to avoid bit-width match requirements.
- int64_t Val = Constant.Val.getInt().getExtValue();
- for (const EnumConstantDecl *ECD : T->castAsEnumDecl()->enumerators())
- if (ECD->getInitVal() == Val)
- return llvm::formatv("{0} ({1})", ECD->getNameAsString(),
- printHex(Constant.Val.getInt()))
- .str();
+ if (T->isEnumeralType() && Constant.Val.isInt()) {
+ const llvm::APSInt &Val = Constant.Val.getInt();
+ if (Val.isRepresentableByInt64()) {
+ // Compare to int64_t to avoid bit-width match requirements.
+ int64_t Val = Constant.Val.getInt().getExtValue();
+ for (const EnumConstantDecl *ECD : T->castAsEnumDecl()->enumerators())
+ if (ECD->getInitVal() == Val)
+ return llvm::formatv("{0} ({1})", ECD->getNameAsString(),
+ printHex(Constant.Val.getInt()))
+ .str();
+ } else if (const auto UVal = Constant.Val.getInt().tryZExtValue()) {
+ for (const EnumConstantDecl *ECD : T->castAsEnumDecl()->enumerators())
+ if (ECD->getInitVal().getZExtValue() == *UVal)
+ return llvm::formatv("{0} ({1})", ECD->getNameAsString(),
+ printHex(Constant.Val.getInt()))
+ .str();
+ } else
+ llvm_unreachable("Unhandled branch in enum symbolic representation");
}
// Show hex value of integers if they're at least 10 (or negative!)
if (T->isIntegralOrEnumerationType() && Constant.Val.isInt() &&
diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp
index 725651a92d4fd..1b0edc2bcfeb5 100644
--- a/clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -5021,12 +5021,10 @@ TEST(Hover, GH2381) {
enum Bar : unsigned long long int {
A = -42ULL,
B = ~0ULL,
- C = 0xFFFFFFFFFFFFFFFFULL,
};
};
constexpr auto va$a^ = Foo::A;
constexpr auto vb$b^ = Foo::B;
- constexpr auto vc$c^ = Foo::C;
)cpp");
TestTU TU = TestTU::withCode(Code.code());
@@ -5047,8 +5045,7 @@ TEST(Hover, GH2381) {
EXPECT_EQ(H->LocalScope, "");
EXPECT_EQ(H->Type, "const Foo::Bar");
EXPECT_EQ(H->Definition, "constexpr auto va = Foo::A");
- // FIXME: Should be "A (FFFFFFFFFFFFFFD6)
- EXPECT_EQ(H->Value, "18446744073709551574");
+ EXPECT_EQ(H->Value, "A (0xffffffffffffffd6)");
}
{
@@ -5061,22 +5058,7 @@ TEST(Hover, GH2381) {
EXPECT_EQ(H->LocalScope, "");
EXPECT_EQ(H->Type, "const Foo::Bar");
EXPECT_EQ(H->Definition, "constexpr auto vb = Foo::B");
- // FIXME: Should be "B (0xFFFFFFFFFFFFFFFF)");
- EXPECT_EQ(H->Value, "18446744073709551615");
- }
-
- {
- auto H = getHover(AST, Code.point("c"), format::getLLVMStyle(), nullptr);
-
- ASSERT_TRUE(H);
- EXPECT_EQ(H->Name, "vc");
- EXPECT_EQ(H->Kind, index::SymbolKind::Variable);
- EXPECT_EQ(H->NamespaceScope, "");
- EXPECT_EQ(H->LocalScope, "");
- EXPECT_EQ(H->Type, "const Foo::Bar");
- EXPECT_EQ(H->Definition, "constexpr auto vc = Foo::C");
- // FIXME: Should be "C (0xFFFFFFFFFFFFFFFF)");
- EXPECT_EQ(H->Value, "18446744073709551615");
+ EXPECT_EQ(H->Value, "B (0xffffffffffffffff)");
}
}
}
More information about the cfe-commits
mailing list