diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp --- a/clang-tools-extra/clangd/InlayHints.cpp +++ b/clang-tools-extra/clangd/InlayHints.cpp @@ -32,6 +32,14 @@ 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::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::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 @@ 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 @@ } 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(D)) + // Do not show hints for the aggregate in a structured binding, + // but show hints for the individual bindings. + if (auto *DD = dyn_cast(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 @@ // (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 @@ 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 &Results; ASTContext &AST; FileID MainFileID; diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp --- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp +++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp @@ -461,19 +461,70 @@ 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 + struct tuple_size {}; + template <> + struct tuple_size { + constexpr static unsigned value = 2; + }; + template + struct tuple_element {}; + template + struct tuple_element { + using type = int; + }; + } + template + 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"); }