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 @@ -842,7 +842,20 @@ 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); + Hover H; + H.range = HI->SymRange; + H.contents = HI->render(); + return Reply(H); + }, + 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 @@ -456,8 +456,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,7 +16,10 @@ #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 "llvm/Support/raw_ostream.h" #include namespace clang { @@ -46,8 +49,63 @@ std::vector findDocumentHighlights(ParsedAST &AST, 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 class Foo {}; + struct Param { + /// The pretty-printed parameter type, e.g. "int", or "typename" (in + /// TemplateParameters) + llvm::Optional Type; + /// None for unnamed parameters. + llvm::Optional Name; + /// None if no default is provided. + llvm::Optional 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 + + /// Contains all of the enclosing namespaces. + std::string NamespaceScope; + /// Remaining named contexts in symbol's qualified name. + std::string LocalScope; + /// Name of the symbol, does not contain any "::". + std::string Name; + llvm::Optional 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 Type; + /// Set for functions and lambadas. + llvm::Optional ReturnType; + /// Set for functions, lambdas and macros with parameters. + llvm::Optional> Parameters; + /// Set for all templates(function, class, variable). + llvm::Optional> TemplateParameters; + + /// Lower to LSP struct. + MarkupContent render() const; +}; +llvm::raw_ostream &operator<<(llvm::raw_ostream &, const HoverInfo::Param &); + /// 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,21 +7,33 @@ //===----------------------------------------------------------------------===// #include "XRefs.h" #include "AST.h" +#include "CodeCompletionStrings.h" #include "FindSymbols.h" #include "Logger.h" +#include "Protocol.h" #include "SourceCode.h" #include "URI.h" #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/LLVM.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/ADT/StringExtras.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 +253,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 +311,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 +332,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 +465,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 +537,171 @@ 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 std::string printDefinition(const Decl *D) { + std::string Definition; + { + llvm::raw_string_ostream OS(Definition); + PrintingPolicy Policy = + printingPolicyForDecls(D->getASTContext().getPrintingPolicy()); + D->print(OS, Policy); + } + return Definition; +} - // Generate the "Declared in" section. - if (NamedScope) { - assert(!NamedScope->empty()); +static void printParams(llvm::raw_ostream &OS, + const std::vector &Params) { + for (size_t I = 0, E = Params.size(); I != E; ++I) { + if (I) + OS << ", "; + OS << Params.at(I); + } +} - H.contents.value += "Declared in "; - H.contents.value += *NamedScope; - H.contents.value += "\n\n"; +static std::vector +fetchTemplateParameters(const TemplateParameterList *Params, + const PrintingPolicy &PP) { + assert(Params); + std::vector TempParameters; + + for (const Decl *Param : *Params) { + HoverInfo::Param P; + P.Type.emplace(); + if (const auto TTP = dyn_cast(Param)) { + P.Type = TTP->wasDeclaredWithTypename() ? "typename" : "class"; + if (TTP->isParameterPack()) + *P.Type += "..."; + + P.Name.emplace(); + llvm::raw_string_ostream Out(*P.Name); + Out << *TTP; + if (TTP->hasDefaultArgument()) + P.Default = TTP->getDefaultArgument().getAsString(PP); + } else if (const auto NTTP = dyn_cast(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(Param)) { + llvm::raw_string_ostream OS(*P.Type); + OS << "template <"; + printParams( + OS, fetchTemplateParameters( + TTPD->getDescribedTemplate()->getTemplateParameters(), PP)); + OS << "> class"; // FIXME: TemplateTemplateParameter doesn't store the + // info on whether this param was a "typename" or + // "class". + 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)); } - // We want to include the template in the Hover. - if (TemplateDecl *TD = D->getDescribedTemplate()) - D = TD; + return TempParameters; +} - std::string DeclText; - llvm::raw_string_ostream OS(DeclText); +/// Generate a \p Hover object given the declaration \p D. +static HoverInfo getHoverContents(const Decl *D) { + HoverInfo HI; PrintingPolicy Policy = printingPolicyForDecls(D->getASTContext().getPrintingPolicy()); + if (const NamedDecl *ND = llvm::dyn_cast(D)) { + HI.Documentation = getDeclComment(ND->getASTContext(), *ND); + HI.Name = printName(D->getASTContext(), *ND); + HI.NamespaceScope = splitQualifiedName(printQualifiedName(*ND)).first.str(); + } - D->print(OS, Policy); + if (llvm::Optional NamedScope = getScopeName(D)) { + assert(!NamedScope->empty()); + HI.LocalScope = std::move(*NamedScope); + } + HI.Kind = indexSymbolKindToSymbolKind(index::getSymbolInfo(D).Kind); - OS.flush(); + // Fill in template params. + if (const TemplateDecl *TD = D->getDescribedTemplate()) { + HI.TemplateParameters = + fetchTemplateParameters(TD->getTemplateParameters(), Policy); + D = TD; + } else if (const FunctionDecl *FD = D->getAsFunction()) { + if (const auto FTD = FD->getDescribedTemplate()) { + HI.TemplateParameters = + fetchTemplateParameters(FTD->getTemplateParameters(), Policy); + D = FTD; + } + } - H.contents.value += DeclText; - return H; + // Fill in types and params. + if (const FunctionDecl *FD = D->getAsFunction()) { + HI.ReturnType.emplace(); + llvm::raw_string_ostream OS(*HI.ReturnType); + FD->getReturnType().print(OS, Policy); + + HI.Parameters.emplace(); + for (const ParmVarDecl *PVD : FD->parameters()) { + 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); + } + 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); + } + } + // FIXME: handle variadics. + } else if (const auto *DD = dyn_cast(D)) { + HI.Type.emplace(); + llvm::raw_string_ostream OS(*HI.Type); + DD->getType().print(OS, Policy); + } + + HI.Definition = printDefinition(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); +static HoverInfo getHoverContents(QualType T, ASTContext &ASTCtx) { + llvm::errs() << "Printing type: "; + T.dump(); + HoverInfo HI; + llvm::raw_string_ostream OS(HI.Name); PrintingPolicy Policy = printingPolicyForDecls(ASTCtx.getPrintingPolicy()); T.print(OS, Policy); - OS.flush(); - H.contents.value += TypeText; - return H; + + if (const TagDecl *TD = T.getTypePtr()->getAsTagDecl()) { + HI.NamespaceScope = splitQualifiedName(printQualifiedName(*TD)).first.str(); + HI.Kind = indexSymbolKindToSymbolKind(index::getSymbolInfo(TD).Kind); + HI.Definition = printDefinition(TD); + // FIXME: Populate documentation + } + 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.Name = Decl.Name; + HI.Kind = indexSymbolKindToSymbolKind( + index::getSymbolInfoForMacro(*Decl.Info).Kind); + // FIXME: Populate documentation // Try to get the full definition, not just the name SourceLocation StartLoc = Decl.Info->getDefinitionLoc(); @@ -586,14 +715,12 @@ 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 = + ("#define " + Buffer.substr(StartOffset, EndOffset - StartOffset)) + .str(); } } - - Hover H; - H.contents.kind = MarkupKind::PlainText; - H.contents.value = "#define " + Definition; - return H; + return HI; } namespace { @@ -708,7 +835,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 +875,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 +1118,38 @@ return Result; } +template class initializer_list {}; + +MarkupContent HoverInfo::render() const { + MarkupContent Content; + Content.kind = MarkupKind::PlainText; + std::vector Output; + + if (!LocalScope.empty()) + Content.value = llvm::formatv("Declared in {0}\n\n", LocalScope); + + 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 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 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 @@ -937,7 +937,7 @@ ^auto i = {1,2}; } )cpp", - "class std::initializer_list", + "template<> class initializer_list {}", }, { R"cpp(// User defined conversion to auto @@ -1012,7 +1012,7 @@ return Bar(); } )cpp", - "struct Bar", + "struct Bar {}", }, { R"cpp(// trailing return type @@ -1021,7 +1021,7 @@ return Bar(); } )cpp", - "struct Bar", + "struct Bar {}", }, { R"cpp(// auto in function return @@ -1030,7 +1030,7 @@ return Bar(); } )cpp", - "struct Bar", + "struct Bar {}", }, { R"cpp(// auto& in function return @@ -1039,7 +1039,7 @@ return Bar(); } )cpp", - "struct Bar", + "struct Bar {}", }, { R"cpp(// auto* in function return @@ -1049,7 +1049,7 @@ return bar; } )cpp", - "struct Bar", + "struct Bar {}", }, { R"cpp(// const auto& in function return @@ -1058,7 +1058,7 @@ return Bar(); } )cpp", - "struct Bar", + "struct Bar {}", }, { R"cpp(// decltype(auto) in function return @@ -1067,7 +1067,7 @@ return Bar(); } )cpp", - "struct Bar", + "struct Bar {}", }, { R"cpp(// decltype(auto) reference in function return @@ -1136,7 +1136,7 @@ ^decltype(test()) i = test(); } )cpp", - "struct Bar", + "struct Bar {}", }, { R"cpp(// decltype of var with decltype. @@ -1185,7 +1185,7 @@ 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(H->render().value, Test.ExpectedHover.str()) << Test.Input; } else EXPECT_EQ("", Test.ExpectedHover.str()) << Test.Input; }