[clang] [extract-api] Don't crash for category in libclang APIs
authorDaniel Grumberg <dgrumberg@apple.com>
Thu, 19 Jan 2023 11:25:17 +0000 (11:25 +0000)
committerDaniel Grumberg <dgrumberg@apple.com>
Fri, 10 Feb 2023 16:30:19 +0000 (16:30 +0000)
Remove failure conditions for categories in libclang and return empty
content instead.

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

clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
clang/test/Index/extract-api-cursor.m

index 01e9b37..8beb016 100644 (file)
@@ -487,6 +487,7 @@ bool generatePathComponents(
   SmallVector<PathComponent, 4> ReverseComponenents;
   ReverseComponenents.emplace_back(Record.USR, Record.Name, Record.getKind());
   const auto *CurrentParent = &Record.ParentInformation;
+  bool FailedToFindParent = false;
   while (CurrentParent && !CurrentParent->empty()) {
     PathComponent CurrentParentComponent(CurrentParent->ParentUSR,
                                          CurrentParent->ParentName,
@@ -509,8 +510,10 @@ bool generatePathComponents(
 
     // The parent record doesn't exist which means the symbol shouldn't be
     // treated as part of the current product.
-    if (!ParentRecord)
-      return true;
+    if (!ParentRecord) {
+      FailedToFindParent = true;
+      break;
+    }
 
     ReverseComponenents.push_back(std::move(CurrentParentComponent));
     CurrentParent = &ParentRecord->ParentInformation;
@@ -519,8 +522,9 @@ bool generatePathComponents(
   for (const auto &PC : reverse(ReverseComponenents))
     ComponentTransformer(PC);
 
-  return false;
+  return FailedToFindParent;
 }
+
 Object serializeParentContext(const PathComponent &PC, Language Lang) {
   Object ParentContextElem;
   ParentContextElem["usr"] = PC.USR;
@@ -533,12 +537,15 @@ template <typename RecordTy>
 Array generateParentContexts(const RecordTy &Record, const APISet &API,
                              Language Lang) {
   Array ParentContexts;
-  if (generatePathComponents(
-          Record, API, [Lang, &ParentContexts](const PathComponent &PC) {
-            ParentContexts.push_back(serializeParentContext(PC, Lang));
-          }))
-    ParentContexts.clear();
-  ParentContexts.pop_back();
+  generatePathComponents(Record, API,
+                         [Lang, &ParentContexts](const PathComponent &PC) {
+                           ParentContexts.push_back(
+                               serializeParentContext(PC, Lang));
+                         });
+
+  // The last component would be the record itself so let's remove it.
+  if (!ParentContexts.empty())
+    ParentContexts.pop_back();
 
   return ParentContexts;
 }
@@ -865,6 +872,9 @@ SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,
   if (!Record)
     return {};
 
+  if (isa<ObjCCategoryRecord>(Record))
+    return {};
+
   Object Root;
   APIIgnoresList EmptyIgnores;
   SymbolGraphSerializer Serializer(API, EmptyIgnores,
index a462c11..078f2f5 100644 (file)
@@ -25,6 +25,12 @@ struct Foo {
 - (void)derivedMethodWithValue:(id<Protocol>)value;
 @end
 
+/// This won't show up in docs because we can't serialize it
+@interface Derived ()
+/// Derived method in category docs, won't show up either.
+- (void)derivedMethodInCategory;
+@end
+
 // RUN: c-index-test -single-symbol-sgfs local %s | FileCheck %s
 
 // Checking for Foo
@@ -53,7 +59,7 @@ struct Foo {
 
 // Checking for baseProperty
 // CHECK-NEXT: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}]
-// CHECK-SAME:"relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
+// CHECK-SAME: "relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
 // CHECK-SAME: "isSystem":false
 // CHECK-SAME: "usr":"c:@S@Foo"}]
 // CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(py)baseProperty","target":"c:objc(cs)Base"
@@ -63,7 +69,7 @@ struct Foo {
 
 // Checking for baseMethodWithArg
 // CHECK-NEXT: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}]
-// CHECK-SAME:"relatedSymbols":[]
+// CHECK-SAME: "relatedSymbols":[]
 // CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(im)baseMethodWithArg:","target":"c:objc(cs)Base"
 // CHECK-SAME: "text":"Base method docs"
 // CHECK-SAME: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"}
@@ -79,7 +85,7 @@ struct Foo {
 
 // Checking for protocolProperty
 // CHECK-NEXT: "parentContexts":[{"kind":"objective-c.protocol","name":"Protocol","usr":"c:objc(pl)Protocol"}]
-// CHECK-SAME:"relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
+// CHECK-SAME: "relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
 // CHECK-SAME: "isSystem":false
 // CHECK-SAME: "usr":"c:@S@Foo"}]
 // CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(pl)Protocol(py)protocolProperty","target":"c:objc(pl)Protocol"
@@ -89,7 +95,7 @@ struct Foo {
 
 // Checking for Derived
 // CHECK-NEXT: "parentContexts":[]
-// CHECK-SAME:"relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
+// CHECK-SAME: "relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
 // CHECK-SAME: "isSystem":false
 // CHECK-SAME: "usr":"c:objc(cs)Base"}]
 // CHECK-SAME: "relationships":[{"kind":"inheritsFrom","source":"c:objc(cs)Derived","target":"c:objc(cs)Base"
@@ -99,8 +105,11 @@ struct Foo {
 
 // Checking for derivedMethodWithValue
 // CHECK-NEXT: "parentContexts":[{"kind":"objective-c.class","name":"Derived","usr":"c:objc(cs)Derived"}]
-// CHECK-SAME:"relatedSymbols":[]
+// CHECK-SAME: "relatedSymbols":[]
 // CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Derived(im)derivedMethodWithValue:","target":"c:objc(cs)Derived"
 // CHECK-SAME: "text":"Derived method docs"
 // CHECK-SAME: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"}
 // CHECK-SAME: "title":"derivedMethodWithValue:"
+
+// CHECK-NOT: This won't show up in docs because we can't serialize it
+// CHECK-NOT: Derived method in category docs, won't show up either.