hokein updated this revision to Diff 163654.
hokein edited the summary of this revision.
hokein added a comment.

Rebase


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50958

Files:
  clangd/XRefs.cpp
  clangd/XRefs.h
  unittests/clangd/XRefsTests.cpp

Index: unittests/clangd/XRefsTests.cpp
===================================================================
--- unittests/clangd/XRefsTests.cpp
+++ unittests/clangd/XRefsTests.cpp
@@ -26,6 +26,7 @@
 using namespace llvm;
 
 namespace {
+using testing::_;
 using testing::ElementsAre;
 using testing::Field;
 using testing::IsEmpty;
@@ -1068,6 +1069,179 @@
       ElementsAre(Location{FooCppUri, FooWithoutHeader.range()}));
 }
 
+TEST(FindReferences, AllWithoutIndex) {
+  const char *Tests[] = {
+      R"cpp(// Local variable
+        int main() {
+          int $foo[[foo]];
+          $foo[[^foo]] = 2;
+          int test1 = $foo[[foo]];
+        }
+      )cpp",
+
+      R"cpp(// Struct
+        namespace ns1 {
+        struct $foo[[Foo]] {};
+        } // namespace ns1
+        int main() {
+          ns1::$foo[[Fo^o]]* Params;
+        }
+      )cpp",
+
+      R"cpp(// Function
+        int $foo[[foo]](int) {}
+        int main() {
+          auto *X = &$foo[[^foo]];
+          $foo[[foo]](42)
+        }
+      )cpp",
+
+      R"cpp(// Field
+        struct Foo {
+          int $foo[[foo]];
+          Foo() : $foo[[foo]](0) {}
+        };
+        int main() {
+          Foo f;
+          f.$foo[[f^oo]] = 1;
+        }
+      )cpp",
+
+      R"cpp(// Method call
+        struct Foo { int [[foo]](); };
+        int Foo::[[foo]]() {}
+        int main() {
+          Foo f;
+          f.^foo();
+        }
+      )cpp",
+
+      R"cpp(// Typedef
+        typedef int $foo[[Foo]];
+        int main() {
+          $foo[[^Foo]] bar;
+        }
+      )cpp",
+
+      R"cpp(// Namespace
+        namespace $foo[[ns]] {
+        struct Foo {};
+        } // namespace ns
+        int main() { $foo[[^ns]]::Foo foo; }
+      )cpp",
+  };
+  for (const char *Test : Tests) {
+    Annotations T(Test);
+    auto AST = TestTU::withCode(T.code()).build();
+    std::vector<Matcher<Location>> ExpectedLocations;
+    for (const auto &R : T.ranges("foo"))
+      ExpectedLocations.push_back(RangeIs(R));
+    EXPECT_THAT(findReferences(AST, T.point()),
+                ElementsAreArray(ExpectedLocations))
+        << Test;
+  }
+}
+
+class MockIndex : public SymbolIndex {
+public:
+  MOCK_CONST_METHOD2(fuzzyFind, bool(const FuzzyFindRequest &,
+                                     llvm::function_ref<void(const Symbol &)>));
+  MOCK_CONST_METHOD2(lookup, void(const LookupRequest &,
+                                  llvm::function_ref<void(const Symbol &)>));
+  MOCK_CONST_METHOD2(findOccurrences,
+                     void(const OccurrencesRequest &,
+                          llvm::function_ref<void(const SymbolOccurrence &)>));
+  MOCK_CONST_METHOD0(estimateMemoryUsage, size_t());
+};
+
+TEST(FindReferences, QueryIndex) {
+  const char *Tests[] = {
+      // Refers to symbols from headers.
+      R"cpp(
+        int main() {
+          F^oo foo;
+        }
+      )cpp",
+      R"cpp(
+        int main() {
+          f^unc();
+        }
+      )cpp",
+      R"cpp(
+        int main() {
+          return I^NT;
+        }
+      )cpp",
+
+      // These are cases of file-local but not function-local symbols, we still
+      // query the index.
+      R"cpp(
+        void MyF^unc() {}
+      )cpp",
+
+      R"cpp(
+        int My^Int = 2;
+      )cpp",
+  };
+
+  TestTU TU;
+  TU.HeaderCode = R"(
+  class Foo {};
+  static const int INT = 3;
+  inline void func() {};
+  )";
+  MockIndex Index;
+  for (const char *Test : Tests) {
+    Annotations T(Test);
+    TU.Code = T.code();
+    auto AST = TU.build();
+    EXPECT_CALL(Index, findOccurrences(_, _));
+    findReferences(AST, T.point(), &Index);
+  }
+}
+
+TEST(FindReferences, DontQueryIndex) {
+  // Don't query index for function-local symbols.
+  const char *Tests[] = {
+      R"cpp(// Local variable in function body
+        int main() {
+          int $foo[[foo]];
+          $foo[[^foo]] = 2;
+        }
+      )cpp",
+
+      R"cpp(// function parameter
+        int f(int fo^o) {
+        }
+      )cpp",
+
+      R"cpp(// function parameter in lambda
+        int f(int foo) {
+          auto func = [](int a, int b) {
+            return ^a = 2;
+          };
+        }
+      )cpp",
+
+      R"cpp(// capture in lambda
+        int f(int foo) {
+          int A;
+          auto func = [&A](int a, int b) {
+            return a = ^A;
+          };
+        }
+      )cpp",
+  };
+
+  MockIndex Index;
+  for (const char *Test : Tests) {
+    Annotations T(Test);
+    auto AST = TestTU::withCode(T.code()).build();
+    EXPECT_CALL(Index, findOccurrences(_, _)).Times(0);
+    findReferences(AST, T.point(), &Index);
+  }
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clangd/XRefs.h
===================================================================
--- clangd/XRefs.h
+++ clangd/XRefs.h
@@ -34,6 +34,9 @@
 /// Get the hover information when hovering at \p Pos.
 llvm::Optional<Hover> getHover(ParsedAST &AST, Position Pos);
 
+/// Returns reference locations of the symbol at a specified \p Pos.
+std::vector<Location> findReferences(ParsedAST &AST, Position Pos,
+                                     const SymbolIndex *Index = nullptr);
 } // namespace clangd
 } // namespace clang
 
Index: clangd/XRefs.cpp
===================================================================
--- clangd/XRefs.cpp
+++ clangd/XRefs.cpp
@@ -174,30 +174,27 @@
   return {DeclMacrosFinder.takeDecls(), DeclMacrosFinder.takeMacroInfos()};
 }
 
-llvm::Optional<Location>
-makeLocation(ParsedAST &AST, const SourceRange &ValSourceRange) {
+Range getTokenRange(ParsedAST &AST, SourceLocation TokLoc) {
   const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
-  const LangOptions &LangOpts = AST.getASTContext().getLangOpts();
-  SourceLocation LocStart = ValSourceRange.getBegin();
+  SourceLocation LocEnd = Lexer::getLocForEndOfToken(
+      TokLoc, 0, SourceMgr, AST.getASTContext().getLangOpts());
+  return {sourceLocToPosition(SourceMgr, TokLoc),
+          sourceLocToPosition(SourceMgr, LocEnd)};
+}
 
-  const FileEntry *F =
-      SourceMgr.getFileEntryForID(SourceMgr.getFileID(LocStart));
+llvm::Optional<Location> makeLocation(ParsedAST &AST, SourceLocation TokLoc) {
+  const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+  const FileEntry *F = SourceMgr.getFileEntryForID(SourceMgr.getFileID(TokLoc));
   if (!F)
     return llvm::None;
-  SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(), 0,
-                                                     SourceMgr, LangOpts);
-  Position Begin = sourceLocToPosition(SourceMgr, LocStart);
-  Position End = sourceLocToPosition(SourceMgr, LocEnd);
-  Range R = {Begin, End};
-  Location L;
-
   auto FilePath = getRealPath(F, SourceMgr);
   if (!FilePath) {
     log("failed to get path!");
     return llvm::None;
   }
+  Location L;
   L.uri = URIForFile(*FilePath);
-  L.range = R;
+  L.range = getTokenRange(AST, TokLoc);
   return L;
 }
 
@@ -223,7 +220,7 @@
 
   for (auto Item : Symbols.Macros) {
     auto Loc = Item.Info->getDefinitionLoc();
-    auto L = makeLocation(AST, SourceRange(Loc, Loc));
+    auto L = makeLocation(AST, Loc);
     if (L)
       Result.push_back(*L);
   }
@@ -266,7 +263,7 @@
 
     auto &Candidate = ResultCandidates[Key];
     auto Loc = findNameLoc(D);
-    auto L = makeLocation(AST, SourceRange(Loc, Loc));
+    auto L = makeLocation(AST, Loc);
     // The declaration in the identified symbols is a definition if possible
     // otherwise it is declaration.
     bool IsDef = getDefinition(D) == D;
@@ -316,64 +313,119 @@
 
 namespace {
 
-/// Finds document highlights that a given list of declarations refers to.
-class DocumentHighlightsFinder : public index::IndexDataConsumer {
-  std::vector<const Decl *> &Decls;
-  std::vector<DocumentHighlight> DocumentHighlights;
-  const ASTContext &AST;
-
+/// Collects all occurrences (and related information) in the main file that a
+/// given whitelist of declarations refers to.
+class OccurrenceCollector : public index::IndexDataConsumer {
 public:
-  DocumentHighlightsFinder(ASTContext &AST, Preprocessor &PP,
-                           std::vector<const Decl *> &Decls)
-      : Decls(Decls), AST(AST) {}
-  std::vector<DocumentHighlight> takeHighlights() {
-    // Don't keep the same highlight multiple times.
-    // This can happen when nodes in the AST are visited twice.
-    std::sort(DocumentHighlights.begin(), DocumentHighlights.end());
-    auto Last =
-        std::unique(DocumentHighlights.begin(), DocumentHighlights.end());
-    DocumentHighlights.erase(Last, DocumentHighlights.end());
-    return std::move(DocumentHighlights);
-  }
-
   bool
   handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles,
                       ArrayRef<index::SymbolRelation> Relations,
                       SourceLocation Loc,
                       index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
-    const SourceManager &SourceMgr = AST.getSourceManager();
-    SourceLocation HighlightStartLoc = SourceMgr.getFileLoc(Loc);
-    if (SourceMgr.getMainFileID() != SourceMgr.getFileID(HighlightStartLoc) ||
-        std::find(Decls.begin(), Decls.end(), D) == Decls.end()) {
+    const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+    SourceLocation FileLoc = SourceMgr.getFileLoc(Loc);
+    // We only collect locations in current main file.
+    if (SourceMgr.getMainFileID() != SourceMgr.getFileID(FileLoc) ||
+        std::find(WhitelistDecls.begin(), WhitelistDecls.end(), D) ==
+            WhitelistDecls.end())
       return true;
+
+    DeclOccurrences[D].emplace_back(FileLoc, Roles);
+    return true;
+  }
+
+protected:
+  OccurrenceCollector(ParsedAST &AST,
+                      const std::vector<const Decl *> &WhitelistDecls)
+      : AST(AST), WhitelistDecls(WhitelistDecls) {
+
+    auto &SM = AST.getASTContext().getSourceManager();
+    auto MainFilePath =
+        getRealPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
+    if (!MainFilePath) {
+      log("Fail to get real path!");
     }
-    SourceLocation End;
-    const LangOptions &LangOpts = AST.getLangOpts();
-    End = Lexer::getLocForEndOfToken(HighlightStartLoc, 0, SourceMgr, LangOpts);
-    SourceRange SR(HighlightStartLoc, End);
+    MainFileURI = URIForFile(*MainFilePath);
+  }
 
-    DocumentHighlightKind Kind = DocumentHighlightKind::Text;
-    if (static_cast<index::SymbolRoleSet>(index::SymbolRole::Write) & Roles)
-      Kind = DocumentHighlightKind::Write;
-    else if (static_cast<index::SymbolRoleSet>(index::SymbolRole::Read) & Roles)
-      Kind = DocumentHighlightKind::Read;
+  ParsedAST &AST;
+  const std::vector<const Decl *> &WhitelistDecls;
 
-    DocumentHighlights.push_back(getDocumentHighlight(SR, Kind));
-    return true;
+  URIForFile MainFileURI;
+
+  using DeclOccurrence = std::pair<SourceLocation, index::SymbolRoleSet>;
+  llvm::DenseMap<const Decl *, std::vector<DeclOccurrence>> DeclOccurrences;
+};
+
+/// Find symbol occurrences that a given whilelist of declarations refers to.
+class OccurrencesFinder : public OccurrenceCollector {
+public:
+  OccurrencesFinder(ParsedAST &AST, std::vector<const Decl *> &WhitelistDecls)
+      : OccurrenceCollector(AST, WhitelistDecls) {}
+
+  std::vector<Location> takeOccurrences() { return std::move(Occurrences); }
+
+  void finish() override {
+    OccurrenceCollector::finish();
+    Occurrences.clear();
+
+    for (auto It : DeclOccurrences) {
+      for (const auto &LocAndRole : It.second) {
+        Occurrences.push_back(
+            {MainFileURI, getTokenRange(AST, LocAndRole.first)});
+      }
+    }
+    // Deduplicate results.
+    std::sort(Occurrences.begin(), Occurrences.end());
+    auto Last = std::unique(Occurrences.begin(), Occurrences.end());
+    Occurrences.erase(Last, Occurrences.end());
   }
 
 private:
-  DocumentHighlight getDocumentHighlight(SourceRange SR,
-                                         DocumentHighlightKind Kind) {
-    const SourceManager &SourceMgr = AST.getSourceManager();
-    Position Begin = sourceLocToPosition(SourceMgr, SR.getBegin());
-    Position End = sourceLocToPosition(SourceMgr, SR.getEnd());
-    Range R = {Begin, End};
+  std::vector<Location> Occurrences;
+};
+
+/// Finds document highlights that a given whitelist of declarations refers to.
+class DocumentHighlightsFinder : public OccurrenceCollector {
+public:
+  DocumentHighlightsFinder(ParsedAST &AST,
+                           const std::vector<const Decl *> &WhitelistDecls)
+      : OccurrenceCollector(AST, WhitelistDecls) {}
+
+  std::vector<DocumentHighlight> takeHighlights() {
+    return std::move(DocumentHighlights);
+  }
+
+  void finish() override {
+    OccurrenceCollector::finish();
+    DocumentHighlights.clear();
+    for (auto It : DeclOccurrences) {
+      for (const auto &LocAndRole : It.second)
+        DocumentHighlights.push_back(
+            createHighlight(LocAndRole.first, LocAndRole.second));
+    }
+    // Don't keep the same highlight multiple times.
+    // This can happen when nodes in the AST are visited twice.
+    std::sort(DocumentHighlights.begin(), DocumentHighlights.end());
+    auto Last =
+        std::unique(DocumentHighlights.begin(), DocumentHighlights.end());
+    DocumentHighlights.erase(Last, DocumentHighlights.end());
+  }
+
+  DocumentHighlight createHighlight(SourceLocation Loc,
+                                    index::SymbolRoleSet Roles) {
     DocumentHighlight DH;
-    DH.range = R;
-    DH.kind = Kind;
+    DH.range = getTokenRange(AST, Loc);
+    DH.kind = DocumentHighlightKind::Text;
+    if (static_cast<index::SymbolRoleSet>(index::SymbolRole::Write) & Roles)
+      DH.kind = DocumentHighlightKind::Write;
+    else if (static_cast<index::SymbolRoleSet>(index::SymbolRole::Read) & Roles)
+      DH.kind = DocumentHighlightKind::Read;
     return DH;
   }
+
+private:
+  std::vector<DocumentHighlight> DocumentHighlights;
 };
 
 } // namespace
@@ -387,8 +439,7 @@
   auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg);
   std::vector<const Decl *> SelectedDecls = Symbols.Decls;
 
-  DocumentHighlightsFinder DocHighlightsFinder(
-      AST.getASTContext(), AST.getPreprocessor(), SelectedDecls);
+  DocumentHighlightsFinder DocHighlightsFinder(AST, SelectedDecls);
 
   index::IndexingOptions IndexOpts;
   IndexOpts.SystemSymbolFilter =
@@ -659,5 +710,63 @@
   return None;
 }
 
+std::vector<Location> findReferences(ParsedAST &AST, Position Pos,
+                                     const SymbolIndex *Index) {
+  const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+  SourceLocation SourceLocationBeg =
+      getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID());
+  // Identified symbols at a specific position.
+  auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg);
+
+  // For local symbols (e.g. symbols that are only visiable in main file,
+  // symbols defined in function body), we can get complete references by
+  // traversing the AST, and we don't want to make unnecessary queries to the
+  // index. Howerver, we don't have a reliable way to distinguish file-local
+  // symbols. We conservatively consider function-local symbols.
+  llvm::DenseSet<SymbolID> PossiblyVisibleIDs;
+  for (const auto *D : Symbols.Decls) {
+    if (auto ID = getSymbolID(D)) {
+      // Ignore if it is a fucntion-scope symbol.
+      if (D->getParentFunctionOrMethod())
+        continue;
+      PossiblyVisibleIDs.insert(*ID);
+    }
+  }
+
+  // Look in the AST for the references from current main file.
+  OccurrencesFinder FindOccurrences(AST, Symbols.Decls);
+  index::IndexingOptions IndexOpts;
+  IndexOpts.SystemSymbolFilter =
+      index::IndexingOptions::SystemSymbolFilterKind::All;
+  IndexOpts.IndexFunctionLocals = true;
+  indexTopLevelDecls(AST.getASTContext(), AST.getLocalTopLevelDecls(),
+                     FindOccurrences, IndexOpts);
+
+  auto Results = FindOccurrences.takeOccurrences();
+
+  // Consult the index for references in other files.
+  // We only need to consider symbols visible to other files.
+  if (Index && !PossiblyVisibleIDs.empty()) {
+    const auto &SM = AST.getASTContext().getSourceManager();
+    auto MainFilePath =
+        getRealPath(SM.getFileEntryForID(SourceMgr.getMainFileID()), SM);
+    if (!MainFilePath) {
+      log("Failed to get real path!");
+      return Results;
+    }
+    auto MainFileURI = URIForFile(*MainFilePath);
+    OccurrencesRequest Request;
+    Request.IDs = std::move(PossiblyVisibleIDs);
+    Request.Filter = AllOccurrenceKinds;
+    Index->findOccurrences(Request, [&](const SymbolOccurrence &O) {
+      if (auto LSPLoc = toLSPLocation(O.Location, /*HintPath=*/*MainFilePath)) {
+        if (MainFileURI != LSPLoc->uri)
+          Results.push_back(*LSPLoc);
+      }
+    });
+  }
+  return Results;
+}
+
 } // namespace clangd
 } // namespace clang
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to