[clang-tools-extra] a15adbc - [clangd] Type hints for structured bindings

Nathan Ridge via cfe-commits cfe-commits at lists.llvm.org
Sun Jul 4 18:53:57 PDT 2021


Author: Nathan Ridge
Date: 2021-07-04T21:53:36-04:00
New Revision: a15adbcddd078e2ca6b860ec9e4b8c2e5afdb76f

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

LOG: [clangd] Type hints for structured bindings

Hints are shown for the individual bindings, not the aggregate.

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

Added: 
    

Modified: 
    clang-tools-extra/clangd/InlayHints.cpp
    clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index c1a8357201e9c..1283aa4dd62cc 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -32,6 +32,14 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
     TypeHintPolicy.SuppressScope = true; // keep type names short
     TypeHintPolicy.AnonymousTagLocations =
         false; // do not print lambda locations
+    // Print canonical types. Otherwise, SuppressScope would result in
+    // things like "metafunction<args>::type" being shorted to just "type",
+    // which is useless. This is particularly important for structured
+    // bindings that use the tuple_element protocol, where the non-canonical
+    // types would be "tuple_element<I, A>::type".
+    // Note, for "auto", we would often prefer sugared types, but the AST
+    // doesn't currently retain them in DeducedType anyways.
+    TypeHintPolicy.PrintCanonicalTypes = true;
   }
 
   bool VisitCXXConstructExpr(CXXConstructExpr *E) {
@@ -76,9 +84,8 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
     if (auto *AT = D->getReturnType()->getContainedAutoType()) {
       QualType Deduced = AT->getDeducedType();
       if (!Deduced.isNull()) {
-        addInlayHint(D->getFunctionTypeLoc().getRParenLoc(),
-                     InlayHintKind::TypeHint,
-                     "-> " + D->getReturnType().getAsString(TypeHintPolicy));
+        addTypeHint(D->getFunctionTypeLoc().getRParenLoc(), D->getReturnType(),
+                    "-> ");
       }
     }
 
@@ -86,10 +93,14 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
   }
 
   bool VisitVarDecl(VarDecl *D) {
-    // Do not show hints for the aggregate in a structured binding.
-    // In the future, we may show hints for the individual bindings.
-    if (isa<DecompositionDecl>(D))
+    // Do not show hints for the aggregate in a structured binding,
+    // but show hints for the individual bindings.
+    if (auto *DD = dyn_cast<DecompositionDecl>(D)) {
+      for (auto *Binding : DD->bindings()) {
+        addTypeHint(Binding->getLocation(), Binding->getType(), ": ");
+      }
       return true;
+    }
 
     if (D->getType()->getContainedAutoType()) {
       if (!D->getType()->isDependentType()) {
@@ -98,8 +109,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
         // (e.g. for `const auto& x = 42`, print `const int&`).
         // Alternatively, we could place the hint on the `auto`
         // (and then just print the type deduced for the `auto`).
-        addInlayHint(D->getLocation(), InlayHintKind::TypeHint,
-                     ": " + D->getType().getAsString(TypeHintPolicy));
+        addTypeHint(D->getLocation(), D->getType(), ": ");
       }
     }
     return true;
@@ -311,6 +321,15 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
         Kind, Label.str()});
   }
 
+  void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix) {
+    // Do not print useless "NULL TYPE" hint.
+    if (!T.getTypePtrOrNull())
+      return;
+
+    addInlayHint(R, InlayHintKind::TypeHint,
+                 std::string(Prefix) + T.getAsString(TypeHintPolicy));
+  }
+
   std::vector<InlayHint> &Results;
   ASTContext &AST;
   FileID MainFileID;

diff  --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 2c5597e17e2f0..1410ed115b6bf 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -461,19 +461,70 @@ TEST(TypeHints, Lambda) {
                   ExpectedHint{": int", "init"});
 }
 
-TEST(TypeHints, StructuredBindings) {
-  // FIXME: Not handled yet.
-  // To handle it, we could print:
-  //  - the aggregate type next to the 'auto', or
-  //  - the individual types inside the brackets
-  // The latter is probably more useful.
+// Structured bindings tests.
+// Note, we hint the individual bindings, not the aggregate.
+
+TEST(TypeHints, StructuredBindings_PublicStruct) {
   assertTypeHints(R"cpp(
+    // Struct with public fields.
     struct Point {
       int x;
       int y;
     };
     Point foo();
-    auto [x, y] = foo();
+    auto [$x[[x]], $y[[y]]] = foo();
+  )cpp",
+                  ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"});
+}
+
+TEST(TypeHints, StructuredBindings_Array) {
+  assertTypeHints(R"cpp(
+    int arr[2];
+    auto [$x[[x]], $y[[y]]] = arr;
+  )cpp",
+                  ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"});
+}
+
+TEST(TypeHints, StructuredBindings_TupleLike) {
+  assertTypeHints(R"cpp(
+    // Tuple-like type.
+    struct IntPair {
+      int a;
+      int b;
+    };
+    namespace std {
+      template <typename T>
+      struct tuple_size {};
+      template <>
+      struct tuple_size<IntPair> {
+        constexpr static unsigned value = 2;
+      };
+      template <unsigned I, typename T>
+      struct tuple_element {};
+      template <unsigned I>
+      struct tuple_element<I, IntPair> {
+        using type = int;
+      };
+    }
+    template <unsigned I>
+    int get(const IntPair& p) {
+      if constexpr (I == 0) {
+        return p.a;
+      } else if constexpr (I == 1) {
+        return p.b;
+      }
+    }
+    IntPair bar();
+    auto [$x[[x]], $y[[y]]] = bar();
+  )cpp",
+                  ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"});
+}
+
+TEST(TypeHints, StructuredBindings_NoInitializer) {
+  assertTypeHints(R"cpp(
+    // No initializer (ill-formed).
+    // Do not show useless "NULL TYPE" hint.    
+    auto [x, y];  /*error-ok*/
   )cpp");
 }
 


        


More information about the cfe-commits mailing list