[clang] 142c3d9 - [clang][ExtractAPI] Reland ExtractAPI for libclang improvements

Daniel Grumberg via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 30 10:14:18 PDT 2023


Author: Daniel Grumberg
Date: 2023-03-30T18:13:58+01:00
New Revision: 142c3d9d1414847fd154c300ff12505283027505

URL: https://github.com/llvm/llvm-project/commit/142c3d9d1414847fd154c300ff12505283027505
DIFF: https://github.com/llvm/llvm-project/commit/142c3d9d1414847fd154c300ff12505283027505.diff

LOG: [clang][ExtractAPI] Reland ExtractAPI for libclang improvements

This relands the changes that were originally introduced by:
- https://reviews.llvm.org/D146656
- https://reviews.llvm.org/D147138

This also fixes the leak that led to these changes being reverted

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

Added: 
    clang/include/clang/ExtractAPI/TypedefUnderlyingTypeResolver.h

Modified: 
    clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
    clang/lib/ExtractAPI/CMakeLists.txt
    clang/lib/ExtractAPI/DeclarationFragments.cpp
    clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
    clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
    clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
    clang/test/Index/extract-api-cursor.m
    clang/test/Index/extract-api-usr.m
    clang/tools/c-index-test/c-index-test.c
    clang/tools/libclang/CXExtractAPI.cpp

Removed: 
    clang/lib/ExtractAPI/ExtractAPIVisitor.cpp
    clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h


################################################################################
diff  --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
index f6546fb4776a6..a31648b80195a 100644
--- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
+++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
@@ -14,24 +14,27 @@
 #ifndef LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H
 #define LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H
 
+#include "llvm/ADT/FunctionExtras.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ParentMapContext.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/ExtractAPI/API.h"
-#include "llvm/ADT/FunctionExtras.h"
+#include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h"
+#include <type_traits>
 
 namespace clang {
 namespace extractapi {
+namespace impl {
 
-/// The RecursiveASTVisitor to traverse symbol declarations and collect API
-/// information.
-class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
-public:
-  ExtractAPIVisitor(ASTContext &Context,
-                    llvm::unique_function<bool(SourceLocation)> LocationChecker,
-                    APISet &API)
-      : Context(Context), API(API),
-        LocationChecker(std::move(LocationChecker)) {}
+template <typename Derived>
+class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
+protected:
+  ExtractAPIVisitorBase(ASTContext &Context, APISet &API)
+      : Context(Context), API(API) {}
 
+public:
   const APISet &getAPI() const { return API; }
 
   bool VisitVarDecl(const VarDecl *Decl);
@@ -50,7 +53,11 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
 
   bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl);
 
-private:
+  bool shouldDeclBeIncluded(const Decl *Decl) const;
+
+  const RawComment *fetchRawCommentForDecl(const Decl *Decl) const;
+
+protected:
   /// Collect API information for the enum constants and associate with the
   /// parent enum.
   void recordEnumConstants(EnumRecord *EnumRecord,
@@ -77,9 +84,582 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
 
   void recordObjCProtocols(ObjCContainerRecord *Container,
                            ObjCInterfaceDecl::protocol_range Protocols);
+
   ASTContext &Context;
   APISet &API;
-  llvm::unique_function<bool(SourceLocation)> LocationChecker;
+
+  StringRef getTypedefName(const TagDecl *Decl) {
+    if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
+      return TypedefDecl->getName();
+
+    return {};
+  }
+
+  bool isInSystemHeader(const Decl *D) {
+    return Context.getSourceManager().isInSystemHeader(D->getLocation());
+  }
+
+private:
+  Derived &getDerivedExtractAPIVisitor() {
+    return *static_cast<Derived *>(this);
+  }
+};
+
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::VisitVarDecl(const VarDecl *Decl) {
+  // skip function parameters.
+  if (isa<ParmVarDecl>(Decl))
+    return true;
+
+  // Skip non-global variables in records (struct/union/class).
+  if (Decl->getDeclContext()->isRecord())
+    return true;
+
+  // Skip local variables inside function or method.
+  if (!Decl->isDefinedOutsideFunctionOrMethod())
+    return true;
+
+  // If this is a template but not specialization or instantiation, skip.
+  if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) &&
+      Decl->getTemplateSpecializationKind() == TSK_Undeclared)
+    return true;
+
+  if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
+    return true;
+
+  // Collect symbol information.
+  StringRef Name = Decl->getName();
+  StringRef USR = API.recordUSR(Decl);
+  PresumedLoc Loc =
+      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+  LinkageInfo Linkage = Decl->getLinkageAndVisibility();
+  DocComment Comment;
+  if (auto *RawComment =
+          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+
+  // Build declaration fragments and sub-heading for the variable.
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
+  DeclarationFragments SubHeading =
+      DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+  // Add the global variable record to the API set.
+  API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
+                   Declaration, SubHeading, isInSystemHeader(Decl));
+  return true;
+}
+
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::VisitFunctionDecl(
+    const FunctionDecl *Decl) {
+  if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
+    // Skip member function in class templates.
+    if (Method->getParent()->getDescribedClassTemplate() != nullptr)
+      return true;
+
+    // Skip methods in records.
+    for (auto P : Context.getParents(*Method)) {
+      if (P.template get<CXXRecordDecl>())
+        return true;
+    }
+
+    // Skip ConstructorDecl and DestructorDecl.
+    if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
+      return true;
+  }
+
+  // Skip templated functions.
+  switch (Decl->getTemplatedKind()) {
+  case FunctionDecl::TK_NonTemplate:
+  case FunctionDecl::TK_DependentNonTemplate:
+    break;
+  case FunctionDecl::TK_MemberSpecialization:
+  case FunctionDecl::TK_FunctionTemplateSpecialization:
+    if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
+      if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
+        return true;
+    }
+    break;
+  case FunctionDecl::TK_FunctionTemplate:
+  case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
+    return true;
+  }
+
+  if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
+    return true;
+
+  // Collect symbol information.
+  StringRef Name = Decl->getName();
+  StringRef USR = API.recordUSR(Decl);
+  PresumedLoc Loc =
+      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+  LinkageInfo Linkage = Decl->getLinkageAndVisibility();
+  DocComment Comment;
+  if (auto *RawComment =
+          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+
+  // Build declaration fragments, sub-heading, and signature of the function.
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
+  DeclarationFragments SubHeading =
+      DeclarationFragmentsBuilder::getSubHeading(Decl);
+  FunctionSignature Signature =
+      DeclarationFragmentsBuilder::getFunctionSignature(Decl);
+
+  // Add the function record to the API set.
+  API.addGlobalFunction(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
+                        Declaration, SubHeading, Signature,
+                        isInSystemHeader(Decl));
+  return true;
+}
+
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::VisitEnumDecl(const EnumDecl *Decl) {
+  if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
+    return true;
+
+  SmallString<128> QualifiedNameBuffer;
+  // Collect symbol information.
+  StringRef Name = Decl->getName();
+  if (Name.empty())
+    Name = getTypedefName(Decl);
+  if (Name.empty()) {
+    llvm::raw_svector_ostream OS(QualifiedNameBuffer);
+    Decl->printQualifiedName(OS);
+    Name = QualifiedNameBuffer.str();
+  }
+
+  StringRef USR = API.recordUSR(Decl);
+  PresumedLoc Loc =
+      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+  DocComment Comment;
+  if (auto *RawComment =
+          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+
+  // Build declaration fragments and sub-heading for the enum.
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
+  DeclarationFragments SubHeading =
+      DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+  EnumRecord *EnumRecord =
+      API.addEnum(API.copyString(Name), USR, Loc, AvailabilitySet(Decl),
+                  Comment, Declaration, SubHeading, isInSystemHeader(Decl));
+
+  // Now collect information about the enumerators in this enum.
+  getDerivedExtractAPIVisitor().recordEnumConstants(EnumRecord,
+                                                    Decl->enumerators());
+
+  return true;
+}
+
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
+  // Skip C++ structs/classes/unions
+  // TODO: support C++ records
+  if (isa<CXXRecordDecl>(Decl))
+    return true;
+
+  if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
+    return true;
+
+  // Collect symbol information.
+  StringRef Name = Decl->getName();
+  if (Name.empty())
+    Name = getTypedefName(Decl);
+  if (Name.empty())
+    return true;
+
+  StringRef USR = API.recordUSR(Decl);
+  PresumedLoc Loc =
+      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+  DocComment Comment;
+  if (auto *RawComment =
+          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+
+  // Build declaration fragments and sub-heading for the struct.
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
+  DeclarationFragments SubHeading =
+      DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+  StructRecord *StructRecord =
+      API.addStruct(Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration,
+                    SubHeading, isInSystemHeader(Decl));
+
+  // Now collect information about the fields in this struct.
+  getDerivedExtractAPIVisitor().recordStructFields(StructRecord,
+                                                   Decl->fields());
+
+  return true;
+}
+
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::VisitObjCInterfaceDecl(
+    const ObjCInterfaceDecl *Decl) {
+  if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
+    return true;
+
+  // Collect symbol information.
+  StringRef Name = Decl->getName();
+  StringRef USR = API.recordUSR(Decl);
+  PresumedLoc Loc =
+      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+  LinkageInfo Linkage = Decl->getLinkageAndVisibility();
+  DocComment Comment;
+  if (auto *RawComment =
+          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+
+  // Build declaration fragments and sub-heading for the interface.
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
+  DeclarationFragments SubHeading =
+      DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+  // Collect super class information.
+  SymbolReference SuperClass;
+  if (const auto *SuperClassDecl = Decl->getSuperClass()) {
+    SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
+    SuperClass.USR = API.recordUSR(SuperClassDecl);
+  }
+
+  ObjCInterfaceRecord *ObjCInterfaceRecord = API.addObjCInterface(
+      Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment, Declaration,
+      SubHeading, SuperClass, isInSystemHeader(Decl));
+
+  // Record all methods (selectors). This doesn't include automatically
+  // synthesized property methods.
+  getDerivedExtractAPIVisitor().recordObjCMethods(ObjCInterfaceRecord,
+                                                  Decl->methods());
+  getDerivedExtractAPIVisitor().recordObjCProperties(ObjCInterfaceRecord,
+                                                     Decl->properties());
+  getDerivedExtractAPIVisitor().recordObjCInstanceVariables(ObjCInterfaceRecord,
+                                                            Decl->ivars());
+  getDerivedExtractAPIVisitor().recordObjCProtocols(ObjCInterfaceRecord,
+                                                    Decl->protocols());
+
+  return true;
+}
+
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::VisitObjCProtocolDecl(
+    const ObjCProtocolDecl *Decl) {
+  if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
+    return true;
+
+  // Collect symbol information.
+  StringRef Name = Decl->getName();
+  StringRef USR = API.recordUSR(Decl);
+  PresumedLoc Loc =
+      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+  DocComment Comment;
+  if (auto *RawComment =
+          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+
+  // Build declaration fragments and sub-heading for the protocol.
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
+  DeclarationFragments SubHeading =
+      DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+  ObjCProtocolRecord *ObjCProtocolRecord =
+      API.addObjCProtocol(Name, USR, Loc, AvailabilitySet(Decl), Comment,
+                          Declaration, SubHeading, isInSystemHeader(Decl));
+
+  getDerivedExtractAPIVisitor().recordObjCMethods(ObjCProtocolRecord,
+                                                  Decl->methods());
+  getDerivedExtractAPIVisitor().recordObjCProperties(ObjCProtocolRecord,
+                                                     Decl->properties());
+  getDerivedExtractAPIVisitor().recordObjCProtocols(ObjCProtocolRecord,
+                                                    Decl->protocols());
+
+  return true;
+}
+
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::VisitTypedefNameDecl(
+    const TypedefNameDecl *Decl) {
+  // Skip ObjC Type Parameter for now.
+  if (isa<ObjCTypeParamDecl>(Decl))
+    return true;
+
+  if (!Decl->isDefinedOutsideFunctionOrMethod())
+    return true;
+
+  if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
+    return true;
+
+  PresumedLoc Loc =
+      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+  StringRef Name = Decl->getName();
+  StringRef USR = API.recordUSR(Decl);
+  DocComment Comment;
+  if (auto *RawComment =
+          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+
+  QualType Type = Decl->getUnderlyingType();
+  SymbolReference SymRef =
+      TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
+                                                                       API);
+
+  API.addTypedef(Name, USR, Loc, AvailabilitySet(Decl), Comment,
+                 DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
+                 DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef,
+                 isInSystemHeader(Decl));
+
+  return true;
+}
+
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::VisitObjCCategoryDecl(
+    const ObjCCategoryDecl *Decl) {
+  if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
+    return true;
+
+  StringRef Name = Decl->getName();
+  StringRef USR = API.recordUSR(Decl);
+  PresumedLoc Loc =
+      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+  DocComment Comment;
+  if (auto *RawComment =
+          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+  // Build declaration fragments and sub-heading for the category.
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
+  DeclarationFragments SubHeading =
+      DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+  const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
+  SymbolReference Interface(InterfaceDecl->getName(),
+                            API.recordUSR(InterfaceDecl));
+
+  ObjCCategoryRecord *ObjCCategoryRecord = API.addObjCCategory(
+      Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading,
+      Interface, isInSystemHeader(Decl));
+
+  getDerivedExtractAPIVisitor().recordObjCMethods(ObjCCategoryRecord,
+                                                  Decl->methods());
+  getDerivedExtractAPIVisitor().recordObjCProperties(ObjCCategoryRecord,
+                                                     Decl->properties());
+  getDerivedExtractAPIVisitor().recordObjCInstanceVariables(ObjCCategoryRecord,
+                                                            Decl->ivars());
+  getDerivedExtractAPIVisitor().recordObjCProtocols(ObjCCategoryRecord,
+                                                    Decl->protocols());
+
+  return true;
+}
+
+/// Collect API information for the enum constants and associate with the
+/// parent enum.
+template <typename Derived>
+void ExtractAPIVisitorBase<Derived>::recordEnumConstants(
+    EnumRecord *EnumRecord, const EnumDecl::enumerator_range Constants) {
+  for (const auto *Constant : Constants) {
+    // Collect symbol information.
+    StringRef Name = Constant->getName();
+    StringRef USR = API.recordUSR(Constant);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Constant->getLocation());
+    DocComment Comment;
+    if (auto *RawComment =
+            getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Constant))
+      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                              Context.getDiagnostics());
+
+    // Build declaration fragments and sub-heading for the enum constant.
+    DeclarationFragments Declaration =
+        DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Constant);
+
+    API.addEnumConstant(EnumRecord, Name, USR, Loc, AvailabilitySet(Constant),
+                        Comment, Declaration, SubHeading,
+                        isInSystemHeader(Constant));
+  }
+}
+
+/// Collect API information for the struct fields and associate with the
+/// parent struct.
+template <typename Derived>
+void ExtractAPIVisitorBase<Derived>::recordStructFields(
+    StructRecord *StructRecord, const RecordDecl::field_range Fields) {
+  for (const auto *Field : Fields) {
+    // Collect symbol information.
+    StringRef Name = Field->getName();
+    StringRef USR = API.recordUSR(Field);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Field->getLocation());
+    DocComment Comment;
+    if (auto *RawComment =
+            getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Field))
+      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                              Context.getDiagnostics());
+
+    // Build declaration fragments and sub-heading for the struct field.
+    DeclarationFragments Declaration =
+        DeclarationFragmentsBuilder::getFragmentsForField(Field);
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Field);
+
+    API.addStructField(StructRecord, Name, USR, Loc, AvailabilitySet(Field),
+                       Comment, Declaration, SubHeading,
+                       isInSystemHeader(Field));
+  }
+}
+
+/// Collect API information for the Objective-C methods and associate with the
+/// parent container.
+template <typename Derived>
+void ExtractAPIVisitorBase<Derived>::recordObjCMethods(
+    ObjCContainerRecord *Container,
+    const ObjCContainerDecl::method_range Methods) {
+  for (const auto *Method : Methods) {
+    // Don't record selectors for properties.
+    if (Method->isPropertyAccessor())
+      continue;
+
+    StringRef Name = API.copyString(Method->getSelector().getAsString());
+    StringRef USR = API.recordUSR(Method);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Method->getLocation());
+    DocComment Comment;
+    if (auto *RawComment =
+            getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Method))
+      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                              Context.getDiagnostics());
+
+    // Build declaration fragments, sub-heading, and signature for the method.
+    DeclarationFragments Declaration =
+        DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Method);
+    FunctionSignature Signature =
+        DeclarationFragmentsBuilder::getFunctionSignature(Method);
+
+    API.addObjCMethod(Container, Name, USR, Loc, AvailabilitySet(Method),
+                      Comment, Declaration, SubHeading, Signature,
+                      Method->isInstanceMethod(), isInSystemHeader(Method));
+  }
+}
+
+template <typename Derived>
+void ExtractAPIVisitorBase<Derived>::recordObjCProperties(
+    ObjCContainerRecord *Container,
+    const ObjCContainerDecl::prop_range Properties) {
+  for (const auto *Property : Properties) {
+    StringRef Name = Property->getName();
+    StringRef USR = API.recordUSR(Property);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Property->getLocation());
+    DocComment Comment;
+    if (auto *RawComment =
+            getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Property))
+      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                              Context.getDiagnostics());
+
+    // Build declaration fragments and sub-heading for the property.
+    DeclarationFragments Declaration =
+        DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Property);
+
+    StringRef GetterName =
+        API.copyString(Property->getGetterName().getAsString());
+    StringRef SetterName =
+        API.copyString(Property->getSetterName().getAsString());
+
+    // Get the attributes for property.
+    unsigned Attributes = ObjCPropertyRecord::NoAttr;
+    if (Property->getPropertyAttributes() &
+        ObjCPropertyAttribute::kind_readonly)
+      Attributes |= ObjCPropertyRecord::ReadOnly;
+
+    API.addObjCProperty(
+        Container, Name, USR, Loc, AvailabilitySet(Property), Comment,
+        Declaration, SubHeading,
+        static_cast<ObjCPropertyRecord::AttributeKind>(Attributes), GetterName,
+        SetterName, Property->isOptional(),
+        !(Property->getPropertyAttributes() &
+          ObjCPropertyAttribute::kind_class),
+        isInSystemHeader(Property));
+  }
+}
+
+template <typename Derived>
+void ExtractAPIVisitorBase<Derived>::recordObjCInstanceVariables(
+    ObjCContainerRecord *Container,
+    const llvm::iterator_range<
+        DeclContext::specific_decl_iterator<ObjCIvarDecl>>
+        Ivars) {
+  for (const auto *Ivar : Ivars) {
+    StringRef Name = Ivar->getName();
+    StringRef USR = API.recordUSR(Ivar);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
+    DocComment Comment;
+    if (auto *RawComment =
+            getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Ivar))
+      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                              Context.getDiagnostics());
+
+    // Build declaration fragments and sub-heading for the instance variable.
+    DeclarationFragments Declaration =
+        DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Ivar);
+
+    ObjCInstanceVariableRecord::AccessControl Access =
+        Ivar->getCanonicalAccessControl();
+
+    API.addObjCInstanceVariable(Container, Name, USR, Loc,
+                                AvailabilitySet(Ivar), Comment, Declaration,
+                                SubHeading, Access, isInSystemHeader(Ivar));
+  }
+}
+
+template <typename Derived>
+void ExtractAPIVisitorBase<Derived>::recordObjCProtocols(
+    ObjCContainerRecord *Container,
+    ObjCInterfaceDecl::protocol_range Protocols) {
+  for (const auto *Protocol : Protocols)
+    Container->Protocols.emplace_back(Protocol->getName(),
+                                      API.recordUSR(Protocol));
+}
+
+} // namespace impl
+
+/// The RecursiveASTVisitor to traverse symbol declarations and collect API
+/// information.
+template <typename Derived = void>
+class ExtractAPIVisitor
+    : public impl::ExtractAPIVisitorBase<std::conditional_t<
+          std::is_same_v<Derived, void>, ExtractAPIVisitor<>, Derived>> {
+  using Base = impl::ExtractAPIVisitorBase<std::conditional_t<
+      std::is_same_v<Derived, void>, ExtractAPIVisitor<>, Derived>>;
+
+public:
+  ExtractAPIVisitor(ASTContext &Context, APISet &API) : Base(Context, API) {}
+
+  bool shouldDeclBeIncluded(const Decl *D) const { return true; }
+  const RawComment *fetchRawCommentForDecl(const Decl *D) const {
+    return this->Context.getRawCommentForDeclNoCache(D);
+  }
 };
 
 } // namespace extractapi

