Repository: bazelbuild/buildtools (go)

Issue:
Buildifier: warning function-docstring-args cannot handle keyword-only function arguments
While working on https://github.com/bazelbuild/rules_proto/pull/22, I noticed that buildifier doesn't know how to handle the separator for keyword-only function arguments.

See https://buildkite.com/bazel/rules-proto/builds/258#76717e64-ae09-4bf7-ac1a-0861e530e45b

```starlark
def direct_source_infos(proto_info, *, provided_sources = []):
    """Returns sequence of `ProtoFileInfo` for `proto_info`'s direct sources.

    Files that are both in `proto_info`'s direct sources and in
    `provided_sources` are skipped. This is useful, e.g., for well-known
    protos that are already provided by the Protobuf runtime.

    Args:
      proto_info: An instance of `ProtoInfo`.
      provided_sources: Optional. A sequence of import paths to ignore.

    Returns: A sequence of `ProtoFileInfo` containing information about
        `proto_info`'s direct sources.
    """

    files = [_info(f, proto_info) for f in proto_info.direct_sources]
    return [f for f in files if f.import_path not in provided_sources]
```

```shell
$ buildifier --lint=warn --warnings=all proto/private/utils/direct_source_infos.bzl 
proto/private/utils/direct_source_infos.bzl:26: function-docstring-args: Argument "" is not documented. (https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#function-docstring-args)
```

Adding documentation for `*:` doesn't seem to fix it (and TBH, documenting that would be weird).

Pull request description:
Don't warn about undocumented *
Fixes #774

Patch:
diff --git a/warn/warn_docstring.go b/warn/warn_docstring.go
index 9d08c91..232c1ff 100644
--- a/warn/warn_docstring.go
+++ b/warn/warn_docstring.go
@@ -83,7 +83,7 @@ func countLeadingSpaces(s string) int {
 	return spaces
 }
 
-var argRegex = regexp.MustCompile(`^ *(\*?\*?\w+)( *\([\w\ ,]+\))?:`)
+var argRegex = regexp.MustCompile(`^ *(\*?\*?\w*)( *\([\w\ ,]+\))?:`)
 
 // parseFunctionDocstring parses a function docstring and returns a docstringInfo object containing
 // the parsed information about the function, its arguments and its return value.
@@ -169,9 +169,12 @@ func getParamName(param build.Expr) string {
 			return ident.Name
 		}
 	case *build.UnaryExpr:
-		// *args or **kwargs
+		// *args, **kwargs, or *
 		if ident, ok := param.X.(*build.Ident); ok {
 			return param.Op + ident.Name
+		} else if param.X == nil {
+			// An asterisk separating position and keyword-only arguments
+			return "*"
 		}
 	}
 	return ""
@@ -281,6 +284,10 @@ func functionDocstringArgsWarning(f *build.File) []*LinterFinding {
 		paramNames := make(map[string]bool)
 		for _, param := range def.Params {
 			name := getParamName(param)
+			if name == "*" {
+				// Not really a parameter but a separator
+				continue
+			}
 			paramNames[name] = true
 			if _, ok := info.args[name]; !ok {
 				notDocumentedArguments = append(notDocumentedArguments, name)


Test patch:
diff --git a/warn/warn_docstring_test.go b/warn/warn_docstring_test.go
index 8205691..ec5c125 100644
--- a/warn/warn_docstring_test.go
+++ b/warn/warn_docstring_test.go
@@ -322,6 +322,39 @@ def f(x, y, z = None, *args, **kwargs):
 		[]string{},
 		scopeEverywhere)
 
+	checkFindings(t, "function-docstring-args", `
+def f(x, *, y, z = None):
+   """This is a function.
+
+   Args:
+    x: x
+    y: y
+    z: z
+   """
+   pass
+`,
+		[]string{},
+		scopeEverywhere)
+
+	checkFindings(t, "function-docstring-args", `
+def f(x, *, y, z = None):
+   """This is a function.
+
+   Args:
+    x: x
+    *: a separator
+    y: y
+    : argument without a name
+    z: z
+   """
+   pass
+`,
+		[]string{
+			`6: Argument "*" is documented but doesn't exist in the function signature.`,
+			`8: Argument "" is documented but doesn't exist in the function signature.`,
+		},
+		scopeEverywhere)
+
 	checkFindings(t, "function-docstring-args", `
 def f(x):
    """