Repository: kythe/kythe
Pull request #5692 (2023-06-09 18:53:22)

feat(go_indexer): add --emit_ref_call_over_identifier flag


Patch:
diff --git a/kythe/go/indexer/BUILD b/kythe/go/indexer/BUILD
index 4a37aa288..8ac0bd99f 100644
--- a/kythe/go/indexer/BUILD
+++ b/kythe/go/indexer/BUILD
@@ -111,6 +111,13 @@ go_indexer_test(
     import_path = "test/fun",
 )
 
+go_indexer_test(
+    name = "funcallid_test",
+    srcs = ["testdata/basic/funcallid.go"],
+    extra_indexer_args = ["-emit_ref_call_over_identifier"],
+    import_path = "test/fun",
+)
+
 go_indexer_test(
     name = "functions_test",
     srcs = ["testdata/basic/functions.go"],
diff --git a/kythe/go/indexer/cmd/go_indexer/go_indexer.go b/kythe/go/indexer/cmd/go_indexer/go_indexer.go
index 23272246b..e5c95297b 100644
--- a/kythe/go/indexer/cmd/go_indexer/go_indexer.go
+++ b/kythe/go/indexer/cmd/go_indexer/go_indexer.go
@@ -50,6 +50,7 @@ var (
 	metaSuffix                     = flag.String("meta", "", "If set, treat files with this suffix as JSON linkage metadata")
 	docBase                        = flag.String("docbase", "http://godoc.org", "If set, use as the base URL for godoc links")
 	onlyEmitDocURIsForStandardLibs = flag.Bool("only_emit_doc_uris_for_standard_libs", false, "If true, the doc/uri fact is only emitted for go std library packages")
+	emitRefCallOverIdentifier      = flag.Bool("emit_ref_call_over_identifier", false, "If true, emit ref/call anchor spans over the function identifier")
 	verbose                        = flag.Bool("verbose", false, "Emit verbose log information")
 	contOnErr                      = flag.Bool("continue", false, "Log errors encountered during analysis but do not exit unsuccessfully")
 	useCompilationCorpusForAll     = flag.Bool("use_compilation_corpus_for_all", false, "If enabled, all Entry VNames are given the corpus of the compilation unit being indexed. This includes items in the go std library and builtin types.")
@@ -165,6 +166,7 @@ func indexGo(ctx context.Context, unit *apb.CompilationUnit, f indexer.Fetcher)
 		UseCompilationCorpusForAll:     *useCompilationCorpusForAll,
 		UseFileAsTopLevelScope:         *useFileAsTopLevelScope,
 		OverrideStdlibCorpus:           *overrideStdlibCorpus,
+		EmitRefCallOverIdentifier:      *emitRefCallOverIdentifier,
 	})
 }
 
