simark created this revision.
Herald added subscribers: cfe-commits, kadircet, arphaman, jkorous, ioeric, 
ilya-biryukov.

This is a work in progress patch to support an eventual "get type
hierarchy" request that does not exist in the LSP today.  I only plan to
support getting parent types (i.e. base classes) for now, because it
doesn't require touching the index.  Finding child classes would come
later.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D51311

Files:
  clangd/ClangdLSPServer.cpp
  clangd/ClangdLSPServer.h
  clangd/ClangdServer.cpp
  clangd/ClangdServer.h
  clangd/Protocol.cpp
  clangd/Protocol.h
  clangd/ProtocolHandlers.cpp
  clangd/ProtocolHandlers.h
  clangd/XRefs.cpp
  clangd/XRefs.h
  unittests/clangd/XRefsTests.cpp

Index: unittests/clangd/XRefsTests.cpp
===================================================================
--- unittests/clangd/XRefsTests.cpp
+++ unittests/clangd/XRefsTests.cpp
@@ -27,6 +27,7 @@
 
 namespace {
 using testing::ElementsAre;
+using testing::Eq;
 using testing::Field;
 using testing::IsEmpty;
 using testing::Matcher;
@@ -1068,6 +1069,166 @@
       ElementsAre(Location{FooCppUri, FooWithoutHeader.range()}));
 }
 
+TEST(TypeHierarchy, SimpleInheritanceOnTypeOrVariable) {
+  Annotations Source(R"cpp(
+$ParentDef^struct Parent
+{
+  int a;
+};
+
+$Child1Def^struct Child1 : Parent
+{
+  int b;
+};
+
+struct Ch$p1^ild2 : Child1
+{
+  int c;
+};
+
+struct Child3 : Child2
+{
+  int d;
+};
+
+int main()
+{
+  Ch$p2^ild2 ch$p3^ild2;
+
+  parent.a = 1;
+  ch$p4^ild2.c = 1;
+}
+)cpp");
+
+  TestTU TU = TestTU::withCode(Source.code());
+  auto AST = TU.build();
+
+  llvm::Optional<TypeHierarchy> Result;
+
+  TypeHierarchy ExpectedResult{{TypeHierarchyResult{
+      "Child1",
+      Source.point("Child1Def"),
+      false,
+      {TypeHierarchyResult{"Parent", Source.point("ParentDef"), false, {}}}}}};
+
+  for (auto Pt : {"p1", "p2", "p3", "p4"}) {
+    Result = getTypeHierarchy(AST, Source.point(Pt));
+    ASSERT_TRUE(bool(Result));
+    EXPECT_THAT(*Result, Eq(ExpectedResult));
+  }
+}
+
+TEST(TypeHierarchy, MultipleInheritanceOnTypeOrVariable) {
+  Annotations Source(R"cpp(
+$Parent1Def^struct Parent1
+{
+  int a;
+};
+
+$Parent2Def^struct Parent2
+{
+  int b;
+};
+
+$Parent3Def^struct Parent3 : Parent2
+{
+  int c;
+};
+
+struct Ch$c1^ild : Parent1, Parent3
+{
+  int d;
+};
+
+int main()
+{
+  Ch$c2^ild  ch$c3^ild;
+
+  ch$c4^ild.a = 1;
+}
+)cpp");
+
+  TestTU TU = TestTU::withCode(Source.code());
+  auto AST = TU.build();
+
+  llvm::Optional<TypeHierarchy> Result;
+  TypeHierarchy ExpectedResult{{
+      TypeHierarchyResult{"Parent1", Source.point("Parent1Def"), false, {}},
+      TypeHierarchyResult{
+          "Parent3",
+          Source.point("Parent3Def"),
+          false,
+          {TypeHierarchyResult{
+              "Parent2", Source.point("Parent2Def"), false, {}}}},
+  }};
+
+  for (auto Pt : {"c1", "c2", "c3", "c4"}) {
+    Result = getTypeHierarchy(AST, Source.point(Pt));
+    ASSERT_TRUE(bool(Result));
+    EXPECT_THAT(*Result, Eq(ExpectedResult));
+  }
+}
+
+TEST(TypeHierarchy, OnMethod) {
+  Annotations Source(R"cpp(
+$ParentDef^struct Parent
+{
+  void method ();
+  void method () const;
+  void method (int x);
+  void method (char x);
+};
+
+$Child1Def^struct Child1 : Parent
+{
+  void method ();
+  void method (char x);
+};
+
+struct Child2 : Child1
+{
+  void met$p1^hod ();
+  void met$p2^hod (int x);
+};
+
+struct Child3 : Child2
+{
+  void method (int x);
+};
+)cpp");
+
+  TestTU TU = TestTU::withCode(Source.code());
+  auto AST = TU.build();
+
+  ASSERT_TRUE(AST.getDiagnostics().empty());
+
+  {
+    TypeHierarchy ExpectedResult{{TypeHierarchyResult{
+        "Child1",
+        Source.point("Child1Def"),
+        true,
+        {TypeHierarchyResult{"Parent", Source.point("ParentDef"), true, {}}}}}};
+
+    llvm::Optional<TypeHierarchy> Result =
+        getTypeHierarchy(AST, Source.point("p1"));
+    ASSERT_TRUE(bool(Result));
+    EXPECT_THAT(*Result, Eq(ExpectedResult));
+  }
+
+  {
+    TypeHierarchy ExpectedResult{{TypeHierarchyResult{
+        "Child1",
+        Source.point("Child1Def"),
+        false,
+        {TypeHierarchyResult{"Parent", Source.point("ParentDef"), true, {}}}}}};
+
+    llvm::Optional<TypeHierarchy> Result =
+        getTypeHierarchy(AST, Source.point("p2"));
+    ASSERT_TRUE(bool(Result));
+    EXPECT_THAT(*Result, Eq(ExpectedResult));
+  }
+}
+
 } // 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);
 
