Repository: kythe/kythe
Pull request #5727 (2023-06-30 02:04:52)

fix(typescript_indexer): improve `childof` coverage
Adds `childof` edges for class/interface properties and enum members. 


Patch:
diff --git a/kythe/typescript/indexer.ts b/kythe/typescript/indexer.ts
index 805b07174..819ed855b 100644
--- a/kythe/typescript/indexer.ts
+++ b/kythe/typescript/indexer.ts
@@ -1640,6 +1640,7 @@ class Visitor {
         ts.isPropertyAssignment(decl) ||
         ts.isShorthandPropertyAssignment(decl)) {
       this.emitSubkind(vname, Subkind.FIELD);
+      this.emitChildofEdge(vname, decl.parent);
     }
     if (ts.isShorthandPropertyAssignment(decl)) {
       const origSym = this.typeChecker.getShorthandAssignmentValueSymbol(decl);
@@ -2070,26 +2071,11 @@ class Visitor {
     this.emitEdge(this.newAnchor(decl), EdgeKind.DEFINES, vname);
 
     if (decl.parent) {
-      // Emit a "childof" edge on class/interface members.
-      if (ts.isClassLike(decl.parent) ||
-          ts.isInterfaceDeclaration(decl.parent)) {
-        const parentName = decl.parent.name;
-        if (parentName !== undefined) {
-          const parentSym = this.host.getSymbolAtLocationFollowingAliases(parentName);
-          if (!parentSym) {
-            todo(
-                this.sourceRoot, parentName,
-                `parent ${parentName} has no symbol`);
-            return;
-          }
-          const kParent = this.host.getSymbolName(parentSym, TSNamespace.TYPE);
-          if (kParent) {
-            this.emitEdge(vname, EdgeKind.CHILD_OF, kParent);
-          }
-        }
-        if (sym) {
-          this.emitOverridesEdgeForFunction(sym, vname, decl.parent);
-        }
+      this.emitChildofEdge(vname, decl.parent);
+      if ((ts.isClassLike(decl.parent) ||
+           ts.isInterfaceDeclaration(decl.parent)) &&
+          sym) {
+        this.emitOverridesEdgeForFunction(sym, vname, decl.parent);
       }
     }
 
@@ -2110,6 +2096,30 @@ class Visitor {
     this.emitMarkedSourceForFunction(decl, vname);
   }
 
+  /**
+   * Emits childof edge from member to their parents. Parent can be class/interface/enum.
+   * See https://kythe.io/docs/schema/#childof
+   */
+  emitChildofEdge(vname: VName, parent: ts.Node) {
+    if (!ts.isClassLike(parent) && !ts.isInterfaceDeclaration(parent) &&
+        !ts.isEnumDeclaration(parent)) {
+      return;
+    }
+    const parentName = parent.name;
+    if (parentName == null) {
+      return;
+    }
+    const parentSym = this.host.getSymbolAtLocationFollowingAliases(parentName);
+    if (!parentSym) {
+      todo(this.sourceRoot, parentName, `parent ${parentName} has no symbol`);
+      return;
+    }
+    const kParent = this.host.getSymbolName(parentSym, TSNamespace.TYPE);
+    if (kParent) {
+      this.emitEdge(vname, EdgeKind.CHILD_OF, kParent);
+    }
+  }
+
   /**
    * Visits a function parameters, which can be recursive in the case of
    * parameters created via bound elements:
@@ -2317,6 +2327,7 @@ class Visitor {
     this.emitNode(kMember, NodeKind.CONSTANT);
     this.emitEdge(this.newAnchor(decl.name), EdgeKind.DEFINES_BINDING, kMember);
     this.emitMarkedSourceForVariable(decl, kMember);
+    this.emitChildofEdge(kMember, decl.parent);
   }
 
   visitExpressionMember(node: ts.Node) {


Test patch:
diff --git a/kythe/typescript/testdata/class.ts b/kythe/typescript/testdata/class.ts
index 9258dbd3f..5058cb9a0 100644
--- a/kythe/typescript/testdata/class.ts
+++ b/kythe/typescript/testdata/class.ts
@@ -12,6 +12,7 @@ interface IFace {
     //- @member defines/binding IFaceMember
     //- IFaceMember.node/kind variable
     //- IFaceMember.subkind field
+    //- IFaceMember childof IFace
     member: number;
 }
 
@@ -33,11 +34,13 @@ class Class implements IFace {
     //- Member.node/kind variable
     //- !{ Member.tag/static _ }
     //- Member.subkind field
+    //- Member childof Class
     member: number;
 
     //- @member defines/binding StaticMember
     //- StaticMember.tag/static _
     //- !{ @member defines/binding Member }
+    //- StaticMember childof Class
     static member: number;
 
     // This ctor declares a new member var named 'otherMember', and also
@@ -46,6 +49,7 @@ class Class implements IFace {
     //- @constructor defines/binding ClassCtor=vname("Class", _, _, _, _)
     //- ClassCtor.node/kind function
     //- ClassCtor.subkind constructor
+    //- ClassCtor childof Class
     //- @otherMember defines/binding OtherMember
     //- OtherMember.node/kind variable
     //- @member defines/binding FakeMember
diff --git a/kythe/typescript/testdata/enum.ts b/kythe/typescript/testdata/enum.ts
index 6275de930..5b48ad59e 100644
--- a/kythe/typescript/testdata/enum.ts
+++ b/kythe/typescript/testdata/enum.ts
@@ -5,8 +5,9 @@ export {}
 //- @Fruit defines/binding FruitVal
 //- FruitVal.node/kind constant
 enum Fruit {
-  //- @APPLE defines/binding APPLE
-  //- APPLE.node/kind constant
+  //- @APPLE defines/binding Apple
+  //- Apple.node/kind constant
+  //- Apple childof FruitType
   APPLE,
   PEAR
 }
@@ -14,5 +15,5 @@ enum Fruit {
 //- @Fruit ref FruitType
 let x: Fruit;
 //- @Fruit ref FruitVal
-//- @APPLE ref APPLE
+//- @APPLE ref Apple
 x = Fruit.APPLE;