kadircet updated this revision to Diff 452927.
kadircet marked 2 inline comments as done.
kadircet added a comment.

- Address comments


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131385/new/

https://reviews.llvm.org/D131385

Files:
  clang-tools-extra/clangd/ClangdLSPServer.cpp
  clang-tools-extra/clangd/ClangdLSPServer.h
  clang-tools-extra/clangd/ClangdServer.cpp
  clang-tools-extra/clangd/ClangdServer.h
  clang-tools-extra/clangd/Protocol.cpp
  clang-tools-extra/clangd/Protocol.h
  clang-tools-extra/clangd/XRefs.cpp
  clang-tools-extra/clangd/XRefs.h
  clang-tools-extra/clangd/test/initialize-params.test
  clang-tools-extra/clangd/test/type-hierarchy-ext.test
  clang-tools-extra/clangd/test/type-hierarchy.test
  clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp

Index: clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp
+++ clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp
@@ -5,6 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+#include "AST.h"
 #include "Annotations.h"
 #include "Matchers.h"
 #include "ParsedAST.h"
@@ -16,6 +17,7 @@
 #include "llvm/Support/Path.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include <vector>
 
 namespace clang {
 namespace clangd {
@@ -26,6 +28,7 @@
 using ::testing::Field;
 using ::testing::IsEmpty;
 using ::testing::Matcher;
+using ::testing::SizeIs;
 using ::testing::UnorderedElementsAre;
 
 // GMock helpers for matching TypeHierarchyItem.
@@ -45,6 +48,10 @@
 // Note: "not resolved" is different from "resolved but empty"!
 MATCHER(parentsNotResolved, "") { return !arg.parents; }
 MATCHER(childrenNotResolved, "") { return !arg.children; }
+MATCHER_P(withResolveID, SID, "") { return arg.symbolID.str() == SID; }
+MATCHER_P(withResolveParents, M, "") {
+  return testing::ExplainMatchResult(M, arg.data.parents, result_listener);
+}
 
 TEST(FindRecordTypeAt, TypeOrVariable) {
   Annotations Source(R"cpp(
@@ -64,8 +71,10 @@
   auto AST = TU.build();
 
   for (Position Pt : Source.points()) {
-    const CXXRecordDecl *RD = findRecordTypeAt(AST, Pt);
-    EXPECT_EQ(&findDecl(AST, "Child2"), static_cast<const NamedDecl *>(RD));
+    auto Records = findRecordTypeAt(AST, Pt);
+    ASSERT_THAT(Records, SizeIs(1));
+    EXPECT_EQ(&findDecl(AST, "Child2"),
+              static_cast<const NamedDecl *>(Records.front()));
   }
 }
 
@@ -86,8 +95,10 @@
   auto AST = TU.build();
 
   for (Position Pt : Source.points()) {
-    const CXXRecordDecl *RD = findRecordTypeAt(AST, Pt);
-    EXPECT_EQ(&findDecl(AST, "Child2"), static_cast<const NamedDecl *>(RD));
+    auto Records = findRecordTypeAt(AST, Pt);
+    ASSERT_THAT(Records, SizeIs(1));
+    EXPECT_EQ(&findDecl(AST, "Child2"),
+              static_cast<const NamedDecl *>(Records.front()));
   }
 }
 
@@ -107,11 +118,10 @@
   auto AST = TU.build();
 
   for (Position Pt : Source.points()) {
-    const CXXRecordDecl *RD = findRecordTypeAt(AST, Pt);
     // A field does not unambiguously specify a record type
     // (possible associated reocrd types could be the field's type,
     // or the type of the record that the field is a member of).
-    EXPECT_EQ(nullptr, RD);
+    EXPECT_THAT(findRecordTypeAt(AST, Pt), SizeIs(0));
   }
 }
 
@@ -359,11 +369,11 @@
   for (Position Pt : Source.points()) {
     // Set ResolveLevels to 0 because it's only used for Children;
     // for Parents, getTypeHierarchy() always returns all levels.
-    llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
-        AST, Pt, /*ResolveLevels=*/0, TypeHierarchyDirection::Parents);
-    ASSERT_TRUE(bool(Result));
+    auto Result = getTypeHierarchy(AST, Pt, /*ResolveLevels=*/0,
+                                   TypeHierarchyDirection::Parents);
+    ASSERT_THAT(Result, SizeIs(1));
     EXPECT_THAT(
-        *Result,
+        Result.front(),
         AllOf(
             withName("Child"), withKind(SymbolKind::Struct),
             parents(AllOf(withName("Parent1"), withKind(SymbolKind::Struct),
@@ -398,11 +408,11 @@
   // The parent is reported as "S" because "S<0>" is an invalid instantiation.
   // We then iterate once more and find "S" again before detecting the
   // recursion.
-  llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
-      AST, Source.points()[0], 0, TypeHierarchyDirection::Parents);
-  ASSERT_TRUE(bool(Result));
+  auto Result = getTypeHierarchy(AST, Source.points()[0], 0,
+                                 TypeHierarchyDirection::Parents);
+  ASSERT_THAT(Result, SizeIs(1));
   EXPECT_THAT(
-      *Result,
+      Result.front(),
       AllOf(withName("S<0>"), withKind(SymbolKind::Struct),
             parents(
                 AllOf(withName("S"), withKind(SymbolKind::Struct),
@@ -432,11 +442,11 @@
 
   // Make sure getTypeHierarchy() doesn't get into an infinite recursion
   // for either a concrete starting point or a dependent starting point.
-  llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
-      AST, Source.point("SRefConcrete"), 0, TypeHierarchyDirection::Parents);
-  ASSERT_TRUE(bool(Result));
+  auto Result = getTypeHierarchy(AST, Source.point("SRefConcrete"), 0,
+                                 TypeHierarchyDirection::Parents);
+  ASSERT_THAT(Result, SizeIs(1));
   EXPECT_THAT(
-      *Result,
+      Result.front(),
       AllOf(withName("S<2>"), withKind(SymbolKind::Struct),
             parents(AllOf(
                 withName("S<1>"), withKind(SymbolKind::Struct),
@@ -445,9 +455,9 @@
                               parents()))))));
   Result = getTypeHierarchy(AST, Source.point("SRefDependent"), 0,
                             TypeHierarchyDirection::Parents);
-  ASSERT_TRUE(bool(Result));
+  ASSERT_THAT(Result, SizeIs(1));
   EXPECT_THAT(
-      *Result,
+      Result.front(),
       AllOf(withName("S"), withKind(SymbolKind::Struct),
             parents(AllOf(withName("S"), withKind(SymbolKind::Struct),
                           selectionRangeIs(Source.range("SDef")), parents()))));
@@ -469,11 +479,11 @@
   auto AST = TU.build();
   auto Index = TU.index();
 
-  llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
-      AST, Source.points()[0], 2, TypeHierarchyDirection::Children, Index.get(),
-      testPath(TU.Filename));
-  ASSERT_TRUE(bool(Result));
-  EXPECT_THAT(*Result,
+  auto Result = getTypeHierarchy(AST, Source.points()[0], 2,
+                                 TypeHierarchyDirection::Children, Index.get(),
+                                 testPath(TU.Filename));
+  ASSERT_THAT(Result, SizeIs(1));
+  EXPECT_THAT(Result.front(),
               AllOf(withName("Parent"), withKind(SymbolKind::Struct),
                     children(AllOf(withName("Child1"),
                                    withKind(SymbolKind::Struct), children()),
@@ -495,12 +505,12 @@
   auto AST = TU.build();
   auto Index = TU.index();
 
-  llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
-      AST, Source.points()[0], 2, TypeHierarchyDirection::Children, Index.get(),
-      testPath(TU.Filename));
-  ASSERT_TRUE(bool(Result));
-  EXPECT_THAT(*Result, AllOf(withName("Parent"), withKind(SymbolKind::Struct),
-                             children()));
+  auto Result = getTypeHierarchy(AST, Source.points()[0], 2,
+                                 TypeHierarchyDirection::Children, Index.get(),
+                                 testPath(TU.Filename));
+  ASSERT_THAT(Result, SizeIs(1));
+  EXPECT_THAT(Result.front(), AllOf(withName("Parent"),
+                                    withKind(SymbolKind::Struct), children()));
 }
 
 TEST(TypeHierarchy, DeriveFromTemplate) {
@@ -521,11 +531,11 @@
   // FIXME: We'd like this to show the implicit specializations Parent<int>
   //        and Child<int>, but currently libIndex does not expose relationships
   //        between implicit specializations.
-  llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
-      AST, Source.points()[0], 2, TypeHierarchyDirection::Children, Index.get(),
-      testPath(TU.Filename));
-  ASSERT_TRUE(bool(Result));
-  EXPECT_THAT(*Result,
+  auto Result = getTypeHierarchy(AST, Source.points()[0], 2,
+                                 TypeHierarchyDirection::Children, Index.get(),
+                                 testPath(TU.Filename));
+  ASSERT_THAT(Result, SizeIs(1));
+  EXPECT_THAT(Result.front(),
               AllOf(withName("Parent"), withKind(SymbolKind::Struct),
                     children(AllOf(withName("Child"),
                                    withKind(SymbolKind::Struct), children()))));
@@ -546,12 +556,12 @@
   TU.HeaderCode = HeaderInPreambleAnnotations.code().str();
   auto AST = TU.build();
 
-  llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
+  std::vector<TypeHierarchyItem> Result = getTypeHierarchy(
       AST, SourceAnnotations.point(), 1, TypeHierarchyDirection::Parents);
 
-  ASSERT_TRUE(Result);
+  ASSERT_THAT(Result, SizeIs(1));
   EXPECT_THAT(
-      *Result,
+      Result.front(),
       AllOf(withName("Child"),
             parents(AllOf(withName("Parent"),
                           selectionRangeIs(HeaderInPreambleAnnotations.range()),
@@ -722,22 +732,21 @@
   auto AST = TU.build();
   auto Index = TU.index();
 
-  llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
-      AST, Source.point(), /*ResolveLevels=*/1,
-      TypeHierarchyDirection::Children, Index.get(), testPath(TU.Filename));
-  ASSERT_TRUE(bool(Result));
+  auto Result = getTypeHierarchy(AST, Source.point(), /*ResolveLevels=*/1,
+                                 TypeHierarchyDirection::Children, Index.get(),
+                                 testPath(TU.Filename));
+  ASSERT_THAT(Result, SizeIs(1));
   EXPECT_THAT(
-      *Result,
-      AllOf(withName("Parent"), withKind(SymbolKind::Struct),
-            parentsNotResolved(),
+      Result.front(),
+      AllOf(withName("Parent"), withKind(SymbolKind::Struct), parents(),
             children(AllOf(withName("Child1"), withKind(SymbolKind::Struct),
                            parentsNotResolved(), childrenNotResolved()))));
 
-  resolveTypeHierarchy((*Result->children)[0], /*ResolveLevels=*/1,
+  resolveTypeHierarchy((*Result.front().children)[0], /*ResolveLevels=*/1,
                        TypeHierarchyDirection::Children, Index.get());
 
   EXPECT_THAT(
-      (*Result->children)[0],
+      (*Result.front().children)[0],
       AllOf(withName("Child1"), withKind(SymbolKind::Struct),
             parentsNotResolved(),
             children(AllOf(withName("Child2a"), withKind(SymbolKind::Struct),
@@ -746,6 +755,53 @@
                            parentsNotResolved(), childrenNotResolved()))));
 }
 
+TEST(Standard, SubTypes) {
+  Annotations Source(R"cpp(
+struct Pare^nt1 {};
+struct Parent2 {};
+struct Child : Parent1, Parent2 {};
+)cpp");
+
+  TestTU TU = TestTU::withCode(Source.code());
+  auto AST = TU.build();
+  auto Index = TU.index();
+
+  auto Result = getTypeHierarchy(AST, Source.point(), /*ResolveLevels=*/1,
+                                 TypeHierarchyDirection::Children, Index.get(),
+                                 testPath(TU.Filename));
+  ASSERT_THAT(Result, SizeIs(1));
+  auto Children = subTypes(Result.front(), Index.get());
+
+  // Make sure parents are populated when getting children.
+  // FIXME: This is partial.
+  EXPECT_THAT(
+      Children,
+      UnorderedElementsAre(
+          AllOf(withName("Child"),
+                withResolveParents(HasValue(UnorderedElementsAre(withResolveID(
+                    getSymbolID(&findDecl(AST, "Parent1")).str())))))));
+}
+
+TEST(Standard, SuperTypes) {
+  Annotations Source(R"cpp(
+struct Parent {};
+struct Chil^d : Parent {};
+)cpp");
+
+  TestTU TU = TestTU::withCode(Source.code());
+  auto AST = TU.build();
+  auto Index = TU.index();
+
+  auto Result = getTypeHierarchy(AST, Source.point(), /*ResolveLevels=*/1,
+                                 TypeHierarchyDirection::Children, Index.get(),
+                                 testPath(TU.Filename));
+  ASSERT_THAT(Result, SizeIs(1));
+  auto Parents = superTypes(Result.front(), Index.get());
+
+  EXPECT_THAT(Parents, HasValue(UnorderedElementsAre(
+                           AllOf(withName("Parent"),
+                                 withResolveParents(HasValue(IsEmpty()))))));
+}
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/test/type-hierarchy.test
===================================================================
--- clang-tools-extra/clangd/test/type-hierarchy.test
+++ clang-tools-extra/clangd/test/type-hierarchy.test
@@ -3,178 +3,140 @@
 ---
 {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"struct Parent {};\nstruct Child1 : Parent {};\nstruct Child2 : Child1 {};\nstruct Child3 : Child2 {};\nstruct Child4 : Child3 {};"}}}
 ---
-{"jsonrpc":"2.0","id":1,"method":"textDocument/typeHierarchy","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":2,"character":11},"direction":2,"resolve":1}}
+{"jsonrpc":"2.0","id":1,"method":"textDocument/prepareTypeHierarchy","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":2,"character":11},"direction":2,"resolve":1}}
 #      CHECK:  "id": 1
 # CHECK-NEXT:  "jsonrpc": "2.0",
-# CHECK-NEXT:  "result": {
-# CHECK-NEXT:    "children": [
-# CHECK-NEXT:      {
-# CHECK-NEXT:        "data": "A6576FE083F2949A",
-# CHECK-NEXT:        "kind": 23,
-# CHECK-NEXT:        "name": "Child3",
-# CHECK-NEXT:        "range": {
-# CHECK-NEXT:          "end": {
-# CHECK-NEXT:            "character": 13,
-# CHECK-NEXT:            "line": 3
-# CHECK-NEXT:          },
-# CHECK-NEXT:          "start": {
-# CHECK-NEXT:            "character": 7,
-# CHECK-NEXT:            "line": 3
-# CHECK-NEXT:          }
-# CHECK-NEXT:        },
-# CHECK-NEXT:        "selectionRange": {
-# CHECK-NEXT:          "end": {
-# CHECK-NEXT:            "character": 13,
-# CHECK-NEXT:            "line": 3
-# CHECK-NEXT:          },
-# CHECK-NEXT:          "start": {
-# CHECK-NEXT:            "character": 7,
-# CHECK-NEXT:            "line": 3
-# CHECK-NEXT:          }
-# CHECK-NEXT:        },
-# CHECK-NEXT:        "uri": "file://{{.*}}/clangd-test/main.cpp"
-# CHECK-NEXT:      }
-# CHECK-NEXT:    ],
-# CHECK-NEXT:    "data": "8A991335E4E67D08",
-# CHECK-NEXT:    "kind": 23,
-# CHECK-NEXT:    "name": "Child2",
-# CHECK-NEXT:    "parents": [
-# CHECK-NEXT:      {
-# CHECK-NEXT:        "data": "ECDC0C46D75120F4",
-# CHECK-NEXT:        "kind": 23,
-# CHECK-NEXT:        "name": "Child1",
-# CHECK-NEXT:        "parents": [
+# CHECK-NEXT:  "result": [
+# CHECK-NEXT:     {
+# CHECK-NEXT:       "data": {
+# CHECK-NEXT:         "parents": [
 # CHECK-NEXT:          {
-# CHECK-NEXT:            "data": "FE546E7B648D69A7",
-# CHECK-NEXT:            "kind": 23,
-# CHECK-NEXT:            "name": "Parent",
-# CHECK-NEXT:            "parents": [],
-# CHECK-NEXT:            "range": {
-# CHECK-NEXT:              "end": {
-# CHECK-NEXT:                "character": 16,
-# CHECK-NEXT:                "line": 0
-# CHECK-NEXT:              },
-# CHECK-NEXT:              "start": {
-# CHECK-NEXT:                "character": 0,
-# CHECK-NEXT:                "line": 0
-# CHECK-NEXT:              }
-# CHECK-NEXT:            },
-# CHECK-NEXT:            "selectionRange": {
-# CHECK-NEXT:              "end": {
-# CHECK-NEXT:                "character": 13,
-# CHECK-NEXT:                "line": 0
-# CHECK-NEXT:              },
-# CHECK-NEXT:              "start": {
-# CHECK-NEXT:                "character": 7,
-# CHECK-NEXT:                "line": 0
-# CHECK-NEXT:              }
-# CHECK-NEXT:            },
-# CHECK-NEXT:            "uri": "file://{{.*}}/clangd-test/main.cpp"
+# CHECK-NEXT:           "parents": [
+# CHECK-NEXT:             {
+# CHECK-NEXT:              "parents": [],
+# CHECK-NEXT:              "symbolID": "FE546E7B648D69A7"
+# CHECK-NEXT:             }
+# CHECK-NEXT:           ],
+# CHECK-NEXT:           "symbolID": "ECDC0C46D75120F4"
 # CHECK-NEXT:          }
 # CHECK-NEXT:        ],
-# CHECK-NEXT:        "range": {
-# CHECK-NEXT:          "end": {
-# CHECK-NEXT:            "character": 25,
-# CHECK-NEXT:            "line": 1
-# CHECK-NEXT:          },
-# CHECK-NEXT:          "start": {
-# CHECK-NEXT:            "character": 0,
-# CHECK-NEXT:            "line": 1
-# CHECK-NEXT:          }
-# CHECK-NEXT:        },
-# CHECK-NEXT:        "selectionRange": {
-# CHECK-NEXT:          "end": {
-# CHECK-NEXT:            "character": 13,
-# CHECK-NEXT:            "line": 1
-# CHECK-NEXT:          },
-# CHECK-NEXT:          "start": {
-# CHECK-NEXT:            "character": 7,
-# CHECK-NEXT:            "line": 1
-# CHECK-NEXT:          }
-# CHECK-NEXT:        },
-# CHECK-NEXT:        "uri": "file://{{.*}}/clangd-test/main.cpp"
-# CHECK-NEXT:      }
-# CHECK-NEXT:    ],
-# CHECK-NEXT:    "range": {
-# CHECK-NEXT:      "end": {
-# CHECK-NEXT:        "character": 25,
-# CHECK-NEXT:        "line": 2
-# CHECK-NEXT:      },
-# CHECK-NEXT:      "start": {
-# CHECK-NEXT:        "character": 0,
-# CHECK-NEXT:        "line": 2
-# CHECK-NEXT:      }
-# CHECK-NEXT:    },
-# CHECK-NEXT:    "selectionRange": {
-# CHECK-NEXT:      "end": {
-# CHECK-NEXT:        "character": 13,
-# CHECK-NEXT:        "line": 2
-# CHECK-NEXT:      },
-# CHECK-NEXT:      "start": {
-# CHECK-NEXT:        "character": 7,
-# CHECK-NEXT:        "line": 2
-# CHECK-NEXT:      }
-# CHECK-NEXT:    },
-# CHECK-NEXT:    "uri": "file://{{.*}}/clangd-test/main.cpp"
-# CHECK-NEXT:  }
+# CHECK-NEXT:        "symbolID": "8A991335E4E67D08"
+# CHECK-NEXT:       },
+# CHECK-NEXT:       "kind": 23,
+# CHECK-NEXT:       "name": "Child2",
+# CHECK-NEXT:       "range": {
+# CHECK-NEXT:         "end": {
+# CHECK-NEXT:           "character": 25,
+# CHECK-NEXT:           "line": 2
+# CHECK-NEXT:         },
+# CHECK-NEXT:         "start": {
+# CHECK-NEXT:           "character": 0,
+# CHECK-NEXT:           "line": 2
+# CHECK-NEXT:         }
+# CHECK-NEXT:       },
+# CHECK-NEXT:       "selectionRange": {
+# CHECK-NEXT:         "end": {
+# CHECK-NEXT:           "character": 13,
+# CHECK-NEXT:           "line": 2
+# CHECK-NEXT:         },
+# CHECK-NEXT:         "start": {
+# CHECK-NEXT:           "character": 7,
+# CHECK-NEXT:           "line": 2
+# CHECK-NEXT:         }
+# CHECK-NEXT:       },
+# CHECK-NEXT:       "uri": "file://{{.*}}/clangd-test/main.cpp"
+# CHECK-NEXT:     }
+# CHECK-NEXT:   ]
 ---
-{"jsonrpc":"2.0","id":2,"method":"typeHierarchy/resolve","params":{"item":{"uri":"test:///main.cpp","data":"A6576FE083F2949A","name":"Child3","kind":23,"range":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}},"selectionRange":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}}},"direction":0,"resolve":1}}
+{"jsonrpc":"2.0","id":2,"method":"typeHierarchy/supertypes","params":{"item":{"uri":"test:///main.cpp","data":{"parents":[{"parents":[{"parents":[],"symbolID":"FE546E7B648D69A7"}],"symbolID":"ECDC0C46D75120F4"}],"symbolID":"8A991335E4E67D08"},"name":"Child2","kind":23,"range":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}},"selectionRange":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}}}}}
 #      CHECK:  "id": 2
 # CHECK-NEXT:  "jsonrpc": "2.0",