+/// Get the type hierarchy information at \p Pos.
+llvm::Optional<TypeHierarchy> getTypeHierarchy(ParsedAST &AST, Position Pos);
+
 } // namespace clangd
 } // namespace clang
 
Index: clangd/XRefs.cpp
===================================================================
--- clangd/XRefs.cpp
+++ clangd/XRefs.cpp
@@ -17,6 +17,10 @@
 #include "clang/Index/IndexingAction.h"
 #include "clang/Index/USRGeneration.h"
 #include "llvm/Support/Path.h"
+
+// temporary
+#include <iostream>
+
 namespace clang {
 namespace clangd {
 using namespace llvm;
@@ -660,5 +664,84 @@
   return None;
 }
 
+static bool MethodMatches(const CXXMethodDecl *Method,
+                          const CXXMethodDecl *Candidate) {
+  // FIXME: How do I determine if Method overrides Candidate?
+
+  return true;
+}
+
+static void getTypeHierarchyParents(const CXXRecordDecl *CXXRD,
+                                    const CXXMethodDecl *Method,
+                                    const SourceManager &SourceMgr,
+                                    std::vector<TypeHierarchyResult> *Result) {
+  for (auto It = CXXRD->bases_begin(); It != CXXRD->bases_end(); It++) {
+    const RecordType *ParentType =
+        It->getType().getTypePtr()->getAs<RecordType>();
+    if (!ParentType)
+      continue;
+
+    const CXXRecordDecl *ParentDecl = ParentType->getAsCXXRecordDecl();
+    if (!ParentDecl)
+      continue;
+
+    StringRef Name = ParentDecl->getName();
+    SourceLocation Loc = ParentDecl->getBeginLoc();
+    Position Pos = sourceLocToPosition(SourceMgr, Loc);
+    Result->emplace_back(TypeHierarchyResult{Name, Pos, false, {}});
+
+    std::cerr << " >>> A parent is " << ParentDecl->getName().str()
+              << std::endl;
+
+    for (const auto &CandidateMethod : ParentDecl->methods()) {
+      if (MethodMatches(Method, CandidateMethod)) {
+        Result->back().DeclaresMethod = true;
+        break;
+      }
+    }
+
+    getTypeHierarchyParents(ParentDecl, Method, SourceMgr,
+                            &Result->back().Parents);
+  }
+}
+
+static TypeHierarchy getTypeHierarchy(const CXXRecordDecl *CXXRD,
+                                      const CXXMethodDecl *Method,
+                                      const SourceManager &SourceMgr) {
+  TypeHierarchy Result;
+  getTypeHierarchyParents(CXXRD, Method, SourceMgr, &Result.Parents);
+  return Result;
+}
+
+Optional<TypeHierarchy> getTypeHierarchy(ParsedAST &AST, Position Pos) {
+  const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+  SourceLocation SourceLocationBeg =
+      getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID());
+  // Identified symbols at a specific position.
+  auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg);
+
+  if (Symbols.Decls.empty())
+    return {};
+
+  const Decl *D = Symbols.Decls[0];
+  const CXXRecordDecl *CXXRD;
+
+  const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D);
+  if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
+    // If this is a variable, use the type of the variable.
+    CXXRD = VD->getType().getTypePtr()->getAsCXXRecordDecl();
+  } else if (Method) {
+    // If this is a method, use the type of the class, but
+    CXXRD = Method->getParent();
+  } else {
+    CXXRD = dyn_cast<CXXRecordDecl>(D);
+  }
+
+  if (CXXRD)
+    return getTypeHierarchy(CXXRD, Method, SourceMgr);
+
+  return {};
+}
+
 } // namespace clangd
 } // namespace clang
