Handle **kwargs and *args macro parameters correctly in Stardoc Progress toward https://github.com/bazelbuild/skydoc/issues/158 RELNOTES: None. PiperOrigin-RevId: 235574880
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/FunctionParamInfo.java b/src/main/java/com/google/devtools/build/skydoc/rendering/FunctionParamInfo.java index 62866ef..81dafe9 100644 --- a/src/main/java/com/google/devtools/build/skydoc/rendering/FunctionParamInfo.java +++ b/src/main/java/com/google/devtools/build/skydoc/rendering/FunctionParamInfo.java
@@ -24,11 +24,25 @@ private final String name; private final String docString; @Nullable private final Object defaultValue; + private final boolean mandatory; - public FunctionParamInfo(String name, String docString, @Nullable Object defaultValue) { + private FunctionParamInfo( + String name, String docString, @Nullable Object defaultValue, boolean mandatory) { this.name = name; this.docString = docString; this.defaultValue = defaultValue; + this.mandatory = mandatory; + } + + /** Constructor to be used for normal parameters. */ + public static FunctionParamInfo forParam( + String name, String docString, @Nullable Object defaultValue) { + return new FunctionParamInfo(name, docString, defaultValue, defaultValue == null); + } + + /** Constructor to be used for *args or **kwargs. */ + public static FunctionParamInfo forSpecialParam(String name, String docString) { + return new FunctionParamInfo(name, docString, null, false); } /** @@ -74,6 +88,6 @@ * Returns 'required' if this parameter is mandatory, otherwise returns 'optional'. */ public String getMandatoryString() { - return defaultValue == null ? "required" : "optional"; + return mandatory ? "required" : "optional"; } }
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/UserDefinedFunctionInfo.java b/src/main/java/com/google/devtools/build/skydoc/rendering/UserDefinedFunctionInfo.java index 258f2e8..afe3f3e 100644 --- a/src/main/java/com/google/devtools/build/skydoc/rendering/UserDefinedFunctionInfo.java +++ b/src/main/java/com/google/devtools/build/skydoc/rendering/UserDefinedFunctionInfo.java
@@ -116,28 +116,57 @@ ImmutableList.Builder<FunctionParamInfo> parameterInfos = ImmutableList.builder(); List<String> paramNames = signature.getSignature().getNames(); + int numMandatoryParams = signature.getSignature().getShape().getMandatoryPositionals(); + + int paramIndex; + // Mandatory parameters. // Mandatory parameters must always come before optional parameters, so this counts // down until all mandatory parameters have been exhausted, and then starts filling in - // the default parameters accordingly. - int numMandatoryParamsLeft = - signature.getDefaultValues() != null - ? paramNames.size() - signature.getDefaultValues().size() - : paramNames.size(); - int optionalParamIndex = 0; + // the optional parameters accordingly. + for (paramIndex = 0; paramIndex < numMandatoryParams; paramIndex++) { + String paramName = paramNames.get(paramIndex); + String paramDoc = paramNameToDocMap.getOrDefault(paramName, ""); + parameterInfos.add(FunctionParamInfo.forParam(paramName, paramDoc, /*default param*/ null)); + } - for (String paramName : paramNames) { - Object defaultParamValue = null; - String paramDoc = ""; - if (numMandatoryParamsLeft == 0) { - defaultParamValue = signature.getDefaultValues().get(optionalParamIndex); - optionalParamIndex++; - } else { - numMandatoryParamsLeft--; + // Parameters with defaults. + if (signature.getDefaultValues() != null) { + for (Object element : signature.getDefaultValues()) { + String paramName = paramNames.get(paramIndex); + String paramDoc = ""; + Object defaultParamValue = element; + if (paramNameToDocMap.containsKey(paramName)) { + paramDoc = paramNameToDocMap.get(paramName); + } + parameterInfos.add(FunctionParamInfo.forParam(paramName, paramDoc, defaultParamValue)); + paramIndex++; } + } + + // *arg + if (signature.getSignature().getShape().hasStarArg()) { + String paramName = paramNames.get(paramIndex); + String paramDoc = ""; if (paramNameToDocMap.containsKey(paramName)) { paramDoc = paramNameToDocMap.get(paramName); + } else if (paramNameToDocMap.containsKey("*" + paramName)) { + paramDoc = paramNameToDocMap.get("*" + paramName); } - parameterInfos.add(new FunctionParamInfo(paramName, paramDoc, defaultParamValue)); + parameterInfos.add(FunctionParamInfo.forSpecialParam(paramName, paramDoc)); + paramIndex++; + } + + // **kwargs + if (signature.getSignature().getShape().hasKwArg()) { + String paramName = paramNames.get(paramIndex); + String paramDoc = ""; + if (paramNameToDocMap.containsKey(paramName)) { + paramDoc = paramNameToDocMap.get(paramName); + } else if (paramNameToDocMap.containsKey("**" + paramName)) { + paramDoc = paramNameToDocMap.get("**" + paramName); + } + parameterInfos.add(FunctionParamInfo.forSpecialParam(paramName, paramDoc)); + paramIndex++; } return parameterInfos.build(); }
diff --git a/src/test/java/com/google/devtools/build/skydoc/BUILD b/src/test/java/com/google/devtools/build/skydoc/BUILD index 56a28a2..fcae8f5 100644 --- a/src/test/java/com/google/devtools/build/skydoc/BUILD +++ b/src/test/java/com/google/devtools/build/skydoc/BUILD
@@ -171,3 +171,10 @@ "my_module", ], ) + +skydoc_test( + name = "macro_kwargs_test", + golden_file = "testdata/macro_kwargs_test/golden.txt", + input_file = "testdata/macro_kwargs_test/input.bzl", + skydoc = "//src/main/java/com/google/devtools/build/skydoc", +)
diff --git a/src/test/java/com/google/devtools/build/skydoc/testdata/macro_kwargs_test/golden.txt b/src/test/java/com/google/devtools/build/skydoc/testdata/macro_kwargs_test/golden.txt new file mode 100644 index 0000000..4c5836a --- /dev/null +++ b/src/test/java/com/google/devtools/build/skydoc/testdata/macro_kwargs_test/golden.txt
@@ -0,0 +1,160 @@ +## macro_with_args + +<pre> +macro_with_args(<a href="#macro_with_args-name">name</a>, <a href="#macro_with_args-args">args</a>) +</pre> + +My args macro is OK. + +### Parameters + +<table class="params-table"> + <colgroup> + <col class="col-param" /> + <col class="col-description" /> + </colgroup> + <tbody> + <tr id="macro_with_args-name"> + <td><code>name</code></td> + <td> + required. + <p> + The name of the test rule. + </p> + </td> + </tr> + <tr id="macro_with_args-args"> + <td><code>args</code></td> + <td> + optional. + <p> + Other arguments to include + </p> + </td> + </tr> + </tbody> +</table> + + +## macro_with_both + +<pre> +macro_with_both(<a href="#macro_with_both-name">name</a>, <a href="#macro_with_both-number">number</a>, <a href="#macro_with_both-args">args</a>, <a href="#macro_with_both-kwargs">kwargs</a>) +</pre> + +Oh wow this macro has both. + +Not much else to say. + + +### Parameters + +<table class="params-table"> + <colgroup> + <col class="col-param" /> + <col class="col-description" /> + </colgroup> + <tbody> + <tr id="macro_with_both-name"> + <td><code>name</code></td> + <td> + required. + <p> + The name of the test rule. + </p> + </td> + </tr> + <tr id="macro_with_both-number"> + <td><code>number</code></td> + <td> + optional. default is <code>3</code> + <p> + Some number used for important things + </p> + </td> + </tr> + <tr id="macro_with_both-args"> + <td><code>args</code></td> + <td> + optional. + <p> + Other arguments to include + </p> + </td> + </tr> + <tr id="macro_with_both-kwargs"> + <td><code>kwargs</code></td> + <td> + optional. + <p> + Other attributes to include + </p> + </td> + </tr> + </tbody> +</table> + + +## macro_with_kwargs + +<pre> +macro_with_kwargs(<a href="#macro_with_kwargs-name">name</a>, <a href="#macro_with_kwargs-config">config</a>, <a href="#macro_with_kwargs-deps">deps</a>, <a href="#macro_with_kwargs-kwargs">kwargs</a>) +</pre> + +My kwargs macro is the best. + +This is a long multi-line doc string. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer +elementum, diam vitae tincidunt pulvinar, nunc tortor volutpat dui, +vitae facilisis odio ligula a tortor. Donec ullamcorper odio eget ipsum tincidunt, +vel mollis eros pellentesque. + + +### Parameters + +<table class="params-table"> + <colgroup> + <col class="col-param" /> + <col class="col-description" /> + </colgroup> + <tbody> + <tr id="macro_with_kwargs-name"> + <td><code>name</code></td> + <td> + required. + <p> + The name of the test rule. + </p> + </td> + </tr> + <tr id="macro_with_kwargs-config"> + <td><code>config</code></td> + <td> + required. + <p> + Config to use for my macro + </p> + </td> + </tr> + <tr id="macro_with_kwargs-deps"> + <td><code>deps</code></td> + <td> + optional. default is <code>[]</code> + <p> + List of my macro's dependencies + </p> + </td> + </tr> + <tr id="macro_with_kwargs-kwargs"> + <td><code>kwargs</code></td> + <td> + optional. + <p> + Other attributes to include + </p> + </td> + </tr> + </tbody> +</table> + +
diff --git a/src/test/java/com/google/devtools/build/skydoc/testdata/macro_kwargs_test/input.bzl b/src/test/java/com/google/devtools/build/skydoc/testdata/macro_kwargs_test/input.bzl new file mode 100644 index 0000000..0f6268d --- /dev/null +++ b/src/test/java/com/google/devtools/build/skydoc/testdata/macro_kwargs_test/input.bzl
@@ -0,0 +1,52 @@ +"""Tests for functions which use *args or **kwargs""" + +def macro_with_kwargs(name, config, deps = [], **kwargs): + """My kwargs macro is the best. + + This is a long multi-line doc string. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer + elementum, diam vitae tincidunt pulvinar, nunc tortor volutpat dui, + vitae facilisis odio ligula a tortor. Donec ullamcorper odio eget ipsum tincidunt, + vel mollis eros pellentesque. + + Args: + name: The name of the test rule. + config: Config to use for my macro + deps: List of my macro's dependencies + **kwargs: Other attributes to include + + Returns: + An empty list. + """ + _ignore = [name, config, deps, kwargs] + return [] + +def macro_with_args(name, *args): + """My args macro is OK. + + Args: + name: The name of the test rule. + *args: Other arguments to include + + Returns: + An empty list. + """ + _ignore = [name, args] + return [] + +def macro_with_both(name, number = 3, *args, **kwargs): + """Oh wow this macro has both. + + Not much else to say. + + Args: + name: The name of the test rule. + number: Some number used for important things + *args: Other arguments to include + **kwargs: Other attributes to include + + Returns: + An empty list. + """ + _ignore = [name, number, args, kwargs] + return []