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,18 @@ #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/raw_ostream.h" #include @@ -421,6 +427,43 @@ } return HI; } + +// Generates hover info for string literals and evaluatable expressions. +// FIXME: Support hover for user-defined literals. +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. + // Unfortunately there's no common base Literal classes inherits from + // (apart from Expr), therefore this is a nasty blacklist. + if (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)) + return llvm::None; + + HoverInfo HI; + // For string literals we choose to show the type, which contains the + // length. + if (auto *SL = llvm::dyn_cast(E)) { + HI.Type = E->getType().getAsString(); + // Not much value in repeating the literal's value, as user is + // directly hovering over it. + return 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; + return HI; + } + return llvm::None; +} } // namespace llvm::Optional getHover(ParsedAST &AST, Position Pos, @@ -450,11 +493,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) } } 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,18 @@ 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;", }; for (const auto &Test : Tests) { @@ -1548,6 +1560,28 @@ HI.ReturnType = "int"; HI.Parameters.emplace(); }}, + { + R"cpp(// sizeof expr + void foo() { + (void)[[size^of]](char); + })cpp", + [](HoverInfo &HI) { + HI.Type = "unsigned long"; + HI.Value = "1"; + }}, + { + R"cpp(// alignof expr + void foo() { + (void)[[align^of]](char); + })cpp", + [](HoverInfo &HI) { + HI.Type = "unsigned long"; + HI.Value = "1"; + }}, + { + R"cpp(// string literals + auto x = [[^"asdf"]];)cpp", + [](HoverInfo &HI) { HI.Type = "const char [5]"; }}, }; // Create a tiny index, so tests above can verify documentation is fetched.