-# CHECK-NEXT:  "result": {
-# CHECK-NEXT:    "children": [
-# CHECK-NEXT:      {
-# CHECK-NEXT:        "data": "5705B382DFC77CBC",
-# CHECK-NEXT:        "kind": 23,
-# CHECK-NEXT:        "name": "Child4",
-# CHECK-NEXT:        "range": {
-# CHECK-NEXT:          "end": {
-# CHECK-NEXT:            "character": 13,
-# CHECK-NEXT:            "line": 4
-# CHECK-NEXT:          },
-# CHECK-NEXT:          "start": {
-# CHECK-NEXT:            "character": 7,
-# CHECK-NEXT:            "line": 4
-# CHECK-NEXT:          }
-# CHECK-NEXT:        },
-# CHECK-NEXT:        "selectionRange": {
-# CHECK-NEXT:          "end": {
-# CHECK-NEXT:            "character": 13,
-# CHECK-NEXT:            "line": 4
-# CHECK-NEXT:          },
-# CHECK-NEXT:          "start": {
-# CHECK-NEXT:            "character": 7,
-# CHECK-NEXT:            "line": 4
-# CHECK-NEXT:          }
-# CHECK-NEXT:        },
-# CHECK-NEXT:        "uri": "file://{{.*}}/clangd-test/main.cpp"
-# CHECK-NEXT:      }
-# CHECK-NEXT:    ],
-# CHECK-NEXT:    "data": "A6576FE083F2949A",
-# CHECK-NEXT:    "kind": 23,
-# CHECK-NEXT:    "name": "Child3",
-# CHECK-NEXT:    "range": {
-# CHECK-NEXT:      "end": {
-# CHECK-NEXT:        "character": 13,
-# CHECK-NEXT:        "line": 3
-# CHECK-NEXT:      },
-# CHECK-NEXT:      "start": {
-# CHECK-NEXT:        "character": 7,
-# CHECK-NEXT:        "line": 3
-# CHECK-NEXT:      }
-# CHECK-NEXT:    },
-# CHECK-NEXT:    "selectionRange": {
-# CHECK-NEXT:      "end": {
-# CHECK-NEXT:        "character": 13,
-# CHECK-NEXT:        "line": 3
-# CHECK-NEXT:      },
-# CHECK-NEXT:      "start": {
-# CHECK-NEXT:        "character": 7,
-# CHECK-NEXT:        "line": 3
-# CHECK-NEXT:      }
-# CHECK-NEXT:    },
-# CHECK-NEXT:    "uri": "file://{{.*}}/clangd-test/main.cpp"
-# CHECK-NEXT:  }
+# CHECK-NEXT:  "result": [
+# CHECK-NEXT:     {
+# CHECK-NEXT:       "data": {
+# CHECK-NEXT:         "parents": [
+# CHECK-NEXT:           {
+# CHECK-NEXT:            "parents": [],
+# CHECK-NEXT:            "symbolID": "FE546E7B648D69A7"
+# CHECK-NEXT:           }
+# CHECK-NEXT:         ],
+# CHECK-NEXT:         "symbolID": "ECDC0C46D75120F4"
+# CHECK-NEXT:       },
+# CHECK-NEXT:       "kind": 23,
+# CHECK-NEXT:       "name": "Child1",
+# CHECK-NEXT:       "range": {
+# CHECK-NEXT:         "end": {
+# CHECK-NEXT:           "character": 13,
+# CHECK-NEXT:           "line": 1
+# CHECK-NEXT:         },
+# CHECK-NEXT:         "start": {
+# CHECK-NEXT:           "character": 7,
+# CHECK-NEXT:           "line": 1
+# CHECK-NEXT:         }
+# CHECK-NEXT:       },
+# CHECK-NEXT:       "selectionRange": {
+# CHECK-NEXT:         "end": {
+# CHECK-NEXT:           "character": 13,
+# CHECK-NEXT:           "line": 1
+# CHECK-NEXT:         },
+# CHECK-NEXT:         "start": {
+# CHECK-NEXT:           "character": 7,
+# CHECK-NEXT:           "line": 1
+# CHECK-NEXT:         }
+# CHECK-NEXT:       },
+# CHECK-NEXT:       "uri": "file://{{.*}}/clangd-test/main.cpp"
+# CHECK-NEXT:     }
+# CHECK-NEXT:  ]
+---
+{"jsonrpc":"2.0","id":2,"method":"typeHierarchy/subtypes","params":{"item":{"uri":"test:///main.cpp","data":{"parents":[{"parents":[{"parents":[],"symbolID":"FE546E7B648D69A7"}],"symbolID":"ECDC0C46D75120F4"}],"symbolID":"8A991335E4E67D08"},"name":"Child2","kind":23,"range":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}},"selectionRange":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}}}}}
+#      CHECK:  "id": 2
+# CHECK-NEXT:  "jsonrpc": "2.0",
+# CHECK-NEXT:  "result": [
+# CHECK-NEXT:     {
+# CHECK-NEXT:       "data": {
+# CHECK-NEXT:         "parents": [
+# CHECK-NEXT:          {
+# CHECK-NEXT:           "parents": [
+# CHECK-NEXT:            {
+# CHECK-NEXT:             "parents": [
+# CHECK-NEXT:               {
+# CHECK-NEXT:                "parents": [],
+# CHECK-NEXT:                "symbolID": "FE546E7B648D69A7"
+# CHECK-NEXT:               }
+# CHECK-NEXT:             ],
+# CHECK-NEXT:             "symbolID": "ECDC0C46D75120F4"
+# CHECK-NEXT:            }
+# CHECK-NEXT:           ],
+# CHECK-NEXT:           "symbolID": "8A991335E4E67D08"
+# CHECK-NEXT:         }
+# CHECK-NEXT:        ],
+# CHECK-NEXT:        "symbolID": "A6576FE083F2949A"
+# CHECK-NEXT:       },
+# CHECK-NEXT:       "kind": 23,
+# CHECK-NEXT:       "name": "Child3",
+# CHECK-NEXT:       "range": {
+# CHECK-NEXT:         "end": {
+# CHECK-NEXT:           "character": 13,
+# CHECK-NEXT:           "line": 3
+# CHECK-NEXT:         },
+# CHECK-NEXT:         "start": {
+# CHECK-NEXT:           "character": 7,
+# CHECK-NEXT:           "line": 3
+# CHECK-NEXT:         }
+# CHECK-NEXT:       },
+# CHECK-NEXT:       "selectionRange": {
+# CHECK-NEXT:         "end": {
+# CHECK-NEXT:           "character": 13,
+# CHECK-NEXT:           "line": 3
+# CHECK-NEXT:         },
+# CHECK-NEXT:         "start": {
+# CHECK-NEXT:           "character": 7,
+# CHECK-NEXT:           "line": 3
+# CHECK-NEXT:         }
+# CHECK-NEXT:       },
+# CHECK-NEXT:       "uri": "file://{{.*}}/clangd-test/main.cpp"
+# CHECK-NEXT:     }
+# CHECK-NEXT:  ]
 ---
 {"jsonrpc":"2.0","id":3,"method":"shutdown"}
 ---
