zixuw updated this revision to Diff 418744.
zixuw marked 2 inline comments as done.
zixuw added a comment.

Rebase upstream changes in D122446 <https://reviews.llvm.org/D122446>:

- Move the change of the `objc_interface.m` test to D122446 
<https://reviews.llvm.org/D122446>.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D122511

Files:
  clang/include/clang/ExtractAPI/API.h
  clang/include/clang/ExtractAPI/DeclarationFragments.h
  clang/lib/ExtractAPI/API.cpp
  clang/lib/ExtractAPI/DeclarationFragments.cpp
  clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
  clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
  clang/test/ExtractAPI/objc_protocol.m

Index: clang/test/ExtractAPI/objc_protocol.m
===================================================================
--- /dev/null
+++ clang/test/ExtractAPI/objc_protocol.m
@@ -0,0 +1,146 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s@INPUT_DIR@%/t@g" %t/reference.output.json.in >> \
+// RUN: %t/reference.output.json
+// RUN: %clang -extract-api -x objective-c-header -target arm64-apple-macosx \
+// RUN: %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
+@protocol Protocol
+@end
+
+@protocol AnotherProtocol <Protocol>
+@end
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationhips": [
+    {
+      "kind": "conformsTo",
+      "source": "c:objc(pl)AnotherProtocol",
+      "target": "c:objc(pl)Protocol"
+    }
+  ],
+  "symbols": [
+    {
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@protocol"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Protocol"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(pl)Protocol"
+      },
+      "kind": {
+        "displayName": "Protocol",
+        "identifier": "objective-c.protocol"
+      },
+      "location": {
+        "character": 11,
+        "line": 1,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Protocol"
+          }
+        ],
+        "title": "Protocol"
+      }
+    },
+    {
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@protocol"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "AnotherProtocol"
+        },
+        {
+          "kind": "text",
+          "spelling": " <"
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:objc(pl)Protocol",
+          "spelling": "Protocol"
+        },
+        {
+          "kind": "text",
+          "spelling": ">"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(pl)AnotherProtocol"
+      },
+      "kind": {
+        "displayName": "Protocol",
+        "identifier": "objective-c.protocol"
+      },
+      "location": {
+        "character": 11,
+        "line": 4,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "AnotherProtocol"
+          }
+        ],
+        "title": "AnotherProtocol"
+      }
+    }
+  ]
+}
Index: clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
===================================================================
--- clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -392,6 +392,10 @@
     Kind["identifier"] = AddLangPrefix("class");
     Kind["displayName"] = "Class";
     break;
+  case APIRecord::RK_ObjCProtocol:
+    Kind["identifier"] = AddLangPrefix("protocol");
+    Kind["displayName"] = "Protocol";
+    break;
   }
 
   return Kind;
@@ -592,6 +596,10 @@
   for (const auto &ObjCInterface : API.getObjCInterfaces())
     serializeObjCContainerRecord(*ObjCInterface.second);
 
+  // Serialize Objective-C protocol records in the API set.
+  for (const auto &ObjCProtocol : API.getObjCProtocols())
+    serializeObjCContainerRecord(*ObjCProtocol.second);
+
   Root["symbols"] = std::move(Symbols);
   Root["relationhips"] = std::move(Relationships);
 
Index: clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
===================================================================
--- clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -260,6 +260,38 @@
     return true;
   }
 
+  bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
+    // Skip forward declaration for protocols (@protocol).
+    if (!Decl->isThisDeclarationADefinition())
+      return true;
+
+    // Collect symbol information.
+    StringRef Name = Decl->getName();
+    StringRef USR = API.recordUSR(Decl);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+    AvailabilityInfo Availability = getAvailability(Decl);
+    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, Availability, Comment, Declaration, SubHeading);
+
+    recordObjCMethods(ObjCProtocolRecord, Decl->methods());
+    recordObjCProperties(ObjCProtocolRecord, Decl->properties());
+    recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
+
+    return true;
+  }
+
 private:
   /// Get availability information of the declaration \p D.
   AvailabilityInfo getAvailability(const Decl *D) const {
Index: clang/lib/ExtractAPI/DeclarationFragments.cpp
===================================================================
--- clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -621,6 +621,35 @@
       .append(std::move(After));
 }
 
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(
+    const ObjCProtocolDecl *Protocol) {
+  DeclarationFragments Fragments;
+  // Build basic protocol declaration.
+  Fragments.append("@protocol", DeclarationFragments::FragmentKind::Keyword)
+      .appendSpace()
+      .append(Protocol->getName(),
+              DeclarationFragments::FragmentKind::Identifier);
+
+  // If this protocol conforms to other protocols, build the conformance list.
+  if (!Protocol->protocols().empty()) {
+    Fragments.append(" <", DeclarationFragments::FragmentKind::Text);
+    for (ObjCProtocolDecl::protocol_iterator It = Protocol->protocol_begin();
+         It != Protocol->protocol_end(); It++) {
+      // Add a leading comma if this is not the first protocol rendered.
+      if (It != Protocol->protocol_begin())
+        Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
+
+      SmallString<128> USR;
+      index::generateUSRForDecl(*It, USR);
+      Fragments.append((*It)->getName(),
+                       DeclarationFragments::FragmentKind::TypeIdentifier, USR);
+    }
+    Fragments.append(">", DeclarationFragments::FragmentKind::Text);
+  }
+
+  return Fragments;
+}
+
 template <typename FunctionT>
 FunctionSignature
 DeclarationFragmentsBuilder::getFunctionSignature(const FunctionT *Function) {
Index: clang/lib/ExtractAPI/API.cpp
===================================================================
--- clang/lib/ExtractAPI/API.cpp
+++ clang/lib/ExtractAPI/API.cpp
@@ -161,6 +161,20 @@
   return Container->Ivars.emplace_back(std::move(Record)).get();
 }
 
