diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -123,6 +123,15 @@ } } +std::string printType(QualType QT, const PrintingPolicy &Policy) { + // TypePrinter doesn't resolve decltypes, so resolve them here. + // FIXME: This doesn't handle composite types that contain a decltype in them. + // We should rather have a printing policy for that. + while (const auto *DT = QT->getAs()) + QT = DT->getUnderlyingType(); + return QT.getAsString(Policy); +} + std::vector fetchTemplateParameters(const TemplateParameterList *Params, const PrintingPolicy &PP) { @@ -131,8 +140,7 @@ for (const Decl *Param : *Params) { HoverInfo::Param P; - P.Type.emplace(); - if (const auto TTP = dyn_cast(Param)) { + if (const auto *TTP = dyn_cast(Param)) { P.Type = TTP->wasDeclaredWithTypename() ? "typename" : "class"; if (TTP->isParameterPack()) *P.Type += "..."; @@ -141,21 +149,21 @@ P.Name = TTP->getNameAsString(); if (TTP->hasDefaultArgument()) P.Default = TTP->getDefaultArgument().getAsString(PP); - } else if (const auto NTTP = dyn_cast(Param)) { + } else if (const auto *NTTP = dyn_cast(Param)) { if (IdentifierInfo *II = NTTP->getIdentifier()) P.Name = II->getName().str(); - llvm::raw_string_ostream Out(*P.Type); - NTTP->getType().print(Out, PP); + P.Type = printType(NTTP->getType(), PP); if (NTTP->isParameterPack()) - Out << "..."; + *P.Type += "..."; if (NTTP->hasDefaultArgument()) { P.Default.emplace(); llvm::raw_string_ostream Out(*P.Default); NTTP->getDefaultArgument()->printPretty(Out, nullptr, PP); } - } else if (const auto TTPD = dyn_cast(Param)) { + } else if (const auto *TTPD = dyn_cast(Param)) { + P.Type.emplace(); llvm::raw_string_ostream OS(*P.Type); OS << "template <"; printParams(OS, @@ -241,7 +249,7 @@ HI.Parameters->emplace_back(); auto &P = HI.Parameters->back(); if (!PVD->getType().isNull()) { - P.Type = PVD->getType().getAsString(Policy); + P.Type = printType(PVD->getType(), Policy); } else { std::string Param; llvm::raw_string_ostream OS(Param); @@ -265,12 +273,12 @@ } else if (llvm::isa(FD)) { HI.ReturnType = "void"; } else { - HI.ReturnType = FD->getReturnType().getAsString(Policy); + HI.ReturnType = printType(FD->getReturnType(), Policy); - QualType FunctionType = FD->getType(); + QualType QT = FD->getType(); if (const VarDecl *VD = llvm::dyn_cast(D)) // Lambdas - FunctionType = VD->getType().getDesugaredType(D->getASTContext()); - HI.Type = FunctionType.getAsString(Policy); + QT = VD->getType().getDesugaredType(D->getASTContext()); + HI.Type = printType(QT, Policy); } // FIXME: handle variadics. } @@ -342,7 +350,7 @@ fetchTemplateParameters(TD->getTemplateParameters(), Policy); D = TD; } else if (const FunctionDecl *FD = D->getAsFunction()) { - if (const auto FTD = FD->getDescribedTemplate()) { + if (const auto *FTD = FD->getDescribedTemplate()) { HI.TemplateParameters = fetchTemplateParameters(FTD->getTemplateParameters(), Policy); D = FTD; @@ -353,7 +361,7 @@ if (const FunctionDecl *FD = getUnderlyingFunction(D)) fillFunctionTypeAndParams(HI, D, FD, Policy); else if (const auto *VD = dyn_cast(D)) - HI.Type = VD->getType().getAsString(Policy); + HI.Type = printType(VD->getType(), Policy); // Fill in value with evaluated initializer if possible. if (const auto *Var = dyn_cast(D)) { @@ -449,7 +457,7 @@ auto Policy = printingPolicyForDecls(AST.getASTContext().getPrintingPolicy()); Policy.SuppressTagKeyword = true; - HI.Type = E->getType().getAsString(Policy); + HI.Type = printType(E->getType(), Policy); HI.Value = *Val; HI.Name = getNameForExpr(E); return HI; diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -1514,6 +1514,56 @@ HI.Name = "cls > >"; HI.Documentation = "type of nested templates."; }}, + { + R"cpp(// type with decltype + int a; + decltype(a) [[b^]] = a;)cpp", + [](HoverInfo &HI) { + HI.Definition = "decltype(a) b = a"; + HI.Kind = index::SymbolKind::Variable; + HI.NamespaceScope = ""; + HI.Name = "b"; + HI.Type = "int"; + }}, + { + R"cpp(// type with decltype + int a; + decltype(a) c; + decltype(c) [[b^]] = a;)cpp", + [](HoverInfo &HI) { + HI.Definition = "decltype(c) b = a"; + HI.Kind = index::SymbolKind::Variable; + HI.NamespaceScope = ""; + HI.Name = "b"; + HI.Type = "int"; + }}, + { + R"cpp(// type with decltype + int a; + const decltype(a) [[b^]] = a;)cpp", + [](HoverInfo &HI) { + HI.Definition = "const decltype(a) b = a"; + HI.Kind = index::SymbolKind::Variable; + HI.NamespaceScope = ""; + HI.Name = "b"; + HI.Type = "int"; + }}, + { + R"cpp(// type with decltype + int a; + auto [[f^oo]](decltype(a) x) -> decltype(a) { return 0; })cpp", + [](HoverInfo &HI) { + HI.Definition = "auto foo(decltype(a) x) -> decltype(a)"; + HI.Kind = index::SymbolKind::Function; + HI.NamespaceScope = ""; + HI.Name = "foo"; + // FIXME: Handle composite types with decltype with a printing + // policy. + HI.Type = "auto (decltype(a)) -> decltype(a)"; + HI.ReturnType = "int"; + HI.Parameters = { + {std::string("int"), std::string("x"), llvm::None}}; + }}, }; // Create a tiny index, so tests above can verify documentation is fetched. @@ -1542,6 +1592,7 @@ Expected.SymRange = T.range(); Case.ExpectedBuilder(Expected); + SCOPED_TRACE(H->present().asPlainText()); EXPECT_EQ(H->NamespaceScope, Expected.NamespaceScope); EXPECT_EQ(H->LocalScope, Expected.LocalScope); EXPECT_EQ(H->Name, Expected.Name);