kadircet created this revision. kadircet added a reviewer: ilya-biryukov. Herald added subscribers: cfe-commits, arphaman, jkorous, ioeric.
Added functionality to suggest FixIts for conversion of '->' to '.' and vice versa. Repository: rCTE Clang Tools Extra https://reviews.llvm.org/D50193 Files: clangd/CodeComplete.cpp clangd/CodeComplete.h clangd/Diagnostics.cpp clangd/Protocol.h clangd/SourceCode.cpp clangd/SourceCode.h unittests/clangd/CodeCompleteTests.cpp
Index: unittests/clangd/CodeCompleteTests.cpp =================================================================== --- unittests/clangd/CodeCompleteTests.cpp +++ unittests/clangd/CodeCompleteTests.cpp @@ -477,7 +477,8 @@ TEST(CompletionTest, ReferencesAffectRanking) { auto Results = completions("int main() { abs^ }", {ns("absl"), func("absb")}); - EXPECT_THAT(Results.Completions, HasSubsequence(Named("absb"), Named("absl"))); + EXPECT_THAT(Results.Completions, + HasSubsequence(Named("absb"), Named("absl"))); Results = completions("int main() { abs^ }", {withReferences(10000, ns("absl")), func("absb")}); EXPECT_THAT(Results.Completions, @@ -1338,6 +1339,78 @@ EXPECT_THAT(Results.Context, CodeCompletionContext::CCC_DotMemberAccess); } +TEST(CompletionTest, FixItForArrowToDot) { + CodeCompleteOptions Opts; + Opts.IncludeFixIts = true; + auto Results = completions( + R"cpp( + class Auxilary { + public: + void AuxFunction(); + }; + class ClassWithPtr { + public: + void MemberFunction(); + Auxilary* operator->() const { return Aux; } + private: + Auxilary* Aux; + }; + namespace ns { + void f() { + ClassWithPtr x; + x->MemberFunction^; + } + } + )cpp", + {}, Opts); + EXPECT_EQ(Results.Completions.size(), 1u); + + TextEdit ReplacementEdit; + ReplacementEdit.range.start.line = 15; + ReplacementEdit.range.start.character = 13; + ReplacementEdit.range.end.line = 15; + ReplacementEdit.range.end.character = 15; + ReplacementEdit.newText = "."; + const auto &C = Results.Completions.front(); + EXPECT_THAT(C.FixIts, ElementsAre(ReplacementEdit)); +} + +TEST(CompletionTest, FixItForDotToArrow) { + CodeCompleteOptions Opts; + Opts.IncludeFixIts = true; + auto Results = completions( + R"cpp( + class Auxilary { + public: + void AuxFunction(); + }; + class ClassWithPtr { + public: + void MemberFunction(); + Auxilary* operator->() const { return Aux; } + private: + Auxilary* Aux; + }; + namespace ns { + void f() { + ClassWithPtr x; + x.AuxFunction^; + } + } + )cpp", + {}, Opts); + EXPECT_EQ(Results.Completions.size(), 1u); + + TextEdit ReplacementEdit; + ReplacementEdit.range.start.line = 15; + ReplacementEdit.range.start.character = 13; + ReplacementEdit.range.end.line = 15; + ReplacementEdit.range.end.character = 14; + ReplacementEdit.newText = "->"; + const auto &C = Results.Completions.front(); + EXPECT_THAT(C.FixIts, ElementsAre(ReplacementEdit)); +} + } // namespace } // namespace clangd } // namespace clang Index: clangd/SourceCode.h =================================================================== --- clangd/SourceCode.h +++ clangd/SourceCode.h @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SOURCECODE_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SOURCECODE_H +#include "Diagnostics.h" #include "Protocol.h" #include "clang/Basic/SourceLocation.h" #include "clang/Tooling/Core/Replacement.h" @@ -64,6 +65,10 @@ /// Get the absolute file path of a given file entry. llvm::Optional<std::string> getAbsoluteFilePath(const FileEntry *F, const SourceManager &SourceMgr); + +TextEdit toTextEdit(const FixItHint &FixIt, const SourceManager &M, + const LangOptions &L); + } // namespace clangd } // namespace clang #endif Index: clangd/SourceCode.cpp =================================================================== --- clangd/SourceCode.cpp +++ clangd/SourceCode.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "SourceCode.h" +#include "Diagnostics.h" #include "Logger.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/SourceManager.h" @@ -199,5 +200,14 @@ return FilePath.str().str(); } +TextEdit toTextEdit(const FixItHint &FixIt, const SourceManager &M, + const LangOptions &L) { + TextEdit Result; + Result.range = + halfOpenToRange(M, Lexer::makeFileCharRange(FixIt.RemoveRange, M, L)); + Result.newText = FixIt.CodeToInsert; + return Result; +} + } // namespace clangd } // namespace clang Index: clangd/Protocol.h =================================================================== --- clangd/Protocol.h +++ clangd/Protocol.h @@ -171,6 +171,10 @@ /// The string to be inserted. For delete operations use an /// empty string. std::string newText; + + bool operator==(const TextEdit &rhs) const { + return newText == rhs.newText && range == rhs.range; + } }; bool fromJSON(const llvm::json::Value &, TextEdit &); llvm::json::Value toJSON(const TextEdit &); Index: clangd/Diagnostics.cpp =================================================================== --- clangd/Diagnostics.cpp +++ clangd/Diagnostics.cpp @@ -70,15 +70,6 @@ return halfOpenToRange(M, R); } -TextEdit toTextEdit(const FixItHint &FixIt, const SourceManager &M, - const LangOptions &L) { - TextEdit Result; - Result.range = - halfOpenToRange(M, Lexer::makeFileCharRange(FixIt.RemoveRange, M, L)); - Result.newText = FixIt.CodeToInsert; - return Result; -} - bool isInsideMainFile(const SourceLocation Loc, const SourceManager &M) { return Loc.isValid() && M.isInMainFile(Loc); } Index: clangd/CodeComplete.h =================================================================== --- clangd/CodeComplete.h +++ clangd/CodeComplete.h @@ -77,6 +77,10 @@ /// FIXME(ioeric): we might want a better way to pass the index around inside /// clangd. const SymbolIndex *Index = nullptr; + + /// Include completions that require small corrections, e.g. change '.' to + /// '->' on member access etc. + bool IncludeFixIts = false; }; // Semi-structured representation of a code-complete suggestion for our C++ API. @@ -115,6 +119,10 @@ // Present if Header is set and should be inserted to use this item. llvm::Optional<TextEdit> HeaderInsertion; + /// Holds information about small corrections that needs to be done. Like + /// converting '->' to '.' on member access. + std::vector<TextEdit> FixIts; + // Scores are used to rank completion items. struct Scores { // The score that items are ranked by. Index: clangd/CodeComplete.cpp =================================================================== --- clangd/CodeComplete.cpp +++ clangd/CodeComplete.cpp @@ -22,6 +22,7 @@ #include "AST.h" #include "CodeCompletionStrings.h" #include "Compiler.h" +#include "Diagnostics.h" #include "FileDistance.h" #include "FuzzyMatch.h" #include "Headers.h" @@ -280,6 +281,10 @@ } Completion.Kind = toCompletionItemKind(C.SemaResult->Kind, C.SemaResult->Declaration); + for (const auto &FixIt : C.SemaResult->FixIts) { + Completion.FixIts.push_back( + toTextEdit(FixIt, ASTCtx.getSourceManager(), {})); + } } if (C.IndexResult) { Completion.Origin |= C.IndexResult->Origin; @@ -909,6 +914,7 @@ // the index can provide results from the preamble. // Tell Sema not to deserialize the preamble to look for results. Result.LoadExternal = !Index; + Result.IncludeFixIts = IncludeFixIts; return Result; } @@ -950,8 +956,8 @@ CompletionRecorder *Recorder = nullptr; int NSema = 0, NIndex = 0, NBoth = 0; // Counters for logging. bool Incomplete = false; // Would more be available with a higher limit? - llvm::Optional<FuzzyMatcher> Filter; // Initialized once Sema runs. - std::vector<std::string> QueryScopes; // Initialized once Sema runs. + llvm::Optional<FuzzyMatcher> Filter; // Initialized once Sema runs. + std::vector<std::string> QueryScopes; // Initialized once Sema runs. // Include-insertion and proximity scoring rely on the include structure. // This is available after Sema has run. llvm::Optional<IncludeInserter> Inserter; // Available during runWithSema. @@ -1054,7 +1060,7 @@ ? queryIndex() : SymbolSlab(); // Merge Sema and Index results, score them, and pick the winners. - auto Top = mergeResults(Recorder->Results, IndexResults); + auto Top = mergeResults(IndexResults); // Convert the results to final form, assembling the expensive strings. CodeCompleteResult Output; for (auto &C : Top) { @@ -1092,9 +1098,7 @@ // Merges Sema and Index results where possible, to form CompletionCandidates. // Groups overloads if desired, to form CompletionCandidate::Bundles. // The bundles are scored and top results are returned, best to worst. - std::vector<ScoredBundle> - mergeResults(const std::vector<CodeCompletionResult> &SemaResults, - const SymbolSlab &IndexResults) { + std::vector<ScoredBundle> mergeResults(const SymbolSlab &IndexResults) { trace::Span Tracer("Merge and score results"); std::vector<CompletionCandidate::Bundle> Bundles; llvm::DenseMap<size_t, size_t> BundleLookup; @@ -1280,8 +1284,11 @@ LSP.insertText += SnippetSuffix; LSP.insertTextFormat = Opts.EnableSnippets ? InsertTextFormat::Snippet : InsertTextFormat::PlainText; + LSP.additionalTextEdits.reserve(FixIts.size() + (HeaderInsertion ? 1 : 0)); + for (const auto &FixIt : FixIts) + LSP.additionalTextEdits.push_back(FixIt); if (HeaderInsertion) - LSP.additionalTextEdits = {*HeaderInsertion}; + LSP.additionalTextEdits.push_back(*HeaderInsertion); return LSP; }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits