[clangd] Type hints for structured bindings

Hints are shown for the individual bindings, not the aggregate.

Differential Revision: https://reviews.llvm.org/D104617
This commit is contained in:
Nathan Ridge 2021-06-14 03:23:35 -04:00
parent a39bb960fc
commit a15adbcddd
2 changed files with 85 additions and 15 deletions

View File

@ -32,6 +32,14 @@ public:
TypeHintPolicy.SuppressScope = true; // keep type names short
TypeHintPolicy.AnonymousTagLocations =
false; // do not print lambda locations
// Print canonical types. Otherwise, SuppressScope would result in
// things like "metafunction<args>::type" being shorted to just "type",
// which is useless. This is particularly important for structured
// bindings that use the tuple_element protocol, where the non-canonical
// types would be "tuple_element<I, A>::type".
// Note, for "auto", we would often prefer sugared types, but the AST
// doesn't currently retain them in DeducedType anyways.
TypeHintPolicy.PrintCanonicalTypes = true;
}
bool VisitCXXConstructExpr(CXXConstructExpr *E) {
@ -76,9 +84,8 @@ public:
if (auto *AT = D->getReturnType()->getContainedAutoType()) {
QualType Deduced = AT->getDeducedType();
if (!Deduced.isNull()) {
addInlayHint(D->getFunctionTypeLoc().getRParenLoc(),
InlayHintKind::TypeHint,
"-> " + D->getReturnType().getAsString(TypeHintPolicy));
addTypeHint(D->getFunctionTypeLoc().getRParenLoc(), D->getReturnType(),
"-> ");
}
}
@ -86,10 +93,14 @@ public:
}
bool VisitVarDecl(VarDecl *D) {
// Do not show hints for the aggregate in a structured binding.
// In the future, we may show hints for the individual bindings.
if (isa<DecompositionDecl>(D))
// Do not show hints for the aggregate in a structured binding,
// but show hints for the individual bindings.
if (auto *DD = dyn_cast<DecompositionDecl>(D)) {
for (auto *Binding : DD->bindings()) {
addTypeHint(Binding->getLocation(), Binding->getType(), ": ");
}
return true;
}
if (D->getType()->getContainedAutoType()) {
if (!D->getType()->isDependentType()) {
@ -98,8 +109,7 @@ public:
// (e.g. for `const auto& x = 42`, print `const int&`).
// Alternatively, we could place the hint on the `auto`
// (and then just print the type deduced for the `auto`).
addInlayHint(D->getLocation(), InlayHintKind::TypeHint,
": " + D->getType().getAsString(TypeHintPolicy));
addTypeHint(D->getLocation(), D->getType(), ": ");
}
}
return true;
@ -311,6 +321,15 @@ private:
Kind, Label.str()});
}
void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix) {
// Do not print useless "NULL TYPE" hint.
if (!T.getTypePtrOrNull())
return;
addInlayHint(R, InlayHintKind::TypeHint,
std::string(Prefix) + T.getAsString(TypeHintPolicy));
}
std::vector<InlayHint> &Results;
ASTContext &AST;
FileID MainFileID;

View File

@ -461,19 +461,70 @@ TEST(TypeHints, Lambda) {
ExpectedHint{": int", "init"});
}
TEST(TypeHints, StructuredBindings) {
// FIXME: Not handled yet.
// To handle it, we could print:
// - the aggregate type next to the 'auto', or
// - the individual types inside the brackets
// The latter is probably more useful.
// Structured bindings tests.
// Note, we hint the individual bindings, not the aggregate.
TEST(TypeHints, StructuredBindings_PublicStruct) {
assertTypeHints(R"cpp(
// Struct with public fields.
struct Point {
int x;
int y;
};
Point foo();
auto [x, y] = foo();
auto [$x[[x]], $y[[y]]] = foo();
)cpp",
ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"});
}
TEST(TypeHints, StructuredBindings_Array) {
assertTypeHints(R"cpp(
int arr[2];
auto [$x[[x]], $y[[y]]] = arr;
)cpp",
ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"});
}
TEST(TypeHints, StructuredBindings_TupleLike) {
assertTypeHints(R"cpp(
// Tuple-like type.
struct IntPair {
int a;
int b;
};
namespace std {
template <typename T>
struct tuple_size {};
template <>
struct tuple_size<IntPair> {
constexpr static unsigned value = 2;
};
template <unsigned I, typename T>
struct tuple_element {};
template <unsigned I>
struct tuple_element<I, IntPair> {
using type = int;
};
}
template <unsigned I>
int get(const IntPair& p) {
if constexpr (I == 0) {
return p.a;
} else if constexpr (I == 1) {
return p.b;
}
}
IntPair bar();
auto [$x[[x]], $y[[y]]] = bar();
)cpp",
ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"});
}
TEST(TypeHints, StructuredBindings_NoInitializer) {
assertTypeHints(R"cpp(
// No initializer (ill-formed).
// Do not show useless "NULL TYPE" hint.
auto [x, y]; /*error-ok*/
)cpp");
}