+ObjCProtocolRecord *APISet::addObjCProtocol(
+    StringRef Name, StringRef USR, PresumedLoc Loc,
+    const AvailabilityInfo &Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading) {
+  auto Result = ObjCProtocols.insert({Name, nullptr});
+  if (Result.second) {
+    // Create the record if it does not already exist.
+    auto Record = std::make_unique<ObjCProtocolRecord>(
+        Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
+    Result.first->second = std::move(Record);
+  }
+  return Result.first->second.get();
+}
+
 StringRef APISet::recordUSR(const Decl *D) {
   SmallString<128> USR;
   index::generateUSRForDecl(D, USR);
@@ -193,3 +207,4 @@
 void ObjCInstanceVariableRecord::anchor() {}
 void ObjCMethodRecord::anchor() {}
 void ObjCInterfaceRecord::anchor() {}
+void ObjCProtocolRecord::anchor() {}
Index: clang/include/clang/ExtractAPI/DeclarationFragments.h
===================================================================
--- clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ clang/include/clang/ExtractAPI/DeclarationFragments.h
@@ -217,6 +217,11 @@
   static DeclarationFragments
   getFragmentsForObjCProperty(const ObjCPropertyDecl *);
 
+  /// Build DeclarationFragments for an Objective-C protocol declaration
+  /// ObjCProtocolDecl.
+  static DeclarationFragments
+  getFragmentsForObjCProtocol(const ObjCProtocolDecl *);
+
   /// Build sub-heading fragments for a NamedDecl.
   static DeclarationFragments getSubHeading(const NamedDecl *);
 
Index: clang/include/clang/ExtractAPI/API.h
===================================================================
--- clang/include/clang/ExtractAPI/API.h
+++ clang/include/clang/ExtractAPI/API.h
@@ -82,6 +82,7 @@
     RK_ObjCIvar,
     RK_ObjCMethod,
     RK_ObjCInterface,
+    RK_ObjCProtocol,
   };
 
 private:
@@ -354,6 +355,25 @@
   virtual void anchor();
 };
 
+/// This holds information associated with Objective-C protocols.
+struct ObjCProtocolRecord : ObjCContainerRecord {
+  ObjCProtocolRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
+                     const AvailabilityInfo &Availability,
+                     const DocComment &Comment,
+                     DeclarationFragments Declaration,
+                     DeclarationFragments SubHeading)
+      : ObjCContainerRecord(RK_ObjCProtocol, Name, USR, Loc, Availability,
+                            LinkageInfo::none(), Comment, Declaration,
+                            SubHeading) {}
+
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_ObjCProtocol;
+  }
+
+private:
+  virtual void anchor();
+};
+
 /// APISet holds the set of API records collected from given inputs.
 class APISet {
 public:
@@ -497,6 +517,19 @@
       DeclarationFragments SubHeading,
       ObjCInstanceVariableRecord::AccessControl Access);
 
+  /// Create and add an Objective-C protocol 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.
+  ObjCProtocolRecord *addObjCProtocol(StringRef Name, StringRef USR,
+                                      PresumedLoc Loc,
+                                      const AvailabilityInfo &Availability,
+                                      const DocComment &Comment,
+                                      DeclarationFragments Declaration,
+                                      DeclarationFragments SubHeading);
+
   /// A map to store the set of GlobalRecord%s with the declaration name as the
   /// key.
   using GlobalRecordMap =
@@ -516,6 +549,11 @@
   using ObjCInterfaceRecordMap =
       llvm::MapVector<StringRef, std::unique_ptr<ObjCInterfaceRecord>>;
 
+  /// A map to store the set of ObjCProtocolRecord%s with the declaration name
+  /// as the key.
+  using ObjCProtocolRecordMap =
+      llvm::MapVector<StringRef, std::unique_ptr<ObjCProtocolRecord>>;
+
   /// Get the target triple for the ExtractAPI invocation.
   const llvm::Triple &getTarget() const { return Target; }
 
@@ -528,6 +566,9 @@
   const ObjCInterfaceRecordMap &getObjCInterfaces() const {
     return ObjCInterfaces;
   }
+  const ObjCProtocolRecordMap &getObjCProtocols() const {
+    return ObjCProtocols;
+  }
 
   /// Generate and store the USR of declaration \p D.
   ///
@@ -557,6 +598,7 @@
   EnumRecordMap Enums;
   StructRecordMap Structs;
   ObjCInterfaceRecordMap ObjCInterfaces;
+  ObjCProtocolRecordMap ObjCProtocols;
 };
 
 } // namespace extractapi
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to