Skip to content

Conversation

@MythreyaK
Copy link
Contributor

@MythreyaK MythreyaK commented Dec 21, 2025

Fix clangd crash when hovering over 64bit values with MSB set.

Fixes clangd/clangd#2381

@MythreyaK MythreyaK force-pushed the mythreyak/clangd-crash-2381 branch from 6ab4a4f to e23269b Compare December 21, 2025 13:01
@github-actions
Copy link

github-actions bot commented Dec 21, 2025

🪟 Windows x64 Test Results

  • 2997 tests passed
  • 30 tests skipped

✅ The build succeeded and all tests passed.

@MythreyaK MythreyaK changed the title [clangd] Fix crash on hover over UL enums with MSB set [clangd] Fix crash on hover over 64bit enums with MSB set Dec 21, 2025
@MythreyaK MythreyaK force-pushed the mythreyak/clangd-crash-2381 branch from b10e34d to 046689a Compare December 21, 2025 14:15
Comment on lines +464 to +469
} 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();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not confident if this is the right thing to do, still exploring this API.

@MythreyaK MythreyaK marked this pull request as ready for review December 21, 2025 15:04
@llvmbot
Copy link
Member

llvmbot commented Dec 21, 2025

@llvm/pr-subscribers-clang-tools-extra

Author: Mythreya Kuricheti (MythreyaK)

Changes

Fix clangd crash when hovering over 64bit values with MSB set.

Fixes clangd/clangd#2381


Full diff: https://github.com/llvm/llvm-project/pull/173187.diff

2 Files Affected:

  • (modified) clang-tools-extra/clangd/Hover.cpp (+20-10)
  • (modified) clang-tools-extra/clangd/unittests/HoverTests.cpp (+47)
diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp index 34369e188d4ec..0d6db829b49f7 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" @@ -450,19 +451,28 @@ 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().getSignificantBits() <= 64) { - // 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() && - 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..1b0edc2bcfeb5 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -5015,6 +5015,53 @@ TEST(Hover, FunctionParameters) { } } +TEST(Hover, GH2381) { + Annotations Code(R"cpp( + struct Foo { + enum Bar : unsigned long long int { + A = -42ULL, + B = ~0ULL, + }; + }; + constexpr auto va$a^ = Foo::A; + constexpr auto vb$b^ = Foo::B; + )cpp"); + + TestTU TU = TestTU::withCode(Code.code()); + + 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(); + + { + 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"); + EXPECT_EQ(H->Value, "A (0xffffffffffffffd6)"); + } + + { + 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"); + EXPECT_EQ(H->Value, "B (0xffffffffffffffff)"); + } + } +} } // namespace } // namespace clangd } // namespace clang 
@llvmbot
Copy link
Member

llvmbot commented Dec 21, 2025

@llvm/pr-subscribers-clangd

Author: Mythreya Kuricheti (MythreyaK)

Changes

Fix clangd crash when hovering over 64bit values with MSB set.

Fixes clangd/clangd#2381


Full diff: https://github.com/llvm/llvm-project/pull/173187.diff

2 Files Affected:

  • (modified) clang-tools-extra/clangd/Hover.cpp (+20-10)
  • (modified) clang-tools-extra/clangd/unittests/HoverTests.cpp (+47)
diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp index 34369e188d4ec..0d6db829b49f7 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" @@ -450,19 +451,28 @@ 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().getSignificantBits() <= 64) { - // 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() && - 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..1b0edc2bcfeb5 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -5015,6 +5015,53 @@ TEST(Hover, FunctionParameters) { } } +TEST(Hover, GH2381) { + Annotations Code(R"cpp( + struct Foo { + enum Bar : unsigned long long int { + A = -42ULL, + B = ~0ULL, + }; + }; + constexpr auto va$a^ = Foo::A; + constexpr auto vb$b^ = Foo::B; + )cpp"); + + TestTU TU = TestTU::withCode(Code.code()); + + 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(); + + { + 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"); + EXPECT_EQ(H->Value, "A (0xffffffffffffffd6)"); + } + + { + 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"); + EXPECT_EQ(H->Value, "B (0xffffffffffffffff)"); + } + } +} } // namespace } // namespace clangd } // namespace clang 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment