[clang] 9fc45ca - [clang][extract-api] Add support for typedefs

Daniel Grumberg via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 6 11:16:33 PDT 2022


Author: Daniel Grumberg
Date: 2022-04-06T19:14:05+01:00
New Revision: 9fc45ca00a19336c0724631aa1b1985dd4f4d536

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

LOG: [clang][extract-api] Add support for typedefs

Typedef records consist of the symbol associated with the underlying
TypedefDecl and a SymbolReference to the underlying type. Additionally
typedefs for anonymous TagTypes use the typedef'd name as the symbol
name in their respective records and USRs. As a result the declaration
fragments for the anonymous TagType are those for the associated
typedef. This means that when the user is defining a typedef to a
typedef to a anonymous type, we use a reference the anonymous TagType
itself and do not emit the typedef to the anonymous type in the
generated symbol graph, including in the type destination of further
typedef symbol records.

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

Added: 
    clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
    clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h
    clang/test/ExtractAPI/typedef.c
    clang/test/ExtractAPI/typedef_anonymous_record.c
    clang/test/ExtractAPI/typedef_chain.c

Modified: 
    clang/include/clang/ExtractAPI/API.h
    clang/include/clang/ExtractAPI/DeclarationFragments.h
    clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
    clang/lib/ExtractAPI/API.cpp
    clang/lib/ExtractAPI/CMakeLists.txt
    clang/lib/ExtractAPI/DeclarationFragments.cpp
    clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
    clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index 62e30b1d4c57b..827cde953c31c 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -88,6 +88,7 @@ struct APIRecord {
     RK_ObjCInterface,
     RK_ObjCProtocol,
     RK_MacroDefinition,
+    RK_Typedef,
   };
 
 private:
@@ -396,6 +397,30 @@ struct MacroDefinitionRecord : APIRecord {
   virtual void anchor();
 };
 
+/// This holds information associated with typedefs.
+///
+/// Note: Typedefs for anonymous enums and structs typically don't get emitted
+/// by the serializers but still get a TypedefRecord. Instead we use the
+/// typedef name as a name for the underlying anonymous struct or enum.
+struct TypedefRecord : APIRecord {
+  SymbolReference UnderlyingType;
+
+  TypedefRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
+                const AvailabilityInfo &Availability, const DocComment &Comment,
+                DeclarationFragments Declaration,
+                DeclarationFragments SubHeading, SymbolReference UnderlyingType)
+      : APIRecord(RK_Typedef, Name, USR, Loc, Availability, LinkageInfo(),
+                  Comment, Declaration, SubHeading),
+        UnderlyingType(UnderlyingType) {}
+
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_Typedef;
+  }
+
+private:
+  virtual void anchor();
+};
+
 /// APISet holds the set of API records collected from given inputs.
 class APISet {
 public:
@@ -564,6 +589,19 @@ class APISet {
                                             DeclarationFragments Declaration,
                                             DeclarationFragments SubHeading);
 
+  /// Create a typedef record into the API set.
+  ///
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  TypedefRecord *addTypedef(StringRef Name, StringRef USR, PresumedLoc Loc,
+                            const AvailabilityInfo &Availability,
+                            const DocComment &Comment,
+                            DeclarationFragments Declaration,
+                            DeclarationFragments SubHeading,
+                            SymbolReference UnderlyingType);
+
   /// A mapping type to store a set of APIRecord%s with the declaration name as
   /// the key.
   template <typename RecordTy,
@@ -587,6 +625,7 @@ class APISet {
     return ObjCProtocols;
   }
   const RecordMap<MacroDefinitionRecord> &getMacros() const { return Macros; }
+  const RecordMap<TypedefRecord> &getTypedefs() const { return Typedefs; }
 
   /// Generate and store the USR of declaration \p D.
   ///
@@ -626,6 +665,7 @@ class APISet {
   RecordMap<ObjCInterfaceRecord> ObjCInterfaces;
   RecordMap<ObjCProtocolRecord> ObjCProtocols;
   RecordMap<MacroDefinitionRecord> Macros;
+  RecordMap<TypedefRecord> Typedefs;
 };
 
 } // namespace extractapi

diff  --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h
index 7f6e3311125aa..80fb5faf68783 100644
--- a/clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h
@@ -230,6 +230,10 @@ class DeclarationFragmentsBuilder {
   static DeclarationFragments getFragmentsForMacro(StringRef Name,
                                                    const MacroDirective *MD);
 
+  /// Build DeclarationFragments for a typedef \p TypedefNameDecl.
+  static DeclarationFragments
+  getFragmentsForTypedef(const TypedefNameDecl *Decl);
+
   /// Build sub-heading fragments for a NamedDecl.
   static DeclarationFragments getSubHeading(const NamedDecl *);
 

diff  --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
index 7b55fd788f6d0..6c9611acd132a 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
@@ -146,6 +146,9 @@ class SymbolGraphSerializer : public APISerializer {
   /// Serialize a macro defintion record.
   void serializeMacroDefinitionRecord(const MacroDefinitionRecord &Record);
 
+  /// Serialize a typedef record.
+  void serializeTypedefRecord(const TypedefRecord &Record);
+
   /// Push a component to the current path components stack.
   ///
   /// \param Component The component to push onto the path components stack.

diff  --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index 66ebbb5f0e999..6d5ac8a1b40c9 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -170,6 +170,17 @@ APISet::addMacroDefinition(StringRef Name, StringRef USR, PresumedLoc Loc,
   return addTopLevelRecord(Macros, Name, USR, Loc, Declaration, SubHeading);
 }
 
+TypedefRecord *APISet::addTypedef(StringRef Name, StringRef USR,
+                                  PresumedLoc Loc,
+                                  const AvailabilityInfo &Availability,
+                                  const DocComment &Comment,
+                                  DeclarationFragments Declaration,
+                                  DeclarationFragments SubHeading,
+                                  SymbolReference UnderlyingType) {
+  return addTopLevelRecord(Typedefs, Name, USR, Loc, Availability, Comment,
+                           Declaration, SubHeading, UnderlyingType);
+}
+
 StringRef APISet::recordUSR(const Decl *D) {
   SmallString<128> USR;
   index::generateUSRForDecl(D, USR);
@@ -211,3 +222,4 @@ void ObjCMethodRecord::anchor() {}
 void ObjCInterfaceRecord::anchor() {}
 void ObjCProtocolRecord::anchor() {}
 void MacroDefinitionRecord::anchor() {}
+void TypedefRecord::anchor() {}

diff  --git a/clang/lib/ExtractAPI/CMakeLists.txt b/clang/lib/ExtractAPI/CMakeLists.txt
index f194ae342c20f..ed79282c14906 100644
--- a/clang/lib/ExtractAPI/CMakeLists.txt
+++ b/clang/lib/ExtractAPI/CMakeLists.txt
@@ -8,6 +8,7 @@ add_clang_library(clangExtractAPI
   DeclarationFragments.cpp
   Serialization/SerializerBase.cpp
   Serialization/SymbolGraphSerializer.cpp
+  TypedefUnderlyingTypeResolver.cpp
 
   LINK_LIBS
   clangAST

diff  --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index 5997a8faef14c..4e0e8da99972c 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ExtractAPI/DeclarationFragments.h"
+#include "TypedefUnderlyingTypeResolver.h"
 #include "clang/Index/USRGeneration.h"
 #include "llvm/ADT/StringSwitch.h"
 
@@ -250,6 +251,31 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
     return Fragments.append(Base.getAsString(),
                             DeclarationFragments::FragmentKind::Keyword);
 
+  // If the type is a typedefed type, get the underlying TypedefNameDecl for a
+  // direct reference to the typedef instead of the wrapped type.
+  if (const TypedefType *TypedefTy = dyn_cast<TypedefType>(T)) {
+    const TypedefNameDecl *Decl = TypedefTy->getDecl();
+    std::string USR =
+        TypedefUnderlyingTypeResolver(Context).getUSRForType(QualType(T, 0));
+    return Fragments.append(Decl->getName(),
+                            DeclarationFragments::FragmentKind::TypeIdentifier,
+                            USR);
+  }
+
+  // If the base type is a TagType (struct/interface/union/class/enum), let's
+  // get the underlying Decl for better names and USRs.
+  if (const TagType *TagTy = dyn_cast<TagType>(Base)) {
+    const TagDecl *Decl = TagTy->getDecl();
+    // Anonymous decl, skip this fragment.
+    if (Decl->getName().empty())
+      return Fragments;
+    SmallString<128> TagUSR;
+    clang::index::generateUSRForDecl(Decl, TagUSR);
+    return Fragments.append(Decl->getName(),
+                            DeclarationFragments::FragmentKind::TypeIdentifier,
+                            TagUSR);
+  }
+
   // If the base type is an ObjCInterfaceType, use the underlying
   // ObjCInterfaceDecl for the true USR.
   if (const auto *ObjCIT = dyn_cast<ObjCInterfaceType>(Base)) {
@@ -426,8 +452,8 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForEnumConstant(
 
 DeclarationFragments
 DeclarationFragmentsBuilder::getFragmentsForEnum(const EnumDecl *EnumDecl) {
-  // TODO: After we support typedef records, if there's a typedef for this enum
-  // just use the declaration fragments of the typedef decl.
+  if (const auto *TypedefNameDecl = EnumDecl->getTypedefNameForAnonDecl())
+    return getFragmentsForTypedef(TypedefNameDecl);
 
   DeclarationFragments Fragments, After;
   Fragments.append("enum", DeclarationFragments::FragmentKind::Keyword);
@@ -457,8 +483,8 @@ DeclarationFragmentsBuilder::getFragmentsForField(const FieldDecl *Field) {
 
 DeclarationFragments
 DeclarationFragmentsBuilder::getFragmentsForStruct(const RecordDecl *Record) {
-  // TODO: After we support typedef records, if there's a typedef for this
-  // struct just use the declaration fragments of the typedef decl.
+  if (const auto *TypedefNameDecl = Record->getTypedefNameForAnonDecl())
+    return getFragmentsForTypedef(TypedefNameDecl);
 
   DeclarationFragments Fragments;
   Fragments.append("struct", DeclarationFragments::FragmentKind::Keyword);
@@ -680,6 +706,20 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(
   return Fragments;
 }
 
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForTypedef(
+    const TypedefNameDecl *Decl) {
+  DeclarationFragments Fragments, After;
+  Fragments.append("typedef", DeclarationFragments::FragmentKind::Keyword)
+      .appendSpace()
+      .append(getFragmentsForType(Decl->getUnderlyingType(),
+                                  Decl->getASTContext(), After))
+      .append(std::move(After))
+      .appendSpace()
+      .append(Decl->getName(), DeclarationFragments::FragmentKind::Identifier);
+
+  return Fragments;
+}
+
 template <typename FunctionT>
 FunctionSignature
 DeclarationFragmentsBuilder::getFunctionSignature(const FunctionT *Function) {

diff  --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
index e4ae0403f260f..493d490674b1c 100644
--- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -12,6 +12,7 @@
 ///
 //===----------------------------------------------------------------------===//
 
+#include "TypedefUnderlyingTypeResolver.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
@@ -41,6 +42,13 @@ using namespace extractapi;
 
 namespace {
 
+StringRef getTypedefName(const TagDecl *Decl) {
+  if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
+    return TypedefDecl->getName();
+
+  return {};
+}
+
 /// The RecursiveASTVisitor to traverse symbol declarations and collect API
 /// information.
 class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
@@ -161,6 +169,8 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
 
     // Collect symbol information.
     StringRef Name = Decl->getName();
+    if (Name.empty())
+      Name = getTypedefName(Decl);
     StringRef USR = API.recordUSR(Decl);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
@@ -196,6 +206,8 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
 
     // Collect symbol information.
     StringRef Name = Decl->getName();
+    if (Name.empty())
+      Name = getTypedefName(Decl);
     StringRef USR = API.recordUSR(Decl);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
@@ -296,6 +308,36 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     return true;
   }
 
+  bool VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
+    // Skip ObjC Type Parameter for now.
+    if (isa<ObjCTypeParamDecl>(Decl))
+      return true;
+
+    if (!Decl->isDefinedOutsideFunctionOrMethod())
+      return true;
+
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+    StringRef Name = Decl->getName();
+    AvailabilityInfo Availability = getAvailability(Decl);
+    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, Availability, Comment,
+                   DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
+                   DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef);
+
+    return true;
+  }
+
 private:
   /// Get availability information of the declaration \p D.
   AvailabilityInfo getAvailability(const Decl *D) const {

diff  --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index 1440ca358d4b5..f38a5bbae7d00 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -408,6 +408,11 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
   case APIRecord::RK_MacroDefinition:
     Kind["identifier"] = AddLangPrefix("macro");
     Kind["displayName"] = "Macro";
+    break;
+  case APIRecord::RK_Typedef:
+    Kind["identifier"] = AddLangPrefix("typealias");
+    Kind["displayName"] = "Type Alias";
+    break;
   }
 
   return Kind;
@@ -618,6 +623,27 @@ void SymbolGraphSerializer::serializeMacroDefinitionRecord(
   Symbols.emplace_back(std::move(*Macro));
 }
 
+void SymbolGraphSerializer::serializeTypedefRecord(
+    const TypedefRecord &Record) {
+  // Typedefs of anonymous types have their entries unified with the underlying
+  // type.
+  bool ShouldDrop = Record.UnderlyingType.Name.empty();
+  // enums declared with `NS_OPTION` have a named enum and a named typedef, with
+  // the same name
+  ShouldDrop |= (Record.UnderlyingType.Name == Record.Name);
+  if (ShouldDrop)
+    return;
+
+  auto TypedefPathComponentGuard = makePathComponentGuard(Record.Name);
+  auto Typedef = serializeAPIRecord(Record);
+  if (!Typedef)
+    return;
+
+  (*Typedef)["type"] = Record.UnderlyingType.USR;
+
+  Symbols.emplace_back(std::move(*Typedef));
+}
+
 SymbolGraphSerializer::PathComponentGuard
 SymbolGraphSerializer::makePathComponentGuard(StringRef Component) {
   return PathComponentGuard(PathComponents, Component);
@@ -651,6 +677,9 @@ Object SymbolGraphSerializer::serialize() {
   for (const auto &Macro : API.getMacros())
     serializeMacroDefinitionRecord(*Macro.second);
 
+  for (const auto &Typedef : API.getTypedefs())
+    serializeTypedefRecord(*Typedef.second);
+
   Root["symbols"] = std::move(Symbols);
   Root["relationships"] = std::move(Relationships);
 

diff  --git a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
new file mode 100644
index 0000000000000..9c165e693e0e7
--- /dev/null
+++ b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
@@ -0,0 +1,79 @@
+//===- ExtractAPI/TypedefUnderlyingTypeResolver.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 UnderlyingTypeResolver.
+///
+//===----------------------------------------------------------------------===//
+
+#include "TypedefUnderlyingTypeResolver.h"
+#include "clang/Index/USRGeneration.h"
+
+using namespace clang;
+using namespace extractapi;
+
+namespace {
+
+const NamedDecl *getUnderlyingTypeDecl(QualType Type) {
+  const NamedDecl *TypeDecl = nullptr;
+
+  const TypedefType *TypedefTy = Type->getAs<TypedefType>();
+  if (TypedefTy)
+    TypeDecl = TypedefTy->getDecl();
+  if (const TagType *TagTy = Type->getAs<TagType>()) {
+    TypeDecl = TagTy->getDecl();
+  } else if (const ObjCInterfaceType *ObjCITy =
+                 Type->getAs<ObjCInterfaceType>()) {
+    TypeDecl = ObjCITy->getDecl();
+  }
+
+  if (TypeDecl && TypedefTy) {
+    // if this is a typedef to another typedef, use the typedef's decl for the
+    // USR - this will actually be in the output, unlike a typedef to an
+    // anonymous decl
+    const TypedefNameDecl *TypedefDecl = TypedefTy->getDecl();
+    if (TypedefDecl->getUnderlyingType()->isTypedefNameType())
+      TypeDecl = TypedefDecl;
+  }
+
+  return TypeDecl;
+}
+
+} // namespace
+
+SymbolReference
+TypedefUnderlyingTypeResolver::getSymbolReferenceForType(QualType Type,
+                                                         APISet &API) const {
+  std::string TypeName = Type.getAsString();
+  SmallString<128> TypeUSR;
+  const NamedDecl *TypeDecl = getUnderlyingTypeDecl(Type);
+  const TypedefType *TypedefTy = Type->getAs<TypedefType>();
+
+  if (TypeDecl) {
+    if (!TypedefTy)
+      TypeName = TypeDecl->getName().str();
+
+    clang::index::generateUSRForDecl(TypeDecl, TypeUSR);
+  } else {
+    clang::index::generateUSRForType(Type, Context, TypeUSR);
+  }
+
+  return {API.copyString(TypeName), API.copyString(TypeUSR)};
+}
+
+std::string TypedefUnderlyingTypeResolver::getUSRForType(QualType Type) const {
+  SmallString<128> TypeUSR;
+  const NamedDecl *TypeDecl = getUnderlyingTypeDecl(Type);
+
+  if (TypeDecl)
+    clang::index::generateUSRForDecl(TypeDecl, TypeUSR);
+  else
+    clang::index::generateUSRForType(Type, Context, TypeUSR);
+
+  return std::string(TypeUSR);
+}

diff  --git a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h
new file mode 100644
index 0000000000000..0096ff2359148
--- /dev/null
+++ b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h
@@ -0,0 +1,46 @@
+//===- ExtractAPI/TypedefUnderlyingTypeResolver.h ---------------*- 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 defines the UnderlyingTypeResolver which is a helper type for
+/// resolving the undelrying type for a given QualType and exposing that
+/// information in various forms.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_UNDERLYING_TYPE_RESOLVER_H
+#define LLVM_CLANG_UNDERLYING_TYPE_RESOLVER_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/ExtractAPI/API.h"
+
+#include <string>
+
+namespace clang {
+namespace extractapi {
+
+struct TypedefUnderlyingTypeResolver {
+
+  /// Get a SymbolReference for the given type.
+  SymbolReference getSymbolReferenceForType(QualType Type, APISet &API) const;
+
+  /// Get a USR for the given type.
+  std::string getUSRForType(QualType Type) const;
+
+  explicit TypedefUnderlyingTypeResolver(ASTContext &Context)
+      : Context(Context) {}
+
+private:
+  ASTContext &Context;
+};
+
+} // namespace extractapi
+} // namespace clang
+
+#endif // LLVM_CLANG_UNDERLYING_TYPE_RESOLVER_H

diff  --git a/clang/test/ExtractAPI/typedef.c b/clang/test/ExtractAPI/typedef.c
new file mode 100644
index 0000000000000..95a4a3d9663a9
--- /dev/null
+++ b/clang/test/ExtractAPI/typedef.c
@@ -0,0 +1,101 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%/t at g" %t/reference.output.json.in >> \
+// RUN: %t/reference.output.json
+// RUN: %clang -extract-api --product-name=Typedef -target arm64-apple-macosx \
+// RUN: -x objective-c-header %t/input.h -o %t/output.json | FileCheck -allow-empty %s
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- input.h
+typedef int MyInt;
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "Typedef",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyInt"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:input.h at T@MyInt"
+      },
+      "kind": {
+        "displayName": "Type Alias",
+        "identifier": "objective-c.typealias"
+      },
+      "location": {
+        "position": {
+          "character": 13,
+          "line": 1
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "MyInt"
+          }
+        ],
+        "title": "MyInt"
+      },
+      "pathComponents": [
+        "MyInt"
+      ],
+      "type": "c:I"
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/typedef_anonymous_record.c b/clang/test/ExtractAPI/typedef_anonymous_record.c
new file mode 100644
index 0000000000000..80eff7cd14062
--- /dev/null
+++ b/clang/test/ExtractAPI/typedef_anonymous_record.c
@@ -0,0 +1,203 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%/t at g" %t/reference.output.json.in >> \
+// RUN: %t/reference.output.json
+// RUN: %clang -extract-api --product-name=TypedefChain -target arm64-apple-macosx \
+// RUN: -x objective-c-header %t/input.h -o %t/output.json | FileCheck -allow-empty %s
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- input.h
+typedef struct { } MyStruct;
+typedef MyStruct MyStructStruct;
+typedef MyStructStruct MyStructStructStruct;
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "TypedefChain",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "struct"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyStruct"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:@SA at MyStruct"
+      },
+      "kind": {
+        "displayName": "Structure",
+        "identifier": "objective-c.struct"
+      },
+      "location": {
+        "position": {
+          "character": 9,
+          "line": 1
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "title": "MyStruct"
+      },
+      "pathComponents": [
+        "MyStruct"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:@SA at MyStruct",
+          "spelling": "MyStruct"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyStructStruct"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:input.h at T@MyStructStruct"
+      },
+      "kind": {
+        "displayName": "Type Alias",
+        "identifier": "objective-c.typealias"
+      },
+      "location": {
+        "position": {
+          "character": 18,
+          "line": 2
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "MyStructStruct"
+          }
+        ],
+        "title": "MyStructStruct"
+      },
+      "pathComponents": [
+        "MyStructStruct"
+      ],
+      "type": "c:@SA at MyStruct"
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:input.h at T@MyStructStruct",
+          "spelling": "MyStructStruct"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyStructStructStruct"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:input.h at T@MyStructStructStruct"
+      },
+      "kind": {
+        "displayName": "Type Alias",
+        "identifier": "objective-c.typealias"
+      },
+      "location": {
+        "position": {
+          "character": 24,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "MyStructStructStruct"
+          }
+        ],
+        "title": "MyStructStructStruct"
+      },
+      "pathComponents": [
+        "MyStructStructStruct"
+      ],
+      "type": "c:input.h at T@MyStructStruct"
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/typedef_chain.c b/clang/test/ExtractAPI/typedef_chain.c
new file mode 100644
index 0000000000000..52f2f75f0ded8
--- /dev/null
+++ b/clang/test/ExtractAPI/typedef_chain.c
@@ -0,0 +1,211 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%/t at g" %t/reference.output.json.in >> \
+// RUN: %t/reference.output.json
+// RUN: %clang -extract-api --product-name=TypedefChain -target arm64-apple-macosx \
+// RUN: -x objective-c-header %t/input.h -o %t/output.json | FileCheck -allow-empty %s
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- input.h
+typedef int MyInt;
+typedef MyInt MyIntInt;
+typedef MyIntInt MyIntIntInt;
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "TypedefChain",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyInt"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:input.h at T@MyInt"
+      },
+      "kind": {
+        "displayName": "Type Alias",
+        "identifier": "objective-c.typealias"
+      },
+      "location": {
+        "position": {
+          "character": 13,
+          "line": 1
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "MyInt"
+          }
+        ],
+        "title": "MyInt"
+      },
+      "pathComponents": [
+        "MyInt"
+      ],
+      "type": "c:I"
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:input.h at T@MyInt",
+          "spelling": "MyInt"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyIntInt"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:input.h at T@MyIntInt"
+      },
+      "kind": {
+        "displayName": "Type Alias",
+        "identifier": "objective-c.typealias"
+      },
+      "location": {
+        "position": {
+          "character": 15,
+          "line": 2
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "MyIntInt"
+          }
+        ],
+        "title": "MyIntInt"
+      },
+      "pathComponents": [
+        "MyIntInt"
+      ],
+      "type": "c:input.h at T@MyInt"
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:input.h at T@MyIntInt",
+          "spelling": "MyIntInt"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyIntIntInt"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:input.h at T@MyIntIntInt"
+      },
+      "kind": {
+        "displayName": "Type Alias",
+        "identifier": "objective-c.typealias"
+      },
+      "location": {
+        "position": {
+          "character": 18,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "MyIntIntInt"
+          }
+        ],
+        "title": "MyIntIntInt"
+      },
+      "pathComponents": [
+        "MyIntIntInt"
+      ],
+      "type": "c:input.h at T@MyIntInt"
+    }
+  ]
+}


        


More information about the cfe-commits mailing list