diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp --- a/clang-tools-extra/clangd/InlayHints.cpp +++ b/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 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(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; + void addTemplateArgHint(llvm::ArrayRef TP, + llvm::ArrayRef 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 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. diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp --- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp +++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp @@ -459,12 +459,13 @@ foo(args...); } template <> - void bar(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 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 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 struct A {}; + template struct B {}; + template struct C {}; + template struct D {}; + + template + using E = A; + template + using F = A<$par0[[U]]>; + template + using G = C<$par1[[int]], $par2[[float]], Ts...>; + + B 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 concept A = true; + bool a = A<$par0[[int]]>; + )cpp", + ExpectedHint{"T: ", "par0"}); +} + +TEST(ParameterHints, ClassSpecialization) { + assertParameterHints(R"cpp( + template struct A {}; + template 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 + constexpr int value = 0; + template + 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 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;