diff  --git a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h b/clang/include/clang/ExtractAPI/TypedefUnderlyingTypeResolver.h
similarity index 100%
rename from clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h
rename to clang/include/clang/ExtractAPI/TypedefUnderlyingTypeResolver.h

diff  --git a/clang/lib/ExtractAPI/CMakeLists.txt b/clang/lib/ExtractAPI/CMakeLists.txt
index 80fafcd6a0196..153d4b992fda7 100644
--- a/clang/lib/ExtractAPI/CMakeLists.txt
+++ b/clang/lib/ExtractAPI/CMakeLists.txt
@@ -8,7 +8,6 @@ add_clang_library(clangExtractAPI
   APIIgnoresList.cpp
   AvailabilityInfo.cpp
   ExtractAPIConsumer.cpp
-  ExtractAPIVisitor.cpp
   DeclarationFragments.cpp
   Serialization/SerializerBase.cpp
   Serialization/SymbolGraphSerializer.cpp

diff  --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index c42a1de2fd358..675d4e8dc8638 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -12,7 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ExtractAPI/DeclarationFragments.h"
-#include "TypedefUnderlyingTypeResolver.h"
+#include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h"
 #include "clang/Index/USRGeneration.h"
 #include "llvm/ADT/StringSwitch.h"
 

diff  --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
index c1e47b10a39fe..c27b2d025374a 100644
--- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -12,8 +12,10 @@
 ///
 //===----------------------------------------------------------------------===//
 
+#include "clang/AST/ASTConcept.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclObjC.h"
 #include "clang/Basic/DiagnosticFrontend.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
@@ -33,6 +35,7 @@
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Casting.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -219,11 +222,34 @@ struct LocationFileChecker {
   llvm::DenseSet<const FileEntry *> ExternalFileEntries;
 };
 
+struct BatchExtractAPIVisitor : ExtractAPIVisitor<BatchExtractAPIVisitor> {
+  bool shouldDeclBeIncluded(const Decl *D) const {
+    bool ShouldBeIncluded = true;
+    // Check that we have the definition for redeclarable types.
+    if (auto *TD = llvm::dyn_cast<TagDecl>(D))
+      ShouldBeIncluded = TD->isThisDeclarationADefinition();
+    else if (auto *Interface = llvm::dyn_cast<ObjCInterfaceDecl>(D))
+      ShouldBeIncluded = Interface->isThisDeclarationADefinition();
+    else if (auto *Protocol = llvm::dyn_cast<ObjCProtocolDecl>(D))
+      ShouldBeIncluded = Protocol->isThisDeclarationADefinition();
+
+    ShouldBeIncluded = ShouldBeIncluded && LCF(D->getLocation());
+    return ShouldBeIncluded;
+  }
+
+  BatchExtractAPIVisitor(LocationFileChecker &LCF, ASTContext &Context,
+                         APISet &API)
+      : ExtractAPIVisitor<BatchExtractAPIVisitor>(Context, API), LCF(LCF) {}
+
+private:
+  LocationFileChecker &LCF;
+};
+
 class ExtractAPIConsumer : public ASTConsumer {
 public:
   ExtractAPIConsumer(ASTContext &Context,
                      std::unique_ptr<LocationFileChecker> LCF, APISet &API)
-      : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {}
+      : Visitor(*LCF, Context, API), LCF(std::move(LCF)) {}
 
   void HandleTranslationUnit(ASTContext &Context) override {
     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
@@ -231,7 +257,7 @@ class ExtractAPIConsumer : public ASTConsumer {
   }
 
 private:
-  ExtractAPIVisitor Visitor;
+  BatchExtractAPIVisitor Visitor;
   std::unique_ptr<LocationFileChecker> LCF;
 };
 

diff  --git a/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp b/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp
deleted file mode 100644
index 24260cf89383d..0000000000000
--- a/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp
+++ /dev/null
@@ -1,560 +0,0 @@
-//===- ExtractAPI/ExtractAPIVisitor.cpp -------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// This file implements the ExtractAPIVisitor an ASTVisitor to collect API
-/// information.
-///
-//===----------------------------------------------------------------------===//
-
-#include "clang/ExtractAPI/ExtractAPIVisitor.h"
-
-#include "TypedefUnderlyingTypeResolver.h"
-#include "clang/AST/ASTConsumer.h"
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclCXX.h"
-#include "clang/AST/ParentMapContext.h"
-#include "clang/AST/RawCommentList.h"
-#include "clang/Basic/SourceLocation.h"
-#include "clang/Basic/SourceManager.h"
-#include "clang/Basic/TargetInfo.h"
-#include "clang/ExtractAPI/API.h"
-#include "clang/ExtractAPI/AvailabilityInfo.h"
-#include "clang/ExtractAPI/DeclarationFragments.h"
-#include "clang/Frontend/ASTConsumers.h"
-#include "clang/Frontend/FrontendOptions.h"
-#include "llvm/Support/raw_ostream.h"
-
-using namespace clang;
-using namespace extractapi;
-
-namespace {
-
-StringRef getTypedefName(const TagDecl *Decl) {
-  if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
-    return TypedefDecl->getName();
-
-  return {};
-}
-
-template <class DeclTy>
-bool isInSystemHeader(const ASTContext &Context, const DeclTy *D) {
-  return Context.getSourceManager().isInSystemHeader(D->getLocation());
-}
-
-} // namespace
-
-bool ExtractAPIVisitor::VisitVarDecl(const VarDecl *Decl) {
-  // skip function parameters.
-  if (isa<ParmVarDecl>(Decl))
-    return true;
-
-  // Skip non-global variables in records (struct/union/class).
-  if (Decl->getDeclContext()->isRecord())
-    return true;
-
-  // Skip local variables inside function or method.
-  if (!Decl->isDefinedOutsideFunctionOrMethod())
-    return true;
-
-  // If this is a template but not specialization or instantiation, skip.
-  if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) &&
-      Decl->getTemplateSpecializationKind() == TSK_Undeclared)
-    return true;
-
-  if (!LocationChecker(Decl->getLocation()))
-    return true;
-
-  // Collect symbol information.
-  StringRef Name = Decl->getName();
-  StringRef USR = API.recordUSR(Decl);
-  PresumedLoc Loc =
-      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  LinkageInfo Linkage = Decl->getLinkageAndVisibility();
-  DocComment Comment;
-  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
-
-  // Build declaration fragments and sub-heading for the variable.
-  DeclarationFragments Declaration =
-      DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
-  DeclarationFragments SubHeading =
-      DeclarationFragmentsBuilder::getSubHeading(Decl);
-
-  // Add the global variable record to the API set.
-  API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
-                   Declaration, SubHeading, isInSystemHeader(Context, Decl));
-  return true;
-}
-
-bool ExtractAPIVisitor::VisitFunctionDecl(const FunctionDecl *Decl) {
-  if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
-    // Skip member function in class templates.
-    if (Method->getParent()->getDescribedClassTemplate() != nullptr)
-      return true;
-
-    // Skip methods in records.
-    for (auto P : Context.getParents(*Method)) {
-      if (P.get<CXXRecordDecl>())
-        return true;
-    }
-
-    // Skip ConstructorDecl and DestructorDecl.
-    if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
-      return true;
-  }
-
-  // Skip templated functions.
-  switch (Decl->getTemplatedKind()) {
-  case FunctionDecl::TK_NonTemplate:
-  case FunctionDecl::TK_DependentNonTemplate:
-    break;
-  case FunctionDecl::TK_MemberSpecialization:
-  case FunctionDecl::TK_FunctionTemplateSpecialization:
-    if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
-      if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
-        return true;
-    }
-    break;
-  case FunctionDecl::TK_FunctionTemplate:
-  case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
-    return true;
-  }
-
-  if (!LocationChecker(Decl->getLocation()))
-    return true;
-
-  // Collect symbol information.
-  StringRef Name = Decl->getName();
-  StringRef USR = API.recordUSR(Decl);
-  PresumedLoc Loc =
-      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  LinkageInfo Linkage = Decl->getLinkageAndVisibility();
-  DocComment Comment;
-  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
-
-  // Build declaration fragments, sub-heading, and signature of the function.
-  DeclarationFragments Declaration =
-      DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
-  DeclarationFragments SubHeading =
-      DeclarationFragmentsBuilder::getSubHeading(Decl);
-  FunctionSignature Signature =
-      DeclarationFragmentsBuilder::getFunctionSignature(Decl);
-
-  // Add the function record to the API set.
-  API.addGlobalFunction(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
-                        Declaration, SubHeading, Signature,
-                        isInSystemHeader(Context, Decl));
-  return true;
-}
-
-bool ExtractAPIVisitor::VisitEnumDecl(const EnumDecl *Decl) {
-  if (!Decl->isComplete())
-    return true;
-
-  // Skip forward declaration.
-  if (!Decl->isThisDeclarationADefinition())
-    return true;
-
-  if (!LocationChecker(Decl->getLocation()))
-    return true;
-
-  SmallString<128> QualifiedNameBuffer;
-  // Collect symbol information.
-  StringRef Name = Decl->getName();
-  if (Name.empty())
-    Name = getTypedefName(Decl);
-  if (Name.empty()) {
-    llvm::raw_svector_ostream OS(QualifiedNameBuffer);
-    Decl->printQualifiedName(OS);
-    Name = QualifiedNameBuffer.str();
-  }
-
-  StringRef USR = API.recordUSR(Decl);
-  PresumedLoc Loc =
-      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
-
-  // Build declaration fragments and sub-heading for the enum.
-  DeclarationFragments Declaration =
-      DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
-  DeclarationFragments SubHeading =
-      DeclarationFragmentsBuilder::getSubHeading(Decl);
-
-  EnumRecord *EnumRecord = API.addEnum(
-      API.copyString(Name), USR, Loc, AvailabilitySet(Decl), Comment,
-      Declaration, SubHeading, isInSystemHeader(Context, Decl));
-
-  // Now collect information about the enumerators in this enum.
-  recordEnumConstants(EnumRecord, Decl->enumerators());
-
-  return true;
-}
-
-bool ExtractAPIVisitor::VisitRecordDecl(const RecordDecl *Decl) {
-  if (!Decl->isCompleteDefinition())
-    return true;
-
-  // Skip C++ structs/classes/unions
-  // TODO: support C++ records
-  if (isa<CXXRecordDecl>(Decl))
-    return true;
-
-  if (!LocationChecker(Decl->getLocation()))
-    return true;
-
-  // Collect symbol information.
-  StringRef Name = Decl->getName();
-  if (Name.empty())
-    Name = getTypedefName(Decl);
-  if (Name.empty())
-    return true;
-
-  StringRef USR = API.recordUSR(Decl);
-  PresumedLoc Loc =
-      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
-
-  // Build declaration fragments and sub-heading for the struct.
-  DeclarationFragments Declaration =
-      DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
-  DeclarationFragments SubHeading =
-      DeclarationFragmentsBuilder::getSubHeading(Decl);
-
-  StructRecord *StructRecord =
-      API.addStruct(Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration,
-                    SubHeading, isInSystemHeader(Context, Decl));
-
-  // Now collect information about the fields in this struct.
-  recordStructFields(StructRecord, Decl->fields());
-
-  return true;
-}
-
-bool ExtractAPIVisitor::VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
-  // Skip forward declaration for classes (@class)
-  if (!Decl->isThisDeclarationADefinition())
-    return true;
-
-  if (!LocationChecker(Decl->getLocation()))
-    return true;
-
-  // Collect symbol information.
-  StringRef Name = Decl->getName();
-  StringRef USR = API.recordUSR(Decl);
-  PresumedLoc Loc =
-      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  LinkageInfo Linkage = Decl->getLinkageAndVisibility();
-  DocComment Comment;
-  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
-
-  // Build declaration fragments and sub-heading for the interface.
-  DeclarationFragments Declaration =
-      DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
-  DeclarationFragments SubHeading =
-      DeclarationFragmentsBuilder::getSubHeading(Decl);
-
-  // Collect super class information.
-  SymbolReference SuperClass;
-  if (const auto *SuperClassDecl = Decl->getSuperClass()) {
-    SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
-    SuperClass.USR = API.recordUSR(SuperClassDecl);
-  }
-
-  ObjCInterfaceRecord *ObjCInterfaceRecord = API.addObjCInterface(
-      Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment, Declaration,
-      SubHeading, SuperClass, isInSystemHeader(Context, Decl));
-
-  // Record all methods (selectors). This doesn't include automatically
-  // synthesized property methods.
-  recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
-  recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
-  recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
-  recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
-
-  return true;
-}
-
-bool ExtractAPIVisitor::VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
-  // Skip forward declaration for protocols (@protocol).
-  if (!Decl->isThisDeclarationADefinition())
-    return true;
-
-  if (!LocationChecker(Decl->getLocation()))
-    return true;
-
-  // Collect symbol information.
-  StringRef Name = Decl->getName();
-  StringRef USR = API.recordUSR(Decl);
-  PresumedLoc Loc =
-      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
-
-  // Build declaration fragments and sub-heading for the protocol.
-  DeclarationFragments Declaration =
-      DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
-  DeclarationFragments SubHeading =
-      DeclarationFragmentsBuilder::getSubHeading(Decl);
-
-  ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
-      Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading,
-      isInSystemHeader(Context, Decl));
-
-  recordObjCMethods(ObjCProtocolRecord, Decl->methods());
-  recordObjCProperties(ObjCProtocolRecord, Decl->properties());
-  recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
-
-  return true;
-}
-
-bool ExtractAPIVisitor::VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
-  // Skip ObjC Type Parameter for now.
-  if (isa<ObjCTypeParamDecl>(Decl))
-    return true;
-
-  if (!Decl->isDefinedOutsideFunctionOrMethod())
-    return true;
-
-  if (!LocationChecker(Decl->getLocation()))
-    return true;
-
-  PresumedLoc Loc =
-      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  StringRef Name = Decl->getName();
-  StringRef USR = API.recordUSR(Decl);
-  DocComment Comment;
-  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
-
-  QualType Type = Decl->getUnderlyingType();
-  SymbolReference SymRef =
-      TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
-                                                                       API);
-
-  API.addTypedef(Name, USR, Loc, AvailabilitySet(Decl), Comment,
-                 DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
-                 DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef,
-                 isInSystemHeader(Context, Decl));
-
-  return true;
-}
-
-bool ExtractAPIVisitor::VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
-  // Collect symbol information.
-  StringRef Name = Decl->getName();
-  StringRef USR = API.recordUSR(Decl);
-  PresumedLoc Loc =
-      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
-  // Build declaration fragments and sub-heading for the category.
-  DeclarationFragments Declaration =
-      DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
-  DeclarationFragments SubHeading =
-      DeclarationFragmentsBuilder::getSubHeading(Decl);
-
-  const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
-  SymbolReference Interface(InterfaceDecl->getName(),
-                            API.recordUSR(InterfaceDecl));
-
-  ObjCCategoryRecord *ObjCCategoryRecord = API.addObjCCategory(
-      Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading,
-      Interface, isInSystemHeader(Context, Decl));
-
-  recordObjCMethods(ObjCCategoryRecord, Decl->methods());
-  recordObjCProperties(ObjCCategoryRecord, Decl->properties());
-  recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars());
-  recordObjCProtocols(ObjCCategoryRecord, Decl->protocols());
-
-  return true;
-}
-
-/// Collect API information for the enum constants and associate with the
-/// parent enum.
-void ExtractAPIVisitor::recordEnumConstants(
-    EnumRecord *EnumRecord, const EnumDecl::enumerator_range Constants) {
-  for (const auto *Constant : Constants) {
-    // Collect symbol information.
-    StringRef Name = Constant->getName();
-    StringRef USR = API.recordUSR(Constant);
-    PresumedLoc Loc =
-        Context.getSourceManager().getPresumedLoc(Constant->getLocation());
-    DocComment Comment;
-    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
-
-    // Build declaration fragments and sub-heading for the enum constant.
-    DeclarationFragments Declaration =
-        DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
-    DeclarationFragments SubHeading =
-        DeclarationFragmentsBuilder::getSubHeading(Constant);
-
-    API.addEnumConstant(EnumRecord, Name, USR, Loc, AvailabilitySet(Constant),
-                        Comment, Declaration, SubHeading,
-                        isInSystemHeader(Context, Constant));
-  }
-}
-
-/// Collect API information for the struct fields and associate with the
-/// parent struct.
-void ExtractAPIVisitor::recordStructFields(
-    StructRecord *StructRecord, const RecordDecl::field_range Fields) {
-  for (const auto *Field : Fields) {
-    // Collect symbol information.
-    StringRef Name = Field->getName();
-    StringRef USR = API.recordUSR(Field);
-    PresumedLoc Loc =
-        Context.getSourceManager().getPresumedLoc(Field->getLocation());
-    DocComment Comment;
-    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
-
-    // Build declaration fragments and sub-heading for the struct field.
-    DeclarationFragments Declaration =
-        DeclarationFragmentsBuilder::getFragmentsForField(Field);
-    DeclarationFragments SubHeading =
-        DeclarationFragmentsBuilder::getSubHeading(Field);
-
-    API.addStructField(StructRecord, Name, USR, Loc, AvailabilitySet(Field),
-                       Comment, Declaration, SubHeading,
-                       isInSystemHeader(Context, Field));
-  }
-}
-
-/// Collect API information for the Objective-C methods and associate with the
-/// parent container.
-void ExtractAPIVisitor::recordObjCMethods(
-    ObjCContainerRecord *Container,
-    const ObjCContainerDecl::method_range Methods) {
-  for (const auto *Method : Methods) {
-    // Don't record selectors for properties.
-    if (Method->isPropertyAccessor())
-      continue;
-
-    StringRef Name = API.copyString(Method->getSelector().getAsString());
-    StringRef USR = API.recordUSR(Method);
-    PresumedLoc Loc =
-        Context.getSourceManager().getPresumedLoc(Method->getLocation());
-    DocComment Comment;
-    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
-
-    // Build declaration fragments, sub-heading, and signature for the method.
-    DeclarationFragments Declaration =
-        DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
-    DeclarationFragments SubHeading =
-        DeclarationFragmentsBuilder::getSubHeading(Method);
-    FunctionSignature Signature =
-        DeclarationFragmentsBuilder::getFunctionSignature(Method);
-
-    API.addObjCMethod(Container, Name, USR, Loc, AvailabilitySet(Method),
-                      Comment, Declaration, SubHeading, Signature,
-                      Method->isInstanceMethod(),
-                      isInSystemHeader(Context, Method));
-  }
-}
-
-void ExtractAPIVisitor::recordObjCProperties(
-    ObjCContainerRecord *Container,
-    const ObjCContainerDecl::prop_range Properties) {
-  for (const auto *Property : Properties) {
-    StringRef Name = Property->getName();
-    StringRef USR = API.recordUSR(Property);
-    PresumedLoc Loc =
-        Context.getSourceManager().getPresumedLoc(Property->getLocation());
-    DocComment Comment;
-    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
-
-    // Build declaration fragments and sub-heading for the property.
-    DeclarationFragments Declaration =
-        DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
-    DeclarationFragments SubHeading =
-        DeclarationFragmentsBuilder::getSubHeading(Property);
-
-    StringRef GetterName =
-        API.copyString(Property->getGetterName().getAsString());
-    StringRef SetterName =
-        API.copyString(Property->getSetterName().getAsString());
-
-    // Get the attributes for property.
-    unsigned Attributes = ObjCPropertyRecord::NoAttr;
-    if (Property->getPropertyAttributes() &
-        ObjCPropertyAttribute::kind_readonly)
-      Attributes |= ObjCPropertyRecord::ReadOnly;
-
-    API.addObjCProperty(
-        Container, Name, USR, Loc, AvailabilitySet(Property), Comment,
-        Declaration, SubHeading,
-        static_cast<ObjCPropertyRecord::AttributeKind>(Attributes), GetterName,
-        SetterName, Property->isOptional(),
-        !(Property->getPropertyAttributes() &
-          ObjCPropertyAttribute::kind_class),
-        isInSystemHeader(Context, Property));
-  }
-}
-
-void ExtractAPIVisitor::recordObjCInstanceVariables(
-    ObjCContainerRecord *Container,
-    const llvm::iterator_range<
-        DeclContext::specific_decl_iterator<ObjCIvarDecl>>
-        Ivars) {
-  for (const auto *Ivar : Ivars) {
-    StringRef Name = Ivar->getName();
-    StringRef USR = API.recordUSR(Ivar);
-    PresumedLoc Loc =
-        Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
-    DocComment Comment;
-    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
-
-    // Build declaration fragments and sub-heading for the instance variable.
-    DeclarationFragments Declaration =
-        DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
-    DeclarationFragments SubHeading =
-        DeclarationFragmentsBuilder::getSubHeading(Ivar);
-
-    ObjCInstanceVariableRecord::AccessControl Access =
-        Ivar->getCanonicalAccessControl();
-
-    API.addObjCInstanceVariable(
-        Container, Name, USR, Loc, AvailabilitySet(Ivar), Comment, Declaration,
-        SubHeading, Access, isInSystemHeader(Context, Ivar));
-  }
-}
-
-void ExtractAPIVisitor::recordObjCProtocols(
-    ObjCContainerRecord *Container,
-    ObjCInterfaceDecl::protocol_range Protocols) {
-  for (const auto *Protocol : Protocols)
-    Container->Protocols.emplace_back(Protocol->getName(),
-                                      API.recordUSR(Protocol));
-}

