[clang] ea35740 - [clang][ExtractAPI] Refactor ExtractAPIVisitor to make it more extensible

Daniel Grumberg via cfe-commits cfe-commits at lists.llvm.org
Mon Mar 27 09:24:31 PDT 2023


Author: Daniel Grumberg
Date: 2023-03-27T17:24:10+01:00
New Revision: ea35740e7e189cdcdd88344ac60a53a5b8a8318d

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

LOG: [clang][ExtractAPI] Refactor ExtractAPIVisitor to make it more extensible

Use CRTP to enable creating statically dispatched subclasses of
ExtractAPIVisitor.
This enables adding extension points and customising the behavior more
easily.

This is used in CXExtractAPI.cpp to create a specialized visitor for
Libclang as well as streamlining the batch implementation in ExtractAPIConsumer.cpp

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/TypedefUnderlyingTypeResolver.cpp
    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..31005787e1b2e 100644
--- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
+++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
@@ -14,23 +14,25 @@
 #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> {
+template <typename Derived>
+class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
 public:
-  ExtractAPIVisitor(ASTContext &Context,
-                    llvm::unique_function<bool(SourceLocation)> LocationChecker,
-                    APISet &API)
-      : Context(Context), API(API),
-        LocationChecker(std::move(LocationChecker)) {}
+  ExtractAPIVisitorBase(ASTContext &Context, APISet &API)
+      : Context(Context), API(API) {}
 
   const APISet &getAPI() const { return API; }
 
@@ -50,7 +52,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 +83,554 @@ 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 &getConcrete() { 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 (!getConcrete().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 = getConcrete().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 (!getConcrete().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 = getConcrete().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 (!getConcrete().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 = getConcrete().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.
+  getConcrete().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 (!getConcrete().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 = getConcrete().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.
+  getConcrete().recordStructFields(StructRecord, Decl->fields());
+
+  return true;
+}
+
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::VisitObjCInterfaceDecl(
+    const ObjCInterfaceDecl *Decl) {
+  if (!getConcrete().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 = getConcrete().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.
+  getConcrete().recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
+  getConcrete().recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
+  getConcrete().recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
+  getConcrete().recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
+
+  return true;
+}
+
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::VisitObjCProtocolDecl(
+    const ObjCProtocolDecl *Decl) {
+  if (!getConcrete().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 = getConcrete().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));
+
+  getConcrete().recordObjCMethods(ObjCProtocolRecord, Decl->methods());
+  getConcrete().recordObjCProperties(ObjCProtocolRecord, Decl->properties());
+  getConcrete().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 (!getConcrete().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 = getConcrete().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 (!getConcrete().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 = getConcrete().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));
+
+  getConcrete().recordObjCMethods(ObjCCategoryRecord, Decl->methods());
+  getConcrete().recordObjCProperties(ObjCCategoryRecord, Decl->properties());
+  getConcrete().recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars());
+  getConcrete().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 = getConcrete().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 = getConcrete().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 = getConcrete().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 = getConcrete().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 = getConcrete().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..4462dd58531d8 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"
@@ -164,7 +167,7 @@ std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
 
 struct LocationFileChecker {
   bool operator()(SourceLocation Loc) {
-    // If the loc refers to a macro expansion we need to first get the file
+    // If the loc refersSourceLocationxpansion we need to first get the file
     // location of the expansion.
     auto &SM = CI.getSourceManager();
     auto FileLoc = SM.getFileLoc(Loc);
@@ -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/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/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