Repository: kythe/kythe
Pull request #6033 (2024-02-23 18:49:05)

feat(go_indexer): add support for Func/Var flag constructors


Patch:
diff --git a/kythe/go/indexer/BUILD b/kythe/go/indexer/BUILD
index d8b6dafec..c2581483e 100644
--- a/kythe/go/indexer/BUILD
+++ b/kythe/go/indexer/BUILD
@@ -1,6 +1,6 @@
 load("//kythe/go/test/tools/empty_corpus_checker:empty_corpus_test.bzl", "empty_corpus_test")
 load("//tools:build_rules/shims.bzl", "go_library", "go_test")
-load(":flags.bzl", "flag_constructors")
+load(":flags.bzl", "flag_constructor", "flag_constructors")
 load(":testdata/go_indexer_test.bzl", "go_indexer_test")
 load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
 
@@ -391,16 +391,57 @@ standard_flags = {
     ],
 }
 
+standard_var_flags = {
+    # Standard func(var, name, default, desc) flags in the "flag" package.
+    "flag": [
+        "BoolVar",
+        "DurationVar",
+        "Float64Var",
+        "Int64Var",
+        "IntVar",
+        "StringVar",
+        "TextVar",
+        "Uint64Var",
+    ],
+}
+
+custom_flags = [
+    # Func flag constructors in the "flag" package.
+    flag_constructor(
+        description_arg_position = 1,
+        func_name = "Func",
+        name_arg_position = 0,
+        pkg_path = "flag",
+    ),
+    flag_constructor(
+        description_arg_position = 1,
+        func_name = "BoolFunc",
+        name_arg_position = 0,
+        pkg_path = "flag",
+    ),
+    # Var flag constructor w/o a default in the "flag" package.
+    flag_constructor(
+        description_arg_position = 2,
+        func_name = "Var",
+        name_arg_position = 1,
+        pkg_path = "flag",
+    ),
+]
+
 flag_constructors(
     name = "standard_flags",
+    custom_flags = custom_flags,
     standard_flags = standard_flags,
+    standard_var_flags = standard_var_flags,
 )
 
 flag_constructors(
     name = "test_flags",
+    custom_flags = custom_flags,
     standard_flags = standard_flags | {
         "flags": ["CustomFlag"],
     },
+    standard_var_flags = standard_var_flags,
 )
 
 go_indexer_test(
diff --git a/kythe/go/indexer/emit.go b/kythe/go/indexer/emit.go
index 2e41a63b9..e035b0a2c 100644
--- a/kythe/go/indexer/emit.go
+++ b/kythe/go/indexer/emit.go
@@ -926,9 +926,6 @@ func (e *emitter) emitFlags(expr *ast.CallExpr, stack stackFunc) {
 		return
 	}
 
-	// TODO: handle *Var typed flag constructors
-	// TODO: handle BoolFunc
-
 	// Parse the flag name
 	nameArg, ok := expr.Args[ctor.NameArgPosition].(*ast.BasicLit)
 	if !ok || nameArg.Kind != token.STRING {
@@ -979,6 +976,7 @@ func (e *emitter) emitFlags(expr *ast.CallExpr, stack stackFunc) {
 	e.writeAnchor(nameArg, anchor, start, end)
 	e.writeEdge(anchor, flagNode, edges.DefinesBinding)
 
+	// TODO: handle *Var typed flag constructors
 	parent := stack(1)
 	var identDef types.Object
 	switch parent := parent.(type) {
diff --git a/kythe/go/indexer/flags.bzl b/kythe/go/indexer/flags.bzl
index 3793493f8..584eb424a 100644
--- a/kythe/go/indexer/flags.bzl
+++ b/kythe/go/indexer/flags.bzl
@@ -1,6 +1,24 @@
 """Starlark library to build FlagConstructors textproto files"""
 
+def flag_constructor(func_name, pkg_path, name_arg_position, description_arg_position):
+    """Produces a text proto encoding of a kythe.proto.FlagConstructor.  Meant
+    to be used as a custom_flags value.
+
+    Args:
+      func_name: the name of the flag constructing function
+      pkg_path: the package path of the flag constructing function
+      name_arg_position: the 0-based position of the name string argument
+      description_arg_position: the 0-based position of the description string argument
+    """
+    return proto.encode_text(struct(
+        func_name = func_name,
+        pkg_path = pkg_path,
+        name_arg_position = name_arg_position,
+        description_arg_position = description_arg_position,
+    ))
+
 def _flag_constructors(ctx):
+    textpb = "# proto-file: kythe/proto/go.proto\n# proto-message: kythe.proto.FlagConstructors\n\n"
     flags = []
     for pkg in ctx.attr.standard_flags:
         for f in ctx.attr.standard_flags[pkg]:
@@ -10,15 +28,28 @@ def _flag_constructors(ctx):
                 "name_arg_position": 0,
                 "description_arg_position": 2,
             }))
+    for pkg in ctx.attr.standard_var_flags:
+        for f in ctx.attr.standard_var_flags[pkg]:
+            flags.append(struct(**{
+                "pkg_path": pkg,
+                "func_name": f,
+                "name_arg_position": 1,
+                "description_arg_position": 3,
+            }))
+    textpb += proto.encode_text(struct(**{"flag": flags}))
+    for f in ctx.attr.custom_flags:
+        textpb += "flag {\n  " + f.replace("\n", "\n  ").strip() + "\n}\n"
     ctx.actions.write(
         output = ctx.outputs.output,
-        content = proto.encode_text(struct(**{"flag": flags})),
+        content = textpb,
     )
 
 flag_constructors = rule(
     implementation = _flag_constructors,
     attrs = {
         "standard_flags": attr.string_list_dict(),
+        "standard_var_flags": attr.string_list_dict(),
+        "custom_flags": attr.string_list(),
     },
     outputs = {"output": "%{name}.textproto"},
 )


Test patch:
diff --git a/kythe/go/indexer/testdata/flags.go b/kythe/go/indexer/testdata/flags.go
index b42cad2f4..23ddbe753 100644
--- a/kythe/go/indexer/testdata/flags.go
+++ b/kythe/go/indexer/testdata/flags.go
@@ -36,6 +36,21 @@ func init() {
 	//- LocalFlag denotes BoolFlag
 	localFlag := flag.Bool("bool_flag", false, "Bool flag")
 
+	var localVar bool
+	//- @"\"var_flag\"" defines/binding VarFlag=vname("flag var_flag", _, _, _, _)
+	//- VarFlag.node/kind google/gflag
+	//- VarFlagDoc documents VarFlag
+	//- VarFlagDoc.node/kind doc
+	//- VarFlagDoc.text "Var flag"
+	flag.BoolVar(&localVar, "var_flag", true, "Var flag")
+
+	//- @"\"func_flag\"" defines/binding FuncFlag=vname("flag func_flag", _, _, _, _)
+	//- FuncFlag.node/kind google/gflag
+	//- FuncFlagDoc documents FuncFlag
+	//- FuncFlagDoc.node/kind doc
+	//- FuncFlagDoc.text "Func flag"
+	flag.BoolFunc("func_flag", "Func flag", func(s string) error { return nil })
+
 	args := flag.Args()
 	fmt.Println(*flagVar, *localFlag, args)
 }