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 @@ -27,6 +27,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/OperationKinds.h" #include "clang/AST/PrettyPrinter.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Type.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Specifiers.h" @@ -550,29 +551,6 @@ return HI; } -/// Generate a \p Hover object given the type \p T. -HoverInfo getHoverContents(QualType T, ASTContext &ASTCtx, - const SymbolIndex *Index, - bool SuppressScope = false) { - HoverInfo HI; - - if (const auto *D = T->getAsTagDecl()) { - HI.Name = printName(ASTCtx, *D); - HI.Kind = index::getSymbolInfo(D).Kind; - - const auto *CommentD = getDeclForComment(D); - HI.Documentation = getDeclComment(ASTCtx, *CommentD); - enhanceFromIndex(HI, *CommentD, Index); - } else { - // Builtin types - auto Policy = printingPolicyForDecls(ASTCtx.getPrintingPolicy()); - Policy.SuppressTagKeyword = true; - Policy.SuppressScope = SuppressScope; - HI.Name = T.getAsString(Policy); - } - return HI; -} - /// Generate a \p Hover object given the macro \p MacroDecl. HoverInfo getHoverContents(const DefinedMacro &Macro, ParsedAST &AST) { HoverInfo HI; @@ -608,6 +586,79 @@ return HI; } +llvm::Optional getThisExprHoverContents(const CXXThisExpr *CTE, + ASTContext &ASTCtx, + const SymbolIndex *Index) { + QualType OriginThisType = CTE->getType()->getPointeeType(); + QualType ClassType = declaredType(OriginThisType->getAsTagDecl()); + // For partial specialization class, origin `this` pointee type will be + // parsed as `InjectedClassNameType`, which will ouput template arguments + // like "type-parameter-0-0". So we retrieve user written class type in this + // case. + QualType PrettyThisType = ASTCtx.getPointerType( + QualType(ClassType.getTypePtr(), OriginThisType.getCVRQualifiers())); + + auto Policy = printingPolicyForDecls(ASTCtx.getPrintingPolicy()); + Policy.SuppressTagKeyword = true; + Policy.SuppressScope = true; + HoverInfo HI; + HI.Name = "this"; + HI.Definition = PrettyThisType.getAsString(Policy); + return HI; +} + +/// Generate a HoverInfo object given the deduced type \p QT +HoverInfo getDeducedTypeHoverContents(QualType QT) { + HoverInfo HI; + HI.Kind = index::SymbolKind::TypeAlias; + + if (QT->isUndeducedAutoType()) { + HI.Definition = "/* not deduced */"; + } else { + CXXRecordDecl *D = QT->getAsCXXRecordDecl(); + if (D && D->isLambda()) + HI.Definition = "(lambda)"; + else + HI.Definition = QT.getAsString(); + } + + return HI; +} + +/// Visit the relevant nodes to display hover information over an "auto" +/// keyword or "decltype()" expression. Only types not returned by +// getDeducedType are handled. +class ExtraAutoTypeHoverVisitor + : public RecursiveASTVisitor { + SourceLocation SearchedLocation; + +public: + ExtraAutoTypeHoverVisitor(SourceLocation SearchedLocation) + : SearchedLocation(SearchedLocation) {} + + bool VisitDeclaratorDecl(DeclaratorDecl *D) { + if (!D->getTypeSourceInfo() || + D->getTypeSourceInfo()->getTypeLoc().getBeginLoc() != SearchedLocation) + return true; + + if (isa(D)) { + // Handle template and [](auto){} + // FIXME: Until we have something useful to display, just display nothing + } else { + // At this point only a few cases are left: + // - decomposition of arrays + // - undeduced auto in a declaration + // Just give the declaration type to getDeducedTypeHoverContents + // which will handle it + HI = getDeducedTypeHoverContents(D->getType()); + } + + return false; + } + + llvm::Optional HI; +}; + bool isLiteral(const Expr *E) { // Unfortunately there's no common base Literal classes inherits from // (apart from Expr), therefore these exclusions. @@ -642,16 +693,7 @@ HoverInfo HI; // For `this` expr we currently generate hover with pointee type. if (const CXXThisExpr *CTE = dyn_cast(E)) { - QualType OriginThisType = CTE->getType()->getPointeeType(); - QualType ClassType = declaredType(OriginThisType->getAsTagDecl()); - // For partial specialization class, origin `this` pointee type will be - // parsed as `InjectedClassNameType`, which will ouput template arguments - // like "type-parameter-0-0". So we retrieve user written class type in this - // case. - QualType PrettyThisType = AST.getASTContext().getPointerType( - QualType(ClassType.getTypePtr(), OriginThisType.getCVRQualifiers())); - return getHoverContents(PrettyThisType, AST.getASTContext(), Index, - /*SuppressScope=*/true); + return getThisExprHoverContents(CTE, AST.getASTContext(), Index); } // For expressions we currently print the type and the value, iff it is // evaluatable. @@ -849,10 +891,22 @@ } } else if (Tok.kind() == tok::kw_auto || Tok.kind() == tok::kw_decltype) { if (auto Deduced = getDeducedType(AST.getASTContext(), Tok.location())) { - HI = getHoverContents(*Deduced, AST.getASTContext(), Index); - HighlightRange = Tok.range(SM).toCharRange(SM); - break; + HI = getDeducedTypeHoverContents(*Deduced); + } else { + ExtraAutoTypeHoverVisitor V(Tok.location()); + V.TraverseAST(AST.getASTContext()); + HI = V.HI; } + + // If we can't find interesting hover information for this + // auto/decltype keyword, return nothing to avoid showing + // irrelevant or incorrect informations. + if (!HI) + return llvm::None; + + HI->Name = tok::getTokenName(Tok.kind()); + HighlightRange = Tok.range(SM).toCharRange(SM); + break; } } 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 @@ -379,6 +379,42 @@ HI.Definition = "class X {}"; }}, + // auto on structured bindings + {R"cpp( + void foo() { + int arr[2]; + [[au^to]] [x, y] = arr; + } + )cpp", + [](HoverInfo &HI) { + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int[2]"; + }}, + // auto on structured bindings + {R"cpp( + void foo() { + struct S { int x; float y; }; + [[au^to]] [x, y] = S(); + } + )cpp", + [](HoverInfo &HI) { + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "struct S"; + }}, + // undeduced auto + {R"cpp( + template + void foo() { + [[au^to]] x = T{}; + } + )cpp", + [](HoverInfo &HI) { + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "/* not deduced */"; + }}, // auto on lambda {R"cpp( void foo() { @@ -386,8 +422,9 @@ } )cpp", [](HoverInfo &HI) { - HI.Name = "(lambda)"; - HI.Kind = index::SymbolKind::Class; + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "(lambda)"; }}, // auto on template instantiation {R"cpp( @@ -397,8 +434,9 @@ } )cpp", [](HoverInfo &HI) { - HI.Name = "Foo"; - HI.Kind = index::SymbolKind::Class; + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "class Foo"; }}, // auto on specialized template {R"cpp( @@ -409,8 +447,9 @@ } )cpp", [](HoverInfo &HI) { - HI.Name = "Foo"; - HI.Kind = index::SymbolKind::Class; + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "class Foo"; }}, // macro @@ -582,8 +621,9 @@ } )cpp", [](HoverInfo &HI) { - HI.Name = "Foo"; - HI.Kind = index::SymbolKind::Class; + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "class Foo"; }}, {// Falls back to primary template, when the type is not instantiated. R"cpp( @@ -955,12 +995,9 @@ llvm::StringRef Tests[] = { "^int main() {}", "void foo() {^}", - R"cpp(// structured binding. Not supported yet - struct Bar {}; - void foo() { - Bar a[2]; - ^auto [x,y] = a; - } + "decltype(au^to) x = 0;", + R"cpp(// Lambda auto parameter. Nothing (Not useful). + auto lamb = [](a^uto){}; )cpp", R"cpp(// Template auto parameter. Nothing (Not useful). template @@ -1545,9 +1582,9 @@ } )cpp", [](HoverInfo &HI) { - HI.Name = "int"; - // FIXME: Should be Builtin/Integral. - HI.Kind = index::SymbolKind::Unknown; + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int"; }}, { R"cpp(// Simple initialization with const auto @@ -1555,14 +1592,22 @@ const ^[[auto]] i = 1; } )cpp", - [](HoverInfo &HI) { HI.Name = "int"; }}, + [](HoverInfo &HI) { + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int"; + }}, { R"cpp(// Simple initialization with const auto& void foo() { const ^[[auto]]& i = 1; } )cpp", - [](HoverInfo &HI) { HI.Name = "int"; }}, + [](HoverInfo &HI) { + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int"; + }}, { R"cpp(// Simple initialization with auto& void foo() { @@ -1570,7 +1615,11 @@ ^[[auto]]& i = x; } )cpp", - [](HoverInfo &HI) { HI.Name = "int"; }}, + [](HoverInfo &HI) { + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int"; + }}, { R"cpp(// Simple initialization with auto* void foo() { @@ -1578,7 +1627,23 @@ ^[[auto]]* i = &a; } )cpp", - [](HoverInfo &HI) { HI.Name = "int"; }}, + [](HoverInfo &HI) { + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int"; + }}, + { + R"cpp(// Simple initialization with auto from pointer + void foo() { + int a = 1; + ^[[auto]] i = &a; + } + )cpp", + [](HoverInfo &HI) { + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int *"; + }}, { R"cpp(// Auto with initializer list. namespace std @@ -1591,8 +1656,9 @@ } )cpp", [](HoverInfo &HI) { - HI.Name = "initializer_list"; - HI.Kind = index::SymbolKind::Class; + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "class std::initializer_list"; }}, { R"cpp(// User defined conversion to auto @@ -1600,14 +1666,22 @@ operator ^[[auto]]() const { return 10; } }; )cpp", - [](HoverInfo &HI) { HI.Name = "int"; }}, + [](HoverInfo &HI) { + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int"; + }}, { R"cpp(// Simple initialization with decltype(auto) void foo() { ^[[decltype]](auto) i = 1; } )cpp", - [](HoverInfo &HI) { HI.Name = "int"; }}, + [](HoverInfo &HI) { + HI.Name = "decltype"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int"; + }}, { R"cpp(// Simple initialization with const decltype(auto) void foo() { @@ -1615,7 +1689,11 @@ ^[[decltype]](auto) i = j; } )cpp", - [](HoverInfo &HI) { HI.Name = "const int"; }}, + [](HoverInfo &HI) { + HI.Name = "decltype"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "const int"; + }}, { R"cpp(// Simple initialization with const& decltype(auto) void foo() { @@ -1624,7 +1702,11 @@ ^[[decltype]](auto) i = j; } )cpp", - [](HoverInfo &HI) { HI.Name = "const int &"; }}, + [](HoverInfo &HI) { + HI.Name = "decltype"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "const int &"; + }}, { R"cpp(// Simple initialization with & decltype(auto) void foo() { @@ -1633,14 +1715,22 @@ ^[[decltype]](auto) i = j; } )cpp", - [](HoverInfo &HI) { HI.Name = "int &"; }}, + [](HoverInfo &HI) { + HI.Name = "decltype"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int &"; + }}, { R"cpp(// simple trailing return type ^[[auto]] main() -> int { return 0; } )cpp", - [](HoverInfo &HI) { HI.Name = "int"; }}, + [](HoverInfo &HI) { + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int"; + }}, { R"cpp(// auto function return with trailing type struct Bar {}; @@ -1649,9 +1739,9 @@ } )cpp", [](HoverInfo &HI) { - HI.Name = "Bar"; - HI.Kind = index::SymbolKind::Struct; - HI.Documentation = "auto function return with trailing type"; + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "struct Bar"; }}, { R"cpp(// trailing return type @@ -1661,9 +1751,9 @@ } )cpp", [](HoverInfo &HI) { - HI.Name = "Bar"; - HI.Kind = index::SymbolKind::Struct; - HI.Documentation = "trailing return type"; + HI.Name = "decltype"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "struct Bar"; }}, { R"cpp(// auto in function return @@ -1673,9 +1763,9 @@ } )cpp", [](HoverInfo &HI) { - HI.Name = "Bar"; - HI.Kind = index::SymbolKind::Struct; - HI.Documentation = "auto in function return"; + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "struct Bar"; }}, { R"cpp(// auto& in function return @@ -1686,9 +1776,9 @@ } )cpp", [](HoverInfo &HI) { - HI.Name = "Bar"; - HI.Kind = index::SymbolKind::Struct; - HI.Documentation = "auto& in function return"; + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "struct Bar"; }}, { R"cpp(// auto* in function return @@ -1699,9 +1789,9 @@ } )cpp", [](HoverInfo &HI) { - HI.Name = "Bar"; - HI.Kind = index::SymbolKind::Struct; - HI.Documentation = "auto* in function return"; + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "struct Bar"; }}, { R"cpp(// const auto& in function return @@ -1712,9 +1802,9 @@ } )cpp", [](HoverInfo &HI) { - HI.Name = "Bar"; - HI.Kind = index::SymbolKind::Struct; - HI.Documentation = "const auto& in function return"; + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "struct Bar"; }}, { R"cpp(// decltype(auto) in function return @@ -1724,9 +1814,9 @@ } )cpp", [](HoverInfo &HI) { - HI.Name = "Bar"; - HI.Kind = index::SymbolKind::Struct; - HI.Documentation = "decltype(auto) in function return"; + HI.Name = "decltype"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "struct Bar"; }}, { R"cpp(// decltype(auto) reference in function return @@ -1735,7 +1825,11 @@ return (a); } )cpp", - [](HoverInfo &HI) { HI.Name = "int &"; }}, + [](HoverInfo &HI) { + HI.Name = "decltype"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int &"; + }}, { R"cpp(// decltype lvalue reference void foo() { @@ -1743,7 +1837,11 @@ ^[[decltype]](I) J = I; } )cpp", - [](HoverInfo &HI) { HI.Name = "int"; }}, + [](HoverInfo &HI) { + HI.Name = "decltype"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int"; + }}, { R"cpp(// decltype lvalue reference void foo() { @@ -1752,7 +1850,11 @@ ^[[decltype]](K) J = I; } )cpp", - [](HoverInfo &HI) { HI.Name = "int &"; }}, + [](HoverInfo &HI) { + HI.Name = "decltype"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int &"; + }}, { R"cpp(// decltype lvalue reference parenthesis void foo() { @@ -1760,7 +1862,11 @@ ^[[decltype]]((I)) J = I; } )cpp", - [](HoverInfo &HI) { HI.Name = "int &"; }}, + [](HoverInfo &HI) { + HI.Name = "decltype"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int &"; + }}, { R"cpp(// decltype rvalue reference void foo() { @@ -1768,7 +1874,11 @@ ^[[decltype]](static_cast(I)) J = static_cast(I); } )cpp", - [](HoverInfo &HI) { HI.Name = "int &&"; }}, + [](HoverInfo &HI) { + HI.Name = "decltype"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int &&"; + }}, { R"cpp(// decltype rvalue reference function call int && bar(); @@ -1777,7 +1887,11 @@ ^[[decltype]](bar()) J = bar(); } )cpp", - [](HoverInfo &HI) { HI.Name = "int &&"; }}, + [](HoverInfo &HI) { + HI.Name = "decltype"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int &&"; + }}, { R"cpp(// decltype of function with trailing return type. struct Bar {}; @@ -1789,10 +1903,9 @@ } )cpp", [](HoverInfo &HI) { - HI.Name = "Bar"; - HI.Kind = index::SymbolKind::Struct; - HI.Documentation = - "decltype of function with trailing return type."; + HI.Name = "decltype"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "struct Bar"; }}, { R"cpp(// decltype of var with decltype. @@ -1802,13 +1915,21 @@ ^[[decltype]](J) K = J; } )cpp", - [](HoverInfo &HI) { HI.Name = "int"; }}, + [](HoverInfo &HI) { + HI.Name = "decltype"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int"; + }}, { R"cpp(// More complicated structured types. int bar(); ^[[auto]] (*foo)() = bar; )cpp", - [](HoverInfo &HI) { HI.Name = "int"; }}, + [](HoverInfo &HI) { + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int"; + }}, { R"cpp(// Should not crash when evaluating the initializer. struct Test {}; @@ -1827,7 +1948,11 @@ typedef int int_type; ^[[auto]] x = int_type(); )cpp", - [](HoverInfo &HI) { HI.Name = "int"; }}, + [](HoverInfo &HI) { + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "int"; + }}, { R"cpp(// auto on alias struct cls {}; @@ -1835,9 +1960,9 @@ ^[[auto]] y = cls_type(); )cpp", [](HoverInfo &HI) { - HI.Name = "cls"; - HI.Kind = index::SymbolKind::Struct; - HI.Documentation = "auto on alias"; + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "struct cls"; }}, { R"cpp(// auto on alias @@ -1846,9 +1971,45 @@ ^[[auto]] z = templ(); )cpp", [](HoverInfo &HI) { - HI.Name = "templ"; - HI.Kind = index::SymbolKind::Struct; - HI.Documentation = "auto on alias"; + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "struct templ"; + }}, + { + R"cpp(// Undeduced auto declaration + template + void foo() { + ^[[auto]] x = T(); + } + )cpp", + [](HoverInfo &HI) { + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "/* not deduced */"; + }}, + { + R"cpp(// Undeduced auto return type + template + ^[[auto]] foo() { + return T(); + } + )cpp", + [](HoverInfo &HI) { + HI.Name = "auto"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "/* not deduced */"; + }}, + { + R"cpp(// Undeduced decltype(auto) return type + template + ^[[decltype]](auto) foo() { + return T(); + } + )cpp", + [](HoverInfo &HI) { + HI.Name = "decltype"; + HI.Kind = index::SymbolKind::TypeAlias; + HI.Definition = "/* not deduced */"; }}, { R"cpp(// should not crash. @@ -2030,7 +2191,10 @@ }; } )cpp", - [](HoverInfo &HI) { HI.Name = "Foo *"; }}, + [](HoverInfo &HI) { + HI.Name = "this"; + HI.Definition = "Foo *"; + }}, { R"cpp(// this expr for template class namespace ns { @@ -2042,7 +2206,10 @@ }; } )cpp", - [](HoverInfo &HI) { HI.Name = "const Foo *"; }}, + [](HoverInfo &HI) { + HI.Name = "this"; + HI.Definition = "const Foo *"; + }}, { R"cpp(// this expr for specialization class namespace ns { @@ -2055,7 +2222,10 @@ }; } )cpp", - [](HoverInfo &HI) { HI.Name = "Foo *"; }}, + [](HoverInfo &HI) { + HI.Name = "this"; + HI.Definition = "Foo *"; + }}, { R"cpp(// this expr for partial specialization struct namespace ns { @@ -2068,7 +2238,10 @@ }; } )cpp", - [](HoverInfo &HI) { HI.Name = "const Foo *"; }}, + [](HoverInfo &HI) { + HI.Name = "this"; + HI.Definition = "const Foo *"; + }}, }; // Create a tiny index, so tests above can verify documentation is fetched. @@ -2119,7 +2292,7 @@ Annotations T(R"cpp( template class X {}; void foo() { - au^to t = X(); + auto t = X(); X^ w; (void)w; })cpp"); @@ -2150,10 +2323,9 @@ // doc template T baz; void foo() { - au^to t = X(); X^(); b^ar(); - au^to T = ba^z>; + auto T = ba^z>; ba^z = 0; })cpp");