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 @@ -345,8 +345,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 @@ -1501,6 +1501,40 @@ 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"; + }}, }; // Create a tiny index, so tests above can verify documentation is fetched.