v1nh1shungry created this revision.
v1nh1shungry added reviewers: nridge, sammccall.
Herald added subscribers: kadircet, arphaman.
Herald added a project: All.
v1nh1shungry requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang-tools-extra.

Relevant issue: https://github.com/clangd/clangd/issues/1387


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D138425

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

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -459,12 +459,13 @@
       foo(args...);
     }
     template <>
-    void bar<int>(int b);
+    void bar<$par0[[int]]>(int b);
     void baz() {
-      bar($param[[42]]);
+      bar($par1[[42]]);
     }
   )cpp",
-                       ExpectedHint{"b: ", "param"});
+                       ExpectedHint{"Args[0]: ", "par0"},
+                       ExpectedHint{"b: ", "par1"});
 }
 
 TEST(ParameterHints, VariadicNameFromSpecializationRecursive) {
@@ -481,12 +482,13 @@
       foo(args...);
     }
     template <>
-    void foo<int>(int b);
+    void foo<$par0[[int]]>(int b);
     void baz() {
-      bar($param[[42]]);
+      bar($par1[[42]]);
     }
   )cpp",
-                       ExpectedHint{"b: ", "param"});
+                       ExpectedHint{"Args[0]: ", "par0"},
+                       ExpectedHint{"b: ", "par1"});
 }
 
 TEST(ParameterHints, VariadicOverloaded) {
@@ -671,13 +673,13 @@
       }
     };
     void foo() {
-      container<S> c;
+      container<$param0[[S]]> c;
       c.emplace($param1[[1]]);
       c.emplace($param2[[2]], $param3[[3]]);
     }
   )cpp",
-      ExpectedHint{"A: ", "param1"}, ExpectedHint{"B: ", "param2"},
-      ExpectedHint{"C: ", "param3"});
+      ExpectedHint{"T: ", "param0"}, ExpectedHint{"A: ", "param1"},
+      ExpectedHint{"B: ", "param2"}, ExpectedHint{"C: ", "param3"});
 }
 
 TEST(ParameterHints, VariadicReferenceHint) {
@@ -1113,6 +1115,70 @@
   EXPECT_EQ(hintsOfKind(*AST, InlayHintKind::Parameter).size(), 0u);
 }
 
+TEST(ParameterHints, TemplateSpecialization) {
+  assertParameterHints(
+      R"cpp(
+    template <class T> struct A {};
+    template <class> struct B {};
+    template <class... Ts> struct C {};
+    template <class...> struct D {};
+
+    template <class T>
+    using E = A<T>;
+    template <class U>
+    using F = A<$par0[[U]]>;
+    template <class... Ts>
+    using G = C<$par1[[int]], $par2[[float]], Ts...>;
+
+    B<int> b;
+    D<$par3[[int]], $par4[[float]]> d;
+  )cpp",
+      ExpectedHint{"T: ", "par0"}, ExpectedHint{"Ts[0]: ", "par1"},
+      ExpectedHint{"Ts[1]: ", "par2"}, ExpectedHint{"[0]: ", "par3"},
+      ExpectedHint{"[1]: ", "par4"});
+}
+
+TEST(ParameterHints, ConceptSpecialization) {
+  assertParameterHints(R"cpp(
+    template <class T> concept A = true;
+    bool a = A<$par0[[int]]>;
+  )cpp",
+                       ExpectedHint{"T: ", "par0"});
+}
+
+TEST(ParameterHints, ClassSpecialization) {
+  assertParameterHints(R"cpp(
+    template <class T, class U> struct A {};
+    template <class T> struct A<$par0[[int]], $par1[[T]]> {};
+    template <> struct A<$par2[[int]], $par3[[float]]> {};
+  )cpp",
+                       ExpectedHint{"T: ", "par0"}, ExpectedHint{"U: ", "par1"},
+                       ExpectedHint{"T: ", "par2"},
+                       ExpectedHint{"U: ", "par3"});
+}
+
+TEST(ParameterHints, VarSpecialization) {
+  assertParameterHints(R"cpp(
+    template <class T, class U>
+    constexpr int value = 0;
+    template <class T>
+    constexpr int value<$par0[[int]], $par1[[T]]> = 0;
+    template <>
+    constexpr int value<$par2[[int]], $par3[[float]]> = 0;
+                       )cpp",
+                       ExpectedHint{"T: ", "par0"}, ExpectedHint{"U: ", "par1"},
+                       ExpectedHint{"T: ", "par2"},
+                       ExpectedHint{"U: ", "par3"});
+}
+
+TEST(ParameterHints, FunctionSpecialization) {
+  assertParameterHints(R"cpp(
+    template <class T> T add(T lhs, T rhs);
+    template <> int add<$par0[[int]]>(int lhs, int rhs);
+  )cpp",
+                       ExpectedHint{"T: ", "par0"});
+}
+
 TEST(TypeHints, Smoke) {
   assertTypeHints(R"cpp(
     auto $waldo[[waldo]] = 42;
Index: clang-tools-extra/clangd/InlayHints.cpp
===================================================================
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -266,6 +266,11 @@
           addReturnTypeHint(D, FTL.getRParenLoc());
       }
     }
