sammccall created this revision.
Herald added subscribers: cfe-commits, jkorous, MaskRay, ioeric, ilya-biryukov.

This folds together overloads items into a single CompletionItem.
It's full of hacks and breaks all the tests.

We may want to experiment with unfolding them sometimes, but this doesn't do 
that yet.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D47957

Files:
  clangd/CodeComplete.cpp

Index: clangd/CodeComplete.cpp
===================================================================
--- clangd/CodeComplete.cpp
+++ clangd/CodeComplete.cpp
@@ -220,20 +220,56 @@
   return HeaderFile{std::move(*Resolved), /*Verbatim=*/false};
 }
 
+size_t overloadToken(const CodeCompletionResult &R) {
+  if (!R.Declaration || !R.Declaration->isFunctionOrFunctionTemplate())
+    return 0;
+  // XXX avoid string copies.
+  if (R.Declaration->isCXXClassMember())
+    return hash_combine('M',
+                        StringRef(R.Declaration->getDeclName().getAsString()));
+  return hash_combine('F',
+                      StringRef(R.Declaration->getQualifiedNameAsString()));
+}
+
+size_t overloadToken(const Symbol &S) {
+  switch (S.SymInfo.Kind) {
+    case index::SymbolKind::ClassMethod:
+    case index::SymbolKind::InstanceMethod:
+    case index::SymbolKind::StaticMethod:
+      return hash_combine('M', S.Name);
+    case index::SymbolKind::Function:
+      return hash_combine('F', StringRef((S.Scope + S.Name).str()));
+    default:
+      return 0;
+  }
+}
+
 /// A code completion result, in clang-native form.
 /// It may be promoted to a CompletionItem if it's among the top-ranked results.
 struct CompletionCandidate {
   llvm::StringRef Name; // Used for filtering and sorting.
   // We may have a result from Sema, from the index, or both.
   const CodeCompletionResult *SemaResult = nullptr;
   const Symbol *IndexResult = nullptr;
 
+  // Returns a token identifying the overload set this is part of.
+  // 0 indicates it's not part of any overload set.
+  size_t overload() const {
+    if (SemaResult && IndexResult)
+      assert(overloadToken(*SemaResult) == overloadToken(*IndexResult));
+    if (IndexResult)
+      return overloadToken(*IndexResult);
+    if (SemaResult)
+      return overloadToken(*SemaResult);
+    return 0;
+  }
+
   // Builds an LSP completion item.
   CompletionItem build(StringRef FileName, const CompletionItemScores &Scores,
                        const CodeCompleteOptions &Opts,
                        CodeCompletionString *SemaCCS,
                        const IncludeInserter *Includes,
-                       llvm::StringRef SemaDocComment) const {
+                       llvm::StringRef SemaDocComment, unsigned Bundle) const {
     assert(bool(SemaResult) == bool(SemaCCS));
     CompletionItem I;
     bool ShouldInsertInclude = true;
@@ -309,10 +345,27 @@
     I.sortText = sortText(Scores.finalScore, Name);
     I.insertTextFormat = Opts.EnableSnippets ? InsertTextFormat::Snippet
                                              : InsertTextFormat::PlainText;
+    if (Bundle) {
+      I.detail = "[overloaded]";
+      I.insertText = Opts.EnableSnippets ? (Name + "(${0})").str() : Name.str();
+      I.label = (Name + "(...)").str();
+    }
     return I;
   }
 };
-using ScoredCandidate = std::pair<CompletionCandidate, CompletionItemScores>;
+struct CandidateBundle {
+  SmallVector<CompletionCandidate, 4> Candidates;
+  // Builds an LSP completion item.
+  CompletionItem build(StringRef FileName, const CompletionItemScores &Scores,
+                       const CodeCompleteOptions &Opts,
+                       CodeCompletionString *SemaCCS,
+                       const IncludeInserter *Includes,
+                       llvm::StringRef SemaDocComment) const {
+    return Candidates.front().build(FileName, Scores, Opts, SemaCCS, Includes,
+                                    SemaDocComment, Candidates.size() > 1);
+  }
+};
+using ScoredBundle = std::pair<CandidateBundle, CompletionItemScores>;
 
 // Determine the symbol ID for a Sema code completion result, if possible.
 llvm::Optional<SymbolID> getSymbolID(const CodeCompletionResult &R) {
@@ -589,10 +642,10 @@
 };
 
 struct ScoredCandidateGreater {
-  bool operator()(const ScoredCandidate &L, const ScoredCandidate &R) {
+  bool operator()(const ScoredBundle &L, const ScoredBundle &R) {
     if (L.second.finalScore != R.second.finalScore)
       return L.second.finalScore > R.second.finalScore;
-    return L.first.Name < R.first.Name; // Earlier name is better.
+    return L.first.Candidates.front().Name < R.first.Candidates.front().Name; // Earlier name is better.
   }
 };
 
@@ -984,13 +1037,30 @@
 
   // Merges the Sema and Index results where possible, scores them, and
   // returns the top results from best to worst.
-  std::vector<std::pair<CompletionCandidate, CompletionItemScores>>
+  std::vector<std::pair<CandidateBundle, CompletionItemScores>>
   mergeResults(const std::vector<CodeCompletionResult> &SemaResults,
                const SymbolSlab &IndexResults) {
     trace::Span Tracer("Merge and score results");
-    // We only keep the best N results at any time, in "native" format.
-    TopN<ScoredCandidate, ScoredCandidateGreater> Top(
-        Opts.Limit == 0 ? std::numeric_limits<size_t>::max() : Opts.Limit);
+    // Candidates are grouped into overload bundles.
+    std::vector<CandidateBundle> Bundles;
+    llvm::DenseMap<size_t, size_t> BundleLookup;
+    auto AddToBundles = [&](const CodeCompletionResult *SemaResult,
+                   const Symbol *IndexResult) {
+      CompletionCandidate C;
+      C.SemaResult = SemaResult;
+      C.IndexResult = IndexResult;
+      C.Name = IndexResult ? IndexResult->Name : Recorder->getName(*SemaResult);
+      auto Overload = C.overload();
+      if (Overload) {
+        auto Ret = BundleLookup.try_emplace(C.overload(), Bundles.size());
+        if (Ret.second)
+          Bundles.emplace_back();
+        Bundles[Ret.first->second].Candidates.push_back(std::move(C));
+      } else {
+        Bundles.emplace_back();
+        Bundles.back().Candidates.push_back(std::move(C));
+      }
+    };
     llvm::DenseSet<const Symbol *> UsedIndexResults;
     auto CorrespondingIndexResult =
         [&](const CodeCompletionResult &SemaResult) -> const Symbol * {
@@ -1005,13 +1075,18 @@
     };
     // Emit all Sema results, merging them with Index results if possible.
     for (auto &SemaResult : Recorder->Results)
-      addCandidate(Top, &SemaResult, CorrespondingIndexResult(SemaResult));
+      AddToBundles(&SemaResult, CorrespondingIndexResult(SemaResult));
     // Now emit any Index-only results.
     for (const auto &IndexResult : IndexResults) {
       if (UsedIndexResults.count(&IndexResult))
         continue;
-      addCandidate(Top, /*SemaResult=*/nullptr, &IndexResult);
+      AddToBundles(/*SemaResult=*/nullptr, &IndexResult);
     }
+    // We only keep the best N results at any time, in "native" format.
+    TopN<ScoredBundle, ScoredCandidateGreater> Top(
+        Opts.Limit == 0 ? std::numeric_limits<size_t>::max() : Opts.Limit);
+    for (auto &Bundle : Bundles)
+      addCandidate(Top, std::move(Bundle));
     return std::move(Top).items();
   }
 
@@ -1025,28 +1100,28 @@
   }
 
   // Scores a candidate and adds it to the TopN structure.
