diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -839,7 +839,17 @@ void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params, Callback> Reply) { Server->findHover(Params.textDocument.uri.file(), Params.position, - std::move(Reply)); + Bind( + [](decltype(Reply) Reply, + llvm::Expected> 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( diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -180,7 +180,7 @@ /// Get code hover for a given position. void findHover(PathRef File, Position Pos, - Callback> CB); + Callback> CB); /// Get information about type hierarchy for a given position. void typeHierarchy(PathRef File, Position Pos, int Resolve, diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -527,8 +527,8 @@ } void ClangdServer::findHover(PathRef File, Position Pos, - Callback> CB) { - auto Action = [Pos](Callback> CB, + Callback> CB) { + auto Action = [Pos](Callback> CB, llvm::Expected InpAST) { if (!InpAST) return CB(InpAST.takeError()); diff --git a/clang-tools-extra/clangd/XRefs.h b/clang-tools-extra/clangd/XRefs.h --- a/clang-tools-extra/clangd/XRefs.h +++ b/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 @@ -46,8 +48,24 @@ std::vector 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 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 getHover(ParsedAST &AST, Position Pos); +llvm::Optional 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). diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/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 makeLocation(ParsedAST &AST, SourceLocation TokLoc, +llvm::Optional 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 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 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 NamedScope = getScopeName(D); +static QualType getDeclType(const Decl *D) { + if (const TypedefNameDecl *TDD = dyn_cast(D)) + return TDD->getUnderlyingType(); + if (const ValueDecl *VD = dyn_cast(D)) + return VD->getType(); + return QualType(); +} - // Generate the "Declared in" section. - if (NamedScope) { - assert(!NamedScope->empty()); +static llvm::Optional 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(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(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(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(D)) { + HI.Sym.Name = printName(D->getASTContext(), *ND); + HI.Documentation = getDeclComment(ND->getASTContext(), *ND); + } else if (const TypeDecl *TD = llvm::dyn_cast(D)) { + HI.Sym.Name = TD->getNameAsString(); + } - OS.flush(); + if (llvm::Optional 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 getHover(ParsedAST &AST, Position Pos) { +llvm::Optional 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 diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/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; }