diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h --- a/clang/include/clang/ExtractAPI/API.h +++ b/clang/include/clang/ExtractAPI/API.h @@ -71,6 +71,7 @@ RK_ObjCInstanceMethod, RK_ObjCInterface, RK_ObjCCategory, + RK_ObjCCategoryModule, RK_ObjCProtocol, RK_MacroDefinition, RK_Typedef, @@ -144,6 +145,9 @@ Comment(Comment), Declaration(Declaration), SubHeading(SubHeading), IsFromSystemHeader(IsFromSystemHeader), Kind(Kind) {} + APIRecord(RecordKind Kind, StringRef USR, StringRef Name) + : USR(USR), Name(Name), Kind(Kind) {} + // Pure virtual destructor to make APIRecord abstract virtual ~APIRecord() = 0; }; @@ -468,6 +472,8 @@ /// This holds information associated with Objective-C categories. struct ObjCCategoryRecord : ObjCContainerRecord { SymbolReference Interface; + /// Determine whether the Category is derived from external class interface. + bool IsFromExternalModule = false; ObjCCategoryRecord(StringRef USR, StringRef Name, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, @@ -488,6 +494,21 @@ virtual void anchor(); }; +struct ObjCCategoryModuleRecord : APIRecord { + // ObjCCategoryRecord%s are stored in and owned by APISet. + SmallVector Categories; + + ObjCCategoryModuleRecord(StringRef USR, StringRef Name) + : APIRecord(RK_ObjCCategoryModule, USR, Name) {} + + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_ObjCCategoryModule; + } + +private: + virtual void anchor(); +}; + /// This holds information associated with Objective-C interfaces/classes. struct ObjCInterfaceRecord : ObjCContainerRecord { SymbolReference SuperClass; @@ -668,6 +689,9 @@ DeclarationFragments SubHeading, bool IsFromSystemHeader); + ObjCCategoryModuleRecord *addObjCCategoryModule(StringRef Name, + StringRef USR); + /// Create and add an Objective-C category record into the API set. /// /// Note: the caller is responsible for keeping the StringRef \p Name and @@ -679,7 +703,7 @@ AvailabilitySet Availability, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, SymbolReference Interface, - bool IsFromSystemHeader); + bool IsFromSystemHeader, bool IsFromExternalModule); /// Create and add an Objective-C interface record into the API set. /// @@ -795,6 +819,9 @@ const RecordMap &getObjCCategories() const { return ObjCCategories; } + const RecordMap &getObjCCategoryModule() const { + return ObjCCategoryModule; + } const RecordMap &getObjCInterfaces() const { return ObjCInterfaces; } @@ -848,6 +875,7 @@ RecordMap Enums; RecordMap Structs; RecordMap ObjCCategories; + RecordMap ObjCCategoryModule; RecordMap ObjCInterfaces; RecordMap ObjCProtocols; RecordMap Macros; diff --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h --- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h +++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h @@ -125,6 +125,15 @@ } } +template +static bool isExternalCategory(const T &Records, const StringRef &Name) { + for (const auto &Record : Records) { + if (Name == Record.second.get()->Name) + return false; + } + return true; +} + template bool ExtractAPIVisitorBase::VisitVarDecl(const VarDecl *Decl) { // skip function parameters. @@ -484,9 +493,16 @@ SymbolReference Interface(InterfaceDecl->getName(), API.recordUSR(InterfaceDecl)); + bool IsFromExternalModule = + isExternalCategory(API.getObjCInterfaces(), InterfaceDecl->getName()); + + if (IsFromExternalModule) + API.addObjCCategoryModule(InterfaceDecl->getName(), + API.recordUSR(InterfaceDecl)); + ObjCCategoryRecord *ObjCCategoryRecord = API.addObjCCategory( Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading, - Interface, isInSystemHeader(Decl)); + Interface, isInSystemHeader(Decl), IsFromExternalModule); getDerivedExtractAPIVisitor().recordObjCMethods(ObjCCategoryRecord, Decl->methods()); diff --git a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h --- a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h +++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h @@ -35,6 +35,10 @@ getDerived()->traverseObjCProtocols(); + getDerived()->traverseObjCCategories(); + + getDerived()->traverseObjCCategoryModule(); + getDerived()->traverseMacroDefinitionRecords(); getDerived()->traverseTypedefRecords(); @@ -70,6 +74,16 @@ getDerived()->visitObjCContainerRecord(*Protocol.second); } + void traverseObjCCategories() { + for (const auto &Category : API.getObjCCategories()) + getDerived()->visitObjCCategoryRecord(*Category.second); + } + + void traverseObjCCategoryModule() { + for (const auto &CategoryModule : API.getObjCCategoryModule()) + getDerived()->visitObjCCategoryModuleRecord(*CategoryModule.second); + } + void traverseMacroDefinitionRecords() { for (const auto &Macro : API.getMacros()) getDerived()->visitMacroDefinitionRecord(*Macro.second); @@ -95,6 +109,12 @@ /// Visit an Objective-C container record. void visitObjCContainerRecord(const ObjCContainerRecord &Record){}; + /// Visit an Objective-C category record. + void visitObjCCategoryRecord(const ObjCCategoryRecord &Record){}; + + /// Visit an Objective-C category module record. + void visitObjCCategoryModuleRecord(const ObjCCategoryModuleRecord &Record){}; + /// Visit a macro definition record. void visitMacroDefinitionRecord(const MacroDefinitionRecord &Record){}; diff --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h --- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h +++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h @@ -163,6 +163,12 @@ /// Visit an Objective-C container record. void visitObjCContainerRecord(const ObjCContainerRecord &Record); + /// Visit an Objective-C category record. + void visitObjCCategoryRecord(const ObjCCategoryRecord &Record); + + /// Visit an Objective-C category module record. + void visitObjCCategoryModuleRecord(const ObjCCategoryModuleRecord &Record); + /// Visit a macro definition record. void visitMacroDefinitionRecord(const MacroDefinitionRecord &Record); diff --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp --- a/clang/lib/ExtractAPI/API.cpp +++ b/clang/lib/ExtractAPI/API.cpp @@ -120,22 +120,39 @@ SubHeading, IsFromSystemHeader); } +ObjCCategoryModuleRecord *APISet::addObjCCategoryModule(StringRef Name, + StringRef USR) { + // Create the category record. + auto *Record = + addTopLevelRecord(USRBasedLookupTable, ObjCCategoryModule, USR, Name); + + return Record; +} + ObjCCategoryRecord *APISet::addObjCCategory( StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, - SymbolReference Interface, bool IsFromSystemHeader) { + SymbolReference Interface, bool IsFromSystemHeader, + bool IsFromExternalModule) { // Create the category record. auto *Record = addTopLevelRecord(USRBasedLookupTable, ObjCCategories, USR, Name, Loc, std::move(Availabilities), Comment, Declaration, SubHeading, Interface, IsFromSystemHeader); - // If this category is extending a known interface, associate it with the - // ObjCInterfaceRecord. - auto It = ObjCInterfaces.find(Interface.USR); - if (It != ObjCInterfaces.end()) - It->second->Categories.push_back(Record); + Record->IsFromExternalModule = IsFromExternalModule; + // If this category is extending an external module, associate it with that + // module. + if (IsFromExternalModule) { + auto It = ObjCCategoryModule.find(Interface.USR); + if (It != ObjCCategoryModule.end()) + It->second->Categories.push_back(Record); + } else { + auto It = ObjCInterfaces.find(Interface.USR); + if (It != ObjCInterfaces.end()) + It->second->Categories.push_back(Record); + } return Record; } @@ -295,6 +312,7 @@ void ObjCInstanceMethodRecord::anchor() {} void ObjCClassMethodRecord::anchor() {} void ObjCCategoryRecord::anchor() {} +void ObjCCategoryModuleRecord::anchor() {} void ObjCInterfaceRecord::anchor() {} void ObjCProtocolRecord::anchor() {} void MacroDefinitionRecord::anchor() {} diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp --- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp +++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -319,7 +319,13 @@ /// Objective-C methods). Can be used as sub-headings for documentation. Object serializeNames(const APIRecord &Record) { Object Names; - Names["title"] = Record.Name; + if (auto *CategoryRecord = + dyn_cast_or_null(&Record)) + Names["title"] = + (CategoryRecord->Interface.Name + " (" + Record.Name + ")").str(); + else + Names["title"] = Record.Name; + serializeArray(Names, "subHeading", serializeDeclarationFragments(Record.SubHeading)); DeclarationFragments NavigatorFragments; @@ -391,9 +397,12 @@ Kind["displayName"] = "Class"; break; case APIRecord::RK_ObjCCategory: - // We don't serialize out standalone Objective-C category symbols yet. - llvm_unreachable("Serializing standalone Objective-C category symbols is " - "not supported."); + Kind["identifier"] = AddLangPrefix("class.extension"); + Kind["displayName"] = "Class Extension"; + break; + case APIRecord::RK_ObjCCategoryModule: + Kind["identifier"] = AddLangPrefix("module.extension"); + Kind["displayName"] = "Module Extension"; break; case APIRecord::RK_ObjCProtocol: Kind["identifier"] = AddLangPrefix("protocol"); @@ -735,6 +744,42 @@ } } +void SymbolGraphSerializer::visitObjCCategoryModuleRecord( + const ObjCCategoryModuleRecord &Record) { + Object Obj; + serializeObject(Obj, "identifier", + serializeIdentifier(Record, API.getLanguage())); + serializeObject( + Obj, "kind", + serializeSymbolKind(APIRecord::RK_ObjCCategoryModule, API.getLanguage())); + Obj["accessLevel"] = "public"; + Symbols.emplace_back(std::move(Obj)); + + // If the category is extended from an external module, it is then member of + // that module. + for (const auto &Category : Record.Categories) + serializeRelationship(RelationshipKind::MemberOf, *Category, Record); +} + +void SymbolGraphSerializer::visitObjCCategoryRecord( + const ObjCCategoryRecord &Record) { + if (!Record.IsFromExternalModule) + return; + + auto ObjCCategory = serializeAPIRecord(Record); + + if (!ObjCCategory) + return; + + Symbols.emplace_back(std::move(*ObjCCategory)); + serializeMembers(Record, Record.Methods); + serializeMembers(Record, Record.Properties); + + // Surface the protocols of the category to the interface. + for (const auto &Protocol : Record.Protocols) + serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol); +} + void SymbolGraphSerializer::visitMacroDefinitionRecord( const MacroDefinitionRecord &Record) { auto Macro = serializeAPIRecord(Record); @@ -767,6 +812,12 @@ case APIRecord::RK_ObjCProtocol: visitObjCContainerRecord(*cast(Record)); break; + case APIRecord::RK_ObjCCategory: + visitObjCCategoryRecord(*cast(Record)); + break; + case APIRecord::RK_ObjCCategoryModule: + visitObjCCategoryModuleRecord(*cast(Record)); + break; case APIRecord::RK_MacroDefinition: visitMacroDefinitionRecord(*cast(Record)); break; @@ -835,9 +886,6 @@ if (!Record) return {}; - if (isa(Record)) - return {}; - Object Root; APIIgnoresList EmptyIgnores; SymbolGraphSerializer Serializer(API, EmptyIgnores, diff --git a/clang/test/ExtractAPI/objc_module_category.m b/clang/test/ExtractAPI/objc_module_category.m new file mode 100644 --- /dev/null +++ b/clang/test/ExtractAPI/objc_module_category.m @@ -0,0 +1,399 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \ +// RUN: %t/reference.output.json.in >> %t/reference.output.json +// RUN: %clang -extract-api -x objective-c-header \ +// RUN: -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 +#import +/// Doc comment 1 +@interface NSString (Category1) +-(void)method1; +@end + +/// Doc comment 2 +@interface NSString (Category2) +-(void)method2; +@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" + } + }, + "relationships": [ + { + "kind": "memberOf", + "source": "c:objc(cs)NSString(im)method1", + "target": "c:objc(cy)NSString@Category1", + "targetFallback": "Category1" + }, + { + "kind": "memberOf", + "source": "c:objc(cs)NSString(im)method2", + "target": "c:objc(cy)NSString@Category2", + "targetFallback": "Category2" + }, + { + "kind": "memberOf", + "source": "c:objc(cy)NSString@Category1", + "target": "c:objc(cs)NSString", + "targetFallback": "NSString" + }, + { + "kind": "memberOf", + "source": "c:objc(cy)NSString@Category2", + "target": "c:objc(cs)NSString", + "targetFallback": "NSString" + } + ], + "symbols": [ + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "@interface" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:objc(cs)NSString", + "spelling": "NSString" + }, + { + "kind": "text", + "spelling": " (" + }, + { + "kind": "identifier", + "spelling": "Category1" + }, + { + "kind": "text", + "spelling": ")" + } + ], + "docComment": { + "lines": [ + { + "range": { + "end": { + "character": 18, + "line": 2 + }, + "start": { + "character": 5, + "line": 2 + } + }, + "text": "Doc comment 1" + } + ] + }, + "identifier": { + "interfaceLanguage": "objective-c", + "precise": "c:objc(cy)NSString@Category1" + }, + "kind": { + "displayName": "Class Extension", + "identifier": "objective-c.class.extension" + }, + "location": { + "position": { + "character": 12, + "line": 3 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Category1" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Category1" + } + ], + "title": "NSString (Category1)" + }, + "pathComponents": [ + "Category1" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "text", + "spelling": "- (" + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + }, + { + "kind": "text", + "spelling": ") " + }, + { + "kind": "identifier", + "spelling": "method1" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + } + ] + }, + "identifier": { + "interfaceLanguage": "objective-c", + "precise": "c:objc(cs)NSString(im)method1" + }, + "kind": { + "displayName": "Instance Method", + "identifier": "objective-c.method" + }, + "location": { + "position": { + "character": 1, + "line": 4 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "method1" + } + ], + "subHeading": [ + { + "kind": "text", + "spelling": "- " + }, + { + "kind": "identifier", + "spelling": "method1" + } + ], + "title": "method1" + }, + "pathComponents": [ + "NSString", + "method1" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "@interface" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:objc(cs)NSString", + "spelling": "NSString" + }, + { + "kind": "text", + "spelling": " (" + }, + { + "kind": "identifier", + "spelling": "Category2" + }, + { + "kind": "text", + "spelling": ")" + } + ], + "docComment": { + "lines": [ + { + "range": { + "end": { + "character": 18, + "line": 7 + }, + "start": { + "character": 5, + "line": 7 + } + }, + "text": "Doc comment 2" + } + ] + }, + "identifier": { + "interfaceLanguage": "objective-c", + "precise": "c:objc(cy)NSString@Category2" + }, + "kind": { + "displayName": "Class Extension", + "identifier": "objective-c.class.extension" + }, + "location": { + "position": { + "character": 12, + "line": 8 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Category2" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Category2" + } + ], + "title": "NSString (Category2)" + }, + "pathComponents": [ + "Category2" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "text", + "spelling": "- (" + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + }, + { + "kind": "text", + "spelling": ") " + }, + { + "kind": "identifier", + "spelling": "method2" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + } + ] + }, + "identifier": { + "interfaceLanguage": "objective-c", + "precise": "c:objc(cs)NSString(im)method2" + }, + "kind": { + "displayName": "Instance Method", + "identifier": "objective-c.method" + }, + "location": { + "position": { + "character": 1, + "line": 9 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "method2" + } + ], + "subHeading": [ + { + "kind": "text", + "spelling": "- " + }, + { + "kind": "identifier", + "spelling": "method2" + } + ], + "title": "method2" + }, + "pathComponents": [ + "NSString", + "method2" + ] + }, + { + "accessLevel": "public", + "identifier": { + "interfaceLanguage": "objective-c", + "precise": "c:objc(cs)NSString" + }, + "kind": { + "displayName": "Module Extension", + "identifier": "objective-c.module.extension" + } + } + ] +} diff --git a/clang/test/ExtractAPI/objc_various_categories.m b/clang/test/ExtractAPI/objc_various_categories.m new file mode 100644 --- /dev/null +++ b/clang/test/ExtractAPI/objc_various_categories.m @@ -0,0 +1,519 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \ +// RUN: %t/reference.output.json.in >> %t/reference.output.json +// RUN: %clang -extract-api -x objective-c-header \ +// RUN: -target arm64-apple-macosx \ +// RUN: %t/myclass_1.h \ +// 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 +#import +#import "myclass_1.h" +#import "myclass_2.h" + +@interface MyClass1 (MyCategory1) +- (int) SomeMethod; +@end + +@interface MyClass2 (MyCategory2) +- (int) SomeMethod; +@end + +@interface NSString (Category) +-(void) StringMethod; +@end + +//--- myclass_1.h +@interface MyClass1 +@end + +//--- myclass_2.h +@interface MyClass2 +@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" + } + }, + "relationships": [ + { + "kind": "memberOf", + "source": "c:objc(cs)MyClass1(im)SomeMethod", + "target": "c:objc(cs)MyClass1", + "targetFallback": "MyClass1" + }, + { + "kind": "memberOf", + "source": "c:objc(cs)MyClass2(im)SomeMethod", + "target": "c:objc(cy)MyClass2@MyCategory2", + "targetFallback": "MyCategory2" + }, + { + "kind": "memberOf", + "source": "c:objc(cs)NSString(im)StringMethod", + "target": "c:objc(cy)NSString@Category", + "targetFallback": "Category" + }, + { + "kind": "memberOf", + "source": "c:objc(cy)MyClass2@MyCategory2", + "target": "c:objc(cs)MyClass2", + "targetFallback": "MyClass2" + }, + { + "kind": "memberOf", + "source": "c:objc(cy)NSString@Category", + "target": "c:objc(cs)NSString", + "targetFallback": "NSString" + } + ], + "symbols": [ + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "@interface" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "MyClass1" + } + ], + "identifier": { + "interfaceLanguage": "objective-c", + "precise": "c:objc(cs)MyClass1" + }, + "kind": { + "displayName": "Class", + "identifier": "objective-c.class" + }, + "location": { + "position": { + "character": 12, + "line": 1 + }, + "uri": "file://INPUT_DIR/myclass_1.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "MyClass1" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "MyClass1" + } + ], + "title": "MyClass1" + }, + "pathComponents": [ + "MyClass1" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "text", + "spelling": "- (" + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": ") " + }, + { + "kind": "identifier", + "spelling": "SomeMethod" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + } + ] + }, + "identifier": { + "interfaceLanguage": "objective-c", + "precise": "c:objc(cs)MyClass1(im)SomeMethod" + }, + "kind": { + "displayName": "Instance Method", + "identifier": "objective-c.method" + }, + "location": { + "position": { + "character": 1, + "line": 6 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "SomeMethod" + } + ], + "subHeading": [ + { + "kind": "text", + "spelling": "- " + }, + { + "kind": "identifier", + "spelling": "SomeMethod" + } + ], + "title": "SomeMethod" + }, + "pathComponents": [ + "MyClass1", + "SomeMethod" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "@interface" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:objc(cs)MyClass2", + "spelling": "MyClass2" + }, + { + "kind": "text", + "spelling": " (" + }, + { + "kind": "identifier", + "spelling": "MyCategory2" + }, + { + "kind": "text", + "spelling": ")" + } + ], + "identifier": { + "interfaceLanguage": "objective-c", + "precise": "c:objc(cy)MyClass2@MyCategory2" + }, + "kind": { + "displayName": "Class Extension", + "identifier": "objective-c.class.extension" + }, + "location": { + "position": { + "character": 12, + "line": 9 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "MyCategory2" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "MyCategory2" + } + ], + "title": "MyClass2 (MyCategory2)" + }, + "pathComponents": [ + "MyCategory2" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "text", + "spelling": "- (" + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": ") " + }, + { + "kind": "identifier", + "spelling": "SomeMethod" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + } + ] + }, + "identifier": { + "interfaceLanguage": "objective-c", + "precise": "c:objc(cs)MyClass2(im)SomeMethod" + }, + "kind": { + "displayName": "Instance Method", + "identifier": "objective-c.method" + }, + "location": { + "position": { + "character": 1, + "line": 10 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "SomeMethod" + } + ], + "subHeading": [ + { + "kind": "text", + "spelling": "- " + }, + { + "kind": "identifier", + "spelling": "SomeMethod" + } + ], + "title": "SomeMethod" + }, + "pathComponents": [ + "MyClass2", + "SomeMethod" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "@interface" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:objc(cs)NSString", + "spelling": "NSString" + }, + { + "kind": "text", + "spelling": " (" + }, + { + "kind": "identifier", + "spelling": "Category" + }, + { + "kind": "text", + "spelling": ")" + } + ], + "identifier": { + "interfaceLanguage": "objective-c", + "precise": "c:objc(cy)NSString@Category" + }, + "kind": { + "displayName": "Class Extension", + "identifier": "objective-c.class.extension" + }, + "location": { + "position": { + "character": 12, + "line": 13 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Category" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Category" + } + ], + "title": "NSString (Category)" + }, + "pathComponents": [ + "Category" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "text", + "spelling": "- (" + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + }, + { + "kind": "text", + "spelling": ") " + }, + { + "kind": "identifier", + "spelling": "StringMethod" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + } + ] + }, + "identifier": { + "interfaceLanguage": "objective-c", + "precise": "c:objc(cs)NSString(im)StringMethod" + }, + "kind": { + "displayName": "Instance Method", + "identifier": "objective-c.method" + }, + "location": { + "position": { + "character": 1, + "line": 14 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "StringMethod" + } + ], + "subHeading": [ + { + "kind": "text", + "spelling": "- " + }, + { + "kind": "identifier", + "spelling": "StringMethod" + } + ], + "title": "StringMethod" + }, + "pathComponents": [ + "NSString", + "StringMethod" + ] + }, + { + "accessLevel": "public", + "identifier": { + "interfaceLanguage": "objective-c", + "precise": "c:objc(cs)MyClass2" + }, + "kind": { + "displayName": "Module Extension", + "identifier": "objective-c.module.extension" + } + }, + { + "accessLevel": "public", + "identifier": { + "interfaceLanguage": "objective-c", + "precise": "c:objc(cs)NSString" + }, + "kind": { + "displayName": "Module Extension", + "identifier": "objective-c.module.extension" + } + } + ] +}