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 @@ -24,7 +24,8 @@ : Results(Results), AST(AST.getASTContext()), MainFileID(AST.getSourceManager().getMainFileID()), Resolver(AST.getHeuristicResolver()), - TypeHintPolicy(this->AST.getPrintingPolicy()) { + TypeHintPolicy(this->AST.getPrintingPolicy()), + StructuredBindingPolicy(this->AST.getPrintingPolicy()) { bool Invalid = false; llvm::StringRef Buf = AST.getSourceManager().getBufferData(MainFileID, &Invalid); @@ -33,14 +34,16 @@ 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; + + // For structured bindings, print canonical types. This is important because + // for bindings that use the tuple_element protocol, the non-canonical types + // would be "tuple_element::type". + // For "auto", we often prefer sugared types, but the AST doesn't currently + // retain them in DeducedType. However, not setting PrintCanonicalTypes for + // "auto" at least allows SuppressDefaultTemplateArgs (set by default) to + // have an effect. + StructuredBindingPolicy = TypeHintPolicy; + StructuredBindingPolicy.PrintCanonicalTypes = true; } bool VisitCXXConstructExpr(CXXConstructExpr *E) { @@ -98,7 +101,8 @@ // but show hints for the individual bindings. if (auto *DD = dyn_cast(D)) { for (auto *Binding : DD->bindings()) { - addTypeHint(Binding->getLocation(), Binding->getType(), ": "); + addTypeHint(Binding->getLocation(), Binding->getType(), ": ", + StructuredBindingPolicy); } return true; } @@ -327,11 +331,16 @@ } void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix) { + addTypeHint(R, T, Prefix, TypeHintPolicy); + } + + void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix, + const PrintingPolicy &Policy) { // Do not print useless "NULL TYPE" hint. if (!T.getTypePtrOrNull()) return; - std::string TypeName = T.getAsString(TypeHintPolicy); + std::string TypeName = T.getAsString(Policy); if (TypeName.length() < TypeNameLimit) addInlayHint(R, InlayHintKind::TypeHint, std::string(Prefix) + TypeName); } @@ -341,7 +350,14 @@ FileID MainFileID; StringRef MainFileBuf; const HeuristicResolver *Resolver; + // We want to suppress default template arguments, but otherwise print + // canonical types. Unfortunately, they're conflicting policies so we can't + // have both. For regular types, suppressing template arguments is more + // important, whereas printing canonical types is crucial for structured + // bindings, so we use two separate policies. (See the constructor where + // the policies are initialized for more details.) PrintingPolicy TypeHintPolicy; + PrintingPolicy StructuredBindingPolicy; static const size_t TypeNameLimit = 32; }; 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 @@ -602,6 +602,16 @@ )cpp"); } +TEST(TypeHints, DefaultTemplateArgs) { + assertTypeHints(R"cpp( + template + struct A {}; + A foo(); + auto $var[[var]] = foo(); + )cpp", + ExpectedHint{": A", "var"}); +} + // FIXME: Low-hanging fruit where we could omit a type hint: // - auto x = TypeName(...); // - auto x = (TypeName) (...);