diff --git a/kythe/go/indexer/emit.go b/kythe/go/indexer/emit.go
index f649971da..473533279 100644
--- a/kythe/go/indexer/emit.go
+++ b/kythe/go/indexer/emit.go
@@ -75,6 +75,11 @@ type EmitOptions struct {
 	// If set, all stdlib nodes are assigned this corpus. This takes precedence
 	// over UseCompilationCorpusForAll for stdlib nodes.
 	OverrideStdlibCorpus string
+
+	// EmitRefCallOverIdentifier determines whether ref/call anchors are emitted
+	// over function identifiers (or the legacy behavior of over the entire
+	// callsite).
+	EmitRefCallOverIdentifier bool
 }
 
 func (e *EmitOptions) emitMarkedSource() bool {
@@ -91,6 +96,13 @@ func (e *EmitOptions) emitAnchorScopes() bool {
 	return e.EmitAnchorScopes
 }
 
+func (e *EmitOptions) emitRefCallOverIdentifier() bool {
+	if e == nil {
+		return false
+	}
+	return e.EmitRefCallOverIdentifier
+}
+
 func (e *EmitOptions) useFileAsTopLevelScope() bool {
 	if e == nil {
 		return false
@@ -317,7 +329,12 @@ func (e *emitter) visitIdent(id *ast.Ident, stack stackFunc) {
 		}
 	}
 	if call, ok := isCall(id, obj, stack); ok {
-		callAnchor := e.writeRef(call, target, edges.RefCall)
+		var callAnchor *spb.VName
+		if e.opts.emitRefCallOverIdentifier() {
+			callAnchor = e.writeRef(id, target, edges.RefCall)
+		} else {
+			callAnchor = e.writeRef(call, target, edges.RefCall)
+		}
 
 		// Paint an edge to the function blamed for the call, or if there is
 		// none then to the package initializer.


Test patch:
diff --git a/kythe/go/indexer/testdata/basic/funcallid.go b/kythe/go/indexer/testdata/basic/funcallid.go
new file mode 100644
index 000000000..a91b0a372
--- /dev/null
+++ b/kythe/go/indexer/testdata/basic/funcallid.go
@@ -0,0 +1,50 @@
+// Package fun tests basic function call references.
+// - @fun defines/binding Pkg
+package fun
+
+//- @"\"os/exec\"" ref/imports OSExec
+import "os/exec"
+
+//- Pkg.node/kind package
+//- Init childof Pkg
+//- Init.node/kind function
+
+// - @F defines/binding Fun = vname("func F", "test", _, "fun", "go")
+func F() int { return 0 }
+
+type T struct{}
+
+// - @M defines/binding Meth=vname("method T.M", "test", _, "fun", "go")
+func (p *T) M() {}
+
+// - @F ref Fun
+// - TCall=@F ref/call Fun
+// - TCall childof Init
+var _ = F()
+
+// - @init defines/binding InitFunc = vname("func init#1", "test", _, "fun", "go")
+func init() {
+	//- @F ref Fun
+	//- FCall=@F ref/call Fun
+	//- FCall childof InitFunc
+	F()
+
+	var t T
+
+	//- @M ref Meth
+	//- MCall=@M ref/call Meth
+	//- MCall childof InitFunc
+	t.M()
+}
+
+func imported() {
+	//- @cmd defines/binding Cmd
+	//- @exec ref OSExec
+	//- @Command ref _ExecCommand
+	cmd := exec.Command("pwd")
+
+	//- @cmd ref Cmd
+	//- @Run ref CmdRun=vname("method Cmd.Run","golang.org","","os/exec","go")
+	//- @Run ref/call CmdRun
+	cmd.Run()
+}
diff --git a/kythe/go/indexer/testdata/go_indexer_test.bzl b/kythe/go/indexer/testdata/go_indexer_test.bzl
index 14c59fa9d..17e802697 100644
--- a/kythe/go/indexer/testdata/go_indexer_test.bzl
+++ b/kythe/go/indexer/testdata/go_indexer_test.bzl
@@ -159,7 +159,7 @@ go_extract = rule(
 def _go_entries(ctx):
     kzip = ctx.attr.kzip.kzip
     indexer = ctx.files._indexer[-1]
-    iargs = [indexer.path]
+    iargs = [indexer.path] + ctx.attr.extra_indexer_args
     output = ctx.outputs.entries
 
     # If the test wants marked source, enable support for it in the indexer.
@@ -215,6 +215,7 @@ go_entries = rule(
         "use_compilation_corpus_for_all": attr.bool(default = False),
         "use_file_as_top_level_scope": attr.bool(default = False),
         "override_stdlib_corpus": attr.string(default = ""),
+        "extra_indexer_args": attr.string_list(),
 
         # The location of the Go indexer binary.
         "_indexer": attr.label(
@@ -266,6 +267,7 @@ def _go_indexer(
         use_file_as_top_level_scope = False,
         override_stdlib_corpus = "",
         metadata_suffix = "",
+        extra_indexer_args = [],
         extra_extractor_args = []):
     if importpath == None:
         importpath = native.package_name() + "/" + name
@@ -291,6 +293,7 @@ def _go_indexer(
         use_compilation_corpus_for_all = use_compilation_corpus_for_all,
         use_file_as_top_level_scope = use_file_as_top_level_scope,
         override_stdlib_corpus = override_stdlib_corpus,
+        extra_indexer_args = extra_indexer_args,
         kzip = ":" + kzip,
         metadata_suffix = metadata_suffix,
     )
@@ -314,6 +317,7 @@ def go_indexer_test(
         use_file_as_top_level_scope = False,
         override_stdlib_corpus = "",
         metadata_suffix = "",
+        extra_indexer_args = [],
         extra_extractor_args = []):
     entries = _go_indexer(
         name = name,
@@ -327,6 +331,7 @@ def go_indexer_test(
         importpath = import_path,
         metadata_suffix = metadata_suffix,
         deps = deps,
+        extra_indexer_args = extra_indexer_args,
         extra_extractor_args = extra_extractor_args,
     )
     go_verifier_test(