Index: clangd/ProtocolHandlers.h
===================================================================
--- clangd/ProtocolHandlers.h
+++ clangd/ProtocolHandlers.h
@@ -54,6 +54,7 @@
   virtual void onRename(RenameParams &Parames) = 0;
   virtual void onDocumentHighlight(TextDocumentPositionParams &Params) = 0;
   virtual void onHover(TextDocumentPositionParams &Params) = 0;
+  virtual void onTypeHierarchy(TextDocumentPositionParams &Params) = 0;
   virtual void onChangeConfiguration(DidChangeConfigurationParams &Params) = 0;
   virtual void onCancelRequest(CancelParams &Params) = 0;
 };
Index: clangd/ProtocolHandlers.cpp
===================================================================
--- clangd/ProtocolHandlers.cpp
+++ clangd/ProtocolHandlers.cpp
@@ -67,6 +67,7 @@
            &ProtocolCallbacks::onSwitchSourceHeader);
   Register("textDocument/rename", &ProtocolCallbacks::onRename);
   Register("textDocument/hover", &ProtocolCallbacks::onHover);
+  Register("textDocument/typeHierarchy", &ProtocolCallbacks::onTypeHierarchy);
   Register("textDocument/documentSymbol", &ProtocolCallbacks::onDocumentSymbol);
   Register("workspace/didChangeWatchedFiles", &ProtocolCallbacks::onFileEvent);
   Register("workspace/executeCommand", &ProtocolCallbacks::onCommand);
Index: clangd/Protocol.h
===================================================================
--- clangd/Protocol.h
+++ clangd/Protocol.h
@@ -881,6 +881,36 @@
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const CancelParams &);
 bool fromJSON(const llvm::json::Value &, CancelParams &);
 
+struct TypeHierarchyResult {
+  std::string Name;
+  Position Pos;
+
+  // Does this node implement the method targeted by the request?
+  bool DeclaresMethod;
+
+  std::vector<TypeHierarchyResult> Parents;
+
+  friend bool operator==(const TypeHierarchyResult &lhs,
+                         const TypeHierarchyResult &rhs) {
+    return lhs.Name == rhs.Name && lhs.Pos == rhs.Pos &&
+           lhs.DeclaresMethod == rhs.DeclaresMethod &&
+           lhs.Parents == rhs.Parents;
+  }
+};
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TypeHierarchyResult &);
+
+struct TypeHierarchy {
+  std::vector<TypeHierarchyResult> Parents;
+
+  friend bool operator==(const TypeHierarchy &lhs, const TypeHierarchy &rhs) {
+    return lhs.Parents == rhs.Parents;
+  }
+};
+
+llvm::json::Value toJSON(const TypeHierarchy &DH);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TypeHierarchy &);
+
 /// Param can be either of type string or number. Returns the result as a
 /// string.
 llvm::Optional<std::string> parseNumberOrString(const llvm::json::Value *Param);
Index: clangd/Protocol.cpp
===================================================================
--- clangd/Protocol.cpp
+++ clangd/Protocol.cpp
@@ -597,6 +597,53 @@
   return O;
 }
 
+json::Value toJSON(const TypeHierarchyResult &THR) {
+  json::Object Result;
+
+  Result["name"] = THR.Name;
+  Result["position"] = toJSON(THR.Pos);
+  // FIXME: write the rest
+
+  return Result;
+}
+
+static llvm::raw_ostream &
+operator<<(llvm::raw_ostream &O, const std::vector<TypeHierarchyResult> &V) {
+  O << '{';
+
+  for (size_t i = 0; i < V.size(); i++) {
+    O << V[i];
+    if (i != 0) {
+      O << " ,";
+    }
+  }
+
+  O << '}';
+
+  return O;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &O,
+                              const TypeHierarchyResult &V) {
+  O << "{Name=" << V.Name << ", Pos=" << V.Pos
+    << ", DeclaresMethod=" << V.DeclaresMethod << ", Parents=" << V.Parents
+    << "}";
+  return O;
+}
+
+json::Value toJSON(const TypeHierarchy &TH) {
+  json::Object Result;
+
+  Result["parents"] = json::Array(TH.Parents);
+
+  return Result;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &O, const TypeHierarchy &V) {
+  O << "Parents=" << V.Parents;
+  return O;
+}
+
 bool fromJSON(const json::Value &Params, DidChangeConfigurationParams &CCP) {
   json::ObjectMapper O(Params);
   return O && O.map("settings", CCP.settings);
Index: clangd/ClangdServer.h
===================================================================
--- clangd/ClangdServer.h
+++ clangd/ClangdServer.h
@@ -149,6 +149,10 @@
   void findHover(PathRef File, Position Pos,
                  Callback<llvm::Optional<Hover>> CB);
 
+  /// Get type hierarchy information for a given position.
+  void findTypeHierarchy(PathRef File, Position Pos,
+                         Callback<llvm::Optional<TypeHierarchy>> CB);
+
   /// Retrieve the top symbols from the workspace matching a query.
   void workspaceSymbols(StringRef Query, int Limit,
                         Callback<std::vector<SymbolInformation>> CB);
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -476,6 +476,18 @@
   WorkScheduler.runWithAST("Hover", File, Bind(Action, std::move(CB)));
 }
 
+void ClangdServer::findTypeHierarchy(
+    PathRef File, Position Pos, Callback<llvm::Optional<TypeHierarchy>> CB) {
+  auto Action = [Pos](Callback<llvm::Optional<TypeHierarchy>> CB,
+                      llvm::Expected<InputsAndAST> InpAST) {
+    if (!InpAST)
+      return CB(InpAST.takeError());
+    CB(clangd::getTypeHierarchy(InpAST->AST, Pos));
+  };
+
+  WorkScheduler.runWithAST("Type Hierarchy", File, Bind(Action, std::move(CB)));
+}
+
 void ClangdServer::consumeDiagnostics(PathRef File, DocVersion Version,
                                       std::vector<Diag> Diags) {
   // We need to serialize access to resulting diagnostics to avoid calling
Index: clangd/ClangdLSPServer.h
===================================================================
--- clangd/ClangdLSPServer.h
+++ clangd/ClangdLSPServer.h
@@ -74,6 +74,7 @@
   void onWorkspaceSymbol(WorkspaceSymbolParams &Params) override;
   void onRename(RenameParams &Parames) override;
   void onHover(TextDocumentPositionParams &Params) override;
+  void onTypeHierarchy(TextDocumentPositionParams &Params) override;
   void onChangeConfiguration(DidChangeConfigurationParams &Params) override;
   void onCancelRequest(CancelParams &Params) override;
 
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -414,6 +414,19 @@
                    });
 }
 
+void ClangdLSPServer::onTypeHierarchy(TextDocumentPositionParams &Params) {
+  Server.findTypeHierarchy(Params.textDocument.uri.file(), Params.position,
+                           [](llvm::Expected<llvm::Optional<TypeHierarchy>> H) {
+                             if (!H) {
+                               replyError(ErrorCode::InternalError,
+                                          llvm::toString(H.takeError()));
+                               return;
+                             }
+
+                             reply(*H);
+                           });
+}
+
 void ClangdLSPServer::applyConfiguration(
     const ClangdConfigurationParamsChange &Settings) {
   // Compilation database change.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to