kadircet created this revision.
kadircet added a reviewer: sammccall.
Herald added subscribers: cfe-commits, arphaman, jkorous, MaskRay,
ilya-biryukov.
Herald added a project: clang.
Change ClangdServer layer to output a structured response for Hover,
which can be rendered by client according to their needs.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D61497
Files:
clang-tools-extra/clangd/ClangdLSPServer.cpp
clang-tools-extra/clangd/ClangdServer.cpp
clang-tools-extra/clangd/ClangdServer.h
clang-tools-extra/clangd/XRefs.cpp
clang-tools-extra/clangd/XRefs.h
clang-tools-extra/clangd/unittests/XRefsTests.cpp
Index: clang-tools-extra/clangd/unittests/XRefsTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -1185,7 +1185,8 @@
auto AST = TU.build();
if (auto H = getHover(AST, T.point())) {
EXPECT_NE("", Test.ExpectedHover) << Test.Input;
- EXPECT_EQ(H->contents.value, Test.ExpectedHover.str()) << Test.Input;
+ EXPECT_EQ(render(*H).contents.value, Test.ExpectedHover.str())
+ << Test.Input;
} else
EXPECT_EQ("", Test.ExpectedHover.str()) << Test.Input;
}
Index: clang-tools-extra/clangd/XRefs.h
===================================================================
--- clang-tools-extra/clangd/XRefs.h
+++ clang-tools-extra/clangd/XRefs.h
@@ -16,6 +16,8 @@
#include "ClangdUnit.h"
#include "Protocol.h"
#include "index/Index.h"
+#include "index/SymbolLocation.h"
+#include "clang/Index/IndexSymbol.h"
#include "llvm/ADT/Optional.h"
#include <vector>
@@ -46,8 +48,24 @@
std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
Position Pos);
+struct HoverInfo {
+ LocatedSymbol Sym;
+ /// Name of the context containing the symbol.
+ std::string ContainerName;
+ index::SymbolInfo SI;
+ /// Includes all the qualifiers.
+ std::string Type;
+ /// Empty for non-functions. First element is the return type.
+ std::vector<LocatedSymbol> Signature;
+ std::string Documentation;
+ /// Line containing the definition of the symbol.
+ std::string Definition;
+ std::string TemplateArgs;
+};
+Hover render(const HoverInfo &HI);
+
/// 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);
/// Returns reference locations of the symbol at a specified \p Pos.
/// \p Limit limits the number of results returned (0 means no limit).
Index: clang-tools-extra/clangd/XRefs.cpp
===================================================================
--- clang-tools-extra/clangd/XRefs.cpp
+++ clang-tools-extra/clangd/XRefs.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "XRefs.h"
#include "AST.h"
+#include "CodeCompletionStrings.h"
#include "FindSymbols.h"
#include "Logger.h"
#include "SourceCode.h"
@@ -14,14 +15,22 @@
#include "index/Merge.h"
#include "index/SymbolCollector.h"
#include "index/SymbolLocation.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Type.h"
+#include "clang/Basic/SourceLocation.h"
#include "clang/Index/IndexDataConsumer.h"
#include "clang/Index/IndexSymbol.h"
#include "clang/Index/IndexingAction.h"
#include "clang/Index/USRGeneration.h"
+#include "llvm/ADT/None.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
namespace clang {
namespace clangd {
@@ -241,17 +250,17 @@
return {DeclMacrosFinder.getFoundDecls(), DeclMacrosFinder.takeMacroInfos()};
}
-Range getTokenRange(ParsedAST &AST, SourceLocation TokLoc) {
- const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
- SourceLocation LocEnd = Lexer::getLocForEndOfToken(
- TokLoc, 0, SourceMgr, AST.getASTContext().getLangOpts());
+Range getTokenRange(ASTContext &AST, SourceLocation TokLoc) {
+ const SourceManager &SourceMgr = AST.getSourceManager();
+ SourceLocation LocEnd =
+ Lexer::getLocForEndOfToken(TokLoc, 0, SourceMgr, AST.getLangOpts());
return {sourceLocToPosition(SourceMgr, TokLoc),
sourceLocToPosition(SourceMgr, LocEnd)};
}
-llvm::Optional<Location> makeLocation(ParsedAST &AST, SourceLocation TokLoc,
+llvm::Optional<Location> makeLocation(ASTContext &AST, SourceLocation TokLoc,
llvm::StringRef TUPath) {
- const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+ const SourceManager &SourceMgr = AST.getSourceManager();
const FileEntry *F = SourceMgr.getFileEntryForID(SourceMgr.getFileID(TokLoc));
if (!F)
return None;
@@ -299,8 +308,8 @@
// As a consequence, there's no need to look them up in the index either.
std::vector<LocatedSymbol> Result;
for (auto M : Symbols.Macros) {
- if (auto Loc =
- makeLocation(AST, M.Info->getDefinitionLoc(), *MainFilePath)) {
+ if (auto Loc = makeLocation(AST.getASTContext(), M.Info->getDefinitionLoc(),
+ *MainFilePath)) {
LocatedSymbol Macro;
Macro.Name = M.Name;
Macro.PreferredDeclaration = *Loc;
@@ -320,7 +329,7 @@
// Emit all symbol locations (declaration or definition) from AST.
for (const Decl *D : Symbols.Decls) {
- auto Loc = makeLocation(AST, findNameLoc(D), *MainFilePath);
+ auto Loc = makeLocation(AST.getASTContext(), findNameLoc(D), *MainFilePath);
if (!Loc)
continue;
@@ -453,7 +462,7 @@
std::vector<DocumentHighlight> Result;
for (const auto &Ref : References) {
DocumentHighlight DH;
- DH.range = getTokenRange(AST, Ref.Loc);
+ DH.range = getTokenRange(AST.getASTContext(), Ref.Loc);
if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Write))
DH.kind = DocumentHighlightKind::Write;
else if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Read))
@@ -525,54 +534,165 @@
return None;
}
-/// Generate a \p Hover object given the declaration \p D.
-static Hover getHoverContents(const Decl *D) {
- Hover H;
- llvm::Optional<std::string> NamedScope = getScopeName(D);
+static QualType getDeclType(const Decl *D) {
+ if (const TypedefNameDecl *TDD = dyn_cast<TypedefNameDecl>(D))
+ return TDD->getUnderlyingType();
+ if (const ValueDecl *VD = dyn_cast<ValueDecl>(D))
+ return VD->getType();
+ return QualType();
+}
- // Generate the "Declared in" section.
- if (NamedScope) {
- assert(!NamedScope->empty());
+static llvm::Optional<Location> getDeclLoc(const SourceLocation &SL,
+ ASTContext &AST) {
+ if (SL.isInvalid())
+ return llvm::None;
+ const auto &SM = AST.getSourceManager();
+ auto MainFilePath =
+ getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
+ if (!MainFilePath) {
+ elog("Couldn't get main file path for: {0}", SL.printToString(SM));
+ return llvm::None;
+ }
+ return makeLocation(AST, SL, *MainFilePath);
+}
- H.contents.value += "Declared in ";
- H.contents.value += *NamedScope;
- H.contents.value += "\n\n";
+static std::string getDefinitionLine(const Decl *D) {
+ std::string Definition;
+ {
+ llvm::raw_string_ostream OS(Definition);
+ PrintingPolicy Policy =
+ printingPolicyForDecls(D->getASTContext().getPrintingPolicy());
+ D->print(OS, Policy);
}
+ return Definition;
+}
- // We want to include the template in the Hover.
- if (TemplateDecl *TD = D->getDescribedTemplate())
- D = TD;
+// FIXME: Expose the implementation in DeclPrinter.
+static void printTemplateParameters(llvm::raw_ostream &Out,
+ const TemplateParameterList *Params,
+ const PrintingPolicy &PP) {
+ assert(Params);
+
+ Out << "template <";
+
+ for (unsigned I = 0, E = Params->size(); I != E; ++I) {
+ if (I != 0)
+ Out << ", ";
+
+ const Decl *Param = Params->getParam(I);
+ if (auto TTP = dyn_cast<TemplateTypeParmDecl>(Param)) {
+
+ if (TTP->wasDeclaredWithTypename())
+ Out << "typename ";
+ else
+ Out << "class ";
+
+ if (TTP->isParameterPack())
+ Out << "...";
+
+ Out << *TTP;
- std::string DeclText;
- llvm::raw_string_ostream OS(DeclText);
+ if (TTP->hasDefaultArgument()) {
+ Out << " = ";
+ Out << TTP->getDefaultArgument().getAsString(PP);
+ };
+ } else if (auto NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
+ StringRef Name;
+ if (IdentifierInfo *II = NTTP->getIdentifier())
+ Name = II->getName();
+ NTTP->print(Out, PP);
+
+ if (NTTP->hasDefaultArgument()) {
+ Out << " = ";
+ NTTP->getDefaultArgument()->printPretty(Out, nullptr, PP);
+ }
+ } else if (auto TTPD = dyn_cast<TemplateTemplateParmDecl>(Param)) {
+ TTPD->print(Out, PP);
+ // FIXME: print the default argument, if present.
+ }
+ }
+ Out << "> ";
+}
+
+/// Generate a \p Hover object given the declaration \p D.
+static HoverInfo getHoverContents(const Decl *D) {
PrintingPolicy Policy =
printingPolicyForDecls(D->getASTContext().getPrintingPolicy());
+ HoverInfo HI;
- D->print(OS, Policy);
+ if (const NamedDecl *ND = llvm::dyn_cast<NamedDecl>(D)) {
+ HI.Sym.Name = printName(D->getASTContext(), *ND);
+ HI.Documentation = getDeclComment(ND->getASTContext(), *ND);
+ } else if (const TypeDecl *TD = llvm::dyn_cast<TypeDecl>(D)) {
+ HI.Sym.Name = TD->getNameAsString();
+ }
- OS.flush();
+ if (llvm::Optional<std::string> NamedScope = getScopeName(D)) {
+ assert(!NamedScope->empty());
+ HI.ContainerName = std::move(*NamedScope);
+ }
+ HI.SI = index::getSymbolInfo(D);
- H.contents.value += DeclText;
- return H;
+ // We want to include the template in the Hover.
+ if (TemplateDecl *TD = D->getDescribedTemplate()) {
+ llvm::raw_string_ostream OS(HI.TemplateArgs);
+ printTemplateParameters(OS, TD->getTemplateParameters(), Policy);
+ D = TD;
+ }
+
+ {
+ if (auto FT = D->getFunctionType()) {
+ HI.Signature.emplace_back();
+ LocatedSymbol &RetType = HI.Signature.back();
+ llvm::raw_string_ostream OS(RetType.Name);
+ FT->getReturnType().print(OS, Policy);
+ // FIXME: Put parameters into signature.
+ }
+ llvm::raw_string_ostream OS(HI.Type);
+ auto QT = getDeclType(D);
+ QT.print(OS, std::move(Policy));
+ }
+
+ if (auto DefLoc = getDeclLoc(findNameLoc(D), D->getASTContext())) {
+ HI.Sym.PreferredDeclaration = *DefLoc;
+ if (getDefinition(D) == D)
+ HI.Sym.Definition = *DefLoc;
+ }
+ HI.Definition = getDefinitionLine(D);
+ return HI;
}
/// Generate a \p Hover object given the type \p T.
-static Hover getHoverContents(QualType T, ASTContext &ASTCtx) {
- Hover H;
- std::string TypeText;
- llvm::raw_string_ostream OS(TypeText);
- PrintingPolicy Policy = printingPolicyForDecls(ASTCtx.getPrintingPolicy());
- T.print(OS, Policy);
- OS.flush();
- H.contents.value += TypeText;
- return H;
+static HoverInfo getHoverContents(QualType T, ASTContext &ASTCtx) {
+ HoverInfo HI;
+ {
+ llvm::raw_string_ostream OS(HI.Type);
+ PrintingPolicy Policy = printingPolicyForDecls(ASTCtx.getPrintingPolicy());
+ T.print(OS, Policy);
+ }
+ if (const TagDecl *TD = T.getTypePtr()->getAsTagDecl()) {
+ if (auto ScopedName = getScopeName(TD))
+ HI.ContainerName = std::move(*ScopedName);
+ HI.SI = index::getSymbolInfo(TD);
+ // FIXME: Populate documentation
+ if (auto DefLoc = getDeclLoc(findNameLoc(TD), TD->getASTContext())) {
+ HI.Sym.PreferredDeclaration = *DefLoc;
+ if (getDefinition(TD) == TD)
+ HI.Sym.Definition = *DefLoc;
+ }
+ HI.Definition = getDefinitionLine(TD);
+ }
+ return HI;
}
/// 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();
- std::string Definition = Decl.Name;
+ HI.Sym.Name = Decl.Name;
+ HI.SI = index::getSymbolInfoForMacro(*Decl.Info);
+ // FIXME: Populate documentation
// Try to get the full definition, not just the name
SourceLocation StartLoc = Decl.Info->getDefinitionLoc();
@@ -586,14 +706,15 @@
unsigned StartOffset = SM.getFileOffset(StartLoc);
unsigned EndOffset = SM.getFileOffset(EndLoc);
if (EndOffset <= Buffer.size() && StartOffset < EndOffset)
- Definition = Buffer.substr(StartOffset, EndOffset - StartOffset).str();
+ HI.Definition =
+ Buffer.substr(StartOffset, EndOffset - StartOffset).str();
}
}
-
- Hover H;
- H.contents.kind = MarkupKind::PlainText;
- H.contents.value = "#define " + Definition;
- return H;
+ if (auto DefLoc = getDeclLoc(StartLoc, AST.getASTContext())) {
+ HI.Sym.PreferredDeclaration = *DefLoc;
+ HI.Sym.Definition = *DefLoc;
+ }
+ return HI;
}
namespace {
@@ -708,7 +829,7 @@
return V.getDeducedType();
}
-llvm::Optional<Hover> getHover(ParsedAST &AST, Position Pos) {
+llvm::Optional<HoverInfo> getHover(ParsedAST &AST, Position Pos) {
const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
SourceLocation SourceLocationBeg =
getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID());
@@ -748,7 +869,7 @@
auto MainFileRefs = findRefs(Symbols.Decls, AST);
for (const auto &Ref : MainFileRefs) {
Location Result;
- Result.range = getTokenRange(AST, Ref.Loc);
+ Result.range = getTokenRange(AST.getASTContext(), Ref.Loc);
Result.uri = URIForFile::canonicalize(*MainFilePath, *MainFilePath);
Results.push_back(std::move(Result));
}
@@ -991,5 +1112,39 @@
return Result;
}
+Hover render(const HoverInfo &HI) {
+ Hover H;
+ H.range = HI.Sym.PreferredDeclaration.range;
+ if (!HI.ContainerName.empty())
+ H.contents.value = llvm::formatv("Declared in {0}\n\n", HI.ContainerName);
+ if (!HI.TemplateArgs.empty())
+ H.contents.value += HI.TemplateArgs;
+
+ if (HI.SI.Kind == index::SymbolKind::Macro)
+ H.contents.value += "#define ";
+
+ if (HI.Signature.empty()) {
+ if (HI.Definition.empty()) {
+ H.contents.value += HI.Type;
+ if (!HI.Sym.Name.empty())
+ H.contents.value += " " + HI.Sym.Name;
+ } else {
+ H.contents.value += HI.Definition;
+ }
+ } else {
+ // Special handling for functions to put name between return type and
+ // parameters.
+ H.contents.value +=
+ llvm::formatv("{0} {1}(", HI.Signature.front().Name, HI.Sym.Name);
+ for (size_t I = 1, E = HI.Signature.size(); I != E; I++) {
+ H.contents.value += HI.Signature[I].Name;
+ if (I != E - 1)
+ H.contents.value += ", ";
+ }
+ H.contents.value += ")";
+ }
+ return H;
+}
+
} // namespace clangd
} // namespace clang
Index: clang-tools-extra/clangd/ClangdServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdServer.h
+++ clang-tools-extra/clangd/ClangdServer.h
@@ -180,7 +180,7 @@
/// Get code hover for a given position.
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.
void typeHierarchy(PathRef File, Position Pos, int Resolve,
Index: clang-tools-extra/clangd/ClangdServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdServer.cpp
+++ clang-tools-extra/clangd/ClangdServer.cpp
@@ -527,8 +527,8 @@
}
void ClangdServer::findHover(PathRef File, Position Pos,
- Callback<llvm::Optional<Hover>> CB) {
- auto Action = [Pos](Callback<llvm::Optional<Hover>> CB,
+ Callback<llvm::Optional<HoverInfo>> CB) {
+ auto Action = [Pos](Callback<llvm::Optional<HoverInfo>> CB,
llvm::Expected<InputsAndAST> InpAST) {
if (!InpAST)
return CB(InpAST.takeError());
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -839,7 +839,17 @@
void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params,
Callback<llvm::Optional<Hover>> Reply) {
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);
+ return Reply(render(std::move(*HI)));
+ },
+ std::move(Reply)));
}
void ClangdLSPServer::onTypeHierarchy(
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits