Skip to content

Commit 1e3e449

Browse files
committed
[clangd] Inlay hints for default args, default initializations, implicit this, lambda captures
1 parent e8fb312 commit 1e3e449

File tree

7 files changed

+260
-13
lines changed

7 files changed

+260
-13
lines changed

clang-tools-extra/clangd/Config.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ struct Config {
147147
bool DeducedTypes = true;
148148
bool Designators = true;
149149
bool BlockEnd = false;
150+
bool LambdaCaptures = false;
151+
bool DefaultInitializations = false;
152+
bool DefaultArguments = false;
153+
bool ImplicitThis = false;
150154
// Limit the length of type names in inlay hints. (0 means no limit)
151155
uint32_t TypeNameLimit = 32;
152156
} InlayHints;

clang-tools-extra/clangd/ConfigCompile.cpp

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
#include "llvm/Support/Regex.h"
4444
#include "llvm/Support/SMLoc.h"
4545
#include "llvm/Support/SourceMgr.h"
46-
#include <algorithm>
4746
#include <memory>
4847
#include <optional>
4948
#include <string>
@@ -504,10 +503,10 @@ struct FragmentCompiler {
504503
auto Fast = isFastTidyCheck(Str);
505504
if (!Fast.has_value()) {
506505
diag(Warning,
507-
llvm::formatv(
508-
"Latency of clang-tidy check '{0}' is not known. "
509-
"It will only run if ClangTidy.FastCheckFilter is Loose or None",
510-
Str)
506+
llvm::formatv("Latency of clang-tidy check '{0}' is not known. "
507+
"It will only run if ClangTidy.FastCheckFilter is "
508+
"Loose or None",
509+
Str)
511510
.str(),
512511
Arg.Range);
513512
} else if (!*Fast) {
@@ -640,6 +639,26 @@ struct FragmentCompiler {
640639
Out.Apply.push_back([Value(**F.BlockEnd)](const Params &, Config &C) {
641640
C.InlayHints.BlockEnd = Value;
642641
});
642+
if (F.LambdaCaptures)
643+
Out.Apply.push_back(
644+
[Value(**F.LambdaCaptures)](const Params &, Config &C) {
645+
C.InlayHints.LambdaCaptures = Value;
646+
});
647+
if (F.DefaultInitializations)
648+
Out.Apply.push_back(
649+
[Value(**F.DefaultInitializations)](const Params &, Config &C) {
650+
C.InlayHints.DefaultInitializations = Value;
651+
});
652+
if (F.DefaultArguments)
653+
Out.Apply.push_back(
654+
[Value(**F.DefaultArguments)](const Params &, Config &C) {
655+
C.InlayHints.DefaultArguments = Value;
656+
});
657+
if (F.ImplicitThis)
658+
Out.Apply.push_back(
659+
[Value(**F.ImplicitThis)](const Params &, Config &C) {
660+
C.InlayHints.ImplicitThis = Value;
661+
});
643662
if (F.TypeNameLimit)
644663
Out.Apply.push_back(
645664
[Value(**F.TypeNameLimit)](const Params &, Config &C) {

clang-tools-extra/clangd/ConfigFragment.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,15 @@ struct Fragment {
327327
std::optional<Located<bool>> Designators;
328328
/// Show defined symbol names at the end of a definition block.
329329
std::optional<Located<bool>> BlockEnd;
330+
/// Show names of captured variables by default capture groups in lambdas.
331+
std::optional<Located<bool>> LambdaCaptures;
332+
/// Show curly braces at the end of implicit default initializations.
333+
std::optional<Located<bool>> DefaultInitializations;
334+
/// Show parameter names and default values of default arguments after all
335+
/// of the explicit arguments.
336+
std::optional<Located<bool>> DefaultArguments;
337+
/// Show implicit dereferencing of this pointer.
338+
std::optional<Located<bool>> ImplicitThis;
330339
/// Limit the length of type name hints. (0 means no limit)
331340
std::optional<Located<uint32_t>> TypeNameLimit;
332341
};

clang-tools-extra/clangd/ConfigYAML.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
#include "llvm/Support/YAMLParser.h"
1515
#include <optional>
1616
#include <string>
17-
#include <system_error>
1817

1918
namespace clang {
2019
namespace clangd {
@@ -260,6 +259,22 @@ class Parser {
260259
if (auto Value = boolValue(N, "BlockEnd"))
261260
F.BlockEnd = *Value;
262261
});
262+
Dict.handle("LambdaCaptures", [&](Node &N) {
263+
if (auto Value = boolValue(N, "LambdaCaptures"))
264+
F.LambdaCaptures = *Value;
265+
});
266+
Dict.handle("DefaultInitializations", [&](Node &N) {
267+
if (auto Value = boolValue(N, "DefaultInitializations"))
268+
F.DefaultInitializations = *Value;
269+
});
270+
Dict.handle("DefaultArguments", [&](Node &N) {
271+
if (auto Value = boolValue(N, "DefaultArguments"))
272+
F.DefaultArguments = *Value;
273+
});
274+
Dict.handle("ImplicitThis", [&](Node &N) {
275+
if (auto Value = boolValue(N, "ImplicitThis"))
276+
F.ImplicitThis = *Value;
277+
});
263278
Dict.handle("TypeNameLimit", [&](Node &N) {
264279
if (auto Value = uint32Value(N, "TypeNameLimit"))
265280
F.TypeNameLimit = *Value;

clang-tools-extra/clangd/InlayHints.cpp

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,33 @@
1313
#include "SourceCode.h"
1414
#include "clang/AST/ASTDiagnostic.h"
1515
#include "clang/AST/Decl.h"
16+
#include "clang/AST/DeclBase.h"
1617
#include "clang/AST/DeclarationName.h"
1718
#include "clang/AST/Expr.h"
1819
#include "clang/AST/ExprCXX.h"
20+
#include "clang/AST/LambdaCapture.h"
1921
#include "clang/AST/RecursiveASTVisitor.h"
2022
#include "clang/AST/Stmt.h"
2123
#include "clang/AST/StmtVisitor.h"
2224
#include "clang/AST/Type.h"
2325
#include "clang/Basic/Builtins.h"
26+
#include "clang/Basic/Lambda.h"
2427
#include "clang/Basic/OperatorKinds.h"
28+
#include "clang/Basic/SourceLocation.h"
2529
#include "clang/Basic/SourceManager.h"
2630
#include "llvm/ADT/DenseSet.h"
31+
#include "llvm/ADT/STLExtras.h"
2732
#include "llvm/ADT/ScopeExit.h"
33+
#include "llvm/ADT/SmallVector.h"
2834
#include "llvm/ADT/StringExtras.h"
2935
#include "llvm/ADT/StringRef.h"
3036
#include "llvm/ADT/Twine.h"
3137
#include "llvm/Support/Casting.h"
3238
#include "llvm/Support/SaveAndRestore.h"
3339
#include "llvm/Support/ScopedPrinter.h"
3440
#include "llvm/Support/raw_ostream.h"
41+
#include <algorithm>
42+
#include <iterator>
3543
#include <optional>
3644
#include <string>
3745

@@ -535,6 +543,34 @@ maybeDropCxxExplicitObjectParameters(ArrayRef<const ParmVarDecl *> Params) {
535543
return Params;
536544
}
537545

546+
llvm::StringRef getLambdaCaptureName(const LambdaCapture &Capture) {
547+
if (Capture.capturesVariable())
548+
return Capture.getCapturedVar()->getName();
549+
if (Capture.capturesThis())
550+
return llvm::StringRef{"this"};
551+
return llvm::StringRef{"unknown"};
552+
}
553+
554+
template <typename R, typename P>
555+
std::string joinAndTruncate(R &&Range, size_t MaxLength,
556+
P &&GetAsStringFunction) {
557+
std::string Out;
558+
bool IsFirst = true;
559+
for (auto &&Element : Range) {
560+
if (!IsFirst)
561+
Out.append(", ");
562+
else
563+
IsFirst = false;
564+
auto AsString = GetAsStringFunction(Element);
565+
if (Out.size() + AsString.size() >= MaxLength) {
566+
Out.append("...");
567+
break;
568+
}
569+
Out.append(AsString);
570+
}
571+
return Out;
572+
}
573+
538574
struct Callee {
539575
// Only one of Decl or Loc is set.
540576
// Loc is for calls through function pointers.
@@ -585,7 +621,8 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
585621
Callee.Decl = E->getConstructor();
586622
if (!Callee.Decl)
587623
return true;
588-
processCall(Callee, {E->getArgs(), E->getNumArgs()});
624+
processCall(Callee, E->getParenOrBraceRange().getEnd(),
625+
{E->getArgs(), E->getNumArgs()});
589626
return true;
590627
}
591628

@@ -614,6 +651,13 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
614651
return RecursiveASTVisitor<InlayHintVisitor>::TraversePseudoObjectExpr(E);
615652
}
616653

654+
bool VisitCXXThisExpr(CXXThisExpr *CTE) {
655+
if (Cfg.InlayHints.ImplicitThis && CTE->isImplicit())
656+
addInlayHint(CTE->getLocation(), HintSide::Left,
657+
InlayHintKind::ImplicitThis, "", "this->", "");
658+
return true;
659+
}
660+
617661
bool VisitCallExpr(CallExpr *E) {
618662
if (!Cfg.InlayHints.Parameters)
619663
return true;
@@ -658,7 +702,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
658702
dyn_cast_or_null<CXXMethodDecl>(Callee.Decl))
659703
if (IsFunctor || Method->hasCXXExplicitFunctionObjectParameter())
660704
Args = Args.drop_front(1);
661-
processCall(Callee, Args);
705+
processCall(Callee, E->getRParenLoc(), Args);
662706
return true;
663707
}
664708

@@ -761,6 +805,15 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
761805
}
762806

763807
bool VisitLambdaExpr(LambdaExpr *E) {
808+
if (Cfg.InlayHints.LambdaCaptures && E->getCaptureDefault() != LCD_None &&
809+
!E->implicit_captures().empty()) {
810+
std::string FormattedCaptureList =
811+
joinAndTruncate(E->implicit_captures(), Cfg.InlayHints.TypeNameLimit,
812+
[](const LambdaCapture &ImplicitCapture) {
813+
return getLambdaCaptureName(ImplicitCapture);
814+
});
815+
addImplicitCaptureHint(E->getCaptureDefaultLoc(), FormattedCaptureList);
816+
}
764817
FunctionDecl *D = E->getCallOperator();
765818
if (!E->hasExplicitResultType())
766819
addReturnTypeHint(D, E->hasExplicitParameters()
@@ -777,6 +830,14 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
777830
}
778831

779832
bool VisitVarDecl(VarDecl *D) {
833+
if (Cfg.InlayHints.DefaultInitializations && D->hasInit() &&
834+
isa<CXXConstructExpr>(D->getInit()) &&
835+
!llvm::dyn_cast<CXXConstructExpr>(D->getInit())
836+
->getParenOrBraceRange()
837+
.isValid())
838+
addInlayHint(D->getEndLoc(), HintSide::Right, InlayHintKind::DefaultInit,
839+
"", "{}", "");
840+
780841
// Do not show hints for the aggregate in a structured binding,
781842
// but show hints for the individual bindings.
782843
if (auto *DD = dyn_cast<DecompositionDecl>(D)) {
@@ -871,7 +932,8 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
871932
private:
872933
using NameVec = SmallVector<StringRef, 8>;
873934

874-
void processCall(Callee Callee, llvm::ArrayRef<const Expr *> Args) {
935+
void processCall(Callee Callee, SourceRange LParenOrBraceRange,
936+
llvm::ArrayRef<const Expr *> Args) {
875937
assert(Callee.Decl || Callee.Loc);
876938

877939
if (!Cfg.InlayHints.Parameters || Args.size() == 0)
@@ -883,6 +945,9 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
883945
if (Ctor->isCopyOrMoveConstructor())
884946
return;
885947

948+
SmallVector<std::string> FormattedDefaultArgs;
949+
bool HasNonDefaultArgs = false;
950+
886951
ArrayRef<const ParmVarDecl *> Params, ForwardedParams;
887952
// Resolve parameter packs to their forwarded parameter
888953
SmallVector<const ParmVarDecl *> ForwardedParamsStorage;
@@ -917,12 +982,34 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
917982
bool NameHint = shouldHintName(Args[I], Name);
918983
bool ReferenceHint = shouldHintReference(Params[I], ForwardedParams[I]);
919984

985+
bool IsDefault = isa<CXXDefaultArgExpr>(Args[I]);
986+
HasNonDefaultArgs |= !IsDefault;
987+
if (Cfg.InlayHints.DefaultArguments && IsDefault) {
988+
auto SourceText = Lexer::getSourceText(
989+
CharSourceRange::getTokenRange(Params[I]->getDefaultArgRange()),
990+
Callee.Decl->getASTContext().getSourceManager(),
991+
Callee.Decl->getASTContext().getLangOpts());
992+
FormattedDefaultArgs.emplace_back(llvm::formatv(
993+
"{0} = {1}", Name,
994+
SourceText.size() > Cfg.InlayHints.TypeNameLimit ? "..."
995+
: SourceText));
996+
}
997+
920998
if (NameHint || ReferenceHint) {
921999
addInlayHint(Args[I]->getSourceRange(), HintSide::Left,
9221000
InlayHintKind::Parameter, ReferenceHint ? "&" : "",
9231001
NameHint ? Name : "", ": ");
9241002
}
9251003
}
1004+
1005+
if (!FormattedDefaultArgs.empty()) {
1006+
std::string Hint =
1007+
joinAndTruncate(FormattedDefaultArgs, Cfg.InlayHints.TypeNameLimit,
1008+
[](const auto &E) { return E; });
1009+
addInlayHint(LParenOrBraceRange, HintSide::Left,
1010+
InlayHintKind::DefaultArgument,
1011+
HasNonDefaultArgs ? ", " : "", Hint, "");
1012+
}
9261013
}
9271014

9281015
static bool isSetter(const FunctionDecl *Callee, const NameVec &ParamNames) {
@@ -1130,6 +1217,10 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
11301217
CHECK_KIND(Type, DeducedTypes);
11311218
CHECK_KIND(Designator, Designators);
11321219
CHECK_KIND(BlockEnd, BlockEnd);
1220+
CHECK_KIND(LambdaCapture, LambdaCaptures);
1221+
CHECK_KIND(DefaultArgument, DefaultArguments);
1222+
CHECK_KIND(DefaultInit, DefaultInitializations);
1223+
CHECK_KIND(ImplicitThis, ImplicitThis);
11331224
#undef CHECK_KIND
11341225
}
11351226

@@ -1182,6 +1273,11 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
11821273
/*Prefix=*/"", Text, /*Suffix=*/"=");
11831274
}
11841275

1276+
void addImplicitCaptureHint(SourceRange R, llvm::StringRef Text) {
1277+
addInlayHint(R, HintSide::Right, InlayHintKind::LambdaCapture,
1278+
/*Prefix=*/": ", Text, /*Suffix=*/"");
1279+
}
1280+
11851281
bool shouldPrintTypeHint(llvm::StringRef TypeName) const noexcept {
11861282
return Cfg.InlayHints.TypeNameLimit == 0 ||
11871283
TypeName.size() < Cfg.InlayHints.TypeNameLimit;

clang-tools-extra/clangd/Protocol.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1676,6 +1676,36 @@ enum class InlayHintKind {
16761676
/// This is a clangd extension.
16771677
BlockEnd = 4,
16781678

1679+
/// An inlay hint that is for a variable captured implicitly in a lambda.
1680+
///
1681+
/// An example of parameter hint for implicit lambda captures:
1682+
/// [&^] { return A; };
1683+
/// Adds an inlay hint ": A".
1684+
LambdaCapture = 5,
1685+
1686+
/// An inlay hint that is for a default argument.
1687+
///
1688+
/// An example of a parameter hint for a default argument:
1689+
/// void foo(bool A = true);
1690+
/// foo(^);
1691+
/// Adds an inlay hint "A = true".
1692+
DefaultArgument = 6,
1693+
1694+
/// A hint for an implicit default initializer.
1695+
///
1696+
/// An example of implicit default construction:
1697+
/// MyObject O^;
1698+
/// Adds a hint for "{}".
1699+
DefaultInit = 7,
1700+
1701+
/// A hint for an implicit usage of this pointer.
1702+
///
1703+
/// An example of implicit this pointer:
1704+
/// struct MyObject { int foo; int bar(); };
1705+
/// MyObject::foo() { return ^bar; }
1706+
/// Adds a hinted "this->".
1707+
ImplicitThis = 8,
1708+
16791709
/// Other ideas for hints that are not currently implemented:
16801710
///
16811711
/// * Chaining hints, showing the types of intermediate expressions

0 commit comments

Comments
 (0)