[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