nridge updated this revision to Diff 184985.
nridge added a comment.

remove unrelated file


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D56370

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/FindSymbols.cpp
  clang-tools-extra/clangd/FindSymbols.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/index/SymbolCollector.cpp
  clang-tools-extra/clangd/index/SymbolCollector.h
  clang-tools-extra/unittests/clangd/Matchers.h
  clang-tools-extra/unittests/clangd/XRefsTests.cpp

Index: clang-tools-extra/unittests/clangd/XRefsTests.cpp
===================================================================
--- clang-tools-extra/unittests/clangd/XRefsTests.cpp
+++ clang-tools-extra/unittests/clangd/XRefsTests.cpp
@@ -25,9 +25,11 @@
 namespace {
 
 using testing::ElementsAre;
+using testing::Eq;
 using testing::Field;
 using testing::IsEmpty;
 using testing::Matcher;
+using testing::Pointee;
 using testing::UnorderedElementsAreArray;
 
 class IgnoreDiagnostics : public DiagnosticsConsumer {
@@ -39,6 +41,15 @@
   return Location{URIForFile::canonicalize(File, testRoot()), Range} == arg;
 }
 
+// GMock helpers for matching TypeHierarchyItem.
+MATCHER_P(WithName, N, "") { return arg.name == N; }
+MATCHER_P(WithKind, Kind, "") { return arg.kind == Kind; }
+MATCHER_P(SelectionRangeIs, R, "") { return arg.selectionRange == R; }
+template <class... ParentMatchers>
+testing::Matcher<TypeHierarchyItem> Parents(ParentMatchers... ParentsM) {
+  return Field(&TypeHierarchyItem::parents, HasValue(ElementsAre(ParentsM...)));
+}
+
 // Extracts ranges from an annotated example, and constructs a matcher for a
 // highlight set. Ranges should be named $read/$write as appropriate.
 Matcher<const std::vector<DocumentHighlight> &>
@@ -1339,6 +1350,160 @@
   }
 }
 
