Skip to content

Commit 41f0a7f

Browse files
committed
after comments
1 parent 9d3a576 commit 41f0a7f

File tree

8 files changed

+191
-5
lines changed

8 files changed

+191
-5
lines changed

clang-tools-extra/clangd/Config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ struct Config {
148148
bool DeducedTypes = true;
149149
bool Designators = true;
150150
bool BlockEnd = false;
151+
bool LambdaCaptures = false;
152+
bool DefaultArguments = false;
151153
// Limit the length of type names in inlay hints. (0 means no limit)
152154
uint32_t TypeNameLimit = 32;
153155
} InlayHints;

clang-tools-extra/clangd/ConfigCompile.cpp

Lines changed: 10 additions & 1 deletion
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>
@@ -654,6 +653,16 @@ struct FragmentCompiler {
654653
Out.Apply.push_back([Value(**F.BlockEnd)](const Params &, Config &C) {
655654
C.InlayHints.BlockEnd = Value;
656655
});
656+
if (F.LambdaCaptures)
657+
Out.Apply.push_back(
658+
[Value(**F.LambdaCaptures)](const Params &, Config &C) {
659+
C.InlayHints.LambdaCaptures = Value;
660+
});
661+
if (F.DefaultArguments)
662+
Out.Apply.push_back(
663+
[Value(**F.DefaultArguments)](const Params &, Config &C) {
664+
C.InlayHints.DefaultArguments = Value;
665+
});
657666
if (F.TypeNameLimit)
658667
Out.Apply.push_back(
659668
[Value(**F.TypeNameLimit)](const Params &, Config &C) {

clang-tools-extra/clangd/ConfigFragment.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,11 @@ struct Fragment {
331331
std::optional<Located<bool>> Designators;
332332
/// Show defined symbol names at the end of a definition block.
333333
std::optional<Located<bool>> BlockEnd;
334+
/// Show names of captured variables by default capture groups in lambdas.
335+
std::optional<Located<bool>> LambdaCaptures;
336+
/// Show parameter names and default values of default arguments after all
337+
/// of the explicit arguments.
338+
std::optional<Located<bool>> DefaultArguments;
334339
/// Limit the length of type name hints. (0 means no limit)
335340
std::optional<Located<uint32_t>> TypeNameLimit;
336341
};

clang-tools-extra/clangd/ConfigYAML.cpp

Lines changed: 8 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 {
@@ -264,6 +263,14 @@ class Parser {
264263
if (auto Value = boolValue(N, "BlockEnd"))
265264
F.BlockEnd = *Value;
266265
});
266+
Dict.handle("LambdaCaptures", [&](Node &N) {
267+
if (auto Value = boolValue(N, "LambdaCaptures"))
268+
F.LambdaCaptures = *Value;
269+
});
270+
Dict.handle("DefaultArguments", [&](Node &N) {
271+
if (auto Value = boolValue(N, "DefaultArguments"))
272+
F.DefaultArguments = *Value;
273+
});
267274
Dict.handle("TypeNameLimit", [&](Node &N) {
268275
if (auto Value = uint32Value(N, "TypeNameLimit"))
269276
F.TypeNameLimit = *Value;

clang-tools-extra/clangd/InlayHints.cpp

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,38 @@
1111
#include "Config.h"
1212
#include "HeuristicResolver.h"
1313
#include "ParsedAST.h"
14+
#include "Protocol.h"
1415
#include "SourceCode.h"
1516
#include "clang/AST/ASTDiagnostic.h"
1617
#include "clang/AST/Decl.h"
18+
#include "clang/AST/DeclBase.h"
1719
#include "clang/AST/DeclarationName.h"
1820
#include "clang/AST/Expr.h"
1921
#include "clang/AST/ExprCXX.h"
22+
#include "clang/AST/LambdaCapture.h"
2023
#include "clang/AST/RecursiveASTVisitor.h"
2124
#include "clang/AST/Stmt.h"
2225
#include "clang/AST/StmtVisitor.h"
2326
#include "clang/AST/Type.h"
2427
#include "clang/Basic/Builtins.h"
28+
#include "clang/Basic/Lambda.h"
2529
#include "clang/Basic/OperatorKinds.h"
30+
#include "clang/Basic/SourceLocation.h"
2631
#include "clang/Basic/SourceManager.h"
2732
#include "llvm/ADT/DenseSet.h"
33+
#include "llvm/ADT/STLExtras.h"
34+
#include "llvm/ADT/SmallVector.h"
2835
#include "llvm/ADT/StringExtras.h"
2936
#include "llvm/ADT/StringRef.h"
3037
#include "llvm/ADT/Twine.h"
3138
#include "llvm/Support/Casting.h"
39+
#include "llvm/Support/ErrorHandling.h"
40+
#include "llvm/Support/FormatVariadic.h"
3241
#include "llvm/Support/SaveAndRestore.h"
3342
#include "llvm/Support/ScopedPrinter.h"
3443
#include "llvm/Support/raw_ostream.h"
44+
#include <algorithm>
45+
#include <iterator>
3546
#include <optional>
3647
#include <string>
3748

@@ -372,6 +383,38 @@ maybeDropCxxExplicitObjectParameters(ArrayRef<const ParmVarDecl *> Params) {
372383
return Params;
373384
}
374385

386+
llvm::StringRef getLambdaCaptureName(const LambdaCapture &Capture) {
387+
switch (Capture.getCaptureKind()) {
388+
case LCK_This:
389+
case LCK_StarThis:
390+
return llvm::StringRef{"this"};
391+
case LCK_ByCopy:
392+
case LCK_ByRef:
393+
case LCK_VLAType:
394+
return Capture.getCapturedVar()->getName();
395+
}
396+
llvm_unreachable("unhandled capture kind");
397+
}
398+
399+
template <typename R, typename P>
400+
std::string joinAndTruncate(R &&Range, size_t MaxLength,
401+
P &&GetAsStringFunction) {
402+
std::string Out;
403+
llvm::raw_string_ostream OS(Out);
404+
llvm::ListSeparator Sep(", ");
405+
for (auto &&Element : Range) {
406+
OS << Sep;
407+
auto AsString = GetAsStringFunction(Element);
408+
if (Out.size() + AsString.size() >= MaxLength) {
409+
OS << "...";
410+
break;
411+
}
412+
OS << AsString;
413+
}
414+
OS.flush();
415+
return Out;
416+
}
417+
375418
struct Callee {
376419
// Only one of Decl or Loc is set.
377420
// Loc is for calls through function pointers.
@@ -422,7 +465,8 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
422465
Callee.Decl = E->getConstructor();
423466
if (!Callee.Decl)
424467
return true;
425-
processCall(Callee, {E->getArgs(), E->getNumArgs()});
468+
processCall(Callee, E->getParenOrBraceRange().getEnd(),
469+
{E->getArgs(), E->getNumArgs()});
426470
return true;
427471
}
428472

@@ -495,7 +539,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
495539
dyn_cast_or_null<CXXMethodDecl>(Callee.Decl))
496540
if (IsFunctor || Method->hasCXXExplicitFunctionObjectParameter())
497541
Args = Args.drop_front(1);
498-
processCall(Callee, Args);
542+
processCall(Callee, E->getRParenLoc(), Args);
499543
return true;
500544
}
501545

@@ -598,6 +642,15 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
598642
}
599643

600644
bool VisitLambdaExpr(LambdaExpr *E) {
645+
if (Cfg.InlayHints.LambdaCaptures && E->getCaptureDefault() != LCD_None &&
646+
!E->implicit_captures().empty()) {
647+
std::string FormattedCaptureList =
648+
joinAndTruncate(E->implicit_captures(), Cfg.InlayHints.TypeNameLimit,
649+
[](const LambdaCapture &ImplicitCapture) {
650+
return getLambdaCaptureName(ImplicitCapture);
651+
});
652+
addImplicitCaptureHint(E->getCaptureDefaultLoc(), FormattedCaptureList);
653+
}
601654
FunctionDecl *D = E->getCallOperator();
602655
if (!E->hasExplicitResultType())
603656
addReturnTypeHint(D, E->hasExplicitParameters()
@@ -709,7 +762,8 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
709762
private:
710763
using NameVec = SmallVector<StringRef, 8>;
711764

712-
void processCall(Callee Callee, llvm::ArrayRef<const Expr *> Args) {
765+
void processCall(Callee Callee, SourceRange LParenOrBraceRange,
766+
llvm::ArrayRef<const Expr *> Args) {
713767
assert(Callee.Decl || Callee.Loc);
714768

715769
if (!Cfg.InlayHints.Parameters || Args.size() == 0)
@@ -721,6 +775,9 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
721775
if (Ctor->isCopyOrMoveConstructor())
722776
return;
723777

778+
SmallVector<std::string> FormattedDefaultArgs;
779+
bool HasNonDefaultArgs = false;
780+
724781
ArrayRef<const ParmVarDecl *> Params, ForwardedParams;
725782
// Resolve parameter packs to their forwarded parameter
726783
SmallVector<const ParmVarDecl *> ForwardedParamsStorage;
@@ -755,12 +812,33 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
755812
bool NameHint = shouldHintName(Args[I], Name);
756813
bool ReferenceHint = shouldHintReference(Params[I], ForwardedParams[I]);
757814

815+
bool IsDefault = isa<CXXDefaultArgExpr>(Args[I]);
816+
HasNonDefaultArgs |= !IsDefault;
817+
if (Cfg.InlayHints.DefaultArguments && IsDefault) {
818+
auto SourceText = Lexer::getSourceText(
819+
CharSourceRange::getTokenRange(Params[I]->getDefaultArgRange()),
820+
AST.getSourceManager(), AST.getLangOpts());
821+
FormattedDefaultArgs.emplace_back(llvm::formatv(
822+
"{0} = {1}", Name,
823+
SourceText.size() > Cfg.InlayHints.TypeNameLimit ? "..."
824+
: SourceText));
825+
}
826+
758827
if (NameHint || ReferenceHint) {
759828
addInlayHint(Args[I]->getSourceRange(), HintSide::Left,
760829
InlayHintKind::Parameter, ReferenceHint ? "&" : "",
761830
NameHint ? Name : "", ": ");
762831
}
763832
}
833+
834+
if (!FormattedDefaultArgs.empty()) {
835+
std::string Hint =
836+
joinAndTruncate(FormattedDefaultArgs, Cfg.InlayHints.TypeNameLimit,
837+
[](const auto &E) { return E; });
838+
addInlayHint(LParenOrBraceRange, HintSide::Left,
839+
InlayHintKind::DefaultArgument,
840+
HasNonDefaultArgs ? ", " : "", Hint, "");
841+
}
764842
}
765843

766844
static bool isSetter(const FunctionDecl *Callee, const NameVec &ParamNames) {
@@ -968,6 +1046,8 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
9681046
CHECK_KIND(Type, DeducedTypes);
9691047
CHECK_KIND(Designator, Designators);
9701048
CHECK_KIND(BlockEnd, BlockEnd);
1049+
CHECK_KIND(LambdaCapture, LambdaCaptures);
1050+
CHECK_KIND(DefaultArgument, DefaultArguments);
9711051
#undef CHECK_KIND
9721052
}
9731053

@@ -1021,6 +1101,11 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
10211101
/*Prefix=*/"", Text, /*Suffix=*/"=");
10221102
}
10231103

1104+
void addImplicitCaptureHint(SourceRange R, llvm::StringRef Text) {
1105+
addInlayHint(R, HintSide::Right, InlayHintKind::LambdaCapture,
1106+
/*Prefix=*/": ", Text, /*Suffix=*/"");
1107+
}
1108+
10241109
bool shouldPrintTypeHint(llvm::StringRef TypeName) const noexcept {
10251110
return Cfg.InlayHints.TypeNameLimit == 0 ||
10261111
TypeName.size() < Cfg.InlayHints.TypeNameLimit;

clang-tools-extra/clangd/Protocol.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,6 +1477,8 @@ llvm::json::Value toJSON(const InlayHintKind &Kind) {
14771477
return 2;
14781478
case InlayHintKind::Designator:
14791479
case InlayHintKind::BlockEnd:
1480+
case InlayHintKind::LambdaCapture:
1481+
case InlayHintKind::DefaultArgument:
14801482
// This is an extension, don't serialize.
14811483
return nullptr;
14821484
}
@@ -1517,6 +1519,10 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, InlayHintKind Kind) {
15171519
return "designator";
15181520
case InlayHintKind::BlockEnd:
15191521
return "block-end";
1522+
case InlayHintKind::LambdaCapture:
1523+
return "lambda-capture";
1524+
case InlayHintKind::DefaultArgument:
1525+
return "default-argument";
15201526
}
15211527
llvm_unreachable("Unknown clang.clangd.InlayHintKind");
15221528
};

clang-tools-extra/clangd/Protocol.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1681,6 +1681,21 @@ enum class InlayHintKind {
16811681
/// This is a clangd extension.
16821682
BlockEnd = 4,
16831683

1684+
/// An inlay hint that is for a variable captured implicitly in a lambda.
1685+
///
1686+
/// An example of parameter hint for implicit lambda captures:
1687+
/// [&^] { return A; };
1688+
/// Adds an inlay hint ": A".
1689+
LambdaCapture = 5,
1690+
1691+
/// An inlay hint that is for a default argument.
1692+
///
1693+
/// An example of a parameter hint for a default argument:
1694+
/// void foo(bool A = true);
1695+
/// foo(^);
1696+
/// Adds an inlay hint "A = true".
1697+
DefaultArgument = 6,
1698+
16841699
/// Other ideas for hints that are not currently implemented:
16851700
///
16861701
/// * Chaining hints, showing the types of intermediate expressions

clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@
1515
#include "support/Context.h"
1616
#include "llvm/ADT/StringRef.h"
1717
#include "llvm/Support/ScopedPrinter.h"
18+
#include "llvm/Support/raw_ostream.h"
1819
#include "gmock/gmock.h"
1920
#include "gtest/gtest.h"
21+
#include <optional>
2022
#include <string>
23+
#include <utility>
2124
#include <vector>
2225

2326
namespace clang {
@@ -81,6 +84,8 @@ Config noHintsConfig() {
8184
C.InlayHints.DeducedTypes = false;
8285
C.InlayHints.Designators = false;
8386
C.InlayHints.BlockEnd = false;
87+
C.InlayHints.LambdaCaptures = false;
88+
C.InlayHints.DefaultArguments = false;
8489
return C;
8590
}
8691

@@ -1465,6 +1470,58 @@ TEST(TypeHints, DefaultTemplateArgs) {
14651470
ExpectedHint{": A<float>", "binding"});
14661471
}
14671472

1473+
TEST(LambdaCaptures, Smoke) {
1474+
Config Cfg;
1475+
Cfg.InlayHints.Parameters = false;
1476+
Cfg.InlayHints.DeducedTypes = false;
1477+
Cfg.InlayHints.Designators = false;
1478+
Cfg.InlayHints.BlockEnd = false;
1479+
Cfg.InlayHints.DefaultArguments = false;
1480+
1481+
Cfg.InlayHints.LambdaCaptures = true;
1482+
Cfg.InlayHints.TypeNameLimit = 10;
1483+
WithContextValue WithCfg(Config::Key, std::move(Cfg));
1484+
1485+
assertHints(InlayHintKind::LambdaCapture, R"cpp(
1486+
void foo() {
1487+
int A = 1;
1488+
int ReallyLongName = 1;
1489+
auto B = [$byvalue[[=]]] { return A; };
1490+
auto C = [$byref[[&]]] { return A; };
1491+
auto D = [$trunc[[=]], &A] { B(); (void)ReallyLongName; return A; };
1492+
}
1493+
)cpp",
1494+
ExpectedHint{": A", "byvalue", Right},
1495+
ExpectedHint{": A", "byref", Right},
1496+
ExpectedHint{": B, ...", "trunc", Right});
1497+
}
1498+
1499+
TEST(DefaultArguments, Smoke) {
1500+
Config Cfg;
1501+
Cfg.InlayHints.Parameters =
1502+
true; // To test interplay of parameters and default parameters
1503+
Cfg.InlayHints.DeducedTypes = false;
1504+
Cfg.InlayHints.Designators = false;
1505+
Cfg.InlayHints.BlockEnd = false;
1506+
Cfg.InlayHints.LambdaCaptures = false;
1507+
1508+
Cfg.InlayHints.DefaultArguments = true;
1509+
WithContextValue WithCfg(Config::Key, std::move(Cfg));
1510+
1511+
const auto *Code = R"cpp(
1512+
int foo(int A = 4) { return A; }
1513+
int bar(int A, int B = 1, bool C = foo($default1[[)]]) { return A; }
1514+
int A = bar($explicit[[2]]$default2[[)]];
1515+
)cpp";
1516+
1517+
assertHints(InlayHintKind::DefaultArgument, Code,
1518+
ExpectedHint{"A = 4", "default1", Left},
1519+
ExpectedHint{", B = 1, C = foo()", "default2", Left});
1520+
1521+
assertHints(InlayHintKind::Parameter, Code,
1522+
ExpectedHint{"A: ", "explicit", Left});
1523+
}
1524+
14681525
TEST(TypeHints, Deduplication) {
14691526
assertTypeHints(R"cpp(
14701527
template <typename T>

0 commit comments

Comments
 (0)