[clang] d1d34ba - [clang][extract-api] Add Objective-C protocol support

Zixu Wang via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 29 14:45:02 PDT 2022


Author: Zixu Wang
Date: 2022-03-29T14:44:49-07:00
New Revision: d1d34bafef56b732b461e12032eaf030e609f55a

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

LOG: [clang][extract-api] Add Objective-C protocol support

Add support for Objective-C protocol declarations in ExtractAPI.

Depends on D122446

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

Added: 
    clang/test/ExtractAPI/objc_protocol.m

Modified: 
    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

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index a2a462be42cd3..57397e7c256ea 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -82,6 +82,7 @@ struct APIRecord {
     RK_ObjCIvar,
     RK_ObjCMethod,
     RK_ObjCInterface,
+    RK_ObjCProtocol,
   };
 
 private:
@@ -354,6 +355,25 @@ struct ObjCInterfaceRecord : ObjCContainerRecord {
   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 @@ class APISet {
       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 @@ class APISet {
   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 @@ class APISet {
   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 @@ class APISet {
   EnumRecordMap Enums;
   StructRecordMap Structs;
   ObjCInterfaceRecordMap ObjCInterfaces;
+  ObjCProtocolRecordMap ObjCProtocols;
 };
 
 } // namespace extractapi

diff  --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h
index f147abb4f425c..ca5b014a1f5a5 100644
--- a/clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h
@@ -217,6 +217,11 @@ class DeclarationFragmentsBuilder {
   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 *);
 

diff  --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index 2456022523adb..1c9314c0be10d 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -161,6 +161,20 @@ ObjCInstanceVariableRecord *APISet::addObjCInstanceVariable(
   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 ObjCPropertyRecord::anchor() {}
 void ObjCInstanceVariableRecord::anchor() {}
 void ObjCMethodRecord::anchor() {}
 void ObjCInterfaceRecord::anchor() {}
+void ObjCProtocolRecord::anchor() {}

diff  --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index b35388ec52ad8..7c6c9dcde9eea 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -621,6 +621,35 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProperty(
       .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) {

diff  --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
index f8b1c9b20fb1e..bcc01dbd710d8 100644
--- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -260,6 +260,38 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     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 {

diff  --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index 3faf9cd751035..1acb67aac67de 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -393,6 +393,10 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
     Kind["identifier"] = AddLangPrefix("class");
     Kind["displayName"] = "Class";
     break;
+  case APIRecord::RK_ObjCProtocol:
+    Kind["identifier"] = AddLangPrefix("protocol");
+    Kind["displayName"] = "Protocol";
+    break;
   }
 
   return Kind;
@@ -593,6 +597,10 @@ Object SymbolGraphSerializer::serialize() {
   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);
 

diff  --git a/clang/test/ExtractAPI/objc_protocol.m b/clang/test/ExtractAPI/objc_protocol.m
new file mode 100644
index 0000000000000..1b6c55ec474d8
--- /dev/null
+++ b/clang/test/ExtractAPI/objc_protocol.m
@@ -0,0 +1,146 @@
+// 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 -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
+ at protocol Protocol
+ at end
+
+ at protocol AnotherProtocol <Protocol>
+ at 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"
+      }
+    }
+  ]
+}


        


More information about the cfe-commits mailing list