diff --git a/clang-tools-extra/clangd/Hover.h b/clang-tools-extra/clangd/Hover.h --- a/clang-tools-extra/clangd/Hover.h +++ b/clang-tools-extra/clangd/Hover.h @@ -18,6 +18,14 @@ namespace clang { namespace clangd { +struct UsedSymbol { + std::string Name; + Position Pos; +}; +inline bool operator<(const UsedSymbol &FstSym, const UsedSymbol &SndSym) { + return FstSym.Name < SndSym.Name; +} + /// 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 @@ -96,6 +104,8 @@ // Set when symbol is inside function call. Contains information extracted // from the callee definition about the argument this is passed as. std::optional CalleeArgInfo; + + std::optional> UsedSymbols; struct PassType { // How the variable is passed to callee. enum PassMode { Ref, ConstRef, Value }; diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -13,8 +13,10 @@ #include "Config.h" #include "FindTarget.h" #include "ParsedAST.h" +#include "Protocol.h" #include "Selection.h" #include "SourceCode.h" +#include "clang-include-cleaner/Analysis.h" #include "index/SymbolCollector.h" #include "support/Markup.h" #include "clang/AST/ASTContext.h" @@ -1084,6 +1086,81 @@ return Candidates.front(); } +// FIXME(bakalova): Remove after merging https://reviews.llvm.org/D143496 +std::vector +collectMacroReferences(ParsedAST &AST) { + const auto &SM = AST.getSourceManager(); + // FIXME: !!this is a hacky way to collect macro references. + std::vector Macros; + auto &PP = AST.getPreprocessor(); + for (const syntax::Token &Tok : + AST.getTokens().spelledTokens(SM.getMainFileID())) { + auto Macro = locateMacroAt(Tok, PP); + if (!Macro) + continue; + if (auto DefLoc = Macro->Info->getDefinitionLoc(); DefLoc.isValid()) + Macros.push_back( + {Tok.location(), + include_cleaner::Macro{/*Name=*/PP.getIdentifierInfo(Tok.text(SM)), + DefLoc}, + include_cleaner::RefType::Explicit}); + } + return Macros; +} + +void addUsedSymbols(const Inclusion &Inc, ParsedAST &AST, + const std::vector &Macros, + HoverInfo &HI) { + const SourceManager &SM = AST.getSourceManager(); + std::set UsedSymbols; + include_cleaner::walkUsed( + AST.getLocalTopLevelDecls(), Macros, AST.getPragmaIncludes(), SM, + [&](const include_cleaner::SymbolReference &Ref, + llvm::ArrayRef Providers) { + if (Ref.RT != include_cleaner::RefType::Explicit || + !Ref.RefLocation.isFileID() || + !SM.isWrittenInMainFile(SM.getSpellingLoc(Ref.RefLocation)) + ) { + return; + } + std::string Name; + switch (Ref.Target.kind()) { + case include_cleaner::Symbol::Declaration: + Name = cast(Ref.Target.declaration()) + .getDeclName() + .getAsString(); + break; + case include_cleaner::Symbol::Macro: + Name = Ref.Target.macro().Name->getName(); + break; + } + + Position Pos = sourceLocToPosition(SM, Ref.RefLocation); + for (const include_cleaner::Header &H : Providers) { + switch (H.kind()) { + case include_cleaner::Header::Physical: + if (H.physical()->tryGetRealPathName() == Inc.Resolved) { + UsedSymbols.insert({Name, Pos}); + } + break; + case include_cleaner::Header::Standard: + if (Inc.Written == H.standard().name()) { + UsedSymbols.insert({Name, Pos}); + } + break; + case include_cleaner::Header::Verbatim: + if (Inc.Written == H.verbatim()) { + UsedSymbols.insert({Name, Pos}); + } + break; + } + } + }); + + if (!UsedSymbols.empty()) { + HI.UsedSymbols = std::optional>{std::move(UsedSymbols)}; + } +} } // namespace std::optional getHover(ParsedAST &AST, Position Pos, @@ -1103,6 +1180,7 @@ if (TokensTouchingCursor.empty()) return std::nullopt; + const auto &Macros = collectMacroReferences(AST); // Show full header file path if cursor is on include directive. for (const auto &Inc : AST.getIncludeStructure().MainFileIncludes) { if (Inc.Resolved.empty() || Inc.HashLine != Pos.line) @@ -1113,6 +1191,7 @@ HI.Definition = URIForFile::canonicalize(Inc.Resolved, AST.tuPath()).file().str(); HI.DefinitionLanguage = ""; + addUsedSymbols(Inc, AST, Macros, HI); return HI; } @@ -1313,6 +1392,25 @@ Output.addCodeBlock(Buffer, DefinitionLanguage); } + if (UsedSymbols) { + Output.addRuler(); + markup::Paragraph &Title = Output.addParagraph(); + Title.appendText("Provides symbols used in this file:"); + markup::BulletList &BL = Output.addBulletList(); + + for (const auto &Sym : *UsedSymbols) { + markup::Document &D = BL.addItem(); + markup::Paragraph &P = D.addParagraph(); + P.appendCode(Sym.Name); + P.appendSpace(); + P.appendText("[Ln: "); + P.appendText(std::to_string(Sym.Pos.line)); + P.appendText(", Col: "); + P.appendText(std::to_string(Sym.Pos.character)); + P.appendText("]"); + } + } + return Output; }