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 @@ -786,6 +786,9 @@ // inspects the given callee with the given args to check whether it // contains Parameters, and sets Info accordingly. void handleCall(FunctionDecl *Callee, typename CallExpr::arg_range Args) { + // Skip functions with less parameters, they can't be the target. + if (Callee->parameters().size() < Parameters.size()) + return; if (std::any_of(Args.begin(), Args.end(), [](const Expr *E) { return dyn_cast(E) != nullptr; })) { @@ -823,8 +826,7 @@ llvm::Optional findPack(typename CallExpr::arg_range Args) { // find the argument directly referring to the first parameter for (auto It = Args.begin(); It != Args.end(); ++It) { - const Expr *Arg = *It; - if (const auto *RefArg = unwrapForward(Arg)) { + if (const auto *RefArg = unwrapForward(*It)) { if (Parameters.front() != dyn_cast(RefArg->getDecl())) continue; return std::distance(Args.begin(), It); @@ -871,6 +873,13 @@ // Maps std::forward(E) to E, identity otherwise. static const DeclRefExpr *unwrapForward(const Expr *E) { E = E->IgnoreImplicitAsWritten(); + // There might be an implicit copy/move constructor call on top of the + // forwarded arg. + // FIXME: Maybe mark that in the AST as so, this might skip explicit calls + // too. + if (const auto *Const = dyn_cast(E)) + if (Const->getConstructor()->isCopyOrMoveConstructor()) + E = Const->getArg(0)->IgnoreImplicitAsWritten(); if (const auto *Call = dyn_cast(E)) { const auto Callee = Call->getBuiltinCallee(); if (Callee == Builtin::BIforward) { 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 @@ -1432,26 +1432,30 @@ TEST(ParameterHints, ArgPacksAndConstructors) { assertParameterHints( R"cpp( - struct Foo{ Foo(int x); }; + struct Foo{ Foo(); Foo(int x); }; void foo(Foo a, int b); - template - void bar(Args... args) { foo(args...); } - + void bar(Args... args) { + foo(args...); + } template - void baz(Args... args) { foo(Foo(args...), args...); } + void baz(Args... args) { foo($param1[[Foo{args...}]], $param2[[1]]); } template - void bax(Args... args) { foo({args...}, args...); } + void bax(Args... args) { foo($param3[[{args...}]], args...); } void foo() { - bar($param1[[Foo{2}]], $param2[[42]]); - baz($param3[[42]]); - bax($param4[[42]]); + bar($param4[[Foo{}]], $param5[[42]]); + bar($param6[[42]], $param7[[42]]); + baz($param8[[42]]); + bax($param9[[42]]); } )cpp", ExpectedHint{"a: ", "param1"}, ExpectedHint{"b: ", "param2"}, - ExpectedHint{"x: ", "param3"}, ExpectedHint{"x: ", "param4"}); + ExpectedHint{"a: ", "param3"}, ExpectedHint{"a: ", "param4"}, + ExpectedHint{"b: ", "param5"}, ExpectedHint{"a: ", "param6"}, + ExpectedHint{"b: ", "param7"}, ExpectedHint{"x: ", "param8"}, + ExpectedHint{"b: ", "param9"}); } // FIXME: Low-hanging fruit where we could omit a type hint: