[clang-tools-extra] 33d93c3 - [clangd] Show values of more expressions on hover

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Tue Nov 19 06:34:15 PST 2019


Author: Sam McCall
Date: 2019-11-19T15:34:04+01:00
New Revision: 33d93c3d0b4a331632902f5fb9874f4e021a2f58

URL: https://github.com/llvm/llvm-project/commit/33d93c3d0b4a331632902f5fb9874f4e021a2f58
DIFF: https://github.com/llvm/llvm-project/commit/33d93c3d0b4a331632902f5fb9874f4e021a2f58.diff

LOG: [clangd] Show values of more expressions on hover

Reviewers: kadircet

Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, usaxena95, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D70359

Added: 
    

Modified: 
    clang-tools-extra/clangd/Hover.cpp
    clang-tools-extra/clangd/unittests/HoverTests.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp
index 0e28e30482eb..feba29dd605d 100644
--- a/clang-tools-extra/clangd/Hover.cpp
+++ b/clang-tools-extra/clangd/Hover.cpp
@@ -16,6 +16,7 @@
 #include "SourceCode.h"
 #include "index/SymbolCollector.h"
 
+#include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTTypeTraits.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/PrettyPrinter.h"
@@ -239,6 +240,46 @@ void fillFunctionTypeAndParams(HoverInfo &HI, const Decl *D,
   // FIXME: handle variadics.
 }
 
+llvm::Optional<std::string> printExprValue(const Expr *E, const ASTContext &Ctx) {
+  Expr::EvalResult Constant;
+  // Evaluating [[foo]]() as "&foo" isn't useful, and prevents us walking up
+  // to the enclosing call.
+  QualType T = E->getType();
+  if (T->isFunctionType() || T->isFunctionPointerType() ||
+      T->isFunctionReferenceType())
+    return llvm::None;
+  // Attempt to evaluate. If expr is dependent, evaluation crashes!
+  if (E->isValueDependent() || !E->EvaluateAsRValue(Constant, Ctx))
+    return llvm::None;
+
+  // Show enums symbolically, not numerically like APValue::printPretty().
+  if (T->isEnumeralType() && Constant.Val.getInt().getMinSignedBits() <= 64) {
+    // Compare to int64_t to avoid bit-width match requirements.
+    int64_t Val = Constant.Val.getInt().getExtValue();
+    for (const EnumConstantDecl *ECD :
+         T->castAs<EnumType>()->getDecl()->enumerators())
+      if (ECD->getInitVal() == Val)
+        return llvm::formatv("{0} ({1})", ECD->getNameAsString(), Val).str();
+  }
+  return Constant.Val.getAsString(Ctx, E->getType());
+}
+
+llvm::Optional<std::string> printExprValue(const SelectionTree::Node *N,
+                                           const ASTContext &Ctx) {
+  for (; N; N = N->Parent) {
+    // Try to evaluate the first evaluable enclosing expression.
+    if (const Expr *E = N->ASTNode.get<Expr>()) {
+      if (auto Val = printExprValue(E, Ctx))
+        return Val;
+    } else if (N->ASTNode.get<Decl>() || N->ASTNode.get<Stmt>()) {
+      // Refuse to cross certain non-exprs. (TypeLoc are OK as part of Exprs).
+      // This tries to ensure we're showing a value related to the cursor.
+      break;
+    }
+  }
+  return llvm::None;
+}
+
 /// Generate a \p Hover object given the declaration \p D.
 HoverInfo getHoverContents(const Decl *D, const SymbolIndex *Index) {
   HoverInfo HI;
@@ -282,18 +323,9 @@ HoverInfo getHoverContents(const Decl *D, const SymbolIndex *Index) {
   }
 
   // Fill in value with evaluated initializer if possible.
-  // FIXME(kadircet): Also set Value field for expressions like "sizeof" and
-  // function calls.
   if (const auto *Var = dyn_cast<VarDecl>(D)) {
-    if (const Expr *Init = Var->getInit()) {
-      Expr::EvalResult Result;
-      if (!Init->isValueDependent() && Init->EvaluateAsRValue(Result, Ctx)) {
-        HI.Value.emplace();
-        llvm::raw_string_ostream ValueOS(*HI.Value);
-        Result.Val.printPretty(ValueOS, const_cast<ASTContext &>(Ctx),
-                               Init->getType());
-      }
-    }
+    if (const Expr *Init = Var->getInit())
+      HI.Value = printExprValue(Init, Ctx);
   } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) {
     // Dependent enums (e.g. nested in template classes) don't have values yet.
     if (!ECD->getType()->isDependentType())
@@ -381,8 +413,16 @@ llvm::Optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
     if (const SelectionTree::Node *N = Selection.commonAncestor()) {
       DeclRelationSet Rel = DeclRelation::TemplatePattern | DeclRelation::Alias;
       auto Decls = targetDecl(N->ASTNode, Rel);
-      if (!Decls.empty())
+      if (!Decls.empty()) {
         HI = getHoverContents(Decls.front(), Index);
+        // Look for a close enclosing expression to show the value of.
+        if (!HI->Value)
+          HI->Value = printExprValue(N, AST.getASTContext());
+      }
+      // 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
index 7b5907faaa2c..530dfe600ecf 100644
--- a/clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -275,6 +275,7 @@ void foo())cpp";
              {std::string("int"), std::string("T"), llvm::None},
              {std::string("bool"), std::string("B"), llvm::None},
          };
+         HI.Value = "false";
          return HI;
        }},
       // Lambda variable
@@ -443,10 +444,23 @@ void foo())cpp";
          HI.Definition = "GREEN";
          HI.Kind = SymbolKind::EnumMember;
          HI.Type = "enum Color";
-         HI.Value = "1";
+         HI.Value = "1"; // Numeric when hovering on the enumerator name.
+       }},
+      {R"cpp(
+        enum Color { RED, GREEN, };
+        Color x = GREEN;
+        Color y = [[^x]];
+       )cpp",
+       [](HoverInfo &HI) {
+         HI.Name = "x";
+         HI.NamespaceScope = "";
+         HI.Definition = "enum Color x = GREEN";
+         HI.Kind = SymbolKind::Variable;
+         HI.Type = "enum Color";
+         HI.Value = "GREEN (1)"; // Symbolic when hovering on an expression.
        }},
       // FIXME: We should use the Decl referenced, even if from an implicit
-      // instantiation. Then the scope would be Add<1, 2> and the value 3.
+      // instantiation. Then the scope would be Add<1, 2>.
       {R"cpp(
         template<int a, int b> struct Add {
           static constexpr int result = a + b;
@@ -460,6 +474,21 @@ void foo())cpp";
          HI.Type = "const int";
          HI.NamespaceScope = "";
          HI.LocalScope = "Add<a, b>::";
+         HI.Value = "3";
+       }},
+      {R"cpp(
+        constexpr int answer() { return 40 + 2; }
+        int x = [[ans^wer]]();
+        )cpp",
+       [](HoverInfo &HI) {
+         HI.Name = "answer";
+         HI.Definition = "constexpr int answer()";
+         HI.Kind = SymbolKind::Function;
+         HI.Type = "int ()";
+         HI.ReturnType = "int";
+         HI.Parameters.emplace();
+         HI.NamespaceScope = "";
+         HI.Value = "42";
        }},
       {R"cpp(
         const char *[[ba^r]] = "1234";


        


More information about the cfe-commits mailing list