-  void addCandidate(TopN<ScoredCandidate, ScoredCandidateGreater> &Candidates,
-                    const CodeCompletionResult *SemaResult,
-                    const Symbol *IndexResult) {
-    CompletionCandidate C;
-    C.SemaResult = SemaResult;
-    C.IndexResult = IndexResult;
-    C.Name = IndexResult ? IndexResult->Name : Recorder->getName(*SemaResult);
-
+  void addCandidate(TopN<ScoredBundle, ScoredCandidateGreater> &Candidates,
+                    CandidateBundle Bundle) {
     SymbolQualitySignals Quality;
     SymbolRelevanceSignals Relevance;
     Relevance.Query = SymbolRelevanceSignals::CodeComplete;
-    if (auto FuzzyScore = fuzzyScore(C))
+    auto First = Bundle.Candidates.front();
+    if (auto FuzzyScore = fuzzyScore(First))
       Relevance.NameMatch = *FuzzyScore;
     else
       return;
-    if (IndexResult) {
-      Quality.merge(*IndexResult);
-      Relevance.merge(*IndexResult);
-    }
-    if (SemaResult) {
-      Quality.merge(*SemaResult);
-      Relevance.merge(*SemaResult);
+    unsigned SemaResult = 0, IndexResult = 0;
+    for (const auto &Candidate : Bundle.Candidates) {
+      if (Candidate.IndexResult) {
+        Quality.merge(*Candidate.IndexResult);
+        Relevance.merge(*Candidate.IndexResult);
+        ++IndexResult;
+      }
+      if (Candidate.SemaResult) {
+        Quality.merge(*Candidate.SemaResult);
+        Relevance.merge(*Candidate.SemaResult);
+        ++SemaResult;
+      }
     }
 
     float QualScore = Quality.evaluate(), RelScore = Relevance.evaluate();
@@ -1059,24 +1134,24 @@
     Scores.symbolScore =
         Scores.filterScore ? Scores.finalScore / Scores.filterScore : QualScore;
 
-    LLVM_DEBUG(llvm::dbgs()
-               << "CodeComplete: " << C.Name << (IndexResult ? " (index)" : "")
-               << (SemaResult ? " (sema)" : "") << " = " << Scores.finalScore
-               << "\n"
-               << Quality << Relevance << "\n");
+    LLVM_DEBUG(llvm::dbgs() << "CodeComplete: " << First.Name << "("
+                            << IndexResult << " index) "
+                            << "(" << SemaResult << " sema)"
+                            << " = " << Scores.finalScore << "\n"
+                            << Quality << Relevance << "\n");
 
     NSema += bool(SemaResult);
     NIndex += bool(IndexResult);
     NBoth += SemaResult && IndexResult;
-    if (Candidates.push({C, Scores}))
+    if (Candidates.push({std::move(Bundle), Scores}))
       Incomplete = true;
   }
 
-  CompletionItem toCompletionItem(const CompletionCandidate &Candidate,
+  CompletionItem toCompletionItem(const CandidateBundle &Candidate,
                                   const CompletionItemScores &Scores) {
     CodeCompletionString *SemaCCS = nullptr;
     std::string DocComment;
-    if (auto *SR = Candidate.SemaResult) {
+    if (auto *SR = Candidate.Candidates.front().SemaResult) {
       SemaCCS = Recorder->codeCompletionString(*SR);
       if (Opts.IncludeComments) {
         assert(Recorder->CCSema);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to