diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -13,6 +13,7 @@ #include "FindTarget.h" #include "FormattedString.h" #include "Logger.h" +#include "ParsedAST.h" #include "Selection.h" #include "SourceCode.h" #include "index/SymbolCollector.h" @@ -21,13 +22,19 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/PrettyPrinter.h" #include "clang/AST/Type.h" #include "clang/Index/IndexSymbol.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include @@ -410,6 +417,45 @@ } return HI; } + +bool isLiteral(const Expr *E) { + // Unfortunately there's no common base Literal classes inherits from + // (apart from Expr), therefore this is a nasty blacklist. + return llvm::isa(E) || llvm::isa(E) || + llvm::isa(E) || + llvm::isa(E) || + llvm::isa(E) || llvm::isa(E) || + llvm::isa(E) || llvm::isa(E) || + llvm::isa(E) || llvm::isa(E); +} + +llvm::StringLiteral getNameForExpr(const Expr *E) { + // FIXME: Come up with names for `special` expressions. + return "expression"; +} + +// Generates hover info for evaluatable expressions. +// FIXME: Support hover for literals (esp user-defined) +llvm::Optional getHoverContents(const Expr *E, ParsedAST &AST) { + // There's not much value in hovering over "42" and getting a hover card + // saying "42 is an int", similar for other literals. + if (isLiteral(E)) + return llvm::None; + + HoverInfo HI; + // For expressions we currently print the type and the value, iff it is + // evaluatable. + if (auto Val = printExprValue(E, AST.getASTContext())) { + auto Policy = + printingPolicyForDecls(AST.getASTContext().getPrintingPolicy()); + Policy.SuppressTagKeyword = true; + HI.Type = E->getType().getAsString(Policy); + HI.Value = *Val; + HI.Name = getNameForExpr(E); + return HI; + } + return llvm::None; +} } // namespace llvm::Optional getHover(ParsedAST &AST, Position Pos, @@ -439,11 +485,11 @@ // Look for a close enclosing expression to show the value of. if (!HI->Value) HI->Value = printExprValue(N, AST.getASTContext()); + } else if (const Expr *E = N->ASTNode.get()) { + HI = getHoverContents(E, AST); } // FIXME: support hovers for other nodes? - // - certain expressions (sizeof etc) // - built-in types - // - literals (esp user-defined) } } @@ -469,6 +515,8 @@ // class `X` // // function `foo` → `int` + // + // expression : `int` // Note that we are making use of a level-3 heading because VSCode renders // level 1 and 2 headers in a huge font, see // https://github.com/microsoft/vscode/issues/88417 for details. diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -601,6 +601,19 @@ R"cpp(// non-named decls don't get hover. Don't crash! ^static_assert(1, ""); )cpp", + R"cpp(// non-evaluatable expr + template void foo() { + (void)[[size^of]](T); + })cpp", + // literals + "auto x = t^rue;", + "auto x = '^A';", + "auto x = ^(int){42};", + "auto x = ^42.;", + "auto x = ^42.0i;", + "auto x = ^42;", + "auto x = ^nullptr;", + "auto x = ^\"asdf\";", }; for (const auto &Test : Tests) { @@ -1501,6 +1514,26 @@ HI.Name = "cls > >"; HI.Documentation = "type of nested templates."; }}, + { + R"cpp(// sizeof expr + void foo() { + (void)[[size^of]](char); + })cpp", + [](HoverInfo &HI) { + HI.Name = "expression"; + HI.Type = "unsigned long"; + HI.Value = "1"; + }}, + { + R"cpp(// alignof expr + void foo() { + (void)[[align^of]](char); + })cpp", + [](HoverInfo &HI) { + HI.Name = "expression"; + HI.Type = "unsigned long"; + HI.Value = "1"; + }}, }; // Create a tiny index, so tests above can verify documentation is fetched.