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 @@ -265,12 +265,17 @@ } else if (llvm::isa(FD)) { HI.ReturnType = "void"; } else { - HI.ReturnType = FD->getReturnType().getAsString(Policy); - - QualType FunctionType = FD->getType(); + QualType QT = FD->getReturnType(); + // TypePrinter doesn't resolve decltypes, so resolve them here. We are going + // to include spelling "decltype(X)" in `HoverInfo::Definition` anyway. + while (auto *DT = QT->getAs()) + QT = DT->getUnderlyingType(); + HI.ReturnType = QT.getAsString(Policy); + + 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 = QT.getAsString(Policy); } // FIXME: handle variadics. } @@ -352,8 +357,14 @@ // Fill in types and params. 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); + else if (const auto *VD = dyn_cast(D)) { + QualType QT = VD->getType(); + // TypePrinter doesn't resolve decltypes, so resolve them here. We are going + // to include spelling "decltype(X)" in `HoverInfo::Definition` anyway. + while (auto *DT = QT->getAs()) + QT = DT->getUnderlyingType(); + HI.Type = QT.getAsString(Policy); + } // Fill in value with evaluated initializer if possible. if (const auto *Var = dyn_cast(D)) { 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 @@ -1534,6 +1534,53 @@ HI.Type = "unsigned long"; HI.Value = "1"; }}, + { + 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) { return 0; })cpp", + [](HoverInfo &HI) { + HI.Definition = "auto foo() -> decltype(a)"; + HI.Kind = index::SymbolKind::Function; + HI.NamespaceScope = ""; + HI.Name = "foo"; + HI.Type = "auto () -> decltype(a)"; + HI.ReturnType = "int"; + HI.Parameters.emplace(); + }}, }; // Create a tiny index, so tests above can verify documentation is fetched.