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 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