[clangd] Support scope proximity in code completion.

Summary:
This should make all-scope completion more usable. Scope proximity for
indexes will be added in followup patch.

Reviewers: sammccall

Reviewed By: sammccall

Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits

Differential Revision: https://reviews.llvm.org/D53131

llvm-svn: 344688
diff --git a/clang-tools-extra/clangd/Quality.cpp b/clang-tools-extra/clangd/Quality.cpp
index 5060a14..1fb47d5 100644
--- a/clang-tools-extra/clangd/Quality.cpp
+++ b/clang-tools-extra/clangd/Quality.cpp
@@ -18,10 +18,16 @@
 #include "clang/Basic/CharInfo.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Sema/CodeCompleteConsumer.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/raw_ostream.h"
+#include <algorithm>
 #include <cmath>
 
 namespace clang {
@@ -268,6 +274,7 @@
   // relevant to non-completion requests, we should recognize class members etc.
 
   SymbolURI = IndexResult.CanonicalDeclaration.FileURI;
+  SymbolScope = IndexResult.Scope;
   IsInstanceMember |= isInstanceMember(IndexResult.SymInfo);
 }
 
@@ -277,6 +284,7 @@
     Forbidden = true;
 
   if (SemaCCResult.Declaration) {
+    SemaSaysInScope = true;
     // We boost things that have decls in the main file. We give a fixed score
     // for all other declarations in sema as they are already included in the
     // translation unit.
@@ -284,7 +292,7 @@
                            hasUsingDeclInMainFile(SemaCCResult))
                               ? 1.0
                               : 0.6;
-    SemaProximityScore = std::max(DeclProximity, SemaProximityScore);
+    SemaFileProximityScore = std::max(DeclProximity, SemaFileProximityScore);
     IsInstanceMember |= isInstanceMember(SemaCCResult.Declaration);
   }
 
@@ -295,8 +303,8 @@
   NeedsFixIts = !SemaCCResult.FixIts.empty();
 }
 
-static std::pair<float, unsigned> proximityScore(llvm::StringRef SymbolURI,
-                                                 URIDistance *D) {
+static std::pair<float, unsigned> uriProximity(llvm::StringRef SymbolURI,
+                                               URIDistance *D) {
   if (!D || SymbolURI.empty())
     return {0.f, 0u};
   unsigned Distance = D->distance(SymbolURI);
@@ -304,6 +312,16 @@
   return {std::exp(Distance * -0.4f / FileDistanceOptions().UpCost), Distance};
 }
 
+static float scopeBoost(ScopeDistance &Distance,
+                        llvm::Optional<llvm::StringRef> SymbolScope) {
+  if (!SymbolScope)
+    return 1;
+  auto D = Distance.distance(*SymbolScope);
+  if (D == FileDistance::Unreachable)
+    return 0.4;
+  return std::max(0.5, 2.0 * std::pow(0.6, D / 2.0));
+}
+
 float SymbolRelevanceSignals::evaluate() const {
   float Score = 1;
 
@@ -312,10 +330,18 @@
 
   Score *= NameMatch;
 
-  // Proximity scores are [0,1] and we translate them into a multiplier in the
-  // range from 1 to 3.
-  Score *= 1 + 2 * std::max(proximityScore(SymbolURI, FileProximityMatch).first,
-                            SemaProximityScore);
+  // File proximity scores are [0,1] and we translate them into a multiplier in
+  // the range from 1 to 3.
+  Score *= 1 + 2 * std::max(uriProximity(SymbolURI, FileProximityMatch).first,
+                            SemaFileProximityScore);
+
+  if (ScopeProximityMatch)
+    // Use a constant scope boost for sema results, as scopes of sema results
+    // can be tricky (e.g. class/function scope). Set to the max boost as we
+    // don't load top-level symbols from the preamble and sema results are
+    // always in the accessible scope.
+    Score *=
+        SemaSaysInScope ? 2.0 : scopeBoost(*ScopeProximityMatch, SymbolScope);
 
   // Symbols like local variables may only be referenced within their scope.
   // Conversely if we're in that scope, it's likely we'll reference them.
@@ -358,15 +384,25 @@
   OS << formatv("\tNeedsFixIts: {0}\n", S.NeedsFixIts);
   OS << formatv("\tIsInstanceMember: {0}\n", S.IsInstanceMember);
   OS << formatv("\tContext: {0}\n", getCompletionKindString(S.Context));
-  OS << formatv("\tSymbol URI: {0}\n", S.SymbolURI);
-  if (S.FileProximityMatch) {
-    auto Score = proximityScore(S.SymbolURI, S.FileProximityMatch);
-    OS << formatv("\tIndex proximity: {0} (distance={1})\n", Score.first,
-                  Score.second);
-  }
-  OS << formatv("\tSema proximity: {0}\n", S.SemaProximityScore);
   OS << formatv("\tQuery type: {0}\n", static_cast<int>(S.Query));
   OS << formatv("\tScope: {0}\n", static_cast<int>(S.Scope));
+
+  OS << formatv("\tSymbol URI: {0}\n", S.SymbolURI);
+  OS << formatv("\tSymbol scope: {0}\n",
+                S.SymbolScope ? *S.SymbolScope : "<None>");
+
+  if (S.FileProximityMatch) {
+    auto Score = uriProximity(S.SymbolURI, S.FileProximityMatch);
+    OS << formatv("\tIndex URI proximity: {0} (distance={1})\n", Score.first,
+                  Score.second);
+  }
+  OS << formatv("\tSema file proximity: {0}\n", S.SemaFileProximityScore);
+
+  OS << formatv("\tSema says in scope: {0}\n", S.SemaSaysInScope);
+  if (S.ScopeProximityMatch)
+    OS << formatv("\tIndex scope boost: {0}\n",
+                  scopeBoost(*S.ScopeProximityMatch, S.SymbolScope));
+
   return OS;
 }