+    if (D->isFunctionTemplateSpecialization()) {
+      auto TP = D->getPrimaryTemplate()->getTemplateParameters()->asArray();
+      if (auto *Args = D->getTemplateSpecializationArgsAsWritten())
+        addTemplateArgHint(TP, Args->arguments());
+    }
     return true;
   }
 
@@ -370,11 +375,80 @@
     return true;
   }
 
+  bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc L) {
+    auto TP = L.getTypePtr()
+                  ->getTemplateName()
+                  .getAsTemplateDecl()
+                  ->getTemplateParameters()
+                  ->asArray();
+    llvm::SmallVector<TemplateArgumentLoc, 1> TA;
+    for (std::size_t I = 0; I < L.getNumArgs(); ++I)
+      TA.push_back(L.getArgLoc(I));
+    addTemplateArgHint(TP, TA);
+    return true;
+  }
+
+  bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E) {
+    auto TP = E->getNamedConcept()->getTemplateParameters()->asArray();
+    auto TA = E->getTemplateArgsAsWritten()->arguments();
+    addTemplateArgHint(TP, TA);
+    return true;
+  }
+
+  bool VisitClassTemplatePartialSpecializationDecl(
+      const ClassTemplatePartialSpecializationDecl *D) {
+    auto TP = D->getSpecializedTemplate()->getTemplateParameters()->asArray();
+    auto TA = D->getTemplateArgsAsWritten()->arguments();
+    addTemplateArgHint(TP, TA);
+    return true;
+  }
+
+  bool
+  VisitVarTemplateSpecializationDecl(const VarTemplateSpecializationDecl *D) {
+    auto TP = D->getSpecializedTemplate()->getTemplateParameters()->asArray();
+    if (const auto *VTPSD =
+            llvm::dyn_cast<VarTemplatePartialSpecializationDecl>(D))
+      addTemplateArgHint(TP, VTPSD->getTemplateArgsAsWritten()->arguments());
+    else
+      addTemplateArgHint(TP, D->getTemplateArgsInfo()->arguments());
+    return true;
+  }
+
   // FIXME: Handle RecoveryExpr to try to hint some invalid calls.
 
 private:
   using NameVec = SmallVector<StringRef, 8>;
 
+  void addTemplateArgHint(llvm::ArrayRef<NamedDecl *> TP,
+                          llvm::ArrayRef<TemplateArgumentLoc> TA) {
+    std::size_t I = 0;
+    for (; I < TA.size(); ++I) {
+      const auto &Arg = TA[I].getArgument();
+      // Pack expansion expressions cause the 1:1 mapping between arguments and
+      // parameters to break down, so we don't add further inlay hints if we
+      // encounter one.
+      if (Arg.isPackExpansion())
+        return;
+      if (TP[I]->isTemplateParameterPack())
+        break;
+      if (auto Name = TP[I]->getName(); shouldHintName(TA[I], Name))
+        addInlayHint(TA[I].getSourceRange(), HintSide::Left,
+                     InlayHintKind::Parameter, "", Name, ": ");
+    }
+    // Handle the parameter pack, if there is one
+    if (I < TA.size()) {
+      auto Name = TP[I]->getName();
+      for (std::size_t J = 0; I < TA.size(); ++I, ++J) {
+        const auto &Arg = TA[I].getArgument();
+        if (Arg.isPackExpansion())
+          return;
+        addInlayHint(TA[I].getSourceRange(), HintSide::Left,
+                     InlayHintKind::Parameter, "",
+                     llvm::formatv("{0}[{1}]", Name, J).str(), ": ");
+      }
+    }
+  }
+
   void processCall(const FunctionDecl *Callee,
                    llvm::ArrayRef<const Expr *> Args) {
     if (!Cfg.InlayHints.Parameters || Args.size() == 0 || !Callee)
@@ -471,6 +545,17 @@
     return true;
   }
 
+  bool shouldHintName(const TemplateArgumentLoc &TA, StringRef ParamName) {
+    if (ParamName.empty())
+      return false;
+    std::string ArgContent;
+    llvm::raw_string_ostream Out(ArgContent);
+    TA.getArgument().print(AST.getPrintingPolicy(), Out, false);
+    if (ArgContent == ParamName)
+      return false;
+    return true;
+  }
+
   bool shouldHintReference(const ParmVarDecl *Param,
                            const ParmVarDecl *ForwardedParam) {
     // We add a & hint only when the argument is passed as mutable reference.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to