Skip to content

Commit ef91746

Browse files
committed
[clangd] Show hower info for expressions
Summary: This currently populates only the Name with the expression's type and Value if expression is evaluatable. Fixes clangd/clangd#56 Reviewers: sammccall Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D72500 (cherry picked from commit 4d14bfa)
1 parent 256a0ea commit ef91746

File tree

2 files changed

+83
-2
lines changed

2 files changed

+83
-2
lines changed

clang-tools-extra/clangd/Hover.cpp

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "FindTarget.h"
1414
#include "FormattedString.h"
1515
#include "Logger.h"
16+
#include "ParsedAST.h"
1617
#include "Selection.h"
1718
#include "SourceCode.h"
1819
#include "index/SymbolCollector.h"
@@ -21,13 +22,19 @@
2122
#include "clang/AST/Decl.h"
2223
#include "clang/AST/DeclBase.h"
2324
#include "clang/AST/DeclTemplate.h"
25+
#include "clang/AST/Expr.h"
26+
#include "clang/AST/ExprCXX.h"
2427
#include "clang/AST/PrettyPrinter.h"
2528
#include "clang/AST/Type.h"
2629
#include "clang/Index/IndexSymbol.h"
30+
#include "llvm/ADT/None.h"
31+
#include "llvm/ADT/Optional.h"
2732
#include "llvm/ADT/STLExtras.h"
2833
#include "llvm/ADT/SmallVector.h"
2934
#include "llvm/ADT/StringExtras.h"
3035
#include "llvm/ADT/StringRef.h"
36+
#include "llvm/Support/Casting.h"
37+
#include "llvm/Support/ErrorHandling.h"
3138
#include "llvm/Support/raw_ostream.h"
3239
#include <string>
3340

@@ -410,6 +417,45 @@ HoverInfo getHoverContents(const DefinedMacro &Macro, ParsedAST &AST) {
410417
}
411418
return HI;
412419
}
420+
421+
bool isLiteral(const Expr *E) {
422+
// Unfortunately there's no common base Literal classes inherits from
423+
// (apart from Expr), therefore this is a nasty blacklist.
424+
return llvm::isa<CharacterLiteral>(E) || llvm::isa<CompoundLiteralExpr>(E) ||
425+
llvm::isa<CXXBoolLiteralExpr>(E) ||
426+
llvm::isa<CXXNullPtrLiteralExpr>(E) ||
427+
llvm::isa<FixedPointLiteral>(E) || llvm::isa<FloatingLiteral>(E) ||
428+
llvm::isa<ImaginaryLiteral>(E) || llvm::isa<IntegerLiteral>(E) ||
429+
llvm::isa<StringLiteral>(E) || llvm::isa<UserDefinedLiteral>(E);
430+
}
431+
432+
llvm::StringLiteral getNameForExpr(const Expr *E) {
433+
// FIXME: Come up with names for `special` expressions.
434+
return "expression";
435+
}
436+
437+
// Generates hover info for evaluatable expressions.
438+
// FIXME: Support hover for literals (esp user-defined)
439+
llvm::Optional<HoverInfo> getHoverContents(const Expr *E, ParsedAST &AST) {
440+
// There's not much value in hovering over "42" and getting a hover card
441+
// saying "42 is an int", similar for other literals.
442+
if (isLiteral(E))
443+
return llvm::None;
444+
445+
HoverInfo HI;
446+
// For expressions we currently print the type and the value, iff it is
447+
// evaluatable.
448+
if (auto Val = printExprValue(E, AST.getASTContext())) {
449+
auto Policy =
450+
printingPolicyForDecls(AST.getASTContext().getPrintingPolicy());
451+
Policy.SuppressTagKeyword = true;
452+
HI.Type = E->getType().getAsString(Policy);
453+
HI.Value = *Val;
454+
HI.Name = getNameForExpr(E);
455+
return HI;
456+
}
457+
return llvm::None;
458+
}
413459
} // namespace
414460

415461
llvm::Optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
@@ -439,11 +485,11 @@ llvm::Optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
439485
// Look for a close enclosing expression to show the value of.
440486
if (!HI->Value)
441487
HI->Value = printExprValue(N, AST.getASTContext());
488+
} else if (const Expr *E = N->ASTNode.get<Expr>()) {
489+
HI = getHoverContents(E, AST);
442490
}
443491
// FIXME: support hovers for other nodes?
444-
// - certain expressions (sizeof etc)
445492
// - built-in types
446-
// - literals (esp user-defined)
447493
}
448494
}
449495

@@ -469,6 +515,8 @@ markup::Document HoverInfo::present() const {
469515
// class `X`
470516
//
471517
// function `foo` → `int`
518+
//
519+
// expression : `int`
472520
// Note that we are making use of a level-3 heading because VSCode renders
473521
// level 1 and 2 headers in a huge font, see
474522
// https://github.com/microsoft/vscode/issues/88417 for details.

clang-tools-extra/clangd/unittests/HoverTests.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,19 @@ TEST(Hover, NoHover) {
601601
R"cpp(// non-named decls don't get hover. Don't crash!
602602
^static_assert(1, "");
603603
)cpp",
604+
R"cpp(// non-evaluatable expr
605+
template <typename T> void foo() {
606+
(void)[[size^of]](T);
607+
})cpp",
608+
// literals
609+
"auto x = t^rue;",
610+
"auto x = '^A';",
611+
"auto x = ^(int){42};",
612+
"auto x = ^42.;",
613+
"auto x = ^42.0i;",
614+
"auto x = ^42;",
615+
"auto x = ^nullptr;",
616+
"auto x = ^\"asdf\";",
604617
};
605618

606619
for (const auto &Test : Tests) {
@@ -1501,6 +1514,26 @@ TEST(Hover, All) {
15011514
HI.Name = "cls<cls<cls<int> > >";
15021515
HI.Documentation = "type of nested templates.";
15031516
}},
1517+
{
1518+
R"cpp(// sizeof expr
1519+
void foo() {
1520+
(void)[[size^of]](char);
1521+
})cpp",
1522+
[](HoverInfo &HI) {
1523+
HI.Name = "expression";
1524+
HI.Type = "unsigned long";
1525+
HI.Value = "1";
1526+
}},
1527+
{
1528+
R"cpp(// alignof expr
1529+
void foo() {
1530+
(void)[[align^of]](char);
1531+
})cpp",
1532+
[](HoverInfo &HI) {
1533+
HI.Name = "expression";
1534+
HI.Type = "unsigned long";
1535+
HI.Value = "1";
1536+
}},
15041537
};
15051538

15061539
// Create a tiny index, so tests above can verify documentation is fetched.

0 commit comments

Comments
 (0)