forked from OSchip/llvm-project
[clangd] Introduce a structured hover response
Summary: Change ClangdServer layer to output a structured response for Hover, which can be rendered by client according to their needs. Reviewers: sammccall, ilya-biryukov Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D61497 llvm-svn: 361803
This commit is contained in:
parent
173a68f1fb
commit
c6578eefdd
|
@ -842,7 +842,20 @@ void ClangdLSPServer::onDocumentHighlight(
|
||||||
void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params,
|
void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params,
|
||||||
Callback<llvm::Optional<Hover>> Reply) {
|
Callback<llvm::Optional<Hover>> Reply) {
|
||||||
Server->findHover(Params.textDocument.uri.file(), Params.position,
|
Server->findHover(Params.textDocument.uri.file(), Params.position,
|
||||||
std::move(Reply));
|
Bind(
|
||||||
|
[](decltype(Reply) Reply,
|
||||||
|
llvm::Expected<llvm::Optional<HoverInfo>> HIorErr) {
|
||||||
|
if (!HIorErr)
|
||||||
|
return Reply(HIorErr.takeError());
|
||||||
|
const auto &HI = HIorErr.get();
|
||||||
|
if (!HI)
|
||||||
|
return Reply(llvm::None);
|
||||||
|
Hover H;
|
||||||
|
H.range = HI->SymRange;
|
||||||
|
H.contents = HI->render();
|
||||||
|
return Reply(H);
|
||||||
|
},
|
||||||
|
std::move(Reply)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangdLSPServer::onTypeHierarchy(
|
void ClangdLSPServer::onTypeHierarchy(
|
||||||
|
|
|
@ -461,15 +461,18 @@ void ClangdServer::findDocumentHighlights(
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangdServer::findHover(PathRef File, Position Pos,
|
void ClangdServer::findHover(PathRef File, Position Pos,
|
||||||
Callback<llvm::Optional<Hover>> CB) {
|
Callback<llvm::Optional<HoverInfo>> CB) {
|
||||||
auto Action = [Pos](Callback<llvm::Optional<Hover>> CB,
|
auto Action = [Pos](Callback<llvm::Optional<HoverInfo>> CB, Path File,
|
||||||
llvm::Expected<InputsAndAST> InpAST) {
|
llvm::Expected<InputsAndAST> InpAST) {
|
||||||
if (!InpAST)
|
if (!InpAST)
|
||||||
return CB(InpAST.takeError());
|
return CB(InpAST.takeError());
|
||||||
CB(clangd::getHover(InpAST->AST, Pos));
|
format::FormatStyle Style = getFormatStyleForFile(
|
||||||
|
File, InpAST->Inputs.Contents, InpAST->Inputs.FS.get());
|
||||||
|
CB(clangd::getHover(InpAST->AST, Pos, std::move(Style)));
|
||||||
};
|
};
|
||||||
|
|
||||||
WorkScheduler.runWithAST("Hover", File, Bind(Action, std::move(CB)));
|
WorkScheduler.runWithAST("Hover", File,
|
||||||
|
Bind(Action, std::move(CB), File.str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve,
|
void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve,
|
||||||
|
|
|
@ -186,7 +186,7 @@ public:
|
||||||
|
|
||||||
/// Get code hover for a given position.
|
/// Get code hover for a given position.
|
||||||
void findHover(PathRef File, Position Pos,
|
void findHover(PathRef File, Position Pos,
|
||||||
Callback<llvm::Optional<Hover>> CB);
|
Callback<llvm::Optional<HoverInfo>> CB);
|
||||||
|
|
||||||
/// Get information about type hierarchy for a given position.
|
/// Get information about type hierarchy for a given position.
|
||||||
void typeHierarchy(PathRef File, Position Pos, int Resolve,
|
void typeHierarchy(PathRef File, Position Pos, int Resolve,
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "llvm/ADT/Hashing.h"
|
#include "llvm/ADT/Hashing.h"
|
||||||
#include "llvm/ADT/SmallString.h"
|
#include "llvm/ADT/SmallString.h"
|
||||||
#include "llvm/ADT/StringSwitch.h"
|
#include "llvm/ADT/StringSwitch.h"
|
||||||
|
#include "llvm/Support/ErrorHandling.h"
|
||||||
#include "llvm/Support/Format.h"
|
#include "llvm/Support/Format.h"
|
||||||
#include "llvm/Support/FormatVariadic.h"
|
#include "llvm/Support/FormatVariadic.h"
|
||||||
#include "llvm/Support/JSON.h"
|
#include "llvm/Support/JSON.h"
|
||||||
|
|
|
@ -7,21 +7,37 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
#include "XRefs.h"
|
#include "XRefs.h"
|
||||||
#include "AST.h"
|
#include "AST.h"
|
||||||
|
#include "CodeCompletionStrings.h"
|
||||||
#include "FindSymbols.h"
|
#include "FindSymbols.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
#include "Protocol.h"
|
||||||
#include "SourceCode.h"
|
#include "SourceCode.h"
|
||||||
#include "URI.h"
|
#include "URI.h"
|
||||||
#include "index/Merge.h"
|
#include "index/Merge.h"
|
||||||
#include "index/SymbolCollector.h"
|
#include "index/SymbolCollector.h"
|
||||||
#include "index/SymbolLocation.h"
|
#include "index/SymbolLocation.h"
|
||||||
|
#include "clang/AST/ASTContext.h"
|
||||||
|
#include "clang/AST/Decl.h"
|
||||||
#include "clang/AST/DeclTemplate.h"
|
#include "clang/AST/DeclTemplate.h"
|
||||||
|
#include "clang/AST/PrettyPrinter.h"
|
||||||
#include "clang/AST/RecursiveASTVisitor.h"
|
#include "clang/AST/RecursiveASTVisitor.h"
|
||||||
#include "clang/AST/Type.h"
|
#include "clang/AST/Type.h"
|
||||||
|
#include "clang/Basic/LLVM.h"
|
||||||
|
#include "clang/Basic/SourceLocation.h"
|
||||||
|
#include "clang/Basic/SourceManager.h"
|
||||||
#include "clang/Index/IndexDataConsumer.h"
|
#include "clang/Index/IndexDataConsumer.h"
|
||||||
#include "clang/Index/IndexSymbol.h"
|
#include "clang/Index/IndexSymbol.h"
|
||||||
#include "clang/Index/IndexingAction.h"
|
#include "clang/Index/IndexingAction.h"
|
||||||
#include "clang/Index/USRGeneration.h"
|
#include "clang/Index/USRGeneration.h"
|
||||||
|
#include "llvm/ADT/ArrayRef.h"
|
||||||
|
#include "llvm/ADT/None.h"
|
||||||
|
#include "llvm/ADT/STLExtras.h"
|
||||||
|
#include "llvm/ADT/StringExtras.h"
|
||||||
|
#include "llvm/ADT/StringRef.h"
|
||||||
|
#include "llvm/Support/Casting.h"
|
||||||
|
#include "llvm/Support/FormatVariadic.h"
|
||||||
#include "llvm/Support/Path.h"
|
#include "llvm/Support/Path.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
namespace clangd {
|
namespace clangd {
|
||||||
|
@ -241,17 +257,17 @@ IdentifiedSymbol getSymbolAtPosition(ParsedAST &AST, SourceLocation Pos) {
|
||||||
return {DeclMacrosFinder.getFoundDecls(), DeclMacrosFinder.takeMacroInfos()};
|
return {DeclMacrosFinder.getFoundDecls(), DeclMacrosFinder.takeMacroInfos()};
|
||||||
}
|
}
|
||||||
|
|
||||||
Range getTokenRange(ParsedAST &AST, SourceLocation TokLoc) {
|
Range getTokenRange(ASTContext &AST, SourceLocation TokLoc) {
|
||||||
const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
|
const SourceManager &SourceMgr = AST.getSourceManager();
|
||||||
SourceLocation LocEnd = Lexer::getLocForEndOfToken(
|
SourceLocation LocEnd =
|
||||||
TokLoc, 0, SourceMgr, AST.getASTContext().getLangOpts());
|
Lexer::getLocForEndOfToken(TokLoc, 0, SourceMgr, AST.getLangOpts());
|
||||||
return {sourceLocToPosition(SourceMgr, TokLoc),
|
return {sourceLocToPosition(SourceMgr, TokLoc),
|
||||||
sourceLocToPosition(SourceMgr, LocEnd)};
|
sourceLocToPosition(SourceMgr, LocEnd)};
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Optional<Location> makeLocation(ParsedAST &AST, SourceLocation TokLoc,
|
llvm::Optional<Location> makeLocation(ASTContext &AST, SourceLocation TokLoc,
|
||||||
llvm::StringRef TUPath) {
|
llvm::StringRef TUPath) {
|
||||||
const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
|
const SourceManager &SourceMgr = AST.getSourceManager();
|
||||||
const FileEntry *F = SourceMgr.getFileEntryForID(SourceMgr.getFileID(TokLoc));
|
const FileEntry *F = SourceMgr.getFileEntryForID(SourceMgr.getFileID(TokLoc));
|
||||||
if (!F)
|
if (!F)
|
||||||
return None;
|
return None;
|
||||||
|
@ -299,8 +315,8 @@ std::vector<LocatedSymbol> locateSymbolAt(ParsedAST &AST, Position Pos,
|
||||||
// As a consequence, there's no need to look them up in the index either.
|
// As a consequence, there's no need to look them up in the index either.
|
||||||
std::vector<LocatedSymbol> Result;
|
std::vector<LocatedSymbol> Result;
|
||||||
for (auto M : Symbols.Macros) {
|
for (auto M : Symbols.Macros) {
|
||||||
if (auto Loc =
|
if (auto Loc = makeLocation(AST.getASTContext(), M.Info->getDefinitionLoc(),
|
||||||
makeLocation(AST, M.Info->getDefinitionLoc(), *MainFilePath)) {
|
*MainFilePath)) {
|
||||||
LocatedSymbol Macro;
|
LocatedSymbol Macro;
|
||||||
Macro.Name = M.Name;
|
Macro.Name = M.Name;
|
||||||
Macro.PreferredDeclaration = *Loc;
|
Macro.PreferredDeclaration = *Loc;
|
||||||
|
@ -320,7 +336,7 @@ std::vector<LocatedSymbol> locateSymbolAt(ParsedAST &AST, Position Pos,
|
||||||
|
|
||||||
// Emit all symbol locations (declaration or definition) from AST.
|
// Emit all symbol locations (declaration or definition) from AST.
|
||||||
for (const Decl *D : Symbols.Decls) {
|
for (const Decl *D : Symbols.Decls) {
|
||||||
auto Loc = makeLocation(AST, findNameLoc(D), *MainFilePath);
|
auto Loc = makeLocation(AST.getASTContext(), findNameLoc(D), *MainFilePath);
|
||||||
if (!Loc)
|
if (!Loc)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -453,7 +469,7 @@ std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
|
||||||
std::vector<DocumentHighlight> Result;
|
std::vector<DocumentHighlight> Result;
|
||||||
for (const auto &Ref : References) {
|
for (const auto &Ref : References) {
|
||||||
DocumentHighlight DH;
|
DocumentHighlight DH;
|
||||||
DH.range = getTokenRange(AST, Ref.Loc);
|
DH.range = getTokenRange(AST.getASTContext(), Ref.Loc);
|
||||||
if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Write))
|
if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Write))
|
||||||
DH.kind = DocumentHighlightKind::Write;
|
DH.kind = DocumentHighlightKind::Write;
|
||||||
else if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Read))
|
else if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Read))
|
||||||
|
@ -477,102 +493,238 @@ static PrintingPolicy printingPolicyForDecls(PrintingPolicy Base) {
|
||||||
return Policy;
|
return Policy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a string representation (e.g. "class MyNamespace::MyClass") of
|
|
||||||
/// the type declaration \p TD.
|
|
||||||
static std::string typeDeclToString(const TypeDecl *TD) {
|
|
||||||
QualType Type = TD->getASTContext().getTypeDeclType(TD);
|
|
||||||
|
|
||||||
PrintingPolicy Policy =
|
|
||||||
printingPolicyForDecls(TD->getASTContext().getPrintingPolicy());
|
|
||||||
|
|
||||||
std::string Name;
|
|
||||||
llvm::raw_string_ostream Stream(Name);
|
|
||||||
Type.print(Stream, Policy);
|
|
||||||
|
|
||||||
return Stream.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return a string representation (e.g. "namespace ns1::ns2") of
|
|
||||||
/// the named declaration \p ND.
|
|
||||||
static std::string namedDeclQualifiedName(const NamedDecl *ND,
|
|
||||||
llvm::StringRef Prefix) {
|
|
||||||
PrintingPolicy Policy =
|
|
||||||
printingPolicyForDecls(ND->getASTContext().getPrintingPolicy());
|
|
||||||
|
|
||||||
std::string Name;
|
|
||||||
llvm::raw_string_ostream Stream(Name);
|
|
||||||
Stream << Prefix << ' ';
|
|
||||||
ND->printQualifiedName(Stream, Policy);
|
|
||||||
|
|
||||||
return Stream.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Given a declaration \p D, return a human-readable string representing the
|
/// Given a declaration \p D, return a human-readable string representing the
|
||||||
/// scope in which it is declared. If the declaration is in the global scope,
|
/// local scope in which it is declared, i.e. class(es) and method name. Returns
|
||||||
/// return the string "global namespace".
|
/// an empty string if it is not local.
|
||||||
static llvm::Optional<std::string> getScopeName(const Decl *D) {
|
static std::string getLocalScope(const Decl *D) {
|
||||||
|
std::vector<std::string> Scopes;
|
||||||
|
const DeclContext *DC = D->getDeclContext();
|
||||||
|
auto GetName = [](const Decl *D) {
|
||||||
|
const NamedDecl *ND = dyn_cast<NamedDecl>(D);
|
||||||
|
std::string Name = ND->getNameAsString();
|
||||||
|
if (!Name.empty())
|
||||||
|
return Name;
|
||||||
|
if (auto RD = dyn_cast<RecordDecl>(D))
|
||||||
|
return ("(anonymous " + RD->getKindName() + ")").str();
|
||||||
|
return std::string("");
|
||||||
|
};
|
||||||
|
while (DC) {
|
||||||
|
if (const TypeDecl *TD = dyn_cast<TypeDecl>(DC))
|
||||||
|
Scopes.push_back(GetName(TD));
|
||||||
|
else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC))
|
||||||
|
Scopes.push_back(FD->getNameAsString());
|
||||||
|
DC = DC->getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return llvm::join(llvm::reverse(Scopes), "::");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the human-readable representation for namespace containing the
|
||||||
|
/// declaration \p D. Returns empty if it is contained global namespace.
|
||||||
|
static std::string getNamespaceScope(const Decl *D) {
|
||||||
const DeclContext *DC = D->getDeclContext();
|
const DeclContext *DC = D->getDeclContext();
|
||||||
|
|
||||||
if (isa<TranslationUnitDecl>(DC))
|
|
||||||
return std::string("global namespace");
|
|
||||||
if (const TypeDecl *TD = dyn_cast<TypeDecl>(DC))
|
if (const TypeDecl *TD = dyn_cast<TypeDecl>(DC))
|
||||||
return typeDeclToString(TD);
|
return getNamespaceScope(TD);
|
||||||
else if (const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC))
|
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC))
|
||||||
return namedDeclQualifiedName(ND, "namespace");
|
return getNamespaceScope(FD);
|
||||||
else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC))
|
if (const NamedDecl *ND = dyn_cast<NamedDecl>(DC))
|
||||||
return namedDeclQualifiedName(FD, "function");
|
return ND->getQualifiedNameAsString();
|
||||||
|
|
||||||
return None;
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string printDefinition(const Decl *D) {
|
||||||
|
std::string Definition;
|
||||||
|
llvm::raw_string_ostream OS(Definition);
|
||||||
|
PrintingPolicy Policy =
|
||||||
|
printingPolicyForDecls(D->getASTContext().getPrintingPolicy());
|
||||||
|
Policy.IncludeTagDefinition = false;
|
||||||
|
D->print(OS, Policy);
|
||||||
|
return Definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void printParams(llvm::raw_ostream &OS,
|
||||||
|
const std::vector<HoverInfo::Param> &Params) {
|
||||||
|
for (size_t I = 0, E = Params.size(); I != E; ++I) {
|
||||||
|
if (I)
|
||||||
|
OS << ", ";
|
||||||
|
OS << Params.at(I);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<HoverInfo::Param>
|
||||||
|
fetchTemplateParameters(const TemplateParameterList *Params,
|
||||||
|
const PrintingPolicy &PP) {
|
||||||
|
assert(Params);
|
||||||
|
std::vector<HoverInfo::Param> TempParameters;
|
||||||
|
|
||||||
|
for (const Decl *Param : *Params) {
|
||||||
|
HoverInfo::Param P;
|
||||||
|
P.Type.emplace();
|
||||||
|
if (const auto TTP = dyn_cast<TemplateTypeParmDecl>(Param)) {
|
||||||
|
P.Type = TTP->wasDeclaredWithTypename() ? "typename" : "class";
|
||||||
|
if (TTP->isParameterPack())
|
||||||
|
*P.Type += "...";
|
||||||
|
|
||||||
|
if (!TTP->getName().empty())
|
||||||
|
P.Name = TTP->getNameAsString();
|
||||||
|
if (TTP->hasDefaultArgument())
|
||||||
|
P.Default = TTP->getDefaultArgument().getAsString(PP);
|
||||||
|
} else if (const auto NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
|
||||||
|
if (IdentifierInfo *II = NTTP->getIdentifier())
|
||||||
|
P.Name = II->getName().str();
|
||||||
|
|
||||||
|
llvm::raw_string_ostream Out(*P.Type);
|
||||||
|
NTTP->getType().print(Out, PP);
|
||||||
|
if (NTTP->isParameterPack())
|
||||||
|
Out << "...";
|
||||||
|
|
||||||
|
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<TemplateTemplateParmDecl>(Param)) {
|
||||||
|
llvm::raw_string_ostream OS(*P.Type);
|
||||||
|
OS << "template <";
|
||||||
|
printParams(OS,
|
||||||
|
fetchTemplateParameters(TTPD->getTemplateParameters(), PP));
|
||||||
|
OS << "> class"; // FIXME: TemplateTemplateParameter doesn't store the
|
||||||
|
// info on whether this param was a "typename" or
|
||||||
|
// "class".
|
||||||
|
if (!TTPD->getName().empty())
|
||||||
|
P.Name = TTPD->getNameAsString();
|
||||||
|
if (TTPD->hasDefaultArgument()) {
|
||||||
|
P.Default.emplace();
|
||||||
|
llvm::raw_string_ostream Out(*P.Default);
|
||||||
|
TTPD->getDefaultArgument().getArgument().print(PP, Out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TempParameters.push_back(std::move(P));
|
||||||
|
}
|
||||||
|
|
||||||
|
return TempParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
static llvm::Optional<Range> getTokenRange(SourceLocation Loc,
|
||||||
|
const ASTContext &Ctx) {
|
||||||
|
if (!Loc.isValid())
|
||||||
|
return llvm::None;
|
||||||
|
SourceLocation End = Lexer::getLocForEndOfToken(
|
||||||
|
Loc, 0, Ctx.getSourceManager(), Ctx.getLangOpts());
|
||||||
|
if (!End.isValid())
|
||||||
|
return llvm::None;
|
||||||
|
return halfOpenToRange(Ctx.getSourceManager(),
|
||||||
|
CharSourceRange::getCharRange(Loc, End));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a \p Hover object given the declaration \p D.
|
/// Generate a \p Hover object given the declaration \p D.
|
||||||
static Hover getHoverContents(const Decl *D) {
|
static HoverInfo getHoverContents(const Decl *D) {
|
||||||
Hover H;
|
HoverInfo HI;
|
||||||
llvm::Optional<std::string> NamedScope = getScopeName(D);
|
const ASTContext &Ctx = D->getASTContext();
|
||||||
|
|
||||||
// Generate the "Declared in" section.
|
HI.NamespaceScope = getNamespaceScope(D);
|
||||||
if (NamedScope) {
|
if (!HI.NamespaceScope->empty())
|
||||||
assert(!NamedScope->empty());
|
HI.NamespaceScope->append("::");
|
||||||
|
HI.LocalScope = getLocalScope(D);
|
||||||
|
if (!HI.LocalScope.empty())
|
||||||
|
HI.LocalScope.append("::");
|
||||||
|
|
||||||
H.contents.value += "Declared in ";
|
PrintingPolicy Policy = printingPolicyForDecls(Ctx.getPrintingPolicy());
|
||||||
H.contents.value += *NamedScope;
|
if (const NamedDecl *ND = llvm::dyn_cast<NamedDecl>(D)) {
|
||||||
H.contents.value += "\n\n";
|
HI.Documentation = getDeclComment(Ctx, *ND);
|
||||||
|
HI.Name = printName(Ctx, *ND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We want to include the template in the Hover.
|
HI.Kind = indexSymbolKindToSymbolKind(index::getSymbolInfo(D).Kind);
|
||||||
if (TemplateDecl *TD = D->getDescribedTemplate())
|
|
||||||
|
// Fill in template params.
|
||||||
|
if (const TemplateDecl *TD = D->getDescribedTemplate()) {
|
||||||
|
HI.TemplateParameters =
|
||||||
|
fetchTemplateParameters(TD->getTemplateParameters(), Policy);
|
||||||
D = TD;
|
D = TD;
|
||||||
|
} else if (const FunctionDecl *FD = D->getAsFunction()) {
|
||||||
|
if (const auto FTD = FD->getDescribedTemplate()) {
|
||||||
|
HI.TemplateParameters =
|
||||||
|
fetchTemplateParameters(FTD->getTemplateParameters(), Policy);
|
||||||
|
D = FTD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string DeclText;
|
// Fill in types and params.
|
||||||
llvm::raw_string_ostream OS(DeclText);
|
if (const FunctionDecl *FD = D->getAsFunction()) {
|
||||||
|
HI.ReturnType.emplace();
|
||||||
|
llvm::raw_string_ostream OS(*HI.ReturnType);
|
||||||
|
FD->getReturnType().print(OS, Policy);
|
||||||
|
|
||||||
PrintingPolicy Policy =
|
HI.Type.emplace();
|
||||||
printingPolicyForDecls(D->getASTContext().getPrintingPolicy());
|
llvm::raw_string_ostream TypeOS(*HI.Type);
|
||||||
|
FD->getReturnType().print(TypeOS, Policy);
|
||||||
|
TypeOS << '(';
|
||||||
|
|
||||||
D->print(OS, Policy);
|
HI.Parameters.emplace();
|
||||||
|
for (const ParmVarDecl *PVD : FD->parameters()) {
|
||||||
|
if (HI.Parameters->size())
|
||||||
|
TypeOS << ", ";
|
||||||
|
HI.Parameters->emplace_back();
|
||||||
|
auto &P = HI.Parameters->back();
|
||||||
|
if (!PVD->getType().isNull()) {
|
||||||
|
P.Type.emplace();
|
||||||
|
llvm::raw_string_ostream OS(*P.Type);
|
||||||
|
PVD->getType().print(OS, Policy);
|
||||||
|
PVD->getType().print(TypeOS, Policy);
|
||||||
|
} else {
|
||||||
|
std::string Param;
|
||||||
|
llvm::raw_string_ostream OS(Param);
|
||||||
|
PVD->dump(OS);
|
||||||
|
OS.flush();
|
||||||
|
elog("Got param with null type: {0}", Param);
|
||||||
|
}
|
||||||
|
if (!PVD->getName().empty())
|
||||||
|
P.Name = PVD->getNameAsString();
|
||||||
|
if (PVD->hasDefaultArg()) {
|
||||||
|
P.Default.emplace();
|
||||||
|
llvm::raw_string_ostream Out(*P.Default);
|
||||||
|
PVD->getDefaultArg()->printPretty(Out, nullptr, Policy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TypeOS << ')';
|
||||||
|
// FIXME: handle variadics.
|
||||||
|
} else if (const auto *VD = dyn_cast<ValueDecl>(D)) {
|
||||||
|
// FIXME: Currently lambdas are also handled as ValueDecls, they should be
|
||||||
|
// more similar to functions.
|
||||||
|
HI.Type.emplace();
|
||||||
|
llvm::raw_string_ostream OS(*HI.Type);
|
||||||
|
VD->getType().print(OS, Policy);
|
||||||
|
}
|
||||||
|
|
||||||
OS.flush();
|
HI.Definition = printDefinition(D);
|
||||||
|
return HI;
|
||||||
H.contents.value += DeclText;
|
|
||||||
return H;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a \p Hover object given the type \p T.
|
/// Generate a \p Hover object given the type \p T.
|
||||||
static Hover getHoverContents(QualType T, ASTContext &ASTCtx) {
|
static HoverInfo getHoverContents(QualType T, const Decl *D,
|
||||||
Hover H;
|
ASTContext &ASTCtx) {
|
||||||
std::string TypeText;
|
HoverInfo HI;
|
||||||
llvm::raw_string_ostream OS(TypeText);
|
llvm::raw_string_ostream OS(HI.Name);
|
||||||
PrintingPolicy Policy = printingPolicyForDecls(ASTCtx.getPrintingPolicy());
|
PrintingPolicy Policy = printingPolicyForDecls(ASTCtx.getPrintingPolicy());
|
||||||
T.print(OS, Policy);
|
T.print(OS, Policy);
|
||||||
OS.flush();
|
|
||||||
H.contents.value += TypeText;
|
if (D)
|
||||||
return H;
|
HI.Kind = indexSymbolKindToSymbolKind(index::getSymbolInfo(D).Kind);
|
||||||
|
return HI;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a \p Hover object given the macro \p MacroDecl.
|
/// Generate a \p Hover object given the macro \p MacroDecl.
|
||||||
static Hover getHoverContents(MacroDecl Decl, ParsedAST &AST) {
|
static HoverInfo getHoverContents(MacroDecl Decl, ParsedAST &AST) {
|
||||||
|
HoverInfo HI;
|
||||||
SourceManager &SM = AST.getASTContext().getSourceManager();
|
SourceManager &SM = AST.getASTContext().getSourceManager();
|
||||||
std::string Definition = Decl.Name;
|
HI.Name = Decl.Name;
|
||||||
|
HI.Kind = indexSymbolKindToSymbolKind(
|
||||||
|
index::getSymbolInfoForMacro(*Decl.Info).Kind);
|
||||||
|
// FIXME: Populate documentation
|
||||||
|
// FIXME: Pupulate parameters
|
||||||
|
|
||||||
// Try to get the full definition, not just the name
|
// Try to get the full definition, not just the name
|
||||||
SourceLocation StartLoc = Decl.Info->getDefinitionLoc();
|
SourceLocation StartLoc = Decl.Info->getDefinitionLoc();
|
||||||
|
@ -586,14 +738,12 @@ static Hover getHoverContents(MacroDecl Decl, ParsedAST &AST) {
|
||||||
unsigned StartOffset = SM.getFileOffset(StartLoc);
|
unsigned StartOffset = SM.getFileOffset(StartLoc);
|
||||||
unsigned EndOffset = SM.getFileOffset(EndLoc);
|
unsigned EndOffset = SM.getFileOffset(EndLoc);
|
||||||
if (EndOffset <= Buffer.size() && StartOffset < EndOffset)
|
if (EndOffset <= Buffer.size() && StartOffset < EndOffset)
|
||||||
Definition = Buffer.substr(StartOffset, EndOffset - StartOffset).str();
|
HI.Definition =
|
||||||
|
("#define " + Buffer.substr(StartOffset, EndOffset - StartOffset))
|
||||||
|
.str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return HI;
|
||||||
Hover H;
|
|
||||||
H.contents.kind = MarkupKind::PlainText;
|
|
||||||
H.contents.value = "#define " + Definition;
|
|
||||||
return H;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -607,14 +757,11 @@ namespace {
|
||||||
/// a deduced type set. The AST should be improved to simplify this scenario.
|
/// a deduced type set. The AST should be improved to simplify this scenario.
|
||||||
class DeducedTypeVisitor : public RecursiveASTVisitor<DeducedTypeVisitor> {
|
class DeducedTypeVisitor : public RecursiveASTVisitor<DeducedTypeVisitor> {
|
||||||
SourceLocation SearchedLocation;
|
SourceLocation SearchedLocation;
|
||||||
llvm::Optional<QualType> DeducedType;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DeducedTypeVisitor(SourceLocation SearchedLocation)
|
DeducedTypeVisitor(SourceLocation SearchedLocation)
|
||||||
: SearchedLocation(SearchedLocation) {}
|
: SearchedLocation(SearchedLocation) {}
|
||||||
|
|
||||||
llvm::Optional<QualType> getDeducedType() { return DeducedType; }
|
|
||||||
|
|
||||||
// Handle auto initializers:
|
// Handle auto initializers:
|
||||||
//- auto i = 1;
|
//- auto i = 1;
|
||||||
//- decltype(auto) i = 1;
|
//- decltype(auto) i = 1;
|
||||||
|
@ -626,8 +773,10 @@ public:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (auto *AT = D->getType()->getContainedAutoType()) {
|
if (auto *AT = D->getType()->getContainedAutoType()) {
|
||||||
if (!AT->getDeducedType().isNull())
|
if (!AT->getDeducedType().isNull()) {
|
||||||
DeducedType = AT->getDeducedType();
|
DeducedType = AT->getDeducedType();
|
||||||
|
this->D = D;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -655,13 +804,17 @@ public:
|
||||||
const AutoType *AT = D->getReturnType()->getContainedAutoType();
|
const AutoType *AT = D->getReturnType()->getContainedAutoType();
|
||||||
if (AT && !AT->getDeducedType().isNull()) {
|
if (AT && !AT->getDeducedType().isNull()) {
|
||||||
DeducedType = AT->getDeducedType();
|
DeducedType = AT->getDeducedType();
|
||||||
|
this->D = D;
|
||||||
} else if (auto DT = dyn_cast<DecltypeType>(D->getReturnType())) {
|
} else if (auto DT = dyn_cast<DecltypeType>(D->getReturnType())) {
|
||||||
// auto in a trailing return type just points to a DecltypeType and
|
// auto in a trailing return type just points to a DecltypeType and
|
||||||
// getContainedAutoType does not unwrap it.
|
// getContainedAutoType does not unwrap it.
|
||||||
if (!DT->getUnderlyingType().isNull())
|
if (!DT->getUnderlyingType().isNull()) {
|
||||||
DeducedType = DT->getUnderlyingType();
|
DeducedType = DT->getUnderlyingType();
|
||||||
|
this->D = D;
|
||||||
|
}
|
||||||
} else if (!D->getReturnType().isNull()) {
|
} else if (!D->getReturnType().isNull()) {
|
||||||
DeducedType = D->getReturnType();
|
DeducedType = D->getReturnType();
|
||||||
|
this->D = D;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -680,16 +833,19 @@ public:
|
||||||
const DecltypeType *DT = dyn_cast<DecltypeType>(TL.getTypePtr());
|
const DecltypeType *DT = dyn_cast<DecltypeType>(TL.getTypePtr());
|
||||||
while (DT && !DT->getUnderlyingType().isNull()) {
|
while (DT && !DT->getUnderlyingType().isNull()) {
|
||||||
DeducedType = DT->getUnderlyingType();
|
DeducedType = DT->getUnderlyingType();
|
||||||
DT = dyn_cast<DecltypeType>(DeducedType->getTypePtr());
|
D = DT->getAsTagDecl();
|
||||||
|
DT = dyn_cast<DecltypeType>(DeducedType.getTypePtr());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QualType DeducedType;
|
||||||
|
const Decl *D = nullptr;
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
/// Retrieves the deduced type at a given location (auto, decltype).
|
/// Retrieves the deduced type at a given location (auto, decltype).
|
||||||
llvm::Optional<QualType> getDeducedType(ParsedAST &AST,
|
bool hasDeducedType(ParsedAST &AST, SourceLocation SourceLocationBeg) {
|
||||||
SourceLocation SourceLocationBeg) {
|
|
||||||
Token Tok;
|
Token Tok;
|
||||||
auto &ASTCtx = AST.getASTContext();
|
auto &ASTCtx = AST.getASTContext();
|
||||||
// Only try to find a deduced type if the token is auto or decltype.
|
// Only try to find a deduced type if the token is auto or decltype.
|
||||||
|
@ -697,18 +853,17 @@ llvm::Optional<QualType> getDeducedType(ParsedAST &AST,
|
||||||
Lexer::getRawToken(SourceLocationBeg, Tok, ASTCtx.getSourceManager(),
|
Lexer::getRawToken(SourceLocationBeg, Tok, ASTCtx.getSourceManager(),
|
||||||
ASTCtx.getLangOpts(), false) ||
|
ASTCtx.getLangOpts(), false) ||
|
||||||
!Tok.is(tok::raw_identifier)) {
|
!Tok.is(tok::raw_identifier)) {
|
||||||
return {};
|
return false;
|
||||||
}
|
}
|
||||||
AST.getPreprocessor().LookUpIdentifierInfo(Tok);
|
AST.getPreprocessor().LookUpIdentifierInfo(Tok);
|
||||||
if (!(Tok.is(tok::kw_auto) || Tok.is(tok::kw_decltype)))
|
if (!(Tok.is(tok::kw_auto) || Tok.is(tok::kw_decltype)))
|
||||||
return {};
|
return false;
|
||||||
|
return true;
|
||||||
DeducedTypeVisitor V(SourceLocationBeg);
|
|
||||||
V.TraverseAST(AST.getASTContext());
|
|
||||||
return V.getDeducedType();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Optional<Hover> getHover(ParsedAST &AST, Position Pos) {
|
llvm::Optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
|
||||||
|
format::FormatStyle Style) {
|
||||||
|
llvm::Optional<HoverInfo> HI;
|
||||||
const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
|
const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
|
||||||
SourceLocation SourceLocationBeg =
|
SourceLocation SourceLocationBeg =
|
||||||
getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID());
|
getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID());
|
||||||
|
@ -716,16 +871,28 @@ llvm::Optional<Hover> getHover(ParsedAST &AST, Position Pos) {
|
||||||
auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg);
|
auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg);
|
||||||
|
|
||||||
if (!Symbols.Macros.empty())
|
if (!Symbols.Macros.empty())
|
||||||
return getHoverContents(Symbols.Macros[0], AST);
|
HI = getHoverContents(Symbols.Macros[0], AST);
|
||||||
|
else if (!Symbols.Decls.empty())
|
||||||
|
HI = getHoverContents(Symbols.Decls[0]);
|
||||||
|
else {
|
||||||
|
if (!hasDeducedType(AST, SourceLocationBeg))
|
||||||
|
return None;
|
||||||
|
|
||||||
if (!Symbols.Decls.empty())
|
DeducedTypeVisitor V(SourceLocationBeg);
|
||||||
return getHoverContents(Symbols.Decls[0]);
|
V.TraverseAST(AST.getASTContext());
|
||||||
|
if (V.DeducedType.isNull())
|
||||||
|
return None;
|
||||||
|
HI = getHoverContents(V.DeducedType, V.D, AST.getASTContext());
|
||||||
|
}
|
||||||
|
|
||||||
auto DeducedType = getDeducedType(AST, SourceLocationBeg);
|
auto Replacements = format::reformat(
|
||||||
if (DeducedType && !DeducedType->isNull())
|
Style, HI->Definition, tooling::Range(0, HI->Definition.size()));
|
||||||
return getHoverContents(*DeducedType, AST.getASTContext());
|
if (auto Formatted =
|
||||||
|
tooling::applyAllReplacements(HI->Definition, Replacements))
|
||||||
|
HI->Definition = *Formatted;
|
||||||
|
|
||||||
return None;
|
HI->SymRange = getTokenRange(SourceLocationBeg, AST.getASTContext());
|
||||||
|
return HI;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Location> findReferences(ParsedAST &AST, Position Pos,
|
std::vector<Location> findReferences(ParsedAST &AST, Position Pos,
|
||||||
|
@ -748,7 +915,7 @@ std::vector<Location> findReferences(ParsedAST &AST, Position Pos,
|
||||||
auto MainFileRefs = findRefs(Symbols.Decls, AST);
|
auto MainFileRefs = findRefs(Symbols.Decls, AST);
|
||||||
for (const auto &Ref : MainFileRefs) {
|
for (const auto &Ref : MainFileRefs) {
|
||||||
Location Result;
|
Location Result;
|
||||||
Result.range = getTokenRange(AST, Ref.Loc);
|
Result.range = getTokenRange(AST.getASTContext(), Ref.Loc);
|
||||||
Result.uri = URIForFile::canonicalize(*MainFilePath, *MainFilePath);
|
Result.uri = URIForFile::canonicalize(*MainFilePath, *MainFilePath);
|
||||||
Results.push_back(std::move(Result));
|
Results.push_back(std::move(Result));
|
||||||
}
|
}
|
||||||
|
@ -991,5 +1158,46 @@ getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels,
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MarkupContent HoverInfo::render() const {
|
||||||
|
MarkupContent Content;
|
||||||
|
Content.kind = MarkupKind::PlainText;
|
||||||
|
std::vector<std::string> Output;
|
||||||
|
|
||||||
|
if (NamespaceScope) {
|
||||||
|
llvm::raw_string_ostream Out(Content.value);
|
||||||
|
Out << "Declared in ";
|
||||||
|
// Drop trailing "::".
|
||||||
|
if (!LocalScope.empty())
|
||||||
|
Out << *NamespaceScope << llvm::StringRef(LocalScope).drop_back(2);
|
||||||
|
else if (NamespaceScope->empty())
|
||||||
|
Out << "global namespace";
|
||||||
|
else
|
||||||
|
Out << llvm::StringRef(*NamespaceScope).drop_back(2);
|
||||||
|
Out << "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Definition.empty()) {
|
||||||
|
Output.push_back(Definition);
|
||||||
|
} else {
|
||||||
|
// Builtin types
|
||||||
|
Output.push_back(Name);
|
||||||
|
}
|
||||||
|
Content.value += llvm::join(Output, " ");
|
||||||
|
return Content;
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
|
||||||
|
const HoverInfo::Param &P) {
|
||||||
|
std::vector<llvm::StringRef> Output;
|
||||||
|
if (P.Type)
|
||||||
|
Output.push_back(*P.Type);
|
||||||
|
if (P.Name)
|
||||||
|
Output.push_back(*P.Name);
|
||||||
|
OS << llvm::join(Output, " ");
|
||||||
|
if (P.Default)
|
||||||
|
OS << " = " << *P.Default;
|
||||||
|
return OS;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace clangd
|
} // namespace clangd
|
||||||
} // namespace clang
|
} // namespace clang
|
||||||
|
|
|
@ -16,7 +16,10 @@
|
||||||
#include "ClangdUnit.h"
|
#include "ClangdUnit.h"
|
||||||
#include "Protocol.h"
|
#include "Protocol.h"
|
||||||
#include "index/Index.h"
|
#include "index/Index.h"
|
||||||
|
#include "index/SymbolLocation.h"
|
||||||
|
#include "clang/Index/IndexSymbol.h"
|
||||||
#include "llvm/ADT/Optional.h"
|
#include "llvm/ADT/Optional.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
|
@ -46,8 +49,73 @@ std::vector<LocatedSymbol> locateSymbolAt(ParsedAST &AST, Position Pos,
|
||||||
std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
|
std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
|
||||||
Position Pos);
|
Position Pos);
|
||||||
|
|
||||||
|
/// Contains detailed information about a Symbol. Especially useful when
|
||||||
|
/// generating hover responses. It can be rendered as a hover panel, or
|
||||||
|
/// embedding clients can use the structured information to provide their own
|
||||||
|
/// UI.
|
||||||
|
struct HoverInfo {
|
||||||
|
/// Represents parameters of a function, a template or a macro.
|
||||||
|
/// For example:
|
||||||
|
/// - void foo(ParamType Name = DefaultValue)
|
||||||
|
/// - #define FOO(Name)
|
||||||
|
/// - template <ParamType Name = DefaultType> class Foo {};
|
||||||
|
struct Param {
|
||||||
|
/// The pretty-printed parameter type, e.g. "int", or "typename" (in
|
||||||
|
/// TemplateParameters)
|
||||||
|
llvm::Optional<std::string> Type;
|
||||||
|
/// None for unnamed parameters.
|
||||||
|
llvm::Optional<std::string> Name;
|
||||||
|
/// None if no default is provided.
|
||||||
|
llvm::Optional<std::string> Default;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// For a variable named Bar, declared in clang::clangd::Foo::getFoo the
|
||||||
|
/// following fields will hold:
|
||||||
|
/// - NamespaceScope: clang::clangd::
|
||||||
|
/// - LocalScope: Foo::getFoo::
|
||||||
|
/// - Name: Bar
|
||||||
|
|
||||||
|
/// Scopes might be None in cases where they don't make sense, e.g. macros and
|
||||||
|
/// auto/decltype.
|
||||||
|
/// Contains all of the enclosing namespaces, empty string means global
|
||||||
|
/// namespace.
|
||||||
|
llvm::Optional<std::string> NamespaceScope;
|
||||||
|
/// Remaining named contexts in symbol's qualified name, empty string means
|
||||||
|
/// symbol is not local.
|
||||||
|
std::string LocalScope;
|
||||||
|
/// Name of the symbol, does not contain any "::".
|
||||||
|
std::string Name;
|
||||||
|
llvm::Optional<Range> SymRange;
|
||||||
|
/// Scope containing the symbol. e.g, "global namespace", "function x::Y"
|
||||||
|
/// - None for deduced types, e.g "auto", "decltype" keywords.
|
||||||
|
SymbolKind Kind;
|
||||||
|
std::string Documentation;
|
||||||
|
/// Source code containing the definition of the symbol.
|
||||||
|
std::string Definition;
|
||||||
|
|
||||||
|
/// Pretty-printed variable type.
|
||||||
|
/// Set only for variables.
|
||||||
|
llvm::Optional<std::string> Type;
|
||||||
|
/// Set for functions and lambadas.
|
||||||
|
llvm::Optional<std::string> ReturnType;
|
||||||
|
/// Set for functions, lambdas and macros with parameters.
|
||||||
|
llvm::Optional<std::vector<Param>> Parameters;
|
||||||
|
/// Set for all templates(function, class, variable).
|
||||||
|
llvm::Optional<std::vector<Param>> TemplateParameters;
|
||||||
|
|
||||||
|
/// Lower to LSP struct.
|
||||||
|
MarkupContent render() const;
|
||||||
|
};
|
||||||
|
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const HoverInfo::Param &);
|
||||||
|
inline bool operator==(const HoverInfo::Param &LHS,
|
||||||
|
const HoverInfo::Param &RHS) {
|
||||||
|
return std::tie(LHS.Type, LHS.Name, LHS.Default) ==
|
||||||
|
std::tie(RHS.Type, RHS.Name, RHS.Default);
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the hover information when hovering at \p Pos.
|
/// Get the hover information when hovering at \p Pos.
|
||||||
llvm::Optional<Hover> getHover(ParsedAST &AST, Position Pos);
|
llvm::Optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
|
||||||
|
format::FormatStyle Style);
|
||||||
|
|
||||||
/// Returns reference locations of the symbol at a specified \p Pos.
|
/// Returns reference locations of the symbol at a specified \p Pos.
|
||||||
/// \p Limit limits the number of results returned (0 means no limit).
|
/// \p Limit limits the number of results returned (0 means no limit).
|
||||||
|
|
|
@ -10,6 +10,16 @@
|
||||||
# CHECK-NEXT: "contents": {
|
# CHECK-NEXT: "contents": {
|
||||||
# CHECK-NEXT: "kind": "plaintext",
|
# CHECK-NEXT: "kind": "plaintext",
|
||||||
# CHECK-NEXT: "value": "Declared in global namespace\n\nvoid foo()"
|
# CHECK-NEXT: "value": "Declared in global namespace\n\nvoid foo()"
|
||||||
|
# CHECK-NEXT: },
|
||||||
|
# CHECK-NEXT: "range": {
|
||||||
|
# CHECK-NEXT: "end": {
|
||||||
|
# CHECK-NEXT: "character": 28,
|
||||||
|
# CHECK-NEXT: "line": 0
|
||||||
|
# CHECK-NEXT: },
|
||||||
|
# CHECK-NEXT: "start": {
|
||||||
|
# CHECK-NEXT: "character": 25,
|
||||||
|
# CHECK-NEXT: "line": 0
|
||||||
|
# CHECK-NEXT: }
|
||||||
# CHECK-NEXT: }
|
# CHECK-NEXT: }
|
||||||
# CHECK-NEXT: }
|
# CHECK-NEXT: }
|
||||||
# CHECK-NEXT:}
|
# CHECK-NEXT:}
|
||||||
|
@ -19,6 +29,29 @@
|
||||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||||
# CHECK-NEXT: "result": null
|
# CHECK-NEXT: "result": null
|
||||||
---
|
---
|
||||||
|
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main2.cpp","languageId":"cpp","version":1,"text":"enum foo{}; int main() { foo f; }\n"}}}
|
||||||
|
---
|
||||||
|
{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"test:///main2.cpp"},"position":{"line":0,"character":27}}}
|
||||||
|
# CHECK: "id": 1,
|
||||||
|
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||||
|
# CHECK-NEXT: "result": {
|
||||||
|
# CHECK-NEXT: "contents": {
|
||||||
|
# CHECK-NEXT: "kind": "plaintext",
|
||||||
|
# CHECK-NEXT: "value": "Declared in global namespace\n\nenum foo {}"
|
||||||
|
# CHECK-NEXT: },
|
||||||
|
# CHECK-NEXT: "range": {
|
||||||
|
# CHECK-NEXT: "end": {
|
||||||
|
# CHECK-NEXT: "character": 28,
|
||||||
|
# CHECK-NEXT: "line": 0
|
||||||
|
# CHECK-NEXT: },
|
||||||
|
# CHECK-NEXT: "start": {
|
||||||
|
# CHECK-NEXT: "character": 25,
|
||||||
|
# CHECK-NEXT: "line": 0
|
||||||
|
# CHECK-NEXT: }
|
||||||
|
# CHECK-NEXT: }
|
||||||
|
# CHECK-NEXT: }
|
||||||
|
# CHECK-NEXT:}
|
||||||
|
---
|
||||||
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
|
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
|
||||||
---
|
---
|
||||||
{"jsonrpc":"2.0","method":"exit"}
|
{"jsonrpc":"2.0","method":"exit"}
|
||||||
|
|
|
@ -59,8 +59,7 @@ ParsedAST TestTU::build() const {
|
||||||
/*OldPreamble=*/nullptr,
|
/*OldPreamble=*/nullptr,
|
||||||
/*OldCompileCommand=*/Inputs.CompileCommand, Inputs,
|
/*OldCompileCommand=*/Inputs.CompileCommand, Inputs,
|
||||||
/*StoreInMemory=*/true, /*PreambleCallback=*/nullptr);
|
/*StoreInMemory=*/true, /*PreambleCallback=*/nullptr);
|
||||||
auto AST = buildAST(FullFilename, createInvocationFromCommandLine(Cmd),
|
auto AST = buildAST(FullFilename, std::move(CI), Inputs, Preamble);
|
||||||
Inputs, Preamble);
|
|
||||||
if (!AST.hasValue()) {
|
if (!AST.hasValue()) {
|
||||||
ADD_FAILURE() << "Failed to build code:\n" << Code;
|
ADD_FAILURE() << "Failed to build code:\n" << Code;
|
||||||
llvm_unreachable("Failed to build TestTU!");
|
llvm_unreachable("Failed to build TestTU!");
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "ClangdUnit.h"
|
#include "ClangdUnit.h"
|
||||||
#include "Compiler.h"
|
#include "Compiler.h"
|
||||||
#include "Matchers.h"
|
#include "Matchers.h"
|
||||||
|
#include "Protocol.h"
|
||||||
#include "SyncAPI.h"
|
#include "SyncAPI.h"
|
||||||
#include "TestFS.h"
|
#include "TestFS.h"
|
||||||
#include "TestTU.h"
|
#include "TestTU.h"
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
#include "index/FileIndex.h"
|
#include "index/FileIndex.h"
|
||||||
#include "index/SymbolCollector.h"
|
#include "index/SymbolCollector.h"
|
||||||
#include "clang/Index/IndexingAction.h"
|
#include "clang/Index/IndexingAction.h"
|
||||||
|
#include "llvm/ADT/None.h"
|
||||||
#include "llvm/Support/Path.h"
|
#include "llvm/Support/Path.h"
|
||||||
#include "llvm/Support/ScopedPrinter.h"
|
#include "llvm/Support/ScopedPrinter.h"
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
|
@ -569,6 +571,306 @@ int [[bar_not_preamble]];
|
||||||
HeaderNotInPreambleAnnotations.range())));
|
HeaderNotInPreambleAnnotations.range())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Hover, Structured) {
|
||||||
|
struct {
|
||||||
|
const char *const Code;
|
||||||
|
const std::function<void(HoverInfo &)> ExpectedBuilder;
|
||||||
|
} Cases[] = {
|
||||||
|
// Global scope.
|
||||||
|
{R"cpp(
|
||||||
|
// Best foo ever.
|
||||||
|
void [[fo^o]]() {}
|
||||||
|
)cpp",
|
||||||
|
[](HoverInfo &HI) {
|
||||||
|
HI.NamespaceScope = "";
|
||||||
|
HI.Name = "foo";
|
||||||
|
HI.Kind = SymbolKind::Function;
|
||||||
|
HI.Documentation = "Best foo ever.";
|
||||||
|
HI.Definition = "void foo()";
|
||||||
|
HI.ReturnType = "void";
|
||||||
|
HI.Type = "void()";
|
||||||
|
HI.Parameters.emplace();
|
||||||
|
}},
|
||||||
|
// Inside namespace
|
||||||
|
{R"cpp(
|
||||||
|
namespace ns1 { namespace ns2 {
|
||||||
|
/// Best foo ever.
|
||||||
|
void [[fo^o]]() {}
|
||||||
|
}}
|
||||||
|
)cpp",
|
||||||
|
[](HoverInfo &HI) {
|
||||||
|
HI.NamespaceScope = "ns1::ns2::";
|
||||||
|
HI.Name = "foo";
|
||||||
|
HI.Kind = SymbolKind::Function;
|
||||||
|
HI.Documentation = "Best foo ever.";
|
||||||
|
HI.Definition = "void foo()";
|
||||||
|
HI.ReturnType = "void";
|
||||||
|
HI.Type = "void()";
|
||||||
|
HI.Parameters.emplace();
|
||||||
|
}},
|
||||||
|
// Field
|
||||||
|
{R"cpp(
|
||||||
|
namespace ns1 { namespace ns2 {
|
||||||
|
struct Foo {
|
||||||
|
int [[b^ar]];
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
)cpp",
|
||||||
|
[](HoverInfo &HI) {
|
||||||
|
HI.NamespaceScope = "ns1::ns2::";
|
||||||
|
HI.LocalScope = "Foo::";
|
||||||
|
HI.Name = "bar";
|
||||||
|
HI.Kind = SymbolKind::Field;
|
||||||
|
HI.Definition = "int bar";
|
||||||
|
HI.Type = "int";
|
||||||
|
}},
|
||||||
|
// Local to class method.
|
||||||
|
{R"cpp(
|
||||||
|
namespace ns1 { namespace ns2 {
|
||||||
|
struct Foo {
|
||||||
|
void foo() {
|
||||||
|
int [[b^ar]];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
)cpp",
|
||||||
|
[](HoverInfo &HI) {
|
||||||
|
HI.NamespaceScope = "ns1::ns2::";
|
||||||
|
HI.LocalScope = "Foo::foo::";
|
||||||
|
HI.Name = "bar";
|
||||||
|
HI.Kind = SymbolKind::Variable;
|
||||||
|
HI.Definition = "int bar";
|
||||||
|
HI.Type = "int";
|
||||||
|
}},
|
||||||
|
// Anon namespace and local scope.
|
||||||
|
{R"cpp(
|
||||||
|
namespace ns1 { namespace {
|
||||||
|
struct {
|
||||||
|
int [[b^ar]];
|
||||||
|
} T;
|
||||||
|
}}
|
||||||
|
)cpp",
|
||||||
|
[](HoverInfo &HI) {
|
||||||
|
HI.NamespaceScope = "ns1::(anonymous)::";
|
||||||
|
HI.LocalScope = "(anonymous struct)::";
|
||||||
|
HI.Name = "bar";
|
||||||
|
HI.Kind = SymbolKind::Field;
|
||||||
|
HI.Definition = "int bar";
|
||||||
|
HI.Type = "int";
|
||||||
|
}},
|
||||||
|
// Variable with template type
|
||||||
|
{R"cpp(
|
||||||
|
template <typename T, class... Ts> class Foo {};
|
||||||
|
Foo<int, char, bool> [[fo^o]];
|
||||||
|
)cpp",
|
||||||
|
[](HoverInfo &HI) {
|
||||||
|
HI.NamespaceScope = "";
|
||||||
|
HI.Name = "foo";
|
||||||
|
HI.Kind = SymbolKind::Variable;
|
||||||
|
HI.Definition = "Foo<int, char, bool> foo";
|
||||||
|
HI.Type = "Foo<int, char, bool>";
|
||||||
|
}},
|
||||||
|
// Implicit template instantiation
|
||||||
|
{R"cpp(
|
||||||
|
template <typename T> class vector{};
|
||||||
|
[[vec^tor]]<int> foo;
|
||||||
|
)cpp",
|
||||||
|
[](HoverInfo &HI) {
|
||||||
|
HI.NamespaceScope = "";
|
||||||
|
HI.Name = "vector";
|
||||||
|
HI.Kind = SymbolKind::Class;
|
||||||
|
HI.Definition = "template <typename T> class vector {}";
|
||||||
|
HI.TemplateParameters = {
|
||||||
|
{std::string("typename"), std::string("T"), llvm::None},
|
||||||
|
};
|
||||||
|
}},
|
||||||
|
// Class template
|
||||||
|
{R"cpp(
|
||||||
|
template <template<typename, bool...> class C,
|
||||||
|
typename = char,
|
||||||
|
int = 0,
|
||||||
|
bool Q = false,
|
||||||
|
class... Ts> class Foo {};
|
||||||
|
template <template<typename, bool...> class T>
|
||||||
|
[[F^oo]]<T> foo;
|
||||||
|
)cpp",
|
||||||
|
[](HoverInfo &HI) {
|
||||||
|
HI.NamespaceScope = "";
|
||||||
|
HI.Name = "Foo";
|
||||||
|
HI.Kind = SymbolKind::Class;
|
||||||
|
HI.Definition =
|
||||||
|
R"cpp(template <template <typename, bool...> class C, typename = char, int = 0,
|
||||||
|
bool Q = false, class... Ts>
|
||||||
|
class Foo {})cpp";
|
||||||
|
HI.TemplateParameters = {
|
||||||
|
{std::string("template <typename, bool...> class"),
|
||||||
|
std::string("C"), llvm::None},
|
||||||
|
{std::string("typename"), llvm::None, std::string("char")},
|
||||||
|
{std::string("int"), llvm::None, std::string("0")},
|
||||||
|
{std::string("bool"), std::string("Q"), std::string("false")},
|
||||||
|
{std::string("class..."), std::string("Ts"), llvm::None},
|
||||||
|
};
|
||||||
|
}},
|
||||||
|
// Function template
|
||||||
|
{R"cpp(
|
||||||
|
template <template<typename, bool...> class C,
|
||||||
|
typename = char,
|
||||||
|
int = 0,
|
||||||
|
bool Q = false,
|
||||||
|
class... Ts> void foo();
|
||||||
|
template<typename, bool...> class Foo;
|
||||||
|
|
||||||
|
void bar() {
|
||||||
|
[[fo^o]]<Foo>();
|
||||||
|
}
|
||||||
|
)cpp",
|
||||||
|
[](HoverInfo &HI) {
|
||||||
|
HI.NamespaceScope = "";
|
||||||
|
HI.Name = "foo";
|
||||||
|
HI.Kind = SymbolKind::Function;
|
||||||
|
HI.Definition =
|
||||||
|
R"cpp(template <template <typename, bool...> class C, typename = char, int = 0,
|
||||||
|
bool Q = false, class... Ts>
|
||||||
|
void foo())cpp";
|
||||||
|
HI.ReturnType = "void";
|
||||||
|
HI.Type = "void()";
|
||||||
|
HI.Parameters.emplace();
|
||||||
|
HI.TemplateParameters = {
|
||||||
|
{std::string("template <typename, bool...> class"),
|
||||||
|
std::string("C"), llvm::None},
|
||||||
|
{std::string("typename"), llvm::None, std::string("char")},
|
||||||
|
{std::string("int"), llvm::None, std::string("0")},
|
||||||
|
{std::string("bool"), std::string("Q"), std::string("false")},
|
||||||
|
{std::string("class..."), std::string("Ts"), llvm::None},
|
||||||
|
};
|
||||||
|
}},
|
||||||
|
// Function decl
|
||||||
|
{R"cpp(
|
||||||
|
template<typename, bool...> class Foo {};
|
||||||
|
Foo<bool, true, false> foo(int, bool T = false);
|
||||||
|
|
||||||
|
void bar() {
|
||||||
|
[[fo^o]](3);
|
||||||
|
}
|
||||||
|
)cpp",
|
||||||
|
[](HoverInfo &HI) {
|
||||||
|
HI.NamespaceScope = "";
|
||||||
|
HI.Name = "foo";
|
||||||
|
HI.Kind = SymbolKind::Function;
|
||||||
|
HI.Definition = "Foo<bool, true, false> foo(int, bool T = false)";
|
||||||
|
HI.ReturnType = "Foo<bool, true, false>";
|
||||||
|
HI.Type = "Foo<bool, true, false>(int, bool)";
|
||||||
|
HI.Parameters = {
|
||||||
|
{std::string("int"), llvm::None, llvm::None},
|
||||||
|
{std::string("bool"), std::string("T"), std::string("false")},
|
||||||
|
};
|
||||||
|
}},
|
||||||
|
// Lambda variable
|
||||||
|
{R"cpp(
|
||||||
|
void foo() {
|
||||||
|
int bar = 5;
|
||||||
|
auto lamb = [&bar](int T, bool B) -> bool { return T && B && bar; };
|
||||||
|
bool res = [[lam^b]](bar, false);
|
||||||
|
}
|
||||||
|
)cpp",
|
||||||
|
[](HoverInfo &HI) {
|
||||||
|
HI.NamespaceScope = "";
|
||||||
|
HI.LocalScope = "foo::";
|
||||||
|
HI.Name = "lamb";
|
||||||
|
HI.Kind = SymbolKind::Variable;
|
||||||
|
HI.Definition = "auto lamb = [&bar](int T, bool B) -> bool {}";
|
||||||
|
HI.Type = std::string("class (lambda)");
|
||||||
|
return HI;
|
||||||
|
}},
|
||||||
|
// Local variable in lambda
|
||||||
|
{R"cpp(
|
||||||
|
void foo() {
|
||||||
|
auto lamb = []{int [[te^st]];};
|
||||||
|
}
|
||||||
|
)cpp",
|
||||||
|
[](HoverInfo &HI) {
|
||||||
|
HI.NamespaceScope = "";
|
||||||
|
HI.LocalScope = "foo::(anonymous class)::operator()::";
|
||||||
|
HI.Name = "test";
|
||||||
|
HI.Kind = SymbolKind::Variable;
|
||||||
|
HI.Definition = "int test";
|
||||||
|
HI.Type = "int";
|
||||||
|
}},
|
||||||
|
|
||||||
|
// auto on lambda
|
||||||
|
{R"cpp(
|
||||||
|
void foo() {
|
||||||
|
[[au^to]] lamb = []{};
|
||||||
|
}
|
||||||
|
)cpp",
|
||||||
|
[](HoverInfo &HI) {
|
||||||
|
HI.Name = "class (lambda)";
|
||||||
|
HI.Kind = SymbolKind::Variable;
|
||||||
|
}},
|
||||||
|
// auto on template instantiation
|
||||||
|
{R"cpp(
|
||||||
|
template<typename T> class Foo{};
|
||||||
|
void foo() {
|
||||||
|
[[au^to]] x = Foo<int>();
|
||||||
|
}
|
||||||
|
)cpp",
|
||||||
|
[](HoverInfo &HI) {
|
||||||
|
HI.Name = "class Foo<int>";
|
||||||
|
HI.Kind = SymbolKind::Variable;
|
||||||
|
}},
|
||||||
|
// auto on specialized template
|
||||||
|
{R"cpp(
|
||||||
|
template<typename T> class Foo{};
|
||||||
|
template<> class Foo<int>{};
|
||||||
|
void foo() {
|
||||||
|
[[au^to]] x = Foo<int>();
|
||||||
|
}
|
||||||
|
)cpp",
|
||||||
|
[](HoverInfo &HI) {
|
||||||
|
HI.Name = "class Foo<int>";
|
||||||
|
HI.Kind = SymbolKind::Variable;
|
||||||
|
}},
|
||||||
|
|
||||||
|
// macro
|
||||||
|
{R"cpp(
|
||||||
|
// Best MACRO ever.
|
||||||
|
#define MACRO(x,y,z) void foo(x, y, z);
|
||||||
|
[[MAC^RO]](int, double d, bool z = false);
|
||||||
|
)cpp",
|
||||||
|
[](HoverInfo &HI) {
|
||||||
|
HI.Name = "MACRO", HI.Kind = SymbolKind::String,
|
||||||
|
HI.Definition = "#define MACRO(x, y, z) void foo(x, y, z);";
|
||||||
|
}},
|
||||||
|
};
|
||||||
|
for (const auto &Case : Cases) {
|
||||||
|
SCOPED_TRACE(Case.Code);
|
||||||
|
|
||||||
|
Annotations T(Case.Code);
|
||||||
|
TestTU TU = TestTU::withCode(T.code());
|
||||||
|
TU.ExtraArgs.push_back("-std=c++17");
|
||||||
|
auto AST = TU.build();
|
||||||
|
ASSERT_TRUE(AST.getDiagnostics().empty());
|
||||||
|
|
||||||
|
auto H = getHover(AST, T.point(), format::getLLVMStyle());
|
||||||
|
ASSERT_TRUE(H);
|
||||||
|
HoverInfo Expected;
|
||||||
|
Expected.SymRange = T.range();
|
||||||
|
Case.ExpectedBuilder(Expected);
|
||||||
|
|
||||||
|
EXPECT_EQ(H->NamespaceScope, Expected.NamespaceScope);
|
||||||
|
EXPECT_EQ(H->LocalScope, Expected.LocalScope);
|
||||||
|
EXPECT_EQ(H->Name, Expected.Name);
|
||||||
|
EXPECT_EQ(H->Kind, Expected.Kind);
|
||||||
|
EXPECT_EQ(H->Documentation, Expected.Documentation);
|
||||||
|
EXPECT_EQ(H->Definition, Expected.Definition);
|
||||||
|
EXPECT_EQ(H->Type, Expected.Type);
|
||||||
|
EXPECT_EQ(H->ReturnType, Expected.ReturnType);
|
||||||
|
EXPECT_EQ(H->Parameters, Expected.Parameters);
|
||||||
|
EXPECT_EQ(H->TemplateParameters, Expected.TemplateParameters);
|
||||||
|
EXPECT_EQ(H->SymRange, Expected.SymRange);
|
||||||
|
}
|
||||||
|
} // namespace clang
|
||||||
|
|
||||||
TEST(Hover, All) {
|
TEST(Hover, All) {
|
||||||
struct OneTest {
|
struct OneTest {
|
||||||
StringRef Input;
|
StringRef Input;
|
||||||
|
@ -591,7 +893,7 @@ TEST(Hover, All) {
|
||||||
int test1 = bonjour;
|
int test1 = bonjour;
|
||||||
}
|
}
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in function main\n\nint bonjour",
|
"Declared in main\n\nint bonjour",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Local variable in method
|
R"cpp(// Local variable in method
|
||||||
|
@ -602,7 +904,7 @@ TEST(Hover, All) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in function s::method\n\nint bonjour",
|
"Declared in s::method\n\nint bonjour",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Struct
|
R"cpp(// Struct
|
||||||
|
@ -613,7 +915,7 @@ TEST(Hover, All) {
|
||||||
ns1::My^Class* Params;
|
ns1::My^Class* Params;
|
||||||
}
|
}
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in namespace ns1\n\nstruct MyClass {}",
|
"Declared in ns1\n\nstruct MyClass {}",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Class
|
R"cpp(// Class
|
||||||
|
@ -624,7 +926,7 @@ TEST(Hover, All) {
|
||||||
ns1::My^Class* Params;
|
ns1::My^Class* Params;
|
||||||
}
|
}
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in namespace ns1\n\nclass MyClass {}",
|
"Declared in ns1\n\nclass MyClass {}",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Union
|
R"cpp(// Union
|
||||||
|
@ -635,7 +937,7 @@ TEST(Hover, All) {
|
||||||
ns1::My^Union Params;
|
ns1::My^Union Params;
|
||||||
}
|
}
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in namespace ns1\n\nunion MyUnion {}",
|
"Declared in ns1\n\nunion MyUnion {}",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Function definition via pointer
|
R"cpp(// Function definition via pointer
|
||||||
|
@ -663,7 +965,7 @@ TEST(Hover, All) {
|
||||||
bar.^x;
|
bar.^x;
|
||||||
}
|
}
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in struct Foo\n\nint x",
|
"Declared in Foo\n\nint x",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Field with initialization
|
R"cpp(// Field with initialization
|
||||||
|
@ -673,7 +975,7 @@ TEST(Hover, All) {
|
||||||
bar.^x;
|
bar.^x;
|
||||||
}
|
}
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in struct Foo\n\nint x = 5",
|
"Declared in Foo\n\nint x = 5",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Static field
|
R"cpp(// Static field
|
||||||
|
@ -682,7 +984,7 @@ TEST(Hover, All) {
|
||||||
Foo::^x;
|
Foo::^x;
|
||||||
}
|
}
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in struct Foo\n\nstatic int x",
|
"Declared in Foo\n\nstatic int x",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Field, member initializer
|
R"cpp(// Field, member initializer
|
||||||
|
@ -691,7 +993,7 @@ TEST(Hover, All) {
|
||||||
Foo() : ^x(0) {}
|
Foo() : ^x(0) {}
|
||||||
};
|
};
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in struct Foo\n\nint x",
|
"Declared in Foo\n\nint x",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Field, GNU old-style field designator
|
R"cpp(// Field, GNU old-style field designator
|
||||||
|
@ -700,7 +1002,7 @@ TEST(Hover, All) {
|
||||||
Foo bar = { ^x : 1 };
|
Foo bar = { ^x : 1 };
|
||||||
}
|
}
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in struct Foo\n\nint x",
|
"Declared in Foo\n\nint x",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Field, field designator
|
R"cpp(// Field, field designator
|
||||||
|
@ -709,7 +1011,7 @@ TEST(Hover, All) {
|
||||||
Foo bar = { .^x = 2 };
|
Foo bar = { .^x = 2 };
|
||||||
}
|
}
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in struct Foo\n\nint x",
|
"Declared in Foo\n\nint x",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Method call
|
R"cpp(// Method call
|
||||||
|
@ -719,7 +1021,7 @@ TEST(Hover, All) {
|
||||||
bar.^x();
|
bar.^x();
|
||||||
}
|
}
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in struct Foo\n\nint x()",
|
"Declared in Foo\n\nint x()",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Static method call
|
R"cpp(// Static method call
|
||||||
|
@ -728,7 +1030,7 @@ TEST(Hover, All) {
|
||||||
Foo::^x();
|
Foo::^x();
|
||||||
}
|
}
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in struct Foo\n\nstatic int x()",
|
"Declared in Foo\n\nstatic int x()",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Typedef
|
R"cpp(// Typedef
|
||||||
|
@ -746,7 +1048,7 @@ TEST(Hover, All) {
|
||||||
} // namespace ns
|
} // namespace ns
|
||||||
int main() { ^ns::Foo::bar(); }
|
int main() { ^ns::Foo::bar(); }
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in global namespace\n\nnamespace ns {\n}",
|
"Declared in global namespace\n\nnamespace ns {}",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Anonymous namespace
|
R"cpp(// Anonymous namespace
|
||||||
|
@ -757,7 +1059,7 @@ TEST(Hover, All) {
|
||||||
} // namespace ns
|
} // namespace ns
|
||||||
int main() { ns::f^oo++; }
|
int main() { ns::f^oo++; }
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in namespace ns::(anonymous)\n\nint foo",
|
"Declared in ns::(anonymous)\n\nint foo",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Macro
|
R"cpp(// Macro
|
||||||
|
@ -783,9 +1085,8 @@ TEST(Hover, All) {
|
||||||
}
|
}
|
||||||
int main() ^MACRO
|
int main() ^MACRO
|
||||||
)cpp",
|
)cpp",
|
||||||
R"cpp(#define MACRO {\
|
"#define MACRO "
|
||||||
return 0;\
|
" \\\n { return 0; }",
|
||||||
})cpp",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Forward class declaration
|
R"cpp(// Forward class declaration
|
||||||
|
@ -812,7 +1113,7 @@ TEST(Hover, All) {
|
||||||
Hel^lo hello = ONE;
|
Hel^lo hello = ONE;
|
||||||
}
|
}
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in global namespace\n\nenum Hello {\n}",
|
"Declared in global namespace\n\nenum Hello {}",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Enumerator
|
R"cpp(// Enumerator
|
||||||
|
@ -823,7 +1124,7 @@ TEST(Hover, All) {
|
||||||
Hello hello = O^NE;
|
Hello hello = O^NE;
|
||||||
}
|
}
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in enum Hello\n\nONE",
|
"Declared in Hello\n\nONE",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Enumerator in anonymous enum
|
R"cpp(// Enumerator in anonymous enum
|
||||||
|
@ -834,7 +1135,7 @@ TEST(Hover, All) {
|
||||||
int hello = O^NE;
|
int hello = O^NE;
|
||||||
}
|
}
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in enum (anonymous)\n\nONE",
|
"Declared in global namespace\n\nONE",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Global variable
|
R"cpp(// Global variable
|
||||||
|
@ -854,7 +1155,7 @@ TEST(Hover, All) {
|
||||||
ns1::he^y++;
|
ns1::he^y++;
|
||||||
}
|
}
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in namespace ns1\n\nstatic int hey = 10",
|
"Declared in ns1\n\nstatic int hey = 10",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Field in anonymous struct
|
R"cpp(// Field in anonymous struct
|
||||||
|
@ -865,7 +1166,7 @@ TEST(Hover, All) {
|
||||||
s.he^llo++;
|
s.he^llo++;
|
||||||
}
|
}
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in struct (anonymous)\n\nint hello",
|
"Declared in (anonymous struct)\n\nint hello",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Templated function
|
R"cpp(// Templated function
|
||||||
|
@ -886,7 +1187,7 @@ TEST(Hover, All) {
|
||||||
};
|
};
|
||||||
void g() { struct outer o; o.v.d^ef++; }
|
void g() { struct outer o; o.v.d^ef++; }
|
||||||
)cpp",
|
)cpp",
|
||||||
"Declared in union outer::(anonymous)\n\nint def",
|
"Declared in outer::(anonymous union)\n\nint def",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
R"cpp(// Nothing
|
R"cpp(// Nothing
|
||||||
|
@ -1194,9 +1495,9 @@ TEST(Hover, All) {
|
||||||
TestTU TU = TestTU::withCode(T.code());
|
TestTU TU = TestTU::withCode(T.code());
|
||||||
TU.ExtraArgs.push_back("-std=c++17");
|
TU.ExtraArgs.push_back("-std=c++17");
|
||||||
auto AST = TU.build();
|
auto AST = TU.build();
|
||||||
if (auto H = getHover(AST, T.point())) {
|
if (auto H = getHover(AST, T.point(), format::getLLVMStyle())) {
|
||||||
EXPECT_NE("", Test.ExpectedHover) << Test.Input;
|
EXPECT_NE("", Test.ExpectedHover) << Test.Input;
|
||||||
EXPECT_EQ(H->contents.value, Test.ExpectedHover.str()) << Test.Input;
|
EXPECT_EQ(H->render().value, Test.ExpectedHover.str()) << Test.Input;
|
||||||
} else
|
} else
|
||||||
EXPECT_EQ("", Test.ExpectedHover.str()) << Test.Input;
|
EXPECT_EQ("", Test.ExpectedHover.str()) << Test.Input;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue