Repository: kythe/kythe
Pull request #5399 (2022-10-19 19:47:03)

feat(go_indexer): implement ref/writes edges for members


Patch:
diff --git a/kythe/go/indexer/emit.go b/kythe/go/indexer/emit.go
index d37d5e501..6a4d6903b 100644
--- a/kythe/go/indexer/emit.go
+++ b/kythe/go/indexer/emit.go
@@ -213,6 +213,26 @@ const (
 	readWriteRef
 )
 
+func exprRefKind(tgt ast.Expr, stack stackFunc, depth int) refKind {
+	switch parent := stack(depth + 1).(type) {
+	case *ast.AssignStmt:
+		// Check if identifier is being assigned; we assume this is not a definition
+		// and checked by the caller.
+		for _, lhs := range parent.Lhs {
+			if lhs == tgt {
+				return writeRef
+			}
+		}
+	case *ast.IncDecStmt:
+		return readWriteRef
+	case *ast.SelectorExpr:
+		if id, ok := tgt.(*ast.Ident); ok && id == parent.Sel {
+			return exprRefKind(parent, stack, depth+1)
+		}
+	}
+	return readRef
+}
+
 // visitIdent handles referring identifiers. Declaring identifiers are handled
 // as part of their parent syntax.
 func (e *emitter) visitIdent(id *ast.Ident, stack stackFunc) {
@@ -258,21 +278,8 @@ func (e *emitter) visitIdent(id *ast.Ident, stack stackFunc) {
 		return
 	}
 
-	refKind := readRef
-	switch parent := stack(1).(type) {
-	case *ast.AssignStmt:
-		// Check if identifier is being assigned
-		for _, lhs := range parent.Lhs {
-			if lhs == id {
-				refKind = writeRef
-				break
-			}
-		}
-	case *ast.IncDecStmt:
-		refKind = readWriteRef
-	}
-
 	var refs []*spb.VName
+	refKind := exprRefKind(id, stack, 0)
 	if refKind == readRef || refKind == readWriteRef {
 		refs = append(refs, e.writeRef(id, target, edges.Ref))
 	}


Test patch:
diff --git a/kythe/go/indexer/testdata/basic/structref.go b/kythe/go/indexer/testdata/basic/structref.go
index afdf0c6a3..606707a43 100644
--- a/kythe/go/indexer/testdata/basic/structref.go
+++ b/kythe/go/indexer/testdata/basic/structref.go
@@ -32,7 +32,7 @@ var ofp = cmd.Stdout
 // - Init.node/kind function
 func init() {
 	//- @cmd ref Cmd
-	//- @Stdout ref CmdStdout
+	//- @Stdout ref/writes CmdStdout
 	//- @os ref OS
 	//- @Stderr ref _OSStderr
 	//-   = vname("var Stderr","golang.org","","os","go")
diff --git a/kythe/go/indexer/testdata/basic/writes.go b/kythe/go/indexer/testdata/basic/writes.go
index cc87f079b..16c5b9efa 100644
--- a/kythe/go/indexer/testdata/basic/writes.go
+++ b/kythe/go/indexer/testdata/basic/writes.go
@@ -3,6 +3,14 @@ package writes
 
 import "fmt"
 
+type S struct {
+	//- @F defines/binding F
+	F int
+
+	//- @Nested defines/binding Nested
+	Nested *S
+}
+
 func f() {
 	//- @x defines/binding X
 	var x int
@@ -22,6 +30,16 @@ func f() {
 	for i := 0; i < 10; i = i + 1 {
 	}
 
+	//- @z defines/binding Z
+	z := S{Nested: &S{}}
+	//- @z ref Z
+	//- @F ref/writes F
+	z.F = 42
+	//- @z ref Z
+	//- @Nested ref Nested
+	//- @F ref/writes F
+	z.Nested.F = 52
+
 	//- @x ref X
 	//- @y ref Y
 	fmt.Println(x, y)
diff --git a/kythe/go/indexer/testdata/generics/genericdep.go b/kythe/go/indexer/testdata/generics/genericdep.go
index 1586553f5..0aeb2527e 100644
--- a/kythe/go/indexer/testdata/generics/genericdep.go
+++ b/kythe/go/indexer/testdata/generics/genericdep.go
@@ -8,7 +8,7 @@ func main() {
 	c := &genericinterface.Container[string]{"element"}
 	//- @Accept ref ContainerAccept
 	c.Accept("yup")
-	//- @Element ref Element
+	//- @Element ref/writes Element
 	c.Element = ""
 }