Skip to content

Commit

Permalink
[clangd] Show parameter hints for operator()
Browse files Browse the repository at this point in the history
Closes clangd/clangd#1742

Reviewed By: nridge

Differential Revision: https://reviews.llvm.org/D158926
  • Loading branch information
zyn0217 committed Sep 10, 2023
1 parent 17bd806 commit cbd6ac6
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 5 deletions.
31 changes: 27 additions & 4 deletions clang-tools-extra/clangd/InlayHints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -586,11 +586,13 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
if (!Cfg.InlayHints.Parameters)
return true;

// Do not show parameter hints for operator calls written using operator
// syntax or user-defined literals. (Among other reasons, the resulting
bool IsFunctor = isFunctionObjectCallExpr(E);
// Do not show parameter hints for user-defined literals or
// operator calls except for operator(). (Among other reasons, the resulting
// hints can look awkward, e.g. the expression can itself be a function
// argument and then we'd get two hints side by side).
if (isa<CXXOperatorCallExpr>(E) || isa<UserDefinedLiteral>(E))
if ((isa<CXXOperatorCallExpr>(E) && !IsFunctor) ||
isa<UserDefinedLiteral>(E))
return true;

auto CalleeDecls = Resolver->resolveCalleeOfCallExpr(E);
Expand All @@ -607,7 +609,22 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
else
return true;

processCall(Callee, {E->getArgs(), E->getNumArgs()});
// N4868 [over.call.object]p3 says,
// The argument list submitted to overload resolution consists of the
// argument expressions present in the function call syntax preceded by the
// implied object argument (E).
//
// However, we don't have the implied object argument for static
// operator() per clang::Sema::BuildCallToObjectOfClassType.
llvm::ArrayRef<const Expr *> Args = {E->getArgs(), E->getNumArgs()};
if (IsFunctor)
// We don't have the implied object argument through
// a function pointer either.
if (const CXXMethodDecl *Method =
dyn_cast_or_null<CXXMethodDecl>(Callee.Decl);
Method && Method->isInstance())
Args = Args.drop_front(1);
processCall(Callee, Args);
return true;
}

Expand Down Expand Up @@ -1203,6 +1220,12 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
return Range{HintStart, HintEnd};
}

static bool isFunctionObjectCallExpr(CallExpr *E) noexcept {
if (auto *CallExpr = dyn_cast<CXXOperatorCallExpr>(E))
return CallExpr->getOperator() == OverloadedOperatorKind::OO_Call;
return false;
}

std::vector<InlayHint> &Results;
ASTContext &AST;
const syntax::TokenBuffer &Tokens;
Expand Down
38 changes: 37 additions & 1 deletion clang-tools-extra/clangd/unittests/InlayHintTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void assertHintsWithHeader(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
ExpectedHints... Expected) {
Annotations Source(AnnotatedSource);
TestTU TU = TestTU::withCode(Source.code());
TU.ExtraArgs.push_back("-std=c++20");
TU.ExtraArgs.push_back("-std=c++23");
TU.HeaderCode = HeaderContent;
auto AST = TU.build();

Expand Down Expand Up @@ -807,6 +807,42 @@ TEST(ParameterHints, Operator) {
)cpp");
}

TEST(ParameterHints, FunctionCallOperator) {
assertParameterHints(R"cpp(
struct W {
void operator()(int x);
};
struct S : W {
using W::operator();
static void operator()(int x, int y);
};
void bar() {
auto l1 = [](int x) {};
auto l2 = [](int x) static {};
S s;
s($1[[1]]);
s.operator()($2[[1]]);
s.operator()($3[[1]], $4[[2]]);
S::operator()($5[[1]], $6[[2]]);
l1($7[[1]]);
l1.operator()($8[[1]]);
l2($9[[1]]);
l2.operator()($10[[1]]);
void (*ptr)(int a, int b) = &S::operator();
ptr($11[[1]], $12[[2]]);
}
)cpp",
ExpectedHint{"x: ", "1"}, ExpectedHint{"x: ", "2"},
ExpectedHint{"x: ", "3"}, ExpectedHint{"y: ", "4"},
ExpectedHint{"x: ", "5"}, ExpectedHint{"y: ", "6"},
ExpectedHint{"x: ", "7"}, ExpectedHint{"x: ", "8"},
ExpectedHint{"x: ", "9"}, ExpectedHint{"x: ", "10"},
ExpectedHint{"a: ", "11"}, ExpectedHint{"b: ", "12"});
}

TEST(ParameterHints, Macros) {
// Handling of macros depends on where the call's argument list comes from.

Expand Down

0 comments on commit cbd6ac6

Please sign in to comment.