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 @@ -10,6 +10,7 @@ #include "Config.h" #include "HeuristicResolver.h" #include "ParsedAST.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/RecursiveASTVisitor.h" @@ -392,6 +393,7 @@ // Don't show hints for variadic parameters. size_t FixedParamCount = getFixedParamCount(Callee); size_t ArgCount = std::min(FixedParamCount, Args.size()); + auto Params = Callee->parameters(); NameVec ParameterNames = chooseParameterNames(Callee, ArgCount); @@ -402,12 +404,18 @@ for (size_t I = 0; I < ArgCount; ++I) { StringRef Name = ParameterNames[I]; - if (!shouldHint(Args[I], Name)) - continue; + bool NameHint = shouldNameHint(Args[I], Name); + std::string Suffix = ": "; + if (!NameHint) { + Name = ""; + Suffix = ""; + } + Suffix += getRefSuffix(Params[I]); - addInlayHint(Args[I]->getSourceRange(), HintSide::Left, - InlayHintKind::ParameterHint, /*Prefix=*/"", Name, - /*Suffix=*/": "); + if (!Name.empty() || !Suffix.empty()) { + addInlayHint(Args[I]->getSourceRange(), HintSide::Left, + InlayHintKind::ParameterHint, /*Prefix=*/"", Name, Suffix); + } } } @@ -434,12 +442,21 @@ return WhatItIsSetting.equals_insensitive(ParamNames[0]); } - bool shouldHint(const Expr *Arg, StringRef ParamName) { + StringRef getRefSuffix(const ParmVarDecl *Param) { + // If the parameter is a non-const reference type, print an inlay hint + auto Type = Param->getType(); + return Type->isReferenceType() && + !Type.getNonReferenceType().isConstQualified() + ? (Type->isLValueReferenceType() ? "&" : "&&") + : ""; + } + + bool shouldNameHint(const Expr *Arg, StringRef ParamName) { if (ParamName.empty()) return false; // If the argument expression is a single name and it matches the - // parameter name exactly, omit the hint. + // parameter name exactly, omit the name hint. if (ParamName == getSpelledIdentifier(Arg)) return false; 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 @@ -138,6 +138,39 @@ )cpp"); } +TEST(ParameterHints, NoNameConstReference) { + // No hint for anonymous const l-value ref parameter. + assertParameterHints(R"cpp( + void foo(const int&); + void bar() { + foo(42); + } + )cpp"); +} + +TEST(ParameterHints, NoNameReference) { + // Reference hint for anonymous l-value ref parameter. + assertParameterHints(R"cpp( + void foo(int&); + void bar() { + int i; + foo($param[[i]]); + } + )cpp", + ExpectedHint{"&", "param"}); +} + +TEST(ParameterHints, NoNameRValueReference) { + // Reference hint for anonymous r-value ref parameter. + assertParameterHints(R"cpp( + void foo(int&&); + void bar() { + foo($param[[42]]); + } + )cpp", + ExpectedHint{"&&", "param"}); +} + TEST(ParameterHints, NameInDefinition) { // Parameter name picked up from definition if necessary. assertParameterHints(R"cpp( @@ -162,6 +195,40 @@ ExpectedHint{"good: ", "good"}); } +TEST(ParameterHints, NameConstReference) { + // Only name hint for const l-value ref parameter. + assertParameterHints(R"cpp( + void foo(const int& param); + void bar() { + foo($param[[42]]); + } + )cpp", + ExpectedHint{"param: ", "param"}); +} + +TEST(ParameterHints, NameReference) { + // Reference and name hint for l-value ref parameter. + assertParameterHints(R"cpp( + void foo(int& param); + void bar() { + int i; + foo($param[[i]]); + } + )cpp", + ExpectedHint{"param: &", "param"}); +} + +TEST(ParameterHints, NameRValueReference) { + // Reference and name hint for r-value ref parameter. + assertParameterHints(R"cpp( + void foo(int&& param); + void bar() { + foo($param[[42]]); + } + )cpp", + ExpectedHint{"param: &&", "param"}); +} + TEST(ParameterHints, Operator) { // No hint for operator call with operator syntax. assertParameterHints(R"cpp( @@ -301,6 +368,21 @@ ExpectedHint{"param: ", "param"}); } +TEST(ParameterHints, ArgMatchesParamReference) { + assertParameterHints(R"cpp( + void foo(int& param); + void foo2(const int& param); + void bar() { + int param; + // show reference hint on mutable reference + foo($param[[param]]); + // but not on const reference + foo2(param); + } + )cpp", + ExpectedHint{"&", "param"}); +} + TEST(ParameterHints, LeadingUnderscore) { assertParameterHints(R"cpp( void foo(int p1, int _p2, int __p3);