[clang-tools-extra] a15adbc - [clangd] Type hints for structured bindings
Nathan Ridge via cfe-commits
cfe-commits at lists.llvm.org
Sun Jul 4 18:53:57 PDT 2021
Author: Nathan Ridge
Date: 2021-07-04T21:53:36-04:00
New Revision: a15adbcddd078e2ca6b860ec9e4b8c2e5afdb76f
URL: https://github.com/llvm/llvm-project/commit/a15adbcddd078e2ca6b860ec9e4b8c2e5afdb76f
DIFF: https://github.com/llvm/llvm-project/commit/a15adbcddd078e2ca6b860ec9e4b8c2e5afdb76f.diff
LOG: [clangd] Type hints for structured bindings
Hints are shown for the individual bindings, not the aggregate.
Differential Revision: https://reviews.llvm.org/D104617
Added:
Modified:
clang-tools-extra/clangd/InlayHints.cpp
clang-tools-extra/clangd/unittests/InlayHintTests.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index c1a8357201e9c..1283aa4dd62cc 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -32,6 +32,14 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
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 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
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 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
}
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 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
// (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 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
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;
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 2c5597e17e2f0..1410ed115b6bf 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -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");
}
More information about the cfe-commits
mailing list