diff  --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index 8a98f5cf0c71f..7676c74af6869 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -547,10 +547,6 @@ Array generateParentContexts(const RecordTy &Record, const APISet &API,
                                serializeParentContext(PC, Lang));
                          });
 
-  // The last component would be the record itself so let's remove it.
-  if (!ParentContexts.empty())
-    ParentContexts.pop_back();
-
   return ParentContexts;
 }
 

diff  --git a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
index 3da2424ea7263..3a5f62c9b2e6c 100644
--- a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
+++ b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
@@ -11,7 +11,7 @@
 ///
 //===----------------------------------------------------------------------===//
 
-#include "TypedefUnderlyingTypeResolver.h"
+#include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h"
 #include "clang/Index/USRGeneration.h"
 
 using namespace clang;

diff  --git a/clang/test/Index/extract-api-cursor.m b/clang/test/Index/extract-api-cursor.m
index 078f2f52e215c..1b27b6f61437b 100644
--- a/clang/test/Index/extract-api-cursor.m
+++ b/clang/test/Index/extract-api-cursor.m
@@ -1,3 +1,5 @@
+// Test is line- and column-sensitive. Run lines are below
+
 /// Foo docs
 struct Foo {
     /// Bar docs
@@ -25,91 +27,94 @@ @interface Derived: Base
 - (void)derivedMethodWithValue:(id<Protocol>)value;
 @end
 
-/// This won't show up in docs because we can't serialize it
- at interface Derived ()
-/// Derived method in category docs, won't show up either.
-- (void)derivedMethodInCategory;
+ at implementation Derived
+- (void)derivedMethodWithValue:(id<Protocol>)value {
+    int a = 5;
+}
 @end
 
-// RUN: c-index-test -single-symbol-sgfs local %s | FileCheck %s
-
-// Checking for Foo
-// CHECK: "parentContexts":[]
-// CHECK-SAME: "relatedSymbols":[]
-// CHECK-SAME: "relationships":[]
-// CHECK-SAME: "text":"Foo docs"
-// CHECK-SAME: "kind":{"displayName":"Structure","identifier":"objective-c.struct"}
-// CHECK-SAME: "title":"Foo"
-
-// Checking for bar
-// CHECK-NEXT: "parentContexts":[{"kind":"objective-c.struct","name":"Foo","usr":"c:@S at Foo"}]
-// CHECK-SAME: "relatedSymbols":[]
-// CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:@S at Foo@FI at bar","target":"c:@S at Foo"
-// CHECK-SAME: "text":"Bar docs"
-// CHECK-SAME: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"}
-// CHECK-SAME: "title":"bar"
-
-// Checking for Base
-// CHECK-NEXT: "parentContexts":[]
-// CHECK-SAME: "relatedSymbols":[]
-// CHECK-SAME: "relationships":[]
-// CHECK-SAME: "text":"Base docs"
-// CHECK-SAME: "kind":{"displayName":"Class","identifier":"objective-c.class"}
-// CHECK-SAME: "title":"Base"
-
-// Checking for baseProperty
-// CHECK-NEXT: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}]
-// CHECK-SAME: "relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
-// CHECK-SAME: "isSystem":false
-// CHECK-SAME: "usr":"c:@S at Foo"}]
-// CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(py)baseProperty","target":"c:objc(cs)Base"
-// CHECK-SAME: "text":"Base property docs"
-// CHECK-SAME: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"}
-// CHECK-SAME: "title":"baseProperty"
-
-// Checking for baseMethodWithArg
-// CHECK-NEXT: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}]
-// CHECK-SAME: "relatedSymbols":[]
-// CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(im)baseMethodWithArg:","target":"c:objc(cs)Base"
-// CHECK-SAME: "text":"Base method docs"
-// CHECK-SAME: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"}
-// CHECK-SAME: "title":"baseMethodWithArg:"
-
-// Checking for Protocol
-// CHECK-NEXT: "parentContexts":[]
-// CHECK-SAME: "relatedSymbols":[]
-// CHECK-SAME: "relationships":[]
-// CHECK-SAME: "text":"Protocol docs"
-// CHECK-SAME: "kind":{"displayName":"Protocol","identifier":"objective-c.protocol"}
-// CHECK-SAME: "title":"Protocol"
-
-// Checking for protocolProperty
-// CHECK-NEXT: "parentContexts":[{"kind":"objective-c.protocol","name":"Protocol","usr":"c:objc(pl)Protocol"}]
-// CHECK-SAME: "relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
-// CHECK-SAME: "isSystem":false
-// CHECK-SAME: "usr":"c:@S at Foo"}]
-// CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(pl)Protocol(py)protocolProperty","target":"c:objc(pl)Protocol"
-// CHECK-SAME: "text":"Protocol property docs"
-// CHECK-SAME: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"}
-// CHECK-SAME: "title":"protocolProperty"
-
-// Checking for Derived
-// CHECK-NEXT: "parentContexts":[]
-// CHECK-SAME: "relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
-// CHECK-SAME: "isSystem":false
-// CHECK-SAME: "usr":"c:objc(cs)Base"}]
-// CHECK-SAME: "relationships":[{"kind":"inheritsFrom","source":"c:objc(cs)Derived","target":"c:objc(cs)Base"
-// CHECK-SAME: "text":"Derived docs"
-// CHECK-SAME: "kind":{"displayName":"Class","identifier":"objective-c.class"}
-// CHECK-SAME: "title":"Derived"
-
-// Checking for derivedMethodWithValue
-// CHECK-NEXT: "parentContexts":[{"kind":"objective-c.class","name":"Derived","usr":"c:objc(cs)Derived"}]
-// CHECK-SAME: "relatedSymbols":[]
-// CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Derived(im)derivedMethodWithValue:","target":"c:objc(cs)Derived"
-// CHECK-SAME: "text":"Derived method docs"
-// CHECK-SAME: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"}
-// CHECK-SAME: "title":"derivedMethodWithValue:"
-
-// CHECK-NOT: This won't show up in docs because we can't serialize it
-// CHECK-NOT: Derived method in category docs, won't show up either.
+// RUN: c-index-test -single-symbol-sgf-at=%s:4:9 local %s | FileCheck -check-prefix=CHECK-FOO %s
+// CHECK-FOO: "parentContexts":[{"kind":"objective-c.struct","name":"Foo","usr":"c:@S at Foo"}]
+// CHECK-FOO: "relatedSymbols":[]
+// CHECK-FOO: "relationships":[]
+// CHECK-FOO: "text":"Foo docs"
+// CHECK-FOO: "kind":{"displayName":"Structure","identifier":"objective-c.struct"}
+// CHECK-FOO: "title":"Foo"
+
+// RUN: c-index-test -single-symbol-sgf-at=%s:6:9 local %s | FileCheck -check-prefix=CHECK-BAR %s
+// CHECK-BAR: "parentContexts":[{"kind":"objective-c.struct","name":"Foo","usr":"c:@S at Foo"},{"kind":"objective-c.property","name":"bar","usr":"c:@S at Foo@FI at bar"}]
+// CHECK-BAR: "relatedSymbols":[]
+// CHECK-BAR: "relationships":[{"kind":"memberOf","source":"c:@S at Foo@FI at bar","target":"c:@S at Foo"
+// CHECK-BAR: "text":"Bar docs"
+// CHECK-BAR: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"}
+// CHECK-BAR: "title":"bar"
+
+// RUN: c-index-test -single-symbol-sgf-at=%s:10:11 local %s | FileCheck -check-prefix=CHECK-BASE %s
+// CHECK-BASE: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}]
+// CHECK-BASE: "relatedSymbols":[]
+// CHECK-BASE: "relationships":[]
+// CHECK-BASE: "text":"Base docs"
+// CHECK-BASE: "kind":{"displayName":"Class","identifier":"objective-c.class"}
+// CHECK-BASE: "title":"Base"
+
+// RUN: c-index-test -single-symbol-sgf-at=%s:12:25 local %s | FileCheck -check-prefix=CHECK-BASE-PROP %s
+// CHECK-BASE-PROP: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"},{"kind":"objective-c.property","name":"baseProperty","usr":"c:objc(cs)Base(py)baseProperty"}]
+// CHECK-BASE-PROP: "relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
+// CHECK-BASE-PROP: "isSystem":false
+// CHECK-BASE-PROP: "usr":"c:@S at Foo"}]
+// CHECK-BASE-PROP: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(py)baseProperty","target":"c:objc(cs)Base"
+// CHECK-BASE-PROP: "text":"Base property docs"
+// CHECK-BASE-PROP: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"}
+// CHECK-BASE-PROP: "title":"baseProperty"
+
+// RUN: c-index-test -single-symbol-sgf-at=%s:15:9 local %s | FileCheck -check-prefix=CHECK-BASE-METHOD %s
+// CHECK-BASE-METHOD: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"},{"kind":"objective-c.method","name":"baseMethodWithArg:","usr":"c:objc(cs)Base(im)baseMethodWithArg:"}]
+// CHECK-BASE-METHOD: "relatedSymbols":[]
+// CHECK-BASE-METHOD: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(im)baseMethodWithArg:","target":"c:objc(cs)Base"
+// CHECK-BASE-METHOD: "text":"Base method docs"
+// CHECK-BASE-METHOD: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"}
+// CHECK-BASE-METHOD: "title":"baseMethodWithArg:"
+
+// RUN: c-index-test -single-symbol-sgf-at=%s:19:11 local %s | FileCheck -check-prefix=CHECK-PROTOCOL %s
+// CHECK-PROTOCOL: "parentContexts":[{"kind":"objective-c.protocol","name":"Protocol","usr":"c:objc(pl)Protocol"}]
+// CHECK-PROTOCOL: "relatedSymbols":[]
+// CHECK-PROTOCOL: "relationships":[]
+// CHECK-PROTOCOL: "text":"Protocol docs"
+// CHECK-PROTOCOL: "kind":{"displayName":"Protocol","identifier":"objective-c.protocol"}
+// CHECK-PROTOCOL: "title":"Protocol"
+
+// RUN: c-index-test -single-symbol-sgf-at=%s:21:27 local %s | FileCheck -check-prefix=CHECK-PROTOCOL-PROP %s
+// CHECK-PROTOCOL-PROP: "parentContexts":[{"kind":"objective-c.protocol","name":"Protocol","usr":"c:objc(pl)Protocol"},{"kind":"objective-c.property","name":"protocolProperty","usr":"c:objc(pl)Protocol(py)protocolProperty"}]
+// CHECK-PROTOCOL-PROP: "relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
+// CHECK-PROTOCOL-PROP: "isSystem":false
+// CHECK-PROTOCOL-PROP: "usr":"c:@S at Foo"}]
+// CHECK-PROTOCOL-PROP: "relationships":[{"kind":"memberOf","source":"c:objc(pl)Protocol(py)protocolProperty","target":"c:objc(pl)Protocol"
+// CHECK-PROTOCOL-PROP: "text":"Protocol property docs"
+// CHECK-PROTOCOL-PROP: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"}
+// CHECK-PROTOCOL-PROP: "title":"protocolProperty"
+
+// RUN: c-index-test -single-symbol-sgf-at=%s:25:15 local %s | FileCheck -check-prefix=CHECK-DERIVED %s
+// CHECK-DERIVED: "parentContexts":[{"kind":"objective-c.class","name":"Derived","usr":"c:objc(cs)Derived"}]
+// CHECK-DERIVED: "relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
+// CHECK-DERIVED: "isSystem":false
+// CHECK-DERIVED: "usr":"c:objc(cs)Base"}]
+// CHECK-DERIVED: "relationships":[{"kind":"inheritsFrom","source":"c:objc(cs)Derived","target":"c:objc(cs)Base"
+// CHECK-DERIVED: "text":"Derived docs"
+// CHECK-DERIVED: "kind":{"displayName":"Class","identifier":"objective-c.class"}
+// CHECK-DERIVED: "title":"Derived"
+
+// RUN: c-index-test -single-symbol-sgf-at=%s:27:11 local %s | FileCheck -check-prefix=CHECK-DERIVED-METHOD %s
+// CHECK-DERIVED-METHOD: "parentContexts":[{"kind":"objective-c.class","name":"Derived","usr":"c:objc(cs)Derived"},{"kind":"objective-c.method","name":"derivedMethodWithValue:","usr":"c:objc(cs)Derived(im)derivedMethodWithValue:"}]
+// CHECK-DERIVED-METHOD: "relatedSymbols":[]
+// CHECK-DERIVED-METHOD: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Derived(im)derivedMethodWithValue:","target":"c:objc(cs)Derived"
+// CHECK-DERIVED-METHOD: "text":"Derived method docs"
+// CHECK-DERIVED-METHOD: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"}
+// CHECK-DERIVED-METHOD: "title":"derivedMethodWithValue:"
+
+// RUN: c-index-test -single-symbol-sgf-at=%s:31:11 local %s | FileCheck -check-prefix=CHECK-DERIVED-METHOD-IMPL %s
+// CHECK-DERIVED-METHOD-IMPL: "parentContexts":[{"kind":"objective-c.class","name":"Derived","usr":"c:objc(cs)Derived"},{"kind":"objective-c.method","name":"derivedMethodWithValue:","usr":"c:objc(cs)Derived(im)derivedMethodWithValue:"}]
+// CHECK-DERIVED-METHOD-IMPL: "relatedSymbols":[]
+// CHECK-DERIVED-METHOD-IMPL: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Derived(im)derivedMethodWithValue:","target":"c:objc(cs)Derived"
+// CHECK-DERIVED-METHOD-IMPL: "text":"Derived method docs"
+// CHECK-DERIVED-METHOD-IMPL: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"}
+// CHECK-DERIVED-METHOD-IMPL: "title":"derivedMethodWithValue:"

diff  --git a/clang/test/Index/extract-api-usr.m b/clang/test/Index/extract-api-usr.m
index 12bfb0a676e66..0b468ee916708 100644
--- a/clang/test/Index/extract-api-usr.m
+++ b/clang/test/Index/extract-api-usr.m
@@ -28,7 +28,7 @@ - (void)derivedMethodWithValue:(id<Protocol>)value;
 
 // Checking for Foo
 // RUN: c-index-test "-single-symbol-sgf-for=c:@S at Foo" %s | FileCheck -check-prefix=CHECK-FOO %s
-// CHECK-FOO: "parentContexts":[]
+// CHECK-FOO: "parentContexts":[{"kind":"objective-c.struct","name":"Foo","usr":"c:@S at Foo"}]
 // CHECK-FOO-SAME: "relatedSymbols":[]
 // CHECK-FOO-SAME: "relationships":[]
 // CHECK-FOO-SAME: "text":"Foo docs"
@@ -38,7 +38,7 @@ - (void)derivedMethodWithValue:(id<Protocol>)value;
 
 // Checking for bar
 // RUN: c-index-test "-single-symbol-sgf-for=c:@S at Foo@FI at bar" %s | FileCheck -check-prefix=CHECK-BAR %s
-// CHECK-BAR: "parentContexts":[{"kind":"objective-c.struct","name":"Foo","usr":"c:@S at Foo"}]
+// CHECK-BAR: "parentContexts":[{"kind":"objective-c.struct","name":"Foo","usr":"c:@S at Foo"},{"kind":"objective-c.property","name":"bar","usr":"c:@S at Foo@FI at bar"}]
 // CHECK-BAR-SAME: "relatedSymbols":[]
 // CHECK-BAR-SAME: "relationships":[{"kind":"memberOf","source":"c:@S at Foo@FI at bar","target":"c:@S at Foo"
 // CHECK-BAR-SAME: "text":"Bar docs"
@@ -47,7 +47,7 @@ - (void)derivedMethodWithValue:(id<Protocol>)value;
 
 // Checking for Base
 // RUN: c-index-test "-single-symbol-sgf-for=c:objc(cs)Base" %s | FileCheck -check-prefix=CHECK-BASE %s
-// CHECK-BASE: "parentContexts":[]
+// CHECK-BASE: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}]
 // CHECK-BASE-SAME: "relatedSymbols":[]
 // CHECK-BASE-SAME: "relationships":[]
 // CHECK-BASE-SAME: "text":"Base docs"
@@ -56,7 +56,7 @@ - (void)derivedMethodWithValue:(id<Protocol>)value;
 
 // Checking for baseProperty
 // RUN: c-index-test "-single-symbol-sgf-for=c:objc(cs)Base(py)baseProperty" %s | FileCheck -check-prefix=CHECK-BASEPROP %s
-// CHECK-BASEPROP: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}]
+// CHECK-BASEPROP: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"},{"kind":"objective-c.property","name":"baseProperty","usr":"c:objc(cs)Base(py)baseProperty"}]
 // CHECK-BASEPROP-SAME:"relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
 // CHECK-BASEPROP-SAME: "isSystem":false
 // CHECK-BASEPROP-SAME: "usr":"c:@S at Foo"}]
@@ -67,7 +67,7 @@ - (void)derivedMethodWithValue:(id<Protocol>)value;
 
 // Checking for baseMethodWithArg
 // RUN: c-index-test "-single-symbol-sgf-for=c:objc(cs)Base(im)baseMethodWithArg:" %s | FileCheck -check-prefix=CHECK-BASEMETHOD %s
-// CHECK-BASEMETHOD: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}]
+// CHECK-BASEMETHOD: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"},{"kind":"objective-c.method","name":"baseMethodWithArg:","usr":"c:objc(cs)Base(im)baseMethodWithArg:"}]
 // CHECK-BASEMETHOD-SAME:"relatedSymbols":[]
 // CHECK-BASEMETHOD-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(im)baseMethodWithArg:","target":"c:objc(cs)Base"
 // CHECK-BASEMETHOD-SAME: "text":"Base method docs"
@@ -76,7 +76,7 @@ - (void)derivedMethodWithValue:(id<Protocol>)value;
 
 // Checking for Protocol
 // RUN: c-index-test "-single-symbol-sgf-for=c:objc(pl)Protocol" %s | FileCheck -check-prefix=CHECK-PROT %s
-// CHECK-PROT: "parentContexts":[]
+// CHECK-PROT: "parentContexts":[{"kind":"objective-c.protocol","name":"Protocol","usr":"c:objc(pl)Protocol"}]
 // CHECK-PROT-SAME: "relatedSymbols":[]
 // CHECK-PROT-SAME: "relationships":[]
 // CHECK-PROT-SAME: "text":"Protocol docs"
@@ -85,7 +85,7 @@ - (void)derivedMethodWithValue:(id<Protocol>)value;
 
 // Checking for protocolProperty
 // RUN: c-index-test "-single-symbol-sgf-for=c:objc(pl)Protocol(py)protocolProperty" %s | FileCheck -check-prefix=CHECK-PROTPROP %s
-// CHECK-PROTPROP: "parentContexts":[{"kind":"objective-c.protocol","name":"Protocol","usr":"c:objc(pl)Protocol"}]
+// CHECK-PROTPROP: "parentContexts":[{"kind":"objective-c.protocol","name":"Protocol","usr":"c:objc(pl)Protocol"},{"kind":"objective-c.property","name":"protocolProperty","usr":"c:objc(pl)Protocol(py)protocolProperty"}]
 // CHECK-PROTPROP-SAME:"relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
 // CHECK-PROTPROP-SAME: "isSystem":false
 // CHECK-PROTPROP-SAME: "usr":"c:@S at Foo"}]
@@ -96,7 +96,7 @@ - (void)derivedMethodWithValue:(id<Protocol>)value;
 
 // Checking for Derived
 // RUN: c-index-test "-single-symbol-sgf-for=c:objc(cs)Derived" %s | FileCheck -check-prefix=CHECK-DERIVED %s
-// CHECK-DERIVED: "parentContexts":[]
+// CHECK-DERIVED: "parentContexts":[{"kind":"objective-c.class","name":"Derived","usr":"c:objc(cs)Derived"}]
 // CHECK-DERIVED-SAME:"relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
 // CHECK-DERIVED-SAME: "isSystem":false
 // CHECK-DERIVED-SAME: "usr":"c:objc(cs)Base"}]
@@ -107,7 +107,7 @@ - (void)derivedMethodWithValue:(id<Protocol>)value;
 
 // Checking for derivedMethodWithValue
 // RUN: c-index-test "-single-symbol-sgf-for=c:objc(cs)Derived(im)derivedMethodWithValue:" %s | FileCheck -check-prefix=CHECK-DERIVEDMETHOD %s
-// CHECK-DERIVEDMETHOD: "parentContexts":[{"kind":"objective-c.class","name":"Derived","usr":"c:objc(cs)Derived"}]
+// CHECK-DERIVEDMETHOD: "parentContexts":[{"kind":"objective-c.class","name":"Derived","usr":"c:objc(cs)Derived"},{"kind":"objective-c.method","name":"derivedMethodWithValue:","usr":"c:objc(cs)Derived(im)derivedMethodWithValue:"}]
 // CHECK-DERIVEDMETHOD-SAME:"relatedSymbols":[]
 // CHECK-DERIVEDMETHOD-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Derived(im)derivedMethodWithValue:","target":"c:objc(cs)Derived"
 // CHECK-DERIVEDMETHOD-SAME: "text":"Derived method docs"

diff  --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c
index 448435e83027c..68a560ca15cf4 100644
--- a/clang/tools/c-index-test/c-index-test.c
+++ b/clang/tools/c-index-test/c-index-test.c
@@ -3,6 +3,7 @@
 #include "clang-c/BuildSystem.h"
 #include "clang-c/CXCompilationDatabase.h"
 #include "clang-c/CXErrorCode.h"
+#include "clang-c/CXSourceLocation.h"
 #include "clang-c/CXString.h"
 #include "clang-c/Documentation.h"
 #include "clang-c/Index.h"
@@ -4881,6 +4882,22 @@ static int perform_test_single_symbol_sgf(const char *input, int argc,
   return result;
 }
 
+static void inspect_single_symbol_sgf_cursor(CXCursor Cursor) {
+  CXSourceLocation CursorLoc;
+  CXString SGFData;
+  const char *SGF;
+  unsigned line, column;
+  CursorLoc = clang_getCursorLocation(Cursor);
+  clang_getSpellingLocation(CursorLoc, 0, &line, &column, 0);
+
+  SGFData = clang_getSymbolGraphForCursor(Cursor);
+  SGF = clang_getCString(SGFData);
+  if (SGF)
+    printf("%d:%d: %s\n", line, column, SGF);
+
+  clang_disposeString(SGFData);
+}
+
 /******************************************************************************/
 /* Command line processing.                                                   */
 /******************************************************************************/
@@ -4940,6 +4957,7 @@ static void print_usage(void) {
     "       c-index-test -print-usr-file <file>\n");
   fprintf(stderr,
           "       c-index-test -single-symbol-sgfs <symbol filter> {<args>*}\n"
+          "       c-index-test -single-symbol-sgf-at=<site> {<args>*}\n"
           "       c-index-test -single-symbol-sgf-for=<usr> {<args>}*\n");
   fprintf(stderr,
     "       c-index-test -write-pch <file> <compiler arguments>\n"
@@ -5076,6 +5094,9 @@ int cindextest_main(int argc, const char **argv) {
   else if (argc > 3 && strcmp(argv[1], "-single-symbol-sgfs") == 0)
     return perform_test_load_source(argc - 3, argv + 3, argv[2],
                                     PrintSingleSymbolSGFs, NULL);
+  else if (argc > 2 && strstr(argv[1], "-single-symbol-sgf-at=") == argv[1])
+    return inspect_cursor_at(
+        argc, argv, "-single-symbol-sgf-at=", inspect_single_symbol_sgf_cursor);
   else if (argc > 2 && strstr(argv[1], "-single-symbol-sgf-for=") == argv[1])
     return perform_test_single_symbol_sgf(argv[1], argc - 2, argv + 2);
 

diff  --git a/clang/tools/libclang/CXExtractAPI.cpp b/clang/tools/libclang/CXExtractAPI.cpp
index 787334ab1bbb2..9128e891538a8 100644
--- a/clang/tools/libclang/CXExtractAPI.cpp
+++ b/clang/tools/libclang/CXExtractAPI.cpp
@@ -18,6 +18,7 @@
 #include "clang-c/Index.h"
 #include "clang-c/Platform.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclObjC.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/ExtractAPI/API.h"
 #include "clang/ExtractAPI/ExtractAPIVisitor.h"
@@ -34,13 +35,73 @@
 using namespace clang;
 using namespace clang::extractapi;
 
+namespace {
+struct LibClangExtractAPIVisitor
+    : ExtractAPIVisitor<LibClangExtractAPIVisitor> {
+  using Base = ExtractAPIVisitor<LibClangExtractAPIVisitor>;
+
+  LibClangExtractAPIVisitor(ASTContext &Context, APISet &API)
+      : ExtractAPIVisitor<LibClangExtractAPIVisitor>(Context, API) {}
+
+  const RawComment *fetchRawCommentForDecl(const Decl *D) const {
+    return Context.getRawCommentForAnyRedecl(D);
+  }
+
+  // We need to visit implementations as well to ensure that when a user clicks
+  // on a method defined only within the implementation that we can still
+  // provide a symbol graph for it.
+  bool VisitObjCImplementationDecl(const ObjCImplementationDecl *Decl) {
+    if (!shouldDeclBeIncluded(Decl))
+      return true;
+
+    const ObjCInterfaceDecl *Interface = Decl->getClassInterface();
+    StringRef Name = Interface->getName();
+    StringRef USR = API.recordUSR(Decl);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+    LinkageInfo Linkage = Decl->getLinkageAndVisibility();
+    DocComment Comment;
+    if (auto *RawComment = fetchRawCommentForDecl(Interface))
+      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                              Context.getDiagnostics());
+
+    // Build declaration fragments and sub-heading by generating them for the
+    // interface.
+    DeclarationFragments Declaration =
+        DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Interface);
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+    // Collect super class information.
+    SymbolReference SuperClass;
+    if (const auto *SuperClassDecl = Decl->getSuperClass()) {
+      SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
+      SuperClass.USR = API.recordUSR(SuperClassDecl);
+    }
+
+    ObjCInterfaceRecord *ObjCInterfaceRecord = API.addObjCInterface(
+        Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment, Declaration,
+        SubHeading, SuperClass, isInSystemHeader(Decl));
+
+    // Record all methods (selectors). This doesn't include automatically
+    // synthesized property methods.
+    recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
+    recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
+    recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
+
+    return true;
+  }
+};
+} // namespace
+
 DEFINE_SIMPLE_CONVERSION_FUNCTIONS(APISet, CXAPISet)
 
-static void WalkupFromMostDerivedType(ExtractAPIVisitor &Visitor, Decl *D);
+static void WalkupFromMostDerivedType(LibClangExtractAPIVisitor &Visitor,
+                                      Decl *D);
 
 template <typename DeclTy>
 static bool WalkupParentContext(DeclContext *Parent,
-                                ExtractAPIVisitor &Visitor) {
+                                LibClangExtractAPIVisitor &Visitor) {
   if (auto *D = dyn_cast<DeclTy>(Parent)) {
     WalkupFromMostDerivedType(Visitor, D);
     return true;
@@ -48,7 +109,8 @@ static bool WalkupParentContext(DeclContext *Parent,
   return false;
 }
 
-static void WalkupFromMostDerivedType(ExtractAPIVisitor &Visitor, Decl *D) {
+static void WalkupFromMostDerivedType(LibClangExtractAPIVisitor &Visitor,
+                                      Decl *D) {
   switch (D->getKind()) {
 #define ABSTRACT_DECL(DECL)
 #define DECL(CLASS, BASE)                                                      \
@@ -84,8 +146,7 @@ enum CXErrorCode clang_createAPISet(CXTranslationUnit tu, CXAPISet *out_api) {
   auto Lang = Unit->getInputKind().getLanguage();
   APISet *API = new APISet(Ctx.getTargetInfo().getTriple(), Lang,
                            Unit->getMainFileName().str());
-  ExtractAPIVisitor Visitor(
-      Ctx, [](SourceLocation Loc) { return true; }, *API);
+  LibClangExtractAPIVisitor Visitor(Ctx, *API);
 
   for (auto It = Unit->top_level_begin(); It != Unit->top_level_end(); ++It) {
     Visitor.TraverseDecl(*It);
@@ -107,45 +168,50 @@ CXString clang_getSymbolGraphForUSR(const char *usr, CXAPISet api) {
 }
 
 CXString clang_getSymbolGraphForCursor(CXCursor cursor) {
+  cursor = clang_getCursorReferenced(cursor);
   CXCursorKind Kind = clang_getCursorKind(cursor);
-  if (clang_isDeclaration(Kind)) {
-    const Decl *D = cxcursor::getCursorDecl(cursor);
+  if (!clang_isDeclaration(Kind))
+    return cxstring::createNull();
 
-    if (!D)
-      return cxstring::createNull();
+  const Decl *D = cxcursor::getCursorDecl(cursor);
 
-    CXTranslationUnit TU = cxcursor::getCursorTU(cursor);
-    if (!TU)
-      return cxstring::createNull();
+  if (!D)
+    return cxstring::createNull();
 
-    ASTUnit *Unit = cxtu::getASTUnit(TU);
+  CXTranslationUnit TU = cxcursor::getCursorTU(cursor);
+  if (!TU)
+    return cxstring::createNull();
 
-    auto &Ctx = Unit->getASTContext();
-    auto Lang = Unit->getInputKind().getLanguage();
-    APISet API(Ctx.getTargetInfo().getTriple(), Lang,
-               Unit->getMainFileName().str());
-    ExtractAPIVisitor Visitor(
-        Ctx, [](SourceLocation Loc) { return true; }, API);
+  ASTUnit *Unit = cxtu::getASTUnit(TU);
 
-    SmallString<128> USR;
-    if (index::generateUSRForDecl(D, USR))
-      return cxstring::createNull();
+  auto &Ctx = Unit->getASTContext();
 
-    WalkupFromMostDerivedType(Visitor, const_cast<Decl *>(D));
-    auto *Record = API.findRecordForUSR(USR);
+  auto Lang = Unit->getInputKind().getLanguage();
+  APISet API(Ctx.getTargetInfo().getTriple(), Lang,
+             Unit->getMainFileName().str());
+  LibClangExtractAPIVisitor Visitor(Ctx, API);
 
-    if (!Record)
-      return cxstring::createNull();
+  const Decl *CanonicalDecl = D->getCanonicalDecl();
+  CanonicalDecl = CanonicalDecl ? CanonicalDecl : D;
 
-    for (const auto &Fragment : Record->Declaration.getFragments()) {
-      if (Fragment.Declaration)
-        WalkupFromMostDerivedType(Visitor,
-                                  const_cast<Decl *>(Fragment.Declaration));
-    }
+  SmallString<128> USR;
+  if (index::generateUSRForDecl(CanonicalDecl, USR))
+    return cxstring::createNull();
 
-    if (auto SGF = SymbolGraphSerializer::serializeSingleSymbolSGF(USR, API))
-      return GenerateCXStringFromSymbolGraphData(std::move(*SGF));
+  WalkupFromMostDerivedType(Visitor, const_cast<Decl *>(CanonicalDecl));
+  auto *Record = API.findRecordForUSR(USR);
+
+  if (!Record)
+    return cxstring::createNull();
+
+  for (const auto &Fragment : Record->Declaration.getFragments()) {
+    if (Fragment.Declaration)
+      WalkupFromMostDerivedType(Visitor,
+                                const_cast<Decl *>(Fragment.Declaration));
   }
 
+  if (auto SGF = SymbolGraphSerializer::serializeSingleSymbolSGF(USR, API))
+    return GenerateCXStringFromSymbolGraphData(std::move(*SGF));
+
   return cxstring::createNull();
 }


        


More information about the cfe-commits mailing list