Index: clang-tools-extra/clangd/test/type-hierarchy-ext.test
===================================================================
--- clang-tools-extra/clangd/test/type-hierarchy-ext.test
+++ clang-tools-extra/clangd/test/type-hierarchy-ext.test
@@ -9,7 +9,9 @@
 # CHECK-NEXT:  "result": {
 # CHECK-NEXT:    "children": [
 # CHECK-NEXT:      {
-# CHECK-NEXT:        "data": "A6576FE083F2949A",
+# CHECK-NEXT:        "data": {
+# CHECK-NEXT:           "symbolID": "A6576FE083F2949A"
+# CHECK-NEXT:        },
 # CHECK-NEXT:        "kind": 23,
 # CHECK-NEXT:        "name": "Child3",
 # CHECK-NEXT:        "range": {
@@ -35,17 +37,41 @@
 # CHECK-NEXT:        "uri": "file://{{.*}}/clangd-test/main.cpp"
 # CHECK-NEXT:      }
 # CHECK-NEXT:    ],
-# CHECK-NEXT:    "data": "8A991335E4E67D08",
+# CHECK-NEXT:    "data": {
+# CHECK-NEXT:        "parents": [
+# CHECK-NEXT:         {
+# CHECK-NEXT:          "parents": [
+# CHECK-NEXT:            {
+# CHECK-NEXT:             "parents": [],
+# CHECK-NEXT:             "symbolID": "FE546E7B648D69A7"
+# CHECK-NEXT:            }
+# CHECK-NEXT:          ],
+# CHECK-NEXT:          "symbolID": "ECDC0C46D75120F4"
+# CHECK-NEXT:         }
+# CHECK-NEXT:        ],
+# CHECK-NEXT:        "symbolID": "8A991335E4E67D08"
+# CHECK-NEXT:    },
 # CHECK-NEXT:    "kind": 23,
 # CHECK-NEXT:    "name": "Child2",
 # CHECK-NEXT:    "parents": [
 # CHECK-NEXT:      {
-# CHECK-NEXT:        "data": "ECDC0C46D75120F4",
+# CHECK-NEXT:        "data": {
+# CHECK-NEXT:          "parents": [
+# CHECK-NEXT:            {
+# CHECK-NEXT:             "parents": [],
+# CHECK-NEXT:             "symbolID": "FE546E7B648D69A7"
+# CHECK-NEXT:            }
+# CHECK-NEXT:          ],
+# CHECK-NEXT:          "symbolID": "ECDC0C46D75120F4"
+# CHECK-NEXT:        },
 # CHECK-NEXT:        "kind": 23,
 # CHECK-NEXT:        "name": "Child1",
 # CHECK-NEXT:        "parents": [
 # CHECK-NEXT:          {
-# CHECK-NEXT:            "data": "FE546E7B648D69A7",
+# CHECK-NEXT:            "data": {
+# CHECK-NEXT:             "parents": [],
+# CHECK-NEXT:             "symbolID": "FE546E7B648D69A7"
+# CHECK-NEXT:            },
 # CHECK-NEXT:            "kind": 23,
 # CHECK-NEXT:            "name": "Parent",
 # CHECK-NEXT:            "parents": [],
@@ -118,13 +144,15 @@
 # CHECK-NEXT:    "uri": "file://{{.*}}/clangd-test/main.cpp"
 # CHECK-NEXT:  }
 ---
-{"jsonrpc":"2.0","id":2,"method":"typeHierarchy/resolve","params":{"item":{"uri":"test:///main.cpp","data":"A6576FE083F2949A","name":"Child3","kind":23,"range":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}},"selectionRange":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}}},"direction":0,"resolve":1}}
+{"jsonrpc":"2.0","id":2,"method":"typeHierarchy/resolve","params":{"item":{"uri":"test:///main.cpp","data":{"symbolID":"A6576FE083F2949A"},"name":"Child3","kind":23,"range":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}},"selectionRange":{"end":{"character":13,"line":3},"start":{"character":7,"line":3}}},"direction":0,"resolve":1}}
 #      CHECK:  "id": 2
 # CHECK-NEXT:  "jsonrpc": "2.0",
 # CHECK-NEXT:  "result": {
 # CHECK-NEXT:    "children": [
 # CHECK-NEXT:      {
-# CHECK-NEXT:        "data": "5705B382DFC77CBC",
+# CHECK-NEXT:        "data": {
+# CHECK-NEXT:          "symbolID": "5705B382DFC77CBC"
+# CHECK-NEXT:        },
 # CHECK-NEXT:        "kind": 23,
 # CHECK-NEXT:        "name": "Child4",
 # CHECK-NEXT:        "range": {
@@ -150,7 +178,9 @@
 # CHECK-NEXT:        "uri": "file://{{.*}}/clangd-test/main.cpp"
 # CHECK-NEXT:      }
 # CHECK-NEXT:    ],
-# CHECK-NEXT:    "data": "A6576FE083F2949A",
+# CHECK-NEXT:    "data": {
+# CHECK-NEXT:      "symbolID": "A6576FE083F2949A"
+# CHECK-NEXT:    },
 # CHECK-NEXT:    "kind": 23,
 # CHECK-NEXT:    "name": "Child3",
 # CHECK-NEXT:    "range": {
Index: clang-tools-extra/clangd/test/initialize-params.test
===================================================================
--- clang-tools-extra/clangd/test/initialize-params.test
+++ clang-tools-extra/clangd/test/initialize-params.test
@@ -88,6 +88,7 @@
 # CHECK-NEXT:          ","
 # CHECK-NEXT:        ]
 # CHECK-NEXT:      },
+# CHECK-NEXT:      "standardTypeHierarchyProvider": true,
 # CHECK-NEXT:      "textDocumentSync": {
 # CHECK-NEXT:        "change": 2,
 # CHECK-NEXT:        "openClose": true,
Index: clang-tools-extra/clangd/XRefs.h
===================================================================
--- clang-tools-extra/clangd/XRefs.h
+++ clang-tools-extra/clangd/XRefs.h
@@ -117,17 +117,26 @@
 /// Get info about symbols at \p Pos.
 std::vector<SymbolDetails> getSymbolInfo(ParsedAST &AST, Position Pos);
 
-/// Find the record type references at \p Pos.
-const CXXRecordDecl *findRecordTypeAt(ParsedAST &AST, Position Pos);
+/// Find the record types referenced at \p Pos.
+std::vector<const CXXRecordDecl *> findRecordTypeAt(ParsedAST &AST,
+                                                    Position Pos);
 
 /// Given a record type declaration, find its base (parent) types.
 std::vector<const CXXRecordDecl *> typeParents(const CXXRecordDecl *CXXRD);
 
 /// Get type hierarchy information at \p Pos.
-llvm::Optional<TypeHierarchyItem> getTypeHierarchy(
+std::vector<TypeHierarchyItem> getTypeHierarchy(
     ParsedAST &AST, Position Pos, int Resolve, TypeHierarchyDirection Direction,
     const SymbolIndex *Index = nullptr, PathRef TUPath = PathRef{});
 
+/// Returns direct parents of a TypeHierarchyItem using SymbolIDs stored inside
+/// the item.
+llvm::Optional<std::vector<TypeHierarchyItem>>
+superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index);
+/// Returns direct children of a TypeHierarchyItem.
+std::vector<TypeHierarchyItem> subTypes(const TypeHierarchyItem &Item,
+                                        const SymbolIndex *Index);
+
 void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels,
                           TypeHierarchyDirection Direction,
                           const SymbolIndex *Index);
Index: clang-tools-extra/clangd/XRefs.cpp
===================================================================
--- clang-tools-extra/clangd/XRefs.cpp
+++ clang-tools-extra/clangd/XRefs.cpp
@@ -51,6 +51,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallSet.h"
@@ -60,6 +61,7 @@
 #include "llvm/Support/Error.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
+#include <vector>
 
 namespace clang {
 namespace clangd {
@@ -864,7 +866,8 @@
   };
 
   ReferenceFinder(const ParsedAST &AST,
-                  const llvm::ArrayRef<const NamedDecl *> Targets, bool PerToken)
+                  const llvm::ArrayRef<const NamedDecl *> Targets,
+                  bool PerToken)
       : PerToken(PerToken), AST(AST) {
     for (const NamedDecl *ND : Targets) {
       const Decl *CD = ND->getCanonicalDecl();
@@ -937,7 +940,7 @@
 };
 
 std::vector<ReferenceFinder::Reference>
-findRefs(const llvm::ArrayRef<const NamedDecl*> TargetDecls, ParsedAST &AST,
+findRefs(const llvm::ArrayRef<const NamedDecl *> TargetDecls, ParsedAST &AST,
          bool PerToken) {
   ReferenceFinder RefFinder(AST, TargetDecls, PerToken);
   index::IndexingOptions IndexOpts;
@@ -1225,8 +1228,8 @@
     if (const SelectionTree::Node *N = ST.commonAncestor()) {
       DeclRelationSet Relations =
           DeclRelation::TemplatePattern | DeclRelation::Alias;
-      auto TargetDecls=
-           targetDecl(N->ASTNode, Relations, AST.getHeuristicResolver());
+      auto TargetDecls =
+          targetDecl(N->ASTNode, Relations, AST.getHeuristicResolver());
       if (!TargetDecls.empty()) {
         // FIXME: we may get multiple DocumentHighlights with the same location
         // and different kinds, deduplicate them.
@@ -1595,29 +1598,32 @@
 
   HI.uri = URIForFile::canonicalize(*FilePath, TUPath);
 
-  // Compute the SymbolID and store it in the 'data' field.
-  // This allows typeHierarchy/resolve to be used to
-  // resolve children of items returned in a previous request
-  // for parents.
-  if (auto ID = getSymbolID(&ND))
-    HI.data = ID.str();
-
   return HI;
 }
 
 static llvm::Optional<TypeHierarchyItem>
 declToTypeHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) {
   auto Result = declToHierarchyItem<TypeHierarchyItem>(ND, TUPath);
-  if (Result)
+  if (Result) {
     Result->deprecated = ND.isDeprecated();
+    // Compute the SymbolID and store it in the 'data' field.
+    // This allows typeHierarchy/resolve to be used to
+    // resolve children of items returned in a previous request
+    // for parents.
+    Result->data.symbolID = getSymbolID(&ND);
+  }
   return Result;
 }
 
 static llvm::Optional<CallHierarchyItem>
 declToCallHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) {
   auto Result = declToHierarchyItem<CallHierarchyItem>(ND, TUPath);
-  if (Result && ND.isDeprecated())
+  if (!Result)
+    return Result;
+  if (ND.isDeprecated())
     Result->tags.push_back(SymbolTag::Deprecated);
+  if (auto ID = getSymbolID(&ND))
+    Result->data = ID.str();
   return Result;
 }
 
@@ -1637,10 +1643,6 @@
   // (https://github.com/clangd/clangd/issues/59).
   HI.range = HI.selectionRange;
   HI.uri = Loc->uri;
-  // Store the SymbolID in the 'data' field. The client will
-  // send this back in requests to resolve additional levels
-  // of the hierarchy.
-  HI.data = S.ID.str();
 
   return HI;
 }
@@ -1648,15 +1650,20 @@
 static llvm::Optional<TypeHierarchyItem>
 symbolToTypeHierarchyItem(const Symbol &S, PathRef TUPath) {
   auto Result = symbolToHierarchyItem<TypeHierarchyItem>(S, TUPath);
-  if (Result)
+  if (Result) {
     Result->deprecated = (S.Flags & Symbol::Deprecated);
+    Result->data.symbolID = S.ID;
+  }
   return Result;
 }
 
 static llvm::Optional<CallHierarchyItem>
 symbolToCallHierarchyItem(const Symbol &S, PathRef TUPath) {
   auto Result = symbolToHierarchyItem<CallHierarchyItem>(S, TUPath);
-  if (Result && (S.Flags & Symbol::Deprecated))
+  if (!Result)
+    return Result;
+  Result->data = S.ID.str();
+  if (S.Flags & Symbol::Deprecated)
     Result->tags.push_back(SymbolTag::Deprecated);
   return Result;
 }
@@ -1681,9 +1688,12 @@
 
 using RecursionProtectionSet = llvm::SmallSet<const CXXRecordDecl *, 4>;
 
+// Extracts parents from AST and populates the type hierarchy item.
 static void fillSuperTypes(const CXXRecordDecl &CXXRD, llvm::StringRef TUPath,
-                           std::vector<TypeHierarchyItem> &SuperTypes,
+                           TypeHierarchyItem &Item,
                            RecursionProtectionSet &RPSet) {
+  Item.parents.emplace();
+  Item.data.parents.emplace();
   // typeParents() will replace dependent template specializations
   // with their class template, so to avoid infinite recursion for
   // certain types of hierarchies, keep the templates encountered
@@ -1699,9 +1709,9 @@
   for (const CXXRecordDecl *ParentDecl : typeParents(&CXXRD)) {
     if (Optional<TypeHierarchyItem> ParentSym =
             declToTypeHierarchyItem(*ParentDecl, TUPath)) {
-      ParentSym->parents.emplace();
-      fillSuperTypes(*ParentDecl, TUPath, *ParentSym->parents, RPSet);
-      SuperTypes.emplace_back(std::move(*ParentSym));
+      fillSuperTypes(*ParentDecl, TUPath, *ParentSym, RPSet);
+      Item.data.parents->emplace_back(ParentSym->data);
+      Item.parents->emplace_back(std::move(*ParentSym));
     }
   }
 
@@ -1710,11 +1720,12 @@
   }
 }
 
-const CXXRecordDecl *findRecordTypeAt(ParsedAST &AST, Position Pos) {
-  auto RecordFromNode =
-      [&AST](const SelectionTree::Node *N) -> const CXXRecordDecl * {
+std::vector<const CXXRecordDecl *> findRecordTypeAt(ParsedAST &AST,
+                                                    Position Pos) {
+  auto RecordFromNode = [&AST](const SelectionTree::Node *N) {
+    std::vector<const CXXRecordDecl *> Records;
     if (!N)
-      return nullptr;
+      return Records;
 
     // Note: explicitReferenceTargets() will search for both template
     // instantiations and template patterns, and prefer the former if available
@@ -1722,30 +1733,32 @@
     // class template).
     auto Decls = explicitReferenceTargets(N->ASTNode, DeclRelation::Underlying,
                                           AST.getHeuristicResolver());
-    if (Decls.empty())
-      return nullptr;
-
-    const NamedDecl *D = Decls[0];
+    for (const NamedDecl *D : Decls) {
 
-    if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
-      // If this is a variable, use the type of the variable.
-      return VD->getType().getTypePtr()->getAsCXXRecordDecl();
-    }
+      if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
+        // If this is a variable, use the type of the variable.
+        Records.push_back(VD->getType().getTypePtr()->getAsCXXRecordDecl());
+        continue;
+      }
 
-    if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
-      // If this is a method, use the type of the class.
-      return Method->getParent();
-    }
+      if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
+        // If this is a method, use the type of the class.
+        Records.push_back(Method->getParent());
+        continue;
+      }
 
-    // We don't handle FieldDecl because it's not clear what behaviour
-    // the user would expect: the enclosing class type (as with a
-    // method), or the field's type (as with a variable).
+      // We don't handle FieldDecl because it's not clear what behaviour
+      // the user would expect: the enclosing class type (as with a
+      // method), or the field's type (as with a variable).
 
-    return dyn_cast<CXXRecordDecl>(D);
+      if (auto *RD = dyn_cast<CXXRecordDecl>(D))
+        Records.push_back(RD);
+    }
+    return Records;
   };
 
   const SourceManager &SM = AST.getSourceManager();
-  const CXXRecordDecl *Result = nullptr;
+  std::vector<const CXXRecordDecl *> Result;
   auto Offset = positionToOffset(SM.getBufferData(SM.getMainFileID()), Pos);
   if (!Offset) {
     llvm::consumeError(Offset.takeError());
@@ -1754,7 +1767,7 @@
   SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), *Offset,
                             *Offset, [&](SelectionTree ST) {
                               Result = RecordFromNode(ST.commonAncestor());
-                              return Result != nullptr;
+                              return !Result.empty();
                             });
   return Result;
 }
@@ -1998,53 +2011,79 @@
   return Result;
 }
 
-llvm::Optional<TypeHierarchyItem>
+std::vector<TypeHierarchyItem>
 getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels,
                  TypeHierarchyDirection Direction, const SymbolIndex *Index,
                  PathRef TUPath) {
-  const CXXRecordDecl *CXXRD = findRecordTypeAt(AST, Pos);
-  if (!CXXRD)
-    return llvm::None;
+  std::vector<TypeHierarchyItem> Results;
+  for (const auto *CXXRD : findRecordTypeAt(AST, Pos)) {
+
+    bool WantChildren = Direction == TypeHierarchyDirection::Children ||
+                        Direction == TypeHierarchyDirection::Both;
+
+    // If we're looking for children, we're doing the lookup in the index.
+    // The index does not store relationships between implicit
+    // specializations, so if we have one, use the template pattern instead.
+    // Note that this needs to be done before the declToTypeHierarchyItem(),
+    // otherwise the type hierarchy item would misleadingly contain the
+    // specialization parameters, while the children would involve classes
+    // that derive from other specializations of the template.
+    if (WantChildren) {
+      if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD))
+        CXXRD = CTSD->getTemplateInstantiationPattern();
+    }
 
-  bool WantParents = Direction == TypeHierarchyDirection::Parents ||
-                     Direction == TypeHierarchyDirection::Both;
-  bool WantChildren = Direction == TypeHierarchyDirection::Children ||
-                      Direction == TypeHierarchyDirection::Both;
-
-  // If we're looking for children, we're doing the lookup in the index.
-  // The index does not store relationships between implicit
-  // specializations, so if we have one, use the template pattern instead.
-  // Note that this needs to be done before the declToTypeHierarchyItem(),
-  // otherwise the type hierarchy item would misleadingly contain the
-  // specialization parameters, while the children would involve classes
-  // that derive from other specializations of the template.
-  if (WantChildren) {
-    if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD))
-      CXXRD = CTSD->getTemplateInstantiationPattern();
-  }
+    Optional<TypeHierarchyItem> Result =
+        declToTypeHierarchyItem(*CXXRD, AST.tuPath());
+    if (!Result)
+      continue;
 
-  Optional<TypeHierarchyItem> Result =
-      declToTypeHierarchyItem(*CXXRD, AST.tuPath());
-  if (!Result)
-    return Result;
+    RecursionProtectionSet RPSet;
+    fillSuperTypes(*CXXRD, AST.tuPath(), *Result, RPSet);
 
-  if (WantParents) {
-    Result->parents.emplace();
+    if (WantChildren && ResolveLevels > 0) {
+      Result->children.emplace();
 
-    RecursionProtectionSet RPSet;
-    fillSuperTypes(*CXXRD, AST.tuPath(), *Result->parents, RPSet);
+      if (Index) {
+        if (auto ID = getSymbolID(CXXRD))
+          fillSubTypes(ID, *Result->children, Index, ResolveLevels, TUPath);
+      }
+    }
+    Results.emplace_back(std::move(*Result));
   }
 
-  if (WantChildren && ResolveLevels > 0) {
-    Result->children.emplace();
+  return Results;
+}
 
-    if (Index) {
-      if (auto ID = getSymbolID(CXXRD))
-        fillSubTypes(ID, *Result->children, Index, ResolveLevels, TUPath);
-    }
+llvm::Optional<std::vector<TypeHierarchyItem>>
+superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index) {
+  std::vector<TypeHierarchyItem> Results;
+  if (!Item.data.parents)
+    return llvm::None;
+  if (Item.data.parents->empty())
+    return Results;
+  LookupRequest Req;
+  llvm::DenseMap<SymbolID, const TypeHierarchyItem::ResolveParams *> IDToData;
+  for (const auto &Parent : *Item.data.parents) {
+    Req.IDs.insert(Parent.symbolID);
+    IDToData[Parent.symbolID] = &Parent;
   }
+  Index->lookup(Req, [&Item, &Results, &IDToData](const Symbol &S) {
+    if (auto THI = symbolToTypeHierarchyItem(S, Item.uri.file())) {
+      THI->data = *IDToData.lookup(S.ID);
+      Results.emplace_back(std::move(*THI));
+    }
+  });
+  return Results;
+}
 
-  return Result;
+std::vector<TypeHierarchyItem> subTypes(const TypeHierarchyItem &Item,
+                                        const SymbolIndex *Index) {
+  std::vector<TypeHierarchyItem> Results;
+  fillSubTypes(Item.data.symbolID, Results, Index, 1, Item.uri.file());
+  for (auto &ChildSym : Results)
+    ChildSym.data.parents = {Item.data};
+  return Results;
 }
 
 void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels,
@@ -2052,18 +2091,13 @@
                           const SymbolIndex *Index) {
   // We only support typeHierarchy/resolve for children, because for parents
   // we ignore ResolveLevels and return all levels of parents eagerly.
-  if (Direction == TypeHierarchyDirection::Parents || ResolveLevels == 0)
+  if (!Index || Direction == TypeHierarchyDirection::Parents ||
+      ResolveLevels == 0)
     return;
 
   Item.children.emplace();
-
-  if (Index && Item.data) {
-    // We store the item's SymbolID in the 'data' field, and the client
-    // passes it back to us in typeHierarchy/resolve.
-    if (Expected<SymbolID> ID = SymbolID::fromStr(*Item.data)) {
-      fillSubTypes(*ID, *Item.children, Index, ResolveLevels, Item.uri.file());
-    }
-  }
+  fillSubTypes(Item.data.symbolID, *Item.children, Index, ResolveLevels,
+               Item.uri.file());
 }
 
 std::vector<CallHierarchyItem>
Index: clang-tools-extra/clangd/Protocol.h
===================================================================
--- clang-tools-extra/clangd/Protocol.h
+++ clang-tools-extra/clangd/Protocol.h
@@ -77,6 +77,9 @@
   }
 };
 
+bool fromJSON(const llvm::json::Value &, SymbolID &, llvm::json::Path);
+llvm::json::Value toJSON(const SymbolID &);
+
 // URI in "file" scheme for a file.
 struct URIForFile {
   URIForFile() = default;
@@ -1379,58 +1382,66 @@
 /// The type hierarchy params is an extension of the
 /// `TextDocumentPositionsParams` with optional properties which can be used to
 /// eagerly resolve the item when requesting from the server.
-struct TypeHierarchyParams : public TextDocumentPositionParams {
+struct TypeHierarchyPrepareParams : public TextDocumentPositionParams {
   /// The hierarchy levels to resolve. `0` indicates no level.
+  /// This is a clangd extension.
   int resolve = 0;
 
   /// The direction of the hierarchy levels to resolve.
+  /// This is a clangd extension.
   TypeHierarchyDirection direction = TypeHierarchyDirection::Parents;
 };
-bool fromJSON(const llvm::json::Value &, TypeHierarchyParams &,
+bool fromJSON(const llvm::json::Value &, TypeHierarchyPrepareParams &,
               llvm::json::Path);
 
 struct TypeHierarchyItem {
-  /// The human readable name of the hierarchy item.
+  /// The name of this item.
   std::string name;
 
-  /// Optional detail for the hierarchy item. It can be, for instance, the
-  /// signature of a function or method.
-  llvm::Optional<std::string> detail;
-
-  /// The kind of the hierarchy item. For instance, class or interface.
+  /// The kind of this item.
   SymbolKind kind;
 
-  /// `true` if the hierarchy item is deprecated. Otherwise, `false`.
-  bool deprecated = false;
+  /// More detail for this item, e.g. the signature of a function.
+  llvm::Optional<std::string> detail;
 
-  /// The URI of the text document where this type hierarchy item belongs to.
+  /// The resource identifier of this item.
   URIForFile uri;
 
-  /// The range enclosing this type hierarchy item not including
-  /// leading/trailing whitespace but everything else like comments. This
-  /// information is typically used to determine if the client's cursor is
-  /// inside the type hierarch item to reveal in the symbol in the UI.
+  /// The range enclosing this symbol not including leading/trailing whitespace
+  /// but everything else, e.g. comments and code.
   Range range;
 
-  /// The range that should be selected and revealed when this type hierarchy
-  /// item is being picked, e.g. the name of a function. Must be contained by
-  /// the `range`.
+  /// The range that should be selected and revealed when this symbol is being
+  /// picked, e.g. the name of a function. Must be contained by the `range`.
   Range selectionRange;
 
-  /// If this type hierarchy item is resolved, it contains the direct parents.
-  /// Could be empty if the item does not have direct parents. If not defined,
-  /// the parents have not been resolved yet.
+  /// Used to resolve a client provided item back.
+  struct ResolveParams {
+    SymbolID symbolID;
+    /// None means parents aren't resolved and empty is no parents.
+    llvm::Optional<std::vector<ResolveParams>> parents;
+  };
+  /// A data entry field that is preserved between a type hierarchy prepare and
+  /// supertypes or subtypes requests. It could also be used to identify the
+  /// type hierarchy in the server, helping improve the performance on resolving
+  /// supertypes and subtypes.
+  ResolveParams data;
+
+  /// `true` if the hierarchy item is deprecated. Otherwise, `false`.
+  /// This is a clangd exntesion.
+  bool deprecated = false;
+
+  /// This is a clangd exntesion.
   llvm::Optional<std::vector<TypeHierarchyItem>> parents;
 
   /// If this type hierarchy item is resolved, it contains the direct children
   /// of the current item. Could be empty if the item does not have any
   /// descendants. If not defined, the children have not been resolved.
+  /// This is a clangd exntesion.
   llvm::Optional<std::vector<TypeHierarchyItem>> children;
-
-  /// An optional 'data' field, which can be used to identify a type hierarchy
-  /// item in a resolve request.
-  llvm::Optional<std::string> data;
 };
+llvm::json::Value toJSON(const TypeHierarchyItem::ResolveParams &);
+bool fromJSON(const TypeHierarchyItem::ResolveParams &);
 llvm::json::Value toJSON(const TypeHierarchyItem &);
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TypeHierarchyItem &);
 bool fromJSON(const llvm::json::Value &, TypeHierarchyItem &, llvm::json::Path);
Index: clang-tools-extra/clangd/Protocol.cpp
===================================================================
--- clang-tools-extra/clangd/Protocol.cpp
+++ clang-tools-extra/clangd/Protocol.cpp
@@ -1207,12 +1207,13 @@
   return true;
 }
 
-bool fromJSON(const llvm::json::Value &Params, TypeHierarchyParams &R,
+bool fromJSON(const llvm::json::Value &Params, TypeHierarchyPrepareParams &R,
               llvm::json::Path P) {
   llvm::json::ObjectMapper O(Params, P);
   return O && O.map("textDocument", R.textDocument) &&
-         O.map("position", R.position) && O.map("resolve", R.resolve) &&
-         O.map("direction", R.direction);
+         O.map("position", R.position) &&
+         mapOptOrNull(Params, "resolve", R.resolve, P) &&
+         mapOptOrNull(Params, "direction", R.direction, P);
 }
 
 llvm::raw_ostream &operator<<(llvm::raw_ostream &O,
@@ -1220,23 +1221,28 @@
   return O << I.name << " - " << toJSON(I);
 }
 
+llvm::json::Value toJSON(const TypeHierarchyItem::ResolveParams &RP) {
+  llvm::json::Object Result{{"symbolID", RP.symbolID}};
+  if (RP.parents)
+    Result["parents"] = RP.parents;
+  return std::move(Result);
+}
+bool fromJSON(const llvm::json::Value &Params,
+              TypeHierarchyItem::ResolveParams &RP, llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return O && O.map("symbolID", RP.symbolID) &&
+         mapOptOrNull(Params, "parents", RP.parents, P);
+}
+
 llvm::json::Value toJSON(const TypeHierarchyItem &I) {
-  llvm::json::Object Result{{"name", I.name},
-                            {"kind", static_cast<int>(I.kind)},
-                            {"range", I.range},
-                            {"selectionRange", I.selectionRange},
-                            {"uri", I.uri}};
+  llvm::json::Object Result{
+      {"name", I.name},   {"kind", static_cast<int>(I.kind)},
+      {"range", I.range}, {"selectionRange", I.selectionRange},
+      {"uri", I.uri},     {"data", I.data},
+  };
 
   if (I.detail)
     Result["detail"] = I.detail;
-  if (I.deprecated)
-    Result["deprecated"] = I.deprecated;
-  if (I.parents)
-    Result["parents"] = I.parents;
-  if (I.children)
-    Result["children"] = I.children;
-  if (I.data)
-    Result["data"] = I.data;
   return std::move(Result);
 }
 
@@ -1258,8 +1264,9 @@
 bool fromJSON(const llvm::json::Value &Params,
               ResolveTypeHierarchyItemParams &R, llvm::json::Path P) {
   llvm::json::ObjectMapper O(Params, P);
-  return O && O.map("item", R.item) && O.map("resolve", R.resolve) &&
-         O.map("direction", R.direction);
+  return O && O.map("item", R.item) &&
+         mapOptOrNull(Params, "resolve", R.resolve, P) &&
+         mapOptOrNull(Params, "direction", R.direction, P);
 }
 
 bool fromJSON(const llvm::json::Value &Params, ReferenceContext &R,
@@ -1510,5 +1517,22 @@
   return OS;
 }
 
+bool fromJSON(const llvm::json::Value &E, SymbolID &S, llvm::json::Path P) {
+  auto Str = E.getAsString();
+  if (!Str) {
+    P.report("expected a string");
+    return false;
+  }
+  auto ID = SymbolID::fromStr(*Str);
+  if (!ID) {
+    elog("Malformed symbolid: {0}", ID.takeError());
+    P.report("malformed symbolid");
+    return false;
+  }
+  S = *ID;
+  return true;
+}
+llvm::json::Value toJSON(const SymbolID &S) { return S.str(); }
+
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/ClangdServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdServer.h
+++ clang-tools-extra/clangd/ClangdServer.h
@@ -253,7 +253,13 @@
   /// Get information about type hierarchy for a given position.
   void typeHierarchy(PathRef File, Position Pos, int Resolve,
                      TypeHierarchyDirection Direction,
-                     Callback<llvm::Optional<TypeHierarchyItem>> CB);
+                     Callback<std::vector<TypeHierarchyItem>> CB);
+  /// Get direct parents of a type hierarchy item.
+  void superTypes(const TypeHierarchyItem &Item,
+                  Callback<llvm::Optional<std::vector<TypeHierarchyItem>>> CB);
+  /// Get direct children of a type hierarchy item.
+  void subTypes(const TypeHierarchyItem &Item,
+                Callback<std::vector<TypeHierarchyItem>> CB);
 
   /// Resolve type hierarchy item in the given direction.
   void resolveTypeHierarchy(TypeHierarchyItem Item, int Resolve,
Index: clang-tools-extra/clangd/ClangdServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdServer.cpp
+++ clang-tools-extra/clangd/ClangdServer.cpp
@@ -751,7 +751,7 @@
 
 void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve,
                                  TypeHierarchyDirection Direction,
-                                 Callback<Optional<TypeHierarchyItem>> CB) {
+                                 Callback<std::vector<TypeHierarchyItem>> CB) {
   auto Action = [File = File.str(), Pos, Resolve, Direction, CB = std::move(CB),
                  this](Expected<InputsAndAST> InpAST) mutable {
     if (!InpAST)
@@ -763,6 +763,22 @@
   WorkScheduler->runWithAST("TypeHierarchy", File, std::move(Action));
 }
 
+void ClangdServer::superTypes(
+    const TypeHierarchyItem &Item,
+    Callback<llvm::Optional<std::vector<TypeHierarchyItem>>> CB) {
+  WorkScheduler->run("typeHierarchy/superTypes", /*Path=*/"",
+                     [=, CB = std::move(CB)]() mutable {
+                       CB(clangd::superTypes(Item, Index));
+                     });
+}
+
+void ClangdServer::subTypes(const TypeHierarchyItem &Item,
+                            Callback<std::vector<TypeHierarchyItem>> CB) {
+  WorkScheduler->run(
+      "typeHierarchy/subTypes", /*Path=*/"",
+      [=, CB = std::move(CB)]() mutable { CB(clangd::subTypes(Item, Index)); });
+}
+
 void ClangdServer::resolveTypeHierarchy(
     TypeHierarchyItem Item, int Resolve, TypeHierarchyDirection Direction,
     Callback<llvm::Optional<TypeHierarchyItem>> CB) {
Index: clang-tools-extra/clangd/ClangdLSPServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.h
+++ clang-tools-extra/clangd/ClangdLSPServer.h
@@ -23,6 +23,7 @@
 #include <chrono>
 #include <cstddef>
 #include <memory>
+#include <vector>
 
 namespace clang {
 namespace clangd {
@@ -132,10 +133,16 @@
   void onRename(const RenameParams &, Callback<WorkspaceEdit>);
   void onHover(const TextDocumentPositionParams &,
                Callback<llvm::Optional<Hover>>);
-  void onTypeHierarchy(const TypeHierarchyParams &,
-                       Callback<llvm::Optional<TypeHierarchyItem>>);
+  void onPrepareTypeHierarchy(const TypeHierarchyPrepareParams &,
+                              Callback<std::vector<TypeHierarchyItem>>);
+  void onSuperTypes(const ResolveTypeHierarchyItemParams &,
+                    Callback<llvm::Optional<std::vector<TypeHierarchyItem>>>);
+  void onSubTypes(const ResolveTypeHierarchyItemParams &,
+                  Callback<std::vector<TypeHierarchyItem>>);
+  void onTypeHierarchy(const TypeHierarchyPrepareParams &,
+                       Callback<llvm::json::Value>);
   void onResolveTypeHierarchy(const ResolveTypeHierarchyItemParams &,
-                              Callback<llvm::Optional<TypeHierarchyItem>>);
+                              Callback<llvm::json::Value>);
   void onPrepareCallHierarchy(const CallHierarchyPrepareParams &,
                               Callback<std::vector<CallHierarchyItem>>);
   void onCallHierarchyIncomingCalls(
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -26,6 +26,8 @@
 #include "support/Trace.h"
 #include "clang/Tooling/Core/Replacement.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/FunctionExtras.h"
+#include "llvm/ADT/None.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/StringRef.h"
@@ -571,8 +573,12 @@
       {"referencesProvider", true},
       {"astProvider", true}, // clangd extension
       {"typeHierarchyProvider", true},
-      {"memoryUsageProvider", true}, // clangd extension
-      {"compilationDatabase",        // clangd extension
+      // Unfortunately our extension made use of the same capability name as the
+      // standard. Advertise this capability to tell clients that implement our
+      // extension we really have support for the standardized one as well.
+      {"standardTypeHierarchyProvider", true}, // clangd extension
+      {"memoryUsageProvider", true},           // clangd extension
+      {"compilationDatabase",                  // clangd extension
        llvm::json::Object{{"automaticReload", true}}},
       {"callHierarchyProvider", true},
       {"clangdInlayHintsProvider", true},
@@ -1183,18 +1189,94 @@
                     });
 }
 
-void ClangdLSPServer::onTypeHierarchy(
-    const TypeHierarchyParams &Params,
-    Callback<Optional<TypeHierarchyItem>> Reply) {
+// Our extension has a different representation on the wire than the standard.
+// https://clangd.llvm.org/extensions#type-hierarchy
+llvm::json::Value serializeTHIForExtension(TypeHierarchyItem THI) {
+  llvm::json::Object Result{{
+      {"name", std::move(THI.name)},
+      {"kind", static_cast<int>(THI.kind)},
+      {"uri", std::move(THI.uri)},
+      {"range", THI.range},
+      {"selectionRange", THI.selectionRange},
+      {"data", std::move(THI.data)},
+  }};
+  if (THI.deprecated)
+    Result["deprecated"] = THI.deprecated;
+  if (THI.detail)
+    Result["detail"] = std::move(*THI.detail);
+
+  if (THI.parents) {
+    llvm::json::Array Parents;
+    for (auto &Parent : *THI.parents)
+      Parents.emplace_back(serializeTHIForExtension(std::move(Parent)));
+    Result["parents"] = std::move(Parents);
+  }
+
+  if (THI.children) {
+    llvm::json::Array Children;
+    for (auto &child : *THI.children)
+      Children.emplace_back(serializeTHIForExtension(std::move(child)));
+    Result["children"] = std::move(Children);
+  }
+  return Result;
+}
+
+void ClangdLSPServer::onTypeHierarchy(const TypeHierarchyPrepareParams &Params,
+                                      Callback<llvm::json::Value> Reply) {
+  auto Serialize =
+      [Reply = std::move(Reply)](
+          llvm::Expected<std::vector<TypeHierarchyItem>> Resp) mutable {
+        if (!Resp) {
+          Reply(Resp.takeError());
+          return;
+        }
+        if (Resp->empty()) {
+          Reply(nullptr);
+          return;
+        }
+        Reply(serializeTHIForExtension(std::move(Resp->front())));
+      };
   Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
-                        Params.resolve, Params.direction, std::move(Reply));
+                        Params.resolve, Params.direction, std::move(Serialize));
 }
 
 void ClangdLSPServer::onResolveTypeHierarchy(
     const ResolveTypeHierarchyItemParams &Params,
-    Callback<Optional<TypeHierarchyItem>> Reply) {
+    Callback<llvm::json::Value> Reply) {
+  auto Serialize =
+      [Reply = std::move(Reply)](
+          llvm::Expected<llvm::Optional<TypeHierarchyItem>> Resp) mutable {
+        if (!Resp) {
+          Reply(Resp.takeError());
+          return;
+        }
+        if (!*Resp) {
+          Reply(std::move(*Resp));
+          return;
+        }
+        Reply(serializeTHIForExtension(std::move(**Resp)));
+      };
   Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction,
-                               std::move(Reply));
+                               std::move(Serialize));
+}
+
+void ClangdLSPServer::onPrepareTypeHierarchy(
+    const TypeHierarchyPrepareParams &Params,
+    Callback<std::vector<TypeHierarchyItem>> Reply) {
+  Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
+                        Params.resolve, Params.direction, std::move(Reply));
+}
+
+void ClangdLSPServer::onSuperTypes(
+    const ResolveTypeHierarchyItemParams &Params,
+    Callback<llvm::Optional<std::vector<TypeHierarchyItem>>> Reply) {
+  Server->superTypes(Params.item, std::move(Reply));
+}
+
+void ClangdLSPServer::onSubTypes(
+    const ResolveTypeHierarchyItemParams &Params,
+    Callback<std::vector<TypeHierarchyItem>> Reply) {
+  Server->subTypes(Params.item, std::move(Reply));
 }
 
 void ClangdLSPServer::onPrepareCallHierarchy(
@@ -1523,6 +1605,9 @@
   Bind.method("textDocument/symbolInfo", this, &ClangdLSPServer::onSymbolInfo);
   Bind.method("textDocument/typeHierarchy", this, &ClangdLSPServer::onTypeHierarchy);
   Bind.method("typeHierarchy/resolve", this, &ClangdLSPServer::onResolveTypeHierarchy);
+  Bind.method("textDocument/prepareTypeHierarchy", this, &ClangdLSPServer::onPrepareTypeHierarchy);
+  Bind.method("typeHierarchy/supertypes", this, &ClangdLSPServer::onSuperTypes);
+  Bind.method("typeHierarchy/subtypes", this, &ClangdLSPServer::onSubTypes);
   Bind.method("textDocument/prepareCallHierarchy", this, &ClangdLSPServer::onPrepareCallHierarchy);
   Bind.method("callHierarchy/incomingCalls", this, &ClangdLSPServer::onCallHierarchyIncomingCalls);
   Bind.method("textDocument/selectionRange", this, &ClangdLSPServer::onSelectionRange);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to