+TEST(SuperTypes, SimpleInheritanceOnTypeOrVariable) {
+  Annotations Source(R"cpp(
+struct $ParentDef[[Parent]]
+{
+  int a;
+};
+
+struct $Child1Def[[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();
+
+  for (auto Pt : {"p1", "p2", "p3", "p4"}) {
+    llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
+        AST, Source.point(Pt), 10, TypeHierarchyDirection::Parents);
+    ASSERT_TRUE(bool(Result));
+    EXPECT_THAT(
+        *Result,
+        AllOf(
+            WithName("Child2"), WithKind(SymbolKind::Struct),
+            Parents(AllOf(
+                WithName("Child1"), WithKind(SymbolKind::Struct),
+                SelectionRangeIs(Source.range("Child1Def")),
+                Parents(AllOf(WithName("Parent"), WithKind(SymbolKind::Struct),
+                              SelectionRangeIs(Source.range("ParentDef")),
+                              Parents()))))));
+  }
+}
+
+TEST(SuperTypes, MultipleInheritanceOnTypeOrVariable) {
+  Annotations Source(R"cpp(
+struct $Parent1Def[[Parent1]]
+{
+  int a;
+};
+
+struct $Parent2Def[[Parent2]]
+{
+  int b;
+};
+
+struct $Parent3Def[[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();
+
+  for (auto Pt : {"c1", "c2", "c3", "c4"}) {
+    llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
+        AST, Source.point(Pt), 10, TypeHierarchyDirection::Parents);
+    ASSERT_TRUE(bool(Result));
+    EXPECT_THAT(
+        *Result,
+        AllOf(
+            WithName("Child"), WithKind(SymbolKind::Struct),
+            Parents(AllOf(WithName("Parent1"), WithKind(SymbolKind::Struct),
+                          SelectionRangeIs(Source.range("Parent1Def")),
+                          Parents()),
+                    AllOf(WithName("Parent3"), WithKind(SymbolKind::Struct),
+                          SelectionRangeIs(Source.range("Parent3Def")),
+                          Parents(AllOf(
+                              WithName("Parent2"), WithKind(SymbolKind::Struct),
+                              SelectionRangeIs(Source.range("Parent2Def")),
+                              Parents()))))));
+  }
+}
+
+TEST(SuperTypes, OnMethod) {
+  Annotations Source(R"cpp(
+struct $ParentDef[[Parent]]
+{
+  void method ();
+  void method () const;
+  void method (int x);
+  void method (char x);
+};
+
+struct $Child1Def[[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());
+
+  for (auto Pt : {"p1", "p2"}) {
+    llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
+        AST, Source.point(Pt), 10, TypeHierarchyDirection::Parents);
+    ASSERT_TRUE(bool(Result));
+    EXPECT_THAT(
+        *Result,
+        AllOf(
+            WithName("Child2"), WithKind(SymbolKind::Struct),
+            Parents(AllOf(
+                WithName("Child1"), WithKind(SymbolKind::Struct),
+                SelectionRangeIs(Source.range("Child1Def")),
+                Parents(AllOf(WithName("Parent"), WithKind(SymbolKind::Struct),
+                              SelectionRangeIs(Source.range("ParentDef")),
+                              Parents()))))));
+  }
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/unittests/clangd/Matchers.h
===================================================================
--- clang-tools-extra/unittests/clangd/Matchers.h
+++ clang-tools-extra/unittests/clangd/Matchers.h
@@ -127,6 +127,73 @@
     llvm::consumeError(ComputedValue.takeError());                             \
   } while (false)
 
+// Implements the HasValue(m) matcher for matching an Optional whose
+// value matches matcher m.
+template <typename InnerMatcher> class OptionalMatcher {
+public:
+  explicit OptionalMatcher(const InnerMatcher &matcher) : matcher_(matcher) {}
+
+  // This type conversion operator template allows Optional(m) to be
+  // used as a matcher for any Optional type whose value type is
+  // compatible with the inner matcher.
+  //
+  // The reason we do this instead of relying on
+  // MakePolymorphicMatcher() is that the latter is not flexible
+  // enough for implementing the DescribeTo() method of Optional().
+  template <typename Optional> operator Matcher<Optional>() const {
+    return MakeMatcher(new Impl<Optional>(matcher_));
+  }
+
+private:
+  // The monomorphic implementation that works for a particular optional type.
+  template <typename Optional>
+  class Impl : public ::testing::MatcherInterface<Optional> {
+  public:
+    using Value = typename std::remove_const<
+        typename std::remove_reference<Optional>::type>::type::value_type;
+
+    explicit Impl(const InnerMatcher &matcher)
+        : matcher_(::testing::MatcherCast<const Value &>(matcher)) {}
+
+    virtual void DescribeTo(::std::ostream *os) const {
+      *os << "has a value that ";
+      matcher_.DescribeTo(os);
+    }
+
+    virtual void DescribeNegationTo(::std::ostream *os) const {
+      *os << "does not have a value that ";
+      matcher_.DescribeTo(os);
+    }
+
+    virtual bool
+    MatchAndExplain(Optional optional,
+                    ::testing::MatchResultListener *listener) const {
+      if (!optional.hasValue())
+        return false;
+
+      *listener << "which has a value ";
+      return MatchPrintAndExplain(*optional, matcher_, listener);
+    }
+
+  private:
+    const Matcher<const Value &> matcher_;
+
+    GTEST_DISALLOW_ASSIGN_(Impl);
+  };
+
+  const InnerMatcher matcher_;
+
+  GTEST_DISALLOW_ASSIGN_(OptionalMatcher);
+};
+
+// Creates a matcher that matches an Optional that has a value
+// that matches inner_matcher.
+template <typename InnerMatcher>
+inline OptionalMatcher<InnerMatcher>
+HasValue(const InnerMatcher &inner_matcher) {
+  return OptionalMatcher<InnerMatcher>(inner_matcher);
+}
+
 } // namespace clangd
 } // namespace clang
 #endif
Index: clang-tools-extra/clangd/index/SymbolCollector.h
===================================================================
--- clang-tools-extra/clangd/index/SymbolCollector.h
+++ clang-tools-extra/clangd/index/SymbolCollector.h
@@ -137,6 +137,18 @@
   llvm::DenseMap<FileID, bool> FilesToIndexCache;
 };
 
+// Returns a URI of \p Path. Firstly, this makes the \p Path absolute using the
+// current working directory of the given SourceManager if the Path is not an
+// absolute path. If failed, this resolves relative paths against \p FallbackDir
+// to get an absolute path. Then, this tries creating an URI for the absolute
+// path with schemes specified in \p Opts. This returns an URI with the first
+// working scheme, if there is any; otherwise, this returns None.
+//
+// The Path can be a path relative to the build directory, or retrieved from
+// the SourceManager.
+std::string toURI(const SourceManager &SM, llvm::StringRef Path,
+                  llvm::StringRef FallbackDir);
+
 } // namespace clangd
 } // namespace clang
 #endif
