#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"
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;
return HI;
}
+llvm::Optional<HoverInfo> getThisExprHoverContents(const CXXThisExpr *CTE,
+ ASTContext &ASTCtx) {
+ 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, const syntax::Token &Tok,
+ ASTContext &ASTCtx,
+ const SymbolIndex *Index) {
+ HoverInfo HI;
+ // FIXME: distinguish decltype(auto) vs decltype(expr)
+ HI.Name = tok::getTokenName(Tok.kind());
+ HI.Kind = index::SymbolKind::TypeAlias;
+
+ auto PP = printingPolicyForDecls(ASTCtx.getLangOpts());
+
+ if (QT->isUndeducedAutoType()) {
+ HI.Definition = "/* not deduced */";
+ } else {
+ HI.Definition = QT.getAsString(PP);
+
+ if (const auto *D = QT->getAsTagDecl()) {
+ const auto *CommentD = getDeclForComment(D);
+ HI.Documentation = getDeclComment(ASTCtx, *CommentD);
+ enhanceFromIndex(HI, *CommentD, Index);
+ }
+ }
+
+ return HI;
+}
+
bool isLiteral(const Expr *E) {
// Unfortunately there's no common base Literal classes inherits from
// (apart from Expr), therefore these exclusions.
HoverInfo HI;
// For `this` expr we currently generate hover with pointee type.
- if (const CXXThisExpr *CTE = dyn_cast<CXXThisExpr>(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);
- }
+ if (const CXXThisExpr *CTE = dyn_cast<CXXThisExpr>(E))
+ return getThisExprHoverContents(CTE, AST.getASTContext());
// For expressions we currently print the type and the value, iff it is
// evaluatable.
if (auto Val = printExprValue(E, AST.getASTContext())) {
}
} 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);
+ HI = getDeducedTypeHoverContents(*Deduced, Tok, AST.getASTContext(),
+ Index);
HighlightRange = Tok.range(SM).toCharRange(SM);
break;
}
+
+ // If we can't find interesting hover information for this
+ // auto/decltype keyword, return nothing to avoid showing
+ // irrelevant or incorrect informations.
+ return llvm::None;
}
}
HI.Definition = "class X {}";
}},
+ // 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<typename T>
+ 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() {
}
)cpp",
[](HoverInfo &HI) {
- HI.Name = "(lambda)";
- HI.Kind = index::SymbolKind::Class;
+ HI.Name = "auto";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "class(lambda)";
}},
// auto on template instantiation
{R"cpp(
}
)cpp",
[](HoverInfo &HI) {
- HI.Name = "Foo<int>";
- HI.Kind = index::SymbolKind::Class;
+ HI.Name = "auto";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "class Foo<int>";
}},
// auto on specialized template
{R"cpp(
}
)cpp",
[](HoverInfo &HI) {
- HI.Name = "Foo<int>";
- HI.Kind = index::SymbolKind::Class;
+ HI.Name = "auto";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "class Foo<int>";
}},
// macro
}
)cpp",
[](HoverInfo &HI) {
- HI.Name = "Foo<X>";
- HI.Kind = index::SymbolKind::Class;
+ HI.Name = "auto";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "class Foo<class X>";
}},
{// Falls back to primary template, when the type is not instantiated.
R"cpp(
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;
- }
- )cpp",
- R"cpp(// Template auto parameter. Nothing (Not useful).
- template<a^uto T>
- void func() {
- }
- void foo() {
- func<1>();
- }
+ // FIXME: "decltype(auto)" should be a single hover
+ "decltype(au^to) x = 0;",
+ // FIXME: not supported yet
+ R"cpp(// Lambda auto parameter
+ auto lamb = [](a^uto){};
)cpp",
R"cpp(// non-named decls don't get hover. Don't crash!
^static_assert(1, "");
}
)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
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() {
^[[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() {
^[[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
}
)cpp",
[](HoverInfo &HI) {
- HI.Name = "initializer_list<int>";
- HI.Kind = index::SymbolKind::Class;
+ HI.Name = "auto";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "class std::initializer_list<int>";
}},
{
R"cpp(// User defined conversion to auto
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() {
^[[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() {
^[[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() {
^[[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 {};
}
)cpp",
[](HoverInfo &HI) {
- HI.Name = "Bar";
- HI.Kind = index::SymbolKind::Struct;
+ HI.Name = "auto";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "struct Bar";
HI.Documentation = "auto function return with trailing type";
}},
{
}
)cpp",
[](HoverInfo &HI) {
- HI.Name = "Bar";
- HI.Kind = index::SymbolKind::Struct;
+ HI.Name = "decltype";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "struct Bar";
HI.Documentation = "trailing return type";
}},
{
}
)cpp",
[](HoverInfo &HI) {
- HI.Name = "Bar";
- HI.Kind = index::SymbolKind::Struct;
+ HI.Name = "auto";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "struct Bar";
HI.Documentation = "auto in function return";
}},
{
}
)cpp",
[](HoverInfo &HI) {
- HI.Name = "Bar";
- HI.Kind = index::SymbolKind::Struct;
+ HI.Name = "auto";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "struct Bar";
HI.Documentation = "auto& in function return";
}},
{
}
)cpp",
[](HoverInfo &HI) {
- HI.Name = "Bar";
- HI.Kind = index::SymbolKind::Struct;
+ HI.Name = "auto";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "struct Bar";
HI.Documentation = "auto* in function return";
}},
{
}
)cpp",
[](HoverInfo &HI) {
- HI.Name = "Bar";
- HI.Kind = index::SymbolKind::Struct;
+ HI.Name = "auto";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "struct Bar";
HI.Documentation = "const auto& in function return";
}},
{
}
)cpp",
[](HoverInfo &HI) {
- HI.Name = "Bar";
- HI.Kind = index::SymbolKind::Struct;
+ HI.Name = "decltype";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "struct Bar";
HI.Documentation = "decltype(auto) in function return";
}},
{
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() {
^[[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() {
^[[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() {
^[[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() {
^[[decltype]](static_cast<int&&>(I)) J = static_cast<int&&>(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();
^[[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 {};
}
)cpp",
[](HoverInfo &HI) {
- HI.Name = "Bar";
- HI.Kind = index::SymbolKind::Struct;
+ HI.Name = "decltype";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "struct Bar";
HI.Documentation =
"decltype of function with trailing return type.";
}},
^[[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(// decltype of dependent type
+ template <typename T>
+ struct X {
+ using Y = ^[[decltype]](T::Z);
+ };
+ )cpp",
+ [](HoverInfo &HI) {
+ HI.Name = "decltype";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "<dependent type>";
+ }},
{
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 {};
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 {};
^[[auto]] y = cls_type();
)cpp",
[](HoverInfo &HI) {
- HI.Name = "cls";
- HI.Kind = index::SymbolKind::Struct;
+ HI.Name = "auto";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "struct cls";
HI.Documentation = "auto on alias";
}},
{
^[[auto]] z = templ<int>();
)cpp",
[](HoverInfo &HI) {
- HI.Name = "templ<int>";
- HI.Kind = index::SymbolKind::Struct;
+ HI.Name = "auto";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "struct templ<int>";
HI.Documentation = "auto on alias";
}},
{
+ R"cpp(// Undeduced auto declaration
+ template<typename T>
+ 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<typename T>
+ ^[[auto]] foo() {
+ return T();
+ }
+ )cpp",
+ [](HoverInfo &HI) {
+ HI.Name = "auto";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "/* not deduced */";
+ }},
+ {
+ R"cpp(// Template auto parameter
+ template<[[a^uto]] T>
+ void func() {
+ }
+ )cpp",
+ [](HoverInfo &HI) {
+ // FIXME: not sure this is what we want, but this
+ // is what we currently get with getDeducedType
+ HI.Name = "auto";
+ HI.Kind = index::SymbolKind::TypeAlias;
+ HI.Definition = "/* not deduced */";
+ }},
+ {
+ R"cpp(// Undeduced decltype(auto) return type
+ template<typename T>
+ ^[[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.
template <class T> struct cls {
int method();
};
}
)cpp",
- [](HoverInfo &HI) { HI.Name = "Foo *"; }},
+ [](HoverInfo &HI) {
+ HI.Name = "this";
+ HI.Definition = "Foo *";
+ }},
{
R"cpp(// this expr for template class
namespace ns {
};
}
)cpp",
- [](HoverInfo &HI) { HI.Name = "const Foo<T> *"; }},
+ [](HoverInfo &HI) {
+ HI.Name = "this";
+ HI.Definition = "const Foo<T> *";
+ }},
{
R"cpp(// this expr for specialization class
namespace ns {
};
}
)cpp",
- [](HoverInfo &HI) { HI.Name = "Foo<int> *"; }},
+ [](HoverInfo &HI) {
+ HI.Name = "this";
+ HI.Definition = "Foo<int> *";
+ }},
{
R"cpp(// this expr for partial specialization struct
namespace ns {
};
}
)cpp",
- [](HoverInfo &HI) { HI.Name = "const Foo<int, F> *"; }},
+ [](HoverInfo &HI) {
+ HI.Name = "this";
+ HI.Definition = "const Foo<int, F> *";
+ }},
};
// Create a tiny index, so tests above can verify documentation is fetched.
Annotations T(R"cpp(
template <typename T> class X {};
void foo() {
- au^to t = X<int>();
+ auto t = X<int>();
X^<int> w;
(void)w;
})cpp");
HI.NamespaceScope = "ns::";
HI.Definition = "ret_type foo(params) {}";
},
- R"(function foo
-
-→ ret_type
-Parameters:
--
-- type
-- type foo
-- type foo = default
-
-// In namespace ns
-ret_type foo(params) {})",
+ "function foo\n"
+ "\n"
+ "→ ret_type\n"
+ "Parameters:\n"
+ "- \n"
+ "- type\n"
+ "- type foo\n"
+ "- type foo = default\n"
+ "\n"
+ "// In namespace ns\n"
+ "ret_type foo(params) {}",
},
{
[](HoverInfo &HI) {
HI.Value = "val";
HI.Definition = "def";
- llvm::StringRef ExpectedMarkdown = R"md(### variable `foo`
-
----
-Value = `val`
-
----
-```cpp
-def
-```)md";
+ llvm::StringRef ExpectedMarkdown = //
+ "### variable `foo` \n"
+ "\n"
+ "---\n"
+ "Value = `val` \n"
+ "\n"
+ "---\n"
+ "```cpp\n"
+ "def\n"
+ "```";
EXPECT_EQ(HI.present().asMarkdown(), ExpectedMarkdown);
llvm::StringRef ExpectedPlaintext = R"pt(variable foo