VitaNuo created this revision. Herald added subscribers: kadircet, arphaman. Herald added a project: All. VitaNuo requested review of this revision. Herald added a project: clang-tools-extra. Herald added a subscriber: cfe-commits.
Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D144862 Files: clang-tools-extra/clangd/ClangdLSPServer.cpp clang-tools-extra/clangd/ClangdLSPServer.h clang-tools-extra/clangd/ClangdServer.cpp clang-tools-extra/clangd/ClangdServer.h clang-tools-extra/clangd/Hover.cpp clang-tools-extra/clangd/Protocol.cpp clang-tools-extra/clangd/Protocol.h clang-tools-extra/clangd/XRefs.cpp clang-tools-extra/clangd/XRefs.h
Index: clang-tools-extra/clangd/XRefs.h =================================================================== --- clang-tools-extra/clangd/XRefs.h +++ clang-tools-extra/clangd/XRefs.h @@ -32,6 +32,8 @@ namespace clangd { class ParsedAST; +std::vector<CodeLens> findCodeLens(ParsedAST &AST); + // Describes where a symbol is declared and defined (as far as clangd knows). // There are three cases: // - a declaration only, no definition is known (e.g. only header seen) Index: clang-tools-extra/clangd/XRefs.cpp =================================================================== --- clang-tools-extra/clangd/XRefs.cpp +++ clang-tools-extra/clangd/XRefs.cpp @@ -16,6 +16,8 @@ #include "Selection.h" #include "SourceCode.h" #include "URI.h" +#include "clang-include-cleaner/Analysis.h" +#include "clang-include-cleaner/Types.h" #include "index/Index.h" #include "index/Merge.h" #include "index/Relation.h" @@ -62,9 +64,11 @@ #include "llvm/Support/raw_ostream.h" #include <optional> #include <vector> +#include <string> namespace clang { namespace clangd { +std::string HelloString = "Hello"; namespace { // Returns the single definition of the entity declared by D, if visible. @@ -74,6 +78,7 @@ // Kinds of nodes that always return nullptr here will not have definitions // reported by locateSymbolAt(). const NamedDecl *getDefinition(const NamedDecl *D) { + std::string AnotherString; assert(D); // Decl has one definition that we can find. if (const auto *TD = dyn_cast<TagDecl>(D)) @@ -118,6 +123,7 @@ // FindSymbols. std::optional<Location> toLSPLocation(const SymbolLocation &Loc, llvm::StringRef TUPath) { + std::string ThirdString; if (!Loc) return std::nullopt; auto Uri = URI::parse(Loc.FileURI); @@ -1213,8 +1219,50 @@ return std::nullopt; } +std::vector<include_cleaner::SymbolReference> +collectMacroReferences(ParsedAST &AST) { + const auto &SM = AST.getSourceManager(); + // FIXME: !!this is a hacky way to collect macro references. + std::vector<include_cleaner::SymbolReference> 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; +} + } // namespace +include_cleaner::Includes +convertIncludes(const SourceManager &SM, + const llvm::ArrayRef<Inclusion> MainFileIncludes) { + include_cleaner::Includes Includes; + for (const Inclusion &Inc : MainFileIncludes) { + include_cleaner::Include TransformedInc; + llvm::StringRef WrittenRef = llvm::StringRef(Inc.Written); + TransformedInc.Spelled = WrittenRef.trim("\"<>"); + TransformedInc.HashLocation = + SM.getComposedLoc(SM.getMainFileID(), Inc.HashOffset); + TransformedInc.Line = Inc.HashLine + 1; + TransformedInc.Angled = WrittenRef.starts_with("<"); + auto FE = SM.getFileManager().getFile(Inc.Resolved); + if (!FE) + continue; + TransformedInc.Resolved = *FE; + Includes.add(std::move(TransformedInc)); + } + return Includes; +} + std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST, Position Pos) { const SourceManager &SM = AST.getSourceManager(); @@ -1231,11 +1279,96 @@ DeclRelation::TemplatePattern | DeclRelation::Alias; auto TargetDecls = targetDecl(N->ASTNode, Relations, AST.getHeuristicResolver()); + + const IncludeStructure &Includes = AST.getIncludeStructure(); + include_cleaner::Includes ConvertedIncludes = + convertIncludes(AST.getSourceManager(), Includes.MainFileIncludes); + include_cleaner::walkUsed( + AST.getLocalTopLevelDecls(), + collectMacroReferences(AST), + AST.getPragmaIncludes(), + AST.getSourceManager(), + [&](const include_cleaner::SymbolReference &Ref, + llvm::ArrayRef<include_cleaner::Header> Providers) { + // std::string SymbolName; + // switch (Ref.Target.kind()) { + // case include_cleaner::Symbol::Macro: + // break; + // case include_cleaner::Symbol::Declaration: + // SymbolName = static_cast<const NamedDecl &>(Ref.Target.declaration()).getNameAsString(); + // if (SymbolName == "string") { + // llvm::errs() << "FOUND STRING" << "\n"; + // llvm::errs() << "CUR LOCATION: " << "\n"; + // llvm::errs() << SM.getDecomposedLoc(*CurLoc).second << "\n"; + // llvm::errs() << "REF LOCATION: " << "\n"; + // llvm::errs() << SM.getDecomposedLoc(Ref.RefLocation).second << "\n"; + // } + // break; + // } + if (Ref.RefLocation != *CurLoc) { + return; + } + llvm::errs() << "GOT PAST THE GUARD" << "\n"; + // the location under cursor corresponds to the current symbol reference + // need to find the position of the include + for (const auto &H : Providers) { + // switch (H.kind()) { + // case include_cleaner::Header::Physical: + // llvm::errs() << "It's a physical provider\n"; + // if (auto HeaderID = Includes.getID(H.physical())) { + // llvm::errs() << "Physical provider: " << Includes.getRealPath(*HeaderID) << "\n"; + // } + // break; + // case include_cleaner::Header::Standard: + // llvm::errs() << "It's a standard provider\n"; + // for (auto HeaderID : Includes.StdlibHeaders.lookup(H.standard())) { + // llvm::errs() << "Standard provider: " << Includes.getRealPath(HeaderID) << "\n"; + // } + // break; + // case include_cleaner::Header::Verbatim: + // llvm::errs() << "It's a verbatim provider\n"; + // for (auto Inc : Includes.MainFileIncludes) { + // if (auto HeaderID = Inc.HeaderID) { + // llvm::errs() << "Verbatim provider: " << + // Includes.getRealPath(static_cast<IncludeStructure::HeaderID>(*HeaderID)) << "\n"; + // } + // } + // } + for (auto Inc : Includes.MainFileIncludes) { + if (Inc.Written == "<string>") { + llvm::errs() << "Included in main: " << Inc.Written << "\n"; + } + } + for (auto *&Inc : ConvertedIncludes.match(H)) { + llvm::errs() << "Found match\n"; + auto FID = SM.getFileID(SM.getFileLoc(Inc->HashLocation)); + llvm::errs() << "FID: " << FID.getHashValue() << "\n"; + auto Highlight = toHighlight(Inc->HashLocation, AST.getTokens()); + llvm::errs() <<(Highlight.has_value() ? "Highlight" : "No highlight") << "\n"; + // found the include in the includes list + // navigate to location + if (auto Highlight = toHighlight(Inc->HashLocation, AST.getTokens())) { + for (unsigned i = 1; i < 17; i++) { + if (auto HighlightNext = toHighlight(SM.getComposedLoc(SM.getMainFileID(), + SM.getDecomposedLoc(Inc->HashLocation).second + i), AST.getTokens())) { + Result.push_back(*HighlightNext); + } + } + llvm::errs() << "COULD BUILD THE HIGHLIGHT FOR " << Inc->Spelled << "\n"; + Result.push_back(*Highlight); + } else { + llvm::errs() << "COULD NOT BUILD THE HIGHLIGHT FOR " << Inc->Spelled << "\n"; + } + } + } + }); + if (!TargetDecls.empty()) { // FIXME: we may get multiple DocumentHighlights with the same location // and different kinds, deduplicate them. for (const auto &Ref : findRefs(TargetDecls, AST, /*PerToken=*/true)) Result.push_back(toHighlight(Ref, SM)); + // TODO (bakalova) include-cleaner analysis here return true; } auto ControlFlow = relatedControlFlow(*N); @@ -1312,6 +1445,90 @@ } } // namespace +std::vector<CodeLens> findCodeLens(ParsedAST &AST) { + // TODO(bakalova) find ranges + // TODO(bakalova) find used symbols + + std::vector<CodeLens> Result; + std::vector<Inclusion> Includes = AST.getIncludeStructure().MainFileIncludes; + + IncludeSpelling S; + S.spelling = "string"; + for (auto Inclusion : Includes) { + std::set<std::string> UsedSymbols; + walkUsed(AST.getLocalTopLevelDecls(), + collectMacroReferences(AST), + AST.getPragmaIncludes(), + AST.getSourceManager(), + [&](const include_cleaner::SymbolReference &Ref, + llvm::ArrayRef<include_cleaner::Header> Providers) { + + std::string Name; + switch(Ref.Target.kind()) { + case include_cleaner::Symbol::Declaration: + Name = cast<NamedDecl>(Ref.Target.declaration()) + .getDeclName() + .getAsString(); + break; + case include_cleaner::Symbol::Macro: + Name = Ref.Target.macro().Name->getName(); + break; + } + for (const include_cleaner::Header &H : Providers) { + switch (H.kind()) { + case include_cleaner::Header::Physical: + if (H.physical()->tryGetRealPathName().contains( + llvm::StringRef(Inclusion.Written).trim("\"<>"))) { + UsedSymbols.insert(Name); + } + break; + case include_cleaner::Header::Standard: + if (Inclusion.Written == H.standard().name()) { + UsedSymbols.insert(Name); + } + break; + // TODO(bakalova) does the verbatim thing include quotes or not? + case include_cleaner::Header::Verbatim: + if (Inclusion.Written == H.verbatim()) { + UsedSymbols.insert(Name); + } + break; + } + } + }); + + CodeLens Lens; + Lens.range = clangd::Range{Position{Inclusion.HashLine, 0}, Position{Inclusion.HashLine, 0}}; + Command cmd; + + std::vector<std::string> UsedSymbolsDeduped; + for (auto Sym : UsedSymbols) { + UsedSymbolsDeduped.push_back(Sym); + } + + for (unsigned i = 0; i < 4 && i < UsedSymbolsDeduped.size(); i++) { + std::string Name = UsedSymbolsDeduped[i]; + cmd.title += Name; + cmd.title += " | "; + } + if (UsedSymbols.size() >= 5) { + cmd.title += UsedSymbolsDeduped[4]; + cmd.title += " | ..."; + } + + cmd.command = "HelloWorld"; + cmd.argument = toJSON(S); + Lens.command = cmd; + Result.push_back(Lens); + } + + + for (auto CodeLens : Result) { + llvm::errs() << "CODE LENS: " << CodeLens << "\n"; + } + return Result; +} + ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit, const SymbolIndex *Index, bool AddContext) { ReferencesResult Results; Index: clang-tools-extra/clangd/Protocol.h =================================================================== --- clang-tools-extra/clangd/Protocol.h +++ clang-tools-extra/clangd/Protocol.h @@ -976,6 +976,12 @@ bool fromJSON(const llvm::json::Value &, WorkspaceEdit &, llvm::json::Path); llvm::json::Value toJSON(const WorkspaceEdit &WE); +struct IncludeSpelling { + std::string spelling; +}; +bool fromJSON(const llvm::json::Value &, IncludeSpelling &, llvm::json::Path); +llvm::json::Value toJSON(const IncludeSpelling &IS); + /// Arguments for the 'applyTweak' command. The server sends these commands as a /// response to the textDocument/codeAction request. The client can later send a /// command back to the server if the user requests to execute a particular code @@ -1876,6 +1882,19 @@ llvm::json::Value toJSON(const ASTNode &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ASTNode &); +struct CodeLensParams { + TextDocumentIdentifier textDocument; +}; +bool fromJSON(const llvm::json::Value &, CodeLensParams &, + llvm::json::Path); + +struct CodeLens { + Range range; + Command command; +}; +llvm::json::Value toJSON(const CodeLens &); +llvm::raw_ostream &operator<<(llvm::raw_ostream &, const CodeLens &); + } // namespace clangd } // namespace clang Index: clang-tools-extra/clangd/Protocol.cpp =================================================================== --- clang-tools-extra/clangd/Protocol.cpp +++ clang-tools-extra/clangd/Protocol.cpp @@ -535,6 +535,17 @@ return O && O.map("textDocument", R.textDocument); } +bool fromJSON(const llvm::json::Value &Params, CodeLensParams &R, + llvm::json::Path P) { + llvm::json::ObjectMapper O(Params, P); + return O && O.map("textDocument", R.textDocument); +} + +bool fromJSON(const llvm::json::Value &Params, IncludeSpelling &R, llvm::json::Path P) { + llvm::json::ObjectMapper O(Params, P); + return O && O.map("spelling", R.spelling); +} + bool fromJSON(const llvm::json::Value &Params, DidSaveTextDocumentParams &R, llvm::json::Path P) { llvm::json::ObjectMapper O(Params, P); @@ -613,6 +624,22 @@ }; } +llvm::json::Value toJSON(const IncludeSpelling &IS) { + return llvm::json::Object{ + {"spelling", IS.spelling} + }; +} + +llvm::json::Value toJSON(const CodeLens &CL) { + return llvm::json::Object { + {"range", CL.range}, + {"command", CL.command} + }; +} +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const CodeLens &CL) { + return OS << "Code lens: " << CL.range.start << '-' << CL.range.end; +} + llvm::json::Value toJSON(DiagnosticTag Tag) { return static_cast<int>(Tag); } llvm::json::Value toJSON(const CodeDescription &D) { Index: clang-tools-extra/clangd/Hover.cpp =================================================================== --- clang-tools-extra/clangd/Hover.cpp +++ clang-tools-extra/clangd/Hover.cpp @@ -15,6 +15,8 @@ #include "ParsedAST.h" #include "Selection.h" #include "SourceCode.h" +#include "clang-include-cleaner/Analysis.h" +#include "clang-include-cleaner/Types.h" #include "index/SymbolCollector.h" #include "support/Markup.h" #include "clang/AST/ASTContext.h" @@ -1084,8 +1086,67 @@ return Candidates.front(); } +std::vector<include_cleaner::SymbolReference> +collectMacroReferences(ParsedAST &AST) { + const auto &SM = AST.getSourceManager(); + // FIXME: !!this is a hacky way to collect macro references. + std::vector<include_cleaner::SymbolReference> 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; +} + } // namespace +void maybeAddHeaderProvider(ParsedAST &AST, HoverInfo &HI, SourceLocation CurLoc) { + llvm::errs() << "CALLED ADD HEADER PROVIDER\n"; + include_cleaner::walkUsed(AST.getLocalTopLevelDecls(), + collectMacroReferences(AST), + AST.getPragmaIncludes(), + AST.getSourceManager(), + [&](const include_cleaner::SymbolReference &Ref, + llvm::ArrayRef<include_cleaner::Header> Providers) { + if (Ref.RefLocation != CurLoc) { + return; + } + StringRef ProviderName; + if (!Providers.empty()) { + include_cleaner::Header FirstProvider = Providers[0]; + // TODO(bakalova) switch on provider type + switch(FirstProvider.kind()) { + case include_cleaner::Header::Physical: + ProviderName = FirstProvider.physical()->tryGetRealPathName(); + llvm::errs() << "PHYSICAL PROVIDER: " << ProviderName << "\n"; + break; + case include_cleaner::Header::Standard: + ProviderName = FirstProvider.standard().name(); + llvm::errs() << "STANDARD PROVIDER: " << ProviderName << "\n"; + break; + case include_cleaner::Header::Verbatim: + ProviderName = FirstProvider.verbatim(); + llvm::errs() << "VERBATIM PROVIDER: " << ProviderName << "\n"; + break; + } + HI.Documentation += "\n\n"; + HI.Documentation += "Provided by `"; + HI.Documentation += ProviderName; + HI.Documentation += "`."; + llvm::errs() << "DOC: " << HI.Documentation << "\n"; + } + }); +} + std::optional<HoverInfo> getHover(ParsedAST &AST, Position Pos, const format::FormatStyle &Style, const SymbolIndex *Index) { @@ -1168,6 +1229,7 @@ if (!HI->Value) HI->Value = printExprValue(N, AST.getASTContext()); maybeAddCalleeArgInfo(N, *HI, PP); + maybeAddHeaderProvider(AST, *HI, *CurLoc); } else if (const Expr *E = N->ASTNode.get<Expr>()) { HI = getHoverContents(N, E, AST, PP, Index); } else if (const Attr *A = N->ASTNode.get<Attr>()) { Index: clang-tools-extra/clangd/ClangdServer.h =================================================================== --- clang-tools-extra/clangd/ClangdServer.h +++ clang-tools-extra/clangd/ClangdServer.h @@ -305,6 +305,8 @@ void findReferences(PathRef File, Position Pos, uint32_t Limit, bool AddContainer, Callback<ReferencesResult> CB); + void findCodeLens(PathRef File, Callback<std::vector<CodeLens>> CB); + /// Run formatting for the \p File with content \p Code. /// If \p Rng is non-null, formats only that region. void formatFile(PathRef File, std::optional<Range> Rng, Index: clang-tools-extra/clangd/ClangdServer.cpp =================================================================== --- clang-tools-extra/clangd/ClangdServer.cpp +++ clang-tools-extra/clangd/ClangdServer.cpp @@ -911,6 +911,17 @@ WorkScheduler->runWithAST("References", File, std::move(Action)); } +void ClangdServer::findCodeLens(PathRef File, Callback<std::vector<CodeLens>> CB) { + auto Action = [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { + if (!InpAST) + return CB(InpAST.takeError()); + + CB(clangd::findCodeLens(InpAST->AST)); + }; + + WorkScheduler->runWithAST("CodeLens", File, std::move(Action)); +} + void ClangdServer::symbolInfo(PathRef File, Position Pos, Callback<std::vector<SymbolDetails>> CB) { auto Action = Index: clang-tools-extra/clangd/ClangdLSPServer.h =================================================================== --- clang-tools-extra/clangd/ClangdLSPServer.h +++ clang-tools-extra/clangd/ClangdLSPServer.h @@ -173,6 +173,9 @@ void onCommandApplyEdit(const WorkspaceEdit &, Callback<llvm::json::Value>); void onCommandApplyTweak(const TweakArgs &, Callback<llvm::json::Value>); + void onCodeLens(const CodeLensParams &Params, Callback<std::vector<CodeLens>> Reply); + void onCommandShowUsedSymbols(const IncludeSpelling &Spelling, Callback<llvm::json::Value> Reply); + /// Outgoing LSP calls. LSPBinder::OutgoingMethod<ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse> Index: clang-tools-extra/clangd/ClangdLSPServer.cpp =================================================================== --- clang-tools-extra/clangd/ClangdLSPServer.cpp +++ clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -71,6 +71,7 @@ const llvm::StringLiteral ApplyFixCommand = "clangd.applyFix"; const llvm::StringLiteral ApplyTweakCommand = "clangd.applyTweak"; +const llvm::StringLiteral ApplyShowUsedSymbols = "HelloWorld"; /// Transforms a tweak into a code action that would apply it if executed. /// EXPECTS: T.prepare() was called and returned true. @@ -586,6 +587,7 @@ {"clangdInlayHintsProvider", true}, {"inlayHintProvider", true}, {"foldingRangeProvider", true}, + {"codeLensProvider", true}, }; { @@ -736,6 +738,14 @@ applyEdit(WE, "Fix applied.", std::move(Reply)); } +void ClangdLSPServer::onCommandShowUsedSymbols(const IncludeSpelling &Spelling, Callback<llvm::json::Value> Reply) { + llvm::errs() << "ONCOMMAND SHOW USED SYMBOLS TRIGGERED\n"; + llvm::json::Value Response = llvm::json::Object{ + {"response", "Hello to you as well!!!"} + }; + return Reply(Response); +} + void ClangdLSPServer::onCommandApplyTweak(const TweakArgs &Args, Callback<llvm::json::Value> Reply) { auto Action = [this, Reply = std::move(Reply)]( @@ -1399,6 +1409,12 @@ }); } +void ClangdLSPServer::onCodeLens(const CodeLensParams &Params, + Callback<std::vector<CodeLens>> Reply) { + llvm::errs() << "TRIGGERED CODE LENS: " << Params.textDocument.uri.file() << "\n"; + Server->findCodeLens(Params.textDocument.uri.file(), std::move(Reply)); +} + void ClangdLSPServer::onGoToType(const TextDocumentPositionParams &Params, Callback<std::vector<Location>> Reply) { Server->findType( @@ -1589,6 +1605,7 @@ Bind.method("textDocument/typeDefinition", this, &ClangdLSPServer::onGoToType); Bind.method("textDocument/implementation", this, &ClangdLSPServer::onGoToImplementation); Bind.method("textDocument/references", this, &ClangdLSPServer::onReference); + Bind.method("textDocument/codeLens", this, &ClangdLSPServer::onCodeLens); Bind.method("textDocument/switchSourceHeader", this, &ClangdLSPServer::onSwitchSourceHeader); Bind.method("textDocument/prepareRename", this, &ClangdLSPServer::onPrepareRename); Bind.method("textDocument/rename", this, &ClangdLSPServer::onRename); @@ -1622,6 +1639,7 @@ Bind.method("textDocument/foldingRange", this, &ClangdLSPServer::onFoldingRange); Bind.command(ApplyFixCommand, this, &ClangdLSPServer::onCommandApplyEdit); Bind.command(ApplyTweakCommand, this, &ClangdLSPServer::onCommandApplyTweak); + Bind.command(ApplyShowUsedSymbols, this, &ClangdLSPServer::onCommandShowUsedSymbols); ApplyWorkspaceEdit = Bind.outgoingMethod("workspace/applyEdit"); PublishDiagnostics = Bind.outgoingNotification("textDocument/publishDiagnostics");
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits