diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp --- a/clang-tools-extra/clangd/AST.cpp +++ b/clang-tools-extra/clangd/AST.cpp @@ -792,50 +792,45 @@ return; } auto OptPackLocation = findPack(Args); - if (OptPackLocation) { - size_t PackLocation = OptPackLocation.value(); - ArrayRef MatchingParams = - Callee->parameters().slice(PackLocation, Parameters.size()); - // Check whether the function has a parameter pack as the last template - // parameter - if (const auto *TTPT = getFunctionPackType(Callee)) { - // In this case: Separate the parameters into head, pack and tail - auto IsExpandedPack = [&](const ParmVarDecl *P) { - return getUnderylingPackType(P) == TTPT; - }; - ForwardingInfo FI; - FI.Head = MatchingParams.take_until(IsExpandedPack); - FI.Pack = MatchingParams.drop_front(FI.Head.size()) - .take_while(IsExpandedPack); - FI.Tail = MatchingParams.drop_front(FI.Head.size() + FI.Pack.size()); - FI.PackTarget = Callee; - Info = FI; - return; - } - // Default case: assume all parameters were fully resolved + if (!OptPackLocation) + return; + ArrayRef MatchingParams = + Callee->parameters().slice(*OptPackLocation, Parameters.size()); + // Check whether the function has a parameter pack as the last template + // parameter + if (const auto *TTPT = getFunctionPackType(Callee)) { + // In this case: Separate the parameters into head, pack and tail + auto IsExpandedPack = [&](const ParmVarDecl *P) { + return getUnderylingPackType(P) == TTPT; + }; ForwardingInfo FI; - FI.Head = MatchingParams; + FI.Head = MatchingParams.take_until(IsExpandedPack); + FI.Pack = + MatchingParams.drop_front(FI.Head.size()).take_while(IsExpandedPack); + FI.Tail = MatchingParams.drop_front(FI.Head.size() + FI.Pack.size()); + FI.PackTarget = Callee; Info = FI; + return; } + // Default case: assume all parameters were fully resolved + ForwardingInfo FI; + FI.Head = MatchingParams; + Info = FI; } // Returns the beginning of the expanded pack represented by Parameters // in the given arguments, if it is there. llvm::Optional findPack(typename CallExpr::arg_range Args) { // find the argument directly referring to the first parameter - auto FirstMatch = std::find_if(Args.begin(), Args.end(), [&](Expr *Arg) { - const auto *RefArg = unwrapArgument(Arg); - if (RefArg) { - if (Parameters.front() == dyn_cast(RefArg->getDecl())) { - return true; - } + for (auto It = Args.begin(); It != Args.end(); ++It) { + const Expr *Arg = *It; + if (const auto *RefArg = unwrapForward(Arg)) { + if (Parameters.front() != dyn_cast(RefArg->getDecl())) + continue; + return std::distance(Args.begin(), It); } - return false; - }); - if (FirstMatch == Args.end()) { - return llvm::None; } - return std::distance(Args.begin(), FirstMatch); + return llvm::None; } static FunctionDecl *getCalleeDeclOrUniqueOverload(CallExpr *E) { @@ -847,7 +842,7 @@ } } // Ignore the callee if the number of arguments is wrong (deal with va_args) - if (Callee->getNumParams() == E->getNumArgs()) + if (Callee && Callee->getNumParams() == E->getNumArgs()) return Callee; return nullptr; } @@ -873,31 +868,16 @@ return MatchingDecl; } - // Removes any implicit cast expressions around the given expression. - static const Expr *unwrapImplicitCast(const Expr *E) { - while (const auto *Cast = dyn_cast(E)) { - E = Cast->getSubExpr(); - } - return E; - } - - // Maps std::forward(E) to E, nullptr otherwise - static const Expr *unwrapForward(const Expr *E) { + // Maps std::forward(E) to E, identity otherwise. + static const DeclRefExpr *unwrapForward(const Expr *E) { + E = E->IgnoreImplicitAsWritten(); if (const auto *Call = dyn_cast(E)) { const auto Callee = Call->getBuiltinCallee(); if (Callee == Builtin::BIforward) { - return Call->getArg(0); + return dyn_cast( + Call->getArg(0)->IgnoreImplicitAsWritten()); } } - return E; - } - - // Maps std::forward(DeclRefExpr) to DeclRefExpr, removing any intermediate - // implicit casts, nullptr otherwise - static const DeclRefExpr *unwrapArgument(const Expr *E) { - E = unwrapImplicitCast(E); - E = unwrapForward(E); - E = unwrapImplicitCast(E); return dyn_cast(E); } }; 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 @@ -1429,6 +1429,31 @@ ElementsAre(labelIs(": int"), labelIs(": char"))); } +TEST(ParameterHints, ArgPacksAndConstructors) { + assertParameterHints( + R"cpp( + struct Foo{ Foo(int x); }; + void foo(Foo a, int b); + + template + void bar(Args... args) { foo(args...); } + + template + void baz(Args... args) { foo(Foo(args...), args...); } + + template + void bax(Args... args) { foo({args...}, args...); } + + void foo() { + bar($param1[[Foo{2}]], $param2[[42]]); + baz($param3[[42]]); + bax($param4[[42]]); + } + )cpp", + ExpectedHint{"a: ", "param1"}, ExpectedHint{"b: ", "param2"}, + ExpectedHint{"x: ", "param3"}, ExpectedHint{"x: ", "param4"}); +} + // FIXME: Low-hanging fruit where we could omit a type hint: // - auto x = TypeName(...); // - auto x = (TypeName) (...);