Skip to content

Commit

Permalink
after comments
Browse files Browse the repository at this point in the history
  • Loading branch information
torshepherd committed Sep 24, 2024
1 parent 9d3a576 commit 41f0a7f
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 5 deletions.
2 changes: 2 additions & 0 deletions clang-tools-extra/clangd/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ struct Config {
bool DeducedTypes = true;
bool Designators = true;
bool BlockEnd = false;
bool LambdaCaptures = false;
bool DefaultArguments = false;
// Limit the length of type names in inlay hints. (0 means no limit)
uint32_t TypeNameLimit = 32;
} InlayHints;
Expand Down
11 changes: 10 additions & 1 deletion clang-tools-extra/clangd/ConfigCompile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
#include "llvm/Support/Regex.h"
#include "llvm/Support/SMLoc.h"
#include "llvm/Support/SourceMgr.h"
#include <algorithm>
#include <memory>
#include <optional>
#include <string>
Expand Down Expand Up @@ -654,6 +653,16 @@ struct FragmentCompiler {
Out.Apply.push_back([Value(**F.BlockEnd)](const Params &, Config &C) {
C.InlayHints.BlockEnd = Value;
});
if (F.LambdaCaptures)
Out.Apply.push_back(
[Value(**F.LambdaCaptures)](const Params &, Config &C) {
C.InlayHints.LambdaCaptures = Value;
});
if (F.DefaultArguments)
Out.Apply.push_back(
[Value(**F.DefaultArguments)](const Params &, Config &C) {
C.InlayHints.DefaultArguments = Value;
});
if (F.TypeNameLimit)
Out.Apply.push_back(
[Value(**F.TypeNameLimit)](const Params &, Config &C) {
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/clangd/ConfigFragment.h
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,11 @@ struct Fragment {
std::optional<Located<bool>> Designators;
/// Show defined symbol names at the end of a definition block.
std::optional<Located<bool>> BlockEnd;
/// Show names of captured variables by default capture groups in lambdas.
std::optional<Located<bool>> LambdaCaptures;
/// Show parameter names and default values of default arguments after all
/// of the explicit arguments.
std::optional<Located<bool>> DefaultArguments;
/// Limit the length of type name hints. (0 means no limit)
std::optional<Located<uint32_t>> TypeNameLimit;
};
Expand Down
9 changes: 8 additions & 1 deletion clang-tools-extra/clangd/ConfigYAML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
#include "llvm/Support/YAMLParser.h"
#include <optional>
#include <string>
#include <system_error>

namespace clang {
namespace clangd {
Expand Down Expand Up @@ -264,6 +263,14 @@ class Parser {
if (auto Value = boolValue(N, "BlockEnd"))
F.BlockEnd = *Value;
});
Dict.handle("LambdaCaptures", [&](Node &N) {
if (auto Value = boolValue(N, "LambdaCaptures"))
F.LambdaCaptures = *Value;
});
Dict.handle("DefaultArguments", [&](Node &N) {
if (auto Value = boolValue(N, "DefaultArguments"))
F.DefaultArguments = *Value;
});
Dict.handle("TypeNameLimit", [&](Node &N) {
if (auto Value = uint32Value(N, "TypeNameLimit"))
F.TypeNameLimit = *Value;
Expand Down
91 changes: 88 additions & 3 deletions clang-tools-extra/clangd/InlayHints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,38 @@
#include "Config.h"
#include "HeuristicResolver.h"
#include "ParsedAST.h"
#include "Protocol.h"
#include "SourceCode.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/LambdaCapture.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/Type.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/Lambda.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <iterator>
#include <optional>
#include <string>

Expand Down Expand Up @@ -372,6 +383,38 @@ maybeDropCxxExplicitObjectParameters(ArrayRef<const ParmVarDecl *> Params) {
return Params;
}

llvm::StringRef getLambdaCaptureName(const LambdaCapture &Capture) {
switch (Capture.getCaptureKind()) {
case LCK_This:
case LCK_StarThis:
return llvm::StringRef{"this"};
case LCK_ByCopy:
case LCK_ByRef:
case LCK_VLAType:
return Capture.getCapturedVar()->getName();
}
llvm_unreachable("unhandled capture kind");
}

template <typename R, typename P>
std::string joinAndTruncate(R &&Range, size_t MaxLength,
P &&GetAsStringFunction) {
std::string Out;
llvm::raw_string_ostream OS(Out);
llvm::ListSeparator Sep(", ");
for (auto &&Element : Range) {
OS << Sep;
auto AsString = GetAsStringFunction(Element);
if (Out.size() + AsString.size() >= MaxLength) {
OS << "...";
break;
}
OS << AsString;
}
OS.flush();
return Out;
}

struct Callee {
// Only one of Decl or Loc is set.
// Loc is for calls through function pointers.
Expand Down Expand Up @@ -422,7 +465,8 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
Callee.Decl = E->getConstructor();
if (!Callee.Decl)
return true;
processCall(Callee, {E->getArgs(), E->getNumArgs()});
processCall(Callee, E->getParenOrBraceRange().getEnd(),
{E->getArgs(), E->getNumArgs()});
return true;
}

Expand Down Expand Up @@ -495,7 +539,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
dyn_cast_or_null<CXXMethodDecl>(Callee.Decl))
if (IsFunctor || Method->hasCXXExplicitFunctionObjectParameter())
Args = Args.drop_front(1);
processCall(Callee, Args);
processCall(Callee, E->getRParenLoc(), Args);
return true;
}

Expand Down Expand Up @@ -598,6 +642,15 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
}

bool VisitLambdaExpr(LambdaExpr *E) {
if (Cfg.InlayHints.LambdaCaptures && E->getCaptureDefault() != LCD_None &&
!E->implicit_captures().empty()) {
std::string FormattedCaptureList =
joinAndTruncate(E->implicit_captures(), Cfg.InlayHints.TypeNameLimit,
[](const LambdaCapture &ImplicitCapture) {
return getLambdaCaptureName(ImplicitCapture);
});
addImplicitCaptureHint(E->getCaptureDefaultLoc(), FormattedCaptureList);
}
FunctionDecl *D = E->getCallOperator();
if (!E->hasExplicitResultType())
addReturnTypeHint(D, E->hasExplicitParameters()
Expand Down Expand Up @@ -709,7 +762,8 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
private:
using NameVec = SmallVector<StringRef, 8>;

void processCall(Callee Callee, llvm::ArrayRef<const Expr *> Args) {
void processCall(Callee Callee, SourceRange LParenOrBraceRange,
llvm::ArrayRef<const Expr *> Args) {
assert(Callee.Decl || Callee.Loc);

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

SmallVector<std::string> FormattedDefaultArgs;
bool HasNonDefaultArgs = false;

ArrayRef<const ParmVarDecl *> Params, ForwardedParams;
// Resolve parameter packs to their forwarded parameter
SmallVector<const ParmVarDecl *> ForwardedParamsStorage;
Expand Down Expand Up @@ -755,12 +812,33 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
bool NameHint = shouldHintName(Args[I], Name);
bool ReferenceHint = shouldHintReference(Params[I], ForwardedParams[I]);

bool IsDefault = isa<CXXDefaultArgExpr>(Args[I]);
HasNonDefaultArgs |= !IsDefault;
if (Cfg.InlayHints.DefaultArguments && IsDefault) {
auto SourceText = Lexer::getSourceText(
CharSourceRange::getTokenRange(Params[I]->getDefaultArgRange()),
AST.getSourceManager(), AST.getLangOpts());
FormattedDefaultArgs.emplace_back(llvm::formatv(
"{0} = {1}", Name,
SourceText.size() > Cfg.InlayHints.TypeNameLimit ? "..."
: SourceText));
}

if (NameHint || ReferenceHint) {
addInlayHint(Args[I]->getSourceRange(), HintSide::Left,
InlayHintKind::Parameter, ReferenceHint ? "&" : "",
NameHint ? Name : "", ": ");
}
}

if (!FormattedDefaultArgs.empty()) {
std::string Hint =
joinAndTruncate(FormattedDefaultArgs, Cfg.InlayHints.TypeNameLimit,
[](const auto &E) { return E; });
addInlayHint(LParenOrBraceRange, HintSide::Left,
InlayHintKind::DefaultArgument,
HasNonDefaultArgs ? ", " : "", Hint, "");
}
}

static bool isSetter(const FunctionDecl *Callee, const NameVec &ParamNames) {
Expand Down Expand Up @@ -968,6 +1046,8 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
CHECK_KIND(Type, DeducedTypes);
CHECK_KIND(Designator, Designators);
CHECK_KIND(BlockEnd, BlockEnd);
CHECK_KIND(LambdaCapture, LambdaCaptures);
CHECK_KIND(DefaultArgument, DefaultArguments);
#undef CHECK_KIND
}

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

void addImplicitCaptureHint(SourceRange R, llvm::StringRef Text) {
addInlayHint(R, HintSide::Right, InlayHintKind::LambdaCapture,
/*Prefix=*/": ", Text, /*Suffix=*/"");
}

bool shouldPrintTypeHint(llvm::StringRef TypeName) const noexcept {
return Cfg.InlayHints.TypeNameLimit == 0 ||
TypeName.size() < Cfg.InlayHints.TypeNameLimit;
Expand Down
6 changes: 6 additions & 0 deletions clang-tools-extra/clangd/Protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1477,6 +1477,8 @@ llvm::json::Value toJSON(const InlayHintKind &Kind) {
return 2;
case InlayHintKind::Designator:
case InlayHintKind::BlockEnd:
case InlayHintKind::LambdaCapture:
case InlayHintKind::DefaultArgument:
// This is an extension, don't serialize.
return nullptr;
}
Expand Down Expand Up @@ -1517,6 +1519,10 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, InlayHintKind Kind) {
return "designator";
case InlayHintKind::BlockEnd:
return "block-end";
case InlayHintKind::LambdaCapture:
return "lambda-capture";
case InlayHintKind::DefaultArgument:
return "default-argument";
}
llvm_unreachable("Unknown clang.clangd.InlayHintKind");
};
Expand Down
15 changes: 15 additions & 0 deletions clang-tools-extra/clangd/Protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -1681,6 +1681,21 @@ enum class InlayHintKind {
/// This is a clangd extension.
BlockEnd = 4,

/// An inlay hint that is for a variable captured implicitly in a lambda.
///
/// An example of parameter hint for implicit lambda captures:
/// [&^] { return A; };
/// Adds an inlay hint ": A".
LambdaCapture = 5,

/// An inlay hint that is for a default argument.
///
/// An example of a parameter hint for a default argument:
/// void foo(bool A = true);
/// foo(^);
/// Adds an inlay hint "A = true".
DefaultArgument = 6,

/// Other ideas for hints that are not currently implemented:
///
/// * Chaining hints, showing the types of intermediate expressions
Expand Down
57 changes: 57 additions & 0 deletions clang-tools-extra/clangd/unittests/InlayHintTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@
#include "support/Context.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <optional>
#include <string>
#include <utility>
#include <vector>

namespace clang {
Expand Down Expand Up @@ -81,6 +84,8 @@ Config noHintsConfig() {
C.InlayHints.DeducedTypes = false;
C.InlayHints.Designators = false;
C.InlayHints.BlockEnd = false;
C.InlayHints.LambdaCaptures = false;
C.InlayHints.DefaultArguments = false;
return C;
}

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

TEST(LambdaCaptures, Smoke) {
Config Cfg;
Cfg.InlayHints.Parameters = false;
Cfg.InlayHints.DeducedTypes = false;
Cfg.InlayHints.Designators = false;
Cfg.InlayHints.BlockEnd = false;
Cfg.InlayHints.DefaultArguments = false;

Cfg.InlayHints.LambdaCaptures = true;
Cfg.InlayHints.TypeNameLimit = 10;
WithContextValue WithCfg(Config::Key, std::move(Cfg));

assertHints(InlayHintKind::LambdaCapture, R"cpp(
void foo() {
int A = 1;
int ReallyLongName = 1;
auto B = [$byvalue[[=]]] { return A; };
auto C = [$byref[[&]]] { return A; };
auto D = [$trunc[[=]], &A] { B(); (void)ReallyLongName; return A; };
}
)cpp",
ExpectedHint{": A", "byvalue", Right},
ExpectedHint{": A", "byref", Right},
ExpectedHint{": B, ...", "trunc", Right});
}

TEST(DefaultArguments, Smoke) {
Config Cfg;
Cfg.InlayHints.Parameters =
true; // To test interplay of parameters and default parameters
Cfg.InlayHints.DeducedTypes = false;
Cfg.InlayHints.Designators = false;
Cfg.InlayHints.BlockEnd = false;
Cfg.InlayHints.LambdaCaptures = false;

Cfg.InlayHints.DefaultArguments = true;
WithContextValue WithCfg(Config::Key, std::move(Cfg));

const auto *Code = R"cpp(
int foo(int A = 4) { return A; }
int bar(int A, int B = 1, bool C = foo($default1[[)]]) { return A; }
int A = bar($explicit[[2]]$default2[[)]];
)cpp";

assertHints(InlayHintKind::DefaultArgument, Code,
ExpectedHint{"A = 4", "default1", Left},
ExpectedHint{", B = 1, C = foo()", "default2", Left});

assertHints(InlayHintKind::Parameter, Code,
ExpectedHint{"A: ", "explicit", Left});
}

TEST(TypeHints, Deduplication) {
assertTypeHints(R"cpp(
template <typename T>
Expand Down

0 comments on commit 41f0a7f

Please sign in to comment.