Index: clang-tools-extra/clangd/index/SymbolCollector.cpp
===================================================================
--- clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -30,27 +30,9 @@
 
 namespace clang {
 namespace clangd {
-namespace {
-
-/// If \p ND is a template specialization, returns the described template.
-/// Otherwise, returns \p ND.
-const NamedDecl &getTemplateOrThis(const NamedDecl &ND) {
-  if (auto T = ND.getDescribedTemplate())
-    return *T;
-  return ND;
-}
 
-// Returns a URI of \p Path. Firstly, this makes the \p Path absolute using the
-// current working directory of the given SourceManager if the Path is not an
-// absolute path. If failed, this resolves relative paths against \p FallbackDir
-// to get an absolute path. Then, this tries creating an URI for the absolute
-// path with schemes specified in \p Opts. This returns an URI with the first
-// working scheme, if there is any; otherwise, this returns None.
-//
-// The Path can be a path relative to the build directory, or retrieved from
-// the SourceManager.
 std::string toURI(const SourceManager &SM, llvm::StringRef Path,
-                  const SymbolCollector::Options &Opts) {
+                  llvm::StringRef FallbackDir) {
   llvm::SmallString<128> AbsolutePath(Path);
   if (auto CanonPath =
           getCanonicalPath(SM.getFileManager().getFile(Path), SM)) {
@@ -58,12 +40,22 @@
   }
   // We don't perform is_absolute check in an else branch because makeAbsolute
   // might return a relative path on some InMemoryFileSystems.
-  if (!llvm::sys::path::is_absolute(AbsolutePath) && !Opts.FallbackDir.empty())
-    llvm::sys::fs::make_absolute(Opts.FallbackDir, AbsolutePath);
+  if (!llvm::sys::path::is_absolute(AbsolutePath) && !FallbackDir.empty())
+    llvm::sys::fs::make_absolute(FallbackDir, AbsolutePath);
   llvm::sys::path::remove_dots(AbsolutePath, /*remove_dot_dot=*/true);
   return URI::create(AbsolutePath).toString();
 }
 
+namespace {
+
+/// If \p ND is a template specialization, returns the described template.
+/// Otherwise, returns \p ND.
+const NamedDecl &getTemplateOrThis(const NamedDecl &ND) {
+  if (auto T = ND.getDescribedTemplate())
+    return *T;
+  return ND;
+}
+
 // All proto generated headers should start with this line.
 static const char *PROTO_HEADER_COMMENT =
     "// Generated by the protocol buffer compiler.  DO NOT EDIT!";
@@ -152,7 +144,7 @@
     if (Header.startswith("<") || Header.startswith("\""))
       return Header.str();
   }
-  return toURI(SM, Header, Opts);
+  return toURI(SM, Header, Opts.FallbackDir);
 }
 
 // Return the symbol range of the token at \p TokLoc.
@@ -192,7 +184,7 @@
   auto Path = SM.getFilename(TokLoc);
   if (Path.empty())
     return None;
-  FileURIStorage = toURI(SM, Path, Opts);
+  FileURIStorage = toURI(SM, Path, Opts.FallbackDir);
   SymbolLocation Result;
   Result.FileURI = FileURIStorage.c_str();
   auto Range = getTokenRange(TokLoc, SM, LangOpts);
@@ -481,7 +473,7 @@
     auto Found = URICache.find(FID);
     if (Found == URICache.end()) {
       if (auto *FileEntry = SM.getFileEntryForID(FID)) {
-        auto FileURI = toURI(SM, FileEntry->getName(), Opts);
+        auto FileURI = toURI(SM, FileEntry->getName(), Opts.FallbackDir);
         Found = URICache.insert({FID, FileURI}).first;
       } else {
         // Ignore cases where we can not find a corresponding file entry
Index: clang-tools-extra/clangd/XRefs.h
===================================================================
--- clang-tools-extra/clangd/XRefs.h
+++ clang-tools-extra/clangd/XRefs.h
@@ -42,6 +42,16 @@
 /// Get info about symbols at \p Pos.
 std::vector<SymbolDetails> getSymbolInfo(ParsedAST &AST, Position Pos);
 
+/// Get type hierarchy information at \p Pos.
+llvm::Optional<TypeHierarchyItem>
+getTypeHierarchy(ParsedAST &AST, Position Pos, llvm::Optional<int> Resolve,
+                 llvm::Optional<TypeHierarchyDirection> Direction);
+
+/// Convert a a URI (such as that returned by toURI()) into a form suitable
+/// for use in protocol replies (e.g. Location.uri, DocumentSymbol.uri).
+/// TUPath is used to resolve the path of URI.
+Optional<URIForFile> getURIForFile(StringRef FileURI, StringRef TUPath);
+
 } // namespace clangd
 } // namespace clang
 
Index: clang-tools-extra/clangd/XRefs.cpp
===================================================================
--- clang-tools-extra/clangd/XRefs.cpp
+++ clang-tools-extra/clangd/XRefs.cpp
@@ -7,9 +7,11 @@
 //===----------------------------------------------------------------------===//
 #include "XRefs.h"
 #include "AST.h"
+#include "FindSymbols.h"
 #include "Logger.h"
 #include "SourceCode.h"
 #include "URI.h"
+#include "index/SymbolCollector.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/Index/IndexDataConsumer.h"
@@ -19,6 +21,22 @@
 
 namespace clang {
 namespace clangd {
+
+Optional<URIForFile> getURIForFile(StringRef FileURI, StringRef TUPath) {
+  auto Uri = URI::parse(FileURI);
+  if (!Uri) {
+    elog("Could not parse URI {0}: {1}", FileURI, Uri.takeError());
+    return None;
+  }
+  auto U = URIForFile::fromURI(*Uri, TUPath);
+  if (!U) {
+    elog("Could not resolve URI {0}: {1}", FileURI, U.takeError());
+    return None;
+  }
+
+  return *U;
+}
+
 namespace {
 
 // Get the definition from a given declaration `D`.
@@ -48,16 +66,10 @@
                                        llvm::StringRef TUPath) {
   if (!Loc)
     return None;
-  auto Uri = URI::parse(Loc.FileURI);
-  if (!Uri) {
-    elog("Could not parse URI {0}: {1}", Loc.FileURI, Uri.takeError());
-    return None;
-  }
-  auto U = URIForFile::fromURI(*Uri, TUPath);
-  if (!U) {
-    elog("Could not resolve URI {0}: {1}", Loc.FileURI, U.takeError());
+
+  auto U = getURIForFile(Loc.FileURI, TUPath);
+  if (!U)
     return None;
-  }
 
   Location LSPLoc;
   LSPLoc.uri = std::move(*U);
@@ -805,5 +817,127 @@
   return Results;
 }
 
+// TODO: Reduce duplication between this function and declToSym().
+static llvm::Optional<TypeHierarchyItem>
+declToTypeHierarchyItem(ASTContext &Ctx, const NamedDecl &ND) {
+  auto &SM = Ctx.getSourceManager();
+
+  SourceLocation NameLoc = findNameLoc(&ND);
+  // getFileLoc is a good choice for us, but we also need to make sure
+  // sourceLocToPosition won't switch files, so we call getSpellingLoc on top of
+  // that to make sure it does not switch files.
+  // FIXME: sourceLocToPosition should not switch files!
+  SourceLocation BeginLoc = SM.getSpellingLoc(SM.getFileLoc(ND.getBeginLoc()));
+  SourceLocation EndLoc = SM.getSpellingLoc(SM.getFileLoc(ND.getEndLoc()));
+  if (NameLoc.isInvalid() || BeginLoc.isInvalid() || EndLoc.isInvalid())
+    return llvm::None;
+
+  Position NameBegin = sourceLocToPosition(SM, NameLoc);
+  Position NameEnd = sourceLocToPosition(
+      SM, Lexer::getLocForEndOfToken(NameLoc, 0, SM, Ctx.getLangOpts()));
+
+  index::SymbolInfo SymInfo = index::getSymbolInfo(&ND);
+  // FIXME: this is not classifying constructors, destructors and operators
+  //        correctly (they're all "methods").
+  SymbolKind SK = indexSymbolKindToSymbolKind(SymInfo.Kind);
+
+  TypeHierarchyItem THI;
+  THI.name = printName(Ctx, ND);
+  THI.kind = SK;
+  THI.deprecated = ND.isDeprecated();
+  THI.range =
+      Range{sourceLocToPosition(SM, BeginLoc), sourceLocToPosition(SM, EndLoc)};
+  THI.selectionRange = Range{NameBegin, NameEnd};
+  if (!THI.range.contains(THI.selectionRange)) {
+    // 'selectionRange' must be contained in 'range', so in cases where clang
+    // reports unrelated ranges we need to reconcile somehow.
+    THI.range = THI.selectionRange;
+  }
+
+  StringRef Filename = SM.getFilename(BeginLoc);
+  std::string FileURI = toURI(SM, Filename, {});
+  std::string TUPath;
+  const FileEntry *FE = SM.getFileEntryForID(SM.getMainFileID());
+  if (auto Path = getCanonicalPath(FE, SM))
+    TUPath = *Path;
+  if (auto U = getURIForFile(FileURI, TUPath)) {
+    THI.uri = std::move(*U);
+  } else {
+    return llvm::None; // Not useful without a uri.
+  }
+
+  return THI;
+}
+
+static Optional<TypeHierarchyItem>
+getTypeHierarchy(const CXXRecordDecl &CXXRD, ASTContext &ASTCtx, int Levels,
+                 TypeHierarchyDirection Direction) {
+  Optional<TypeHierarchyItem> Result = declToTypeHierarchyItem(ASTCtx, CXXRD);
+  if (Result && Levels > 0) {
+    if (Direction == TypeHierarchyDirection::Parents ||
+        Direction == TypeHierarchyDirection::Both) {
+      Result->parents.emplace();
+      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;
+
+        if (Optional<TypeHierarchyItem> ParentSym =
+                getTypeHierarchy(*ParentDecl, ASTCtx, Levels - 1, Direction)) {
+          Result->parents->emplace_back(std::move(*ParentSym));
+        }
+      }
+    }
+
+    if (Direction == TypeHierarchyDirection::Children ||
+        Direction == TypeHierarchyDirection::Both) {
+      Result->children.emplace();
+
+      // TODO: Populate subtypes.
+    }
+  }
+  return Result;
+}
+
+llvm::Optional<TypeHierarchyItem>
+getTypeHierarchy(ParsedAST &AST, Position Pos, llvm::Optional<int> Resolve,
+                 llvm::Optional<TypeHierarchyDirection> Direction) {
+  ASTContext &ASTCtx = AST.getASTContext();
+  const SourceManager &SourceMgr = ASTCtx.getSourceManager();
+  SourceLocation SourceLocationBeg =
+      getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID());
+  IdentifiedSymbol Symbols = getSymbolAtPosition(AST, SourceLocationBeg);
+
+  if (Symbols.Decls.empty())
+    return {};
+
+  const Decl *D = Symbols.Decls[0].D;
+  const CXXRecordDecl *CXXRD;
+
+  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 (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
+    // If this is a method, use the type of the class.
+    CXXRD = Method->getParent();
+  } else {
+    CXXRD = dyn_cast<CXXRecordDecl>(D);
+  }
+
+  if (CXXRD) {
+    int ResolveLevels = Resolve.getValueOr(0);
+    TypeHierarchyDirection ResolveDirection =
+        Direction.getValueOr(TypeHierarchyDirection::Parents);
+    return getTypeHierarchy(*CXXRD, ASTCtx, ResolveLevels, ResolveDirection);
+  }
+
+  return {};
+}
+
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/Protocol.h
===================================================================
--- clang-tools-extra/clangd/Protocol.h
+++ clang-tools-extra/clangd/Protocol.h
@@ -1014,6 +1014,82 @@
 llvm::json::Value toJSON(const DocumentHighlight &DH);
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DocumentHighlight &);
 
+enum class TypeHierarchyDirection { Children = 0, Parents = 1, Both = 2 };
+bool fromJSON(const llvm::json::Value &E, TypeHierarchyDirection &Out);
+
+/// 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 {
+  /// The hierarchy levels to resolve. `0` indicates no level.
+  /// When not defined, it is treated as `0`.
+  llvm::Optional<int> resolve;
+
+  /// The direction of the hierarchy levels to resolve.
+  llvm::Optional<TypeHierarchyDirection> direction;
+};
+bool fromJSON(const llvm::json::Value &, TypeHierarchyParams &);
+
+struct TypeHierarchyItem {
+  /// The human readable name of the hierarchy 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.
+  SymbolKind kind;
+
+  /// `true` if the hierarchy item is deprecatd. Otherwise, `false`.
+  /// It is `false` by default.
+  llvm::Optional<bool> deprecated;
+
+  /// The URI of the text document where this type hierarchy item belongs to.
+  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.
+  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`.
+  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.
+  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.
+  llvm::Optional<std::vector<TypeHierarchyItem>> children;
+
+  /// The protocol has a slot here for an optional 'data' filed, which can
+  /// be used to identify a type hierarchy item in a resolve request. We don't
+  /// need this (the item itself is sufficient to identify what to resolve)
+  /// so don't declare it.
+};
+llvm::json::Value toJSON(const TypeHierarchyItem &);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TypeHierarchyItem &);
+
+/// Parameters for the `typeHierarchy/resolve` request.
+struct ResolveTypeHierarchyItemParams {
+  /// The item to resolve.
+  TypeHierarchyItem item;
+
+  /// The hierarchy levels to resolve. `0` indicates no level.
+  int resolve;
+
+  /// The direction of the hierarchy levels to resolve.
+  TypeHierarchyDirection direction;
+};
+bool fromJSON(const llvm::json::Value &, ResolveTypeHierarchyItemParams &);
+
 struct ReferenceParams : public TextDocumentPositionParams {
   // For now, no options like context.includeDeclaration are supported.
 };
Index: clang-tools-extra/clangd/Protocol.cpp
===================================================================
--- clang-tools-extra/clangd/Protocol.cpp
+++ clang-tools-extra/clangd/Protocol.cpp
@@ -813,6 +813,81 @@
   return true;
 }
 
+bool fromJSON(const llvm::json::Value &E, TypeHierarchyDirection &Out) {
+  if (auto T = E.getAsInteger()) {
+    if (*T < static_cast<int>(TypeHierarchyDirection::Children) ||
+        *T > static_cast<int>(TypeHierarchyDirection::Both))
+      return false;
+    Out = static_cast<TypeHierarchyDirection>(*T);
+    return true;
+  }
+  return false;
+}
+
+bool fromJSON(const llvm::json::Value &Params, TypeHierarchyParams &R) {
+  if (!fromJSON(Params, static_cast<TextDocumentPositionParams &>(R)))
+    return false;
+  if (auto *Resolve = Params.getAsObject()->get("resolve"))
+    if (!fromJSON(*Resolve, R.resolve))
+      return false;
+  if (auto *Direction = Params.getAsObject()->get("direction"))
+    return fromJSON(*Direction, R.direction);
+  return true;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &O,
+                              const TypeHierarchyItem &I) {
+  return O << I.name << " - " << toJSON(I);
+}
+
+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}};
+
+  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;
+  // Older gcc cannot compile 'return Result', even though it is legal.
+  return llvm::json::Value(std::move(Result));
+}
+
+bool fromJSON(const llvm::json::Value &Params, TypeHierarchyItem &I) {
+  llvm::json::ObjectMapper O(Params);
+  if (!O)
+    return true; // 'any' type in LSP.
+
+  O.map("name", I.name);
+  O.map("detail", I.detail);
+  O.map("kind", I.kind);
+  O.map("deprecated", I.kind);
+  O.map("range", I.range);
+  O.map("selectionRange", I.selectionRange);
+  O.map("uri", I.uri);
+  O.map("parents", I.parents);
+  O.map("children", I.children);
+  return true;
+}
+
+bool fromJSON(const llvm::json::Value &Params,
+              ResolveTypeHierarchyItemParams &R) {
+  llvm::json::ObjectMapper O(Params);
+  if (!O)
+    return true; // 'any' type in LSP.
+
+  O.map("item", R.item);
+  O.map("resolve", R.resolve);
+  O.map("direction", R.direction);
+  return true;
+}
+
 bool fromJSON(const llvm::json::Value &Params, ReferenceParams &R) {
   TextDocumentPositionParams &Base = R;
   return fromJSON(Params, Base);
Index: clang-tools-extra/clangd/FindSymbols.h
===================================================================
--- clang-tools-extra/clangd/FindSymbols.h
+++ clang-tools-extra/clangd/FindSymbols.h
@@ -13,17 +13,28 @@
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FINDSYMBOLS_H
 
 #include "Protocol.h"
+#include "index/Index.h"
 #include "llvm/ADT/StringRef.h"
 
 namespace clang {
+class ASTContext;
+class NamedDecl;
+
 namespace clangd {
 class ParsedAST;
 class SymbolIndex;
 
+// Convert a index::SymbolKind to clangd::SymbolKind (LSP)
+// Note, some are not perfect matches and should be improved when this LSP
+// issue is addressed:
+// https://github.com/Microsoft/language-server-protocol/issues/344
+SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind);
+
 /// Searches for the symbols matching \p Query. The syntax of \p Query can be
-/// the non-qualified name or fully qualified of a symbol. For example, "vector"
-/// will match the symbol std::vector and "std::vector" would also match it.
-/// Direct children of scopes (namepaces, etc) can be listed with a trailing
+/// the non-qualified name or fully qualified of a symbol. For example,
+/// "vector" will match the symbol std::vector and "std::vector" would also
+/// match it. Direct children of scopes (namepaces, etc) can be listed with a
+/// trailing
 /// "::". For example, "std::" will list all children of the std namespace and
 /// "::" alone will list all children of the global namespace.
 /// \p Limit limits the number of results returned (0 means no limit).
Index: clang-tools-extra/clangd/FindSymbols.cpp
===================================================================
--- clang-tools-extra/clangd/FindSymbols.cpp
+++ clang-tools-extra/clangd/FindSymbols.cpp
@@ -13,6 +13,7 @@
 #include "Logger.h"
 #include "Quality.h"
 #include "SourceCode.h"
+#include "XRefs.h"
 #include "index/Index.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/Index/IndexDataConsumer.h"
@@ -26,12 +27,7 @@
 
 namespace clang {
 namespace clangd {
-namespace {
 
-// Convert a index::SymbolKind to clangd::SymbolKind (LSP)
-// Note, some are not perfect matches and should be improved when this LSP
-// issue is addressed:
-// https://github.com/Microsoft/language-server-protocol/issues/344
 SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind) {
   switch (Kind) {
   case index::SymbolKind::Unknown:
@@ -87,6 +83,7 @@
   llvm_unreachable("invalid symbol kind");
 }
 
+namespace {
 using ScoredSymbolInfo = std::pair<float, SymbolInformation>;
 struct ScoredSymbolGreater {
   bool operator()(const ScoredSymbolInfo &L, const ScoredSymbolInfo &R) {
@@ -218,6 +215,7 @@
     // reports unrelated ranges we need to reconcile somehow.
     SI.range = SI.selectionRange;
   }
+
   return SI;
 }
 
Index: clang-tools-extra/clangd/ClangdServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdServer.h
+++ clang-tools-extra/clangd/ClangdServer.h
@@ -183,6 +183,16 @@
   void findHover(PathRef File, Position Pos,
                  Callback<llvm::Optional<Hover>> CB);
 
+  /// Get information about type hierarchy for a given position.
+  void findTypeHierarchy(PathRef File, Position Pos, llvm::Optional<int> Resolve,
+                         llvm::Optional<TypeHierarchyDirection> Direction,
+                         Callback<llvm::Optional<TypeHierarchyItem>> CB);
+
+  /// Resolve the given type hierarchy item in the given direction.
+  void resolveTypeHierarchy(TypeHierarchyItem Item, int Resolve,
+                            TypeHierarchyDirection Direction,
+                            Callback<llvm::Optional<TypeHierarchyItem>> CB);
+
   /// Retrieve the top symbols from the workspace matching a query.
   void workspaceSymbols(StringRef Query, int Limit,
                         Callback<std::vector<SymbolInformation>> CB);
Index: clang-tools-extra/clangd/ClangdServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdServer.cpp
+++ clang-tools-extra/clangd/ClangdServer.cpp
@@ -514,6 +514,37 @@
   WorkScheduler.runWithAST("Hover", File, Bind(Action, std::move(CB)));
 }
 
+void ClangdServer::findTypeHierarchy(
+    PathRef File, Position Pos, llvm::Optional<int> Resolve,
+    llvm::Optional<TypeHierarchyDirection> Direction,
+    Callback<Optional<TypeHierarchyItem>> CB) {
+  auto Action = [Pos, Resolve,
+                 Direction](Callback<Optional<TypeHierarchyItem>> CB,
+                            Expected<InputsAndAST> InpAST) {
+    if (!InpAST)
+      return CB(InpAST.takeError());
+    CB(clangd::getTypeHierarchy(InpAST->AST, Pos, Resolve, Direction));
+  };
+
+  WorkScheduler.runWithAST("Type Hierarchy", File, Bind(Action, std::move(CB)));
+}
+
+void ClangdServer::resolveTypeHierarchy(
+    TypeHierarchyItem Item, int Resolve, TypeHierarchyDirection Direction,
+    Callback<llvm::Optional<TypeHierarchyItem>> CB) {
+  auto Action = [Item, Resolve,
+                 Direction](Callback<Optional<TypeHierarchyItem>> CB,
+                            Expected<InputsAndAST> InpAST) {
+    if (!InpAST)
+      return CB(InpAST.takeError());
+    CB(clangd::getTypeHierarchy(InpAST->AST, Item.range.start, Resolve,
+                                Direction));
+  };
+
+  WorkScheduler.runWithAST("Type Hierarchy Resolve", Item.uri.file(),
+                           Bind(Action, std::move(CB)));
+}
+
 tooling::CompileCommand ClangdServer::getCompileCommand(PathRef File) {
   trace::Span Span("GetCompileCommand");
   llvm::Optional<tooling::CompileCommand> C = CDB.getCompileCommand(File);
Index: clang-tools-extra/clangd/ClangdLSPServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.h
+++ clang-tools-extra/clangd/ClangdLSPServer.h
@@ -91,6 +91,10 @@
   void onRename(const RenameParams &, Callback<WorkspaceEdit>);
   void onHover(const TextDocumentPositionParams &,
                Callback<llvm::Optional<Hover>>);
+  void onTypeHierarchy(const TypeHierarchyParams &,
+                       Callback<llvm::Optional<TypeHierarchyItem>>);
+  void onResolveTypeHierarchy(const ResolveTypeHierarchyItemParams &,
+                              Callback<llvm::Optional<TypeHierarchyItem>>);
   void onChangeConfiguration(const DidChangeConfigurationParams &);
   void onSymbolInfo(const TextDocumentPositionParams &,
                     Callback<std::vector<SymbolDetails>>);
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -367,6 +367,7 @@
                   {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND,
                    ExecuteCommandParams::CLANGD_APPLY_TWEAK}},
              }},
+            {"typeHierarchyProvider", true},
         }}}});
 }
 
@@ -750,6 +751,20 @@
                     std::move(Reply));
 }
 
+void ClangdLSPServer::onTypeHierarchy(
+    const TypeHierarchyParams &Params,
+    Callback<Optional<TypeHierarchyItem>> Reply) {
+  Server->findTypeHierarchy(Params.textDocument.uri.file(), Params.position,
+                            Params.resolve, Params.direction, std::move(Reply));
+}
+
+void ClangdLSPServer::onResolveTypeHierarchy(
+    const ResolveTypeHierarchyItemParams &Params,
+    Callback<llvm::Optional<TypeHierarchyItem>> Reply) {
+  Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction,
+                               std::move(Reply));
+}
+
 void ClangdLSPServer::applyConfiguration(
     const ConfigurationSettings &Settings) {
   // Per-file update to the compilation database.
@@ -828,6 +843,8 @@
   MsgHandler->bind("workspace/didChangeWatchedFiles", &ClangdLSPServer::onFileEvent);
   MsgHandler->bind("workspace/didChangeConfiguration", &ClangdLSPServer::onChangeConfiguration);
   MsgHandler->bind("textDocument/symbolInfo", &ClangdLSPServer::onSymbolInfo);
+  MsgHandler->bind("textDocument/typeHierarchy", &ClangdLSPServer::onTypeHierarchy);
+  MsgHandler->bind("typeHierarchy/resolve", &ClangdLSPServer::onResolveTypeHierarchy);
   // clang-format on
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to