Support multi-level namespace structs
Fixes https://github.com/bazelbuild/skydoc/issues/165.
PiperOrigin-RevId: 258559367
diff --git a/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java b/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
index b1ac195..b5e1df1 100644
--- a/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
+++ b/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
@@ -389,13 +389,9 @@
userDefinedFunctionMap.put(envEntry.getKey(), userDefinedFunction);
}
if (envEntry.getValue() instanceof FakeStructApi) {
- FakeStructApi struct = (FakeStructApi) envEntry.getValue();
- for (String field : struct.getFieldNames()) {
- if (struct.getValue(field) instanceof UserDefinedFunction) {
- UserDefinedFunction userDefinedFunction = (UserDefinedFunction) struct.getValue(field);
- userDefinedFunctionMap.put(envEntry.getKey() + "." + field, userDefinedFunction);
- }
- }
+ String namespaceName = envEntry.getKey();
+ FakeStructApi namespace = (FakeStructApi) envEntry.getValue();
+ putStructFields(namespaceName, namespace, userDefinedFunctionMap);
}
}
@@ -403,6 +399,31 @@
}
/**
+ * Recursively adds functions defined in {@code namespace}, and in its nested namespaces, to
+ * {@code userDefinedFunctionMap}.
+ *
+ * <p>Each entry's key is the fully qualified function name, e.g. {@code
+ * "outernamespace.innernamespace.func"}. {@code namespaceName} is the fully qualified name of
+ * {@code namespace} itself.
+ */
+ private static void putStructFields(
+ String namespaceName,
+ FakeStructApi namespace,
+ ImmutableMap.Builder<String, UserDefinedFunction> userDefinedFunctionMap)
+ throws EvalException {
+ for (String field : namespace.getFieldNames()) {
+ String qualifiedFieldName = namespaceName + "." + field;
+ if (namespace.getValue(field) instanceof UserDefinedFunction) {
+ UserDefinedFunction userDefinedFunction = (UserDefinedFunction) namespace.getValue(field);
+ userDefinedFunctionMap.put(qualifiedFieldName, userDefinedFunction);
+ } else if (namespace.getValue(field) instanceof FakeStructApi) {
+ FakeStructApi innerNamespace = (FakeStructApi) namespace.getValue(field);
+ putStructFields(qualifiedFieldName, innerNamespace, userDefinedFunctionMap);
+ }
+ }
+ }
+
+ /**
* Recursively evaluates/interprets the skylark file at a given path and its transitive skylark
* dependencies using a fake build API and collects information about all rule definitions made in
* those files.
diff --git a/src/test/java/com/google/devtools/build/skydoc/BUILD b/src/test/java/com/google/devtools/build/skydoc/BUILD
index 0e9fdb9..050190d 100644
--- a/src/test/java/com/google/devtools/build/skydoc/BUILD
+++ b/src/test/java/com/google/devtools/build/skydoc/BUILD
@@ -182,19 +182,37 @@
)
skydoc_test(
- name = "module_test",
- golden_file = "testdata/module_test/golden.txt",
- input_file = "testdata/module_test/input.bzl",
+ name = "namespace_test",
+ golden_file = "testdata/namespace_test/golden.txt",
+ input_file = "testdata/namespace_test/input.bzl",
skydoc = "//src/main/java/com/google/devtools/build/skydoc",
)
skydoc_test(
- name = "module_test_with_whitelist",
- golden_file = "testdata/module_test/golden.txt",
- input_file = "testdata/module_test/input.bzl",
+ name = "namespace_test_with_whitelist",
+ golden_file = "testdata/namespace_test/golden.txt",
+ input_file = "testdata/namespace_test/input.bzl",
skydoc = "//src/main/java/com/google/devtools/build/skydoc",
whitelisted_symbols = [
- "my_module",
+ "my_namespace",
+ ],
+)
+
+skydoc_test(
+ name = "multi_level_namespace_test",
+ golden_file = "testdata/multi_level_namespace_test/golden.txt",
+ input_file = "testdata/multi_level_namespace_test/input.bzl",
+ skydoc = "//src/main/java/com/google/devtools/build/skydoc",
+)
+
+skydoc_test(
+ name = "multi_level_namespace_test_with_whitelist",
+ golden_file = "testdata/multi_level_namespace_test_with_whitelist/golden.txt",
+ input_file = "testdata/multi_level_namespace_test_with_whitelist/input.bzl",
+ skydoc = "//src/main/java/com/google/devtools/build/skydoc",
+ whitelisted_symbols = [
+ "my_namespace",
+ "other_namespace.foo.nothing",
],
)
diff --git a/src/test/java/com/google/devtools/build/skydoc/testdata/multi_level_namespace_test/golden.txt b/src/test/java/com/google/devtools/build/skydoc/testdata/multi_level_namespace_test/golden.txt
new file mode 100644
index 0000000..58f662a
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/skydoc/testdata/multi_level_namespace_test/golden.txt
@@ -0,0 +1,119 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_namespace.min"></a>
+
+## my_namespace.min
+
+<pre>
+my_namespace.min(<a href="#my_namespace.min-integers">integers</a>)
+</pre>
+
+Returns the minimum of given elements.
+
+### Parameters
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="my_namespace.min-integers">
+ <td><code>integers</code></td>
+ <td>
+ required.
+ <p>
+ A list of integers. Must not be empty.
+ </p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+
+<a name="#my_namespace.math.min"></a>
+
+## my_namespace.math.min
+
+<pre>
+my_namespace.math.min(<a href="#my_namespace.math.min-integers">integers</a>)
+</pre>
+
+Returns the minimum of given elements.
+
+### Parameters
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="my_namespace.math.min-integers">
+ <td><code>integers</code></td>
+ <td>
+ required.
+ <p>
+ A list of integers. Must not be empty.
+ </p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+
+<a name="#my_namespace.foo.bar.baz"></a>
+
+## my_namespace.foo.bar.baz
+
+<pre>
+my_namespace.foo.bar.baz()
+</pre>
+
+This function does nothing.
+
+
+
+<a name="#my_namespace.one.two.min"></a>
+
+## my_namespace.one.two.min
+
+<pre>
+my_namespace.one.two.min(<a href="#my_namespace.one.two.min-integers">integers</a>)
+</pre>
+
+Returns the minimum of given elements.
+
+### Parameters
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="my_namespace.one.two.min-integers">
+ <td><code>integers</code></td>
+ <td>
+ required.
+ <p>
+ A list of integers. Must not be empty.
+ </p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+
+<a name="#my_namespace.one.three.does_nothing"></a>
+
+## my_namespace.one.three.does_nothing
+
+<pre>
+my_namespace.one.three.does_nothing()
+</pre>
+
+This function does nothing.
+
+
+
diff --git a/src/test/java/com/google/devtools/build/skydoc/testdata/multi_level_namespace_test/input.bzl b/src/test/java/com/google/devtools/build/skydoc/testdata/multi_level_namespace_test/input.bzl
new file mode 100644
index 0000000..8b0b436
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/skydoc/testdata/multi_level_namespace_test/input.bzl
@@ -0,0 +1,32 @@
+"""A test that verifies documenting a multi-leveled namespace of functions."""
+
+def _min(integers):
+ """Returns the minimum of given elements.
+
+ Args:
+ integers: A list of integers. Must not be empty.
+
+ Returns:
+ The minimum integer in the given list.
+ """
+ _ignore = [integers]
+ return 42
+
+def _does_nothing():
+ """This function does nothing."""
+ pass
+
+my_namespace = struct(
+ dropped_field = "Note this field should not be documented",
+ min = _min,
+ math = struct(min = _min),
+ foo = struct(
+ bar = struct(baz = _does_nothing),
+ num = 12,
+ string = "Hello!",
+ ),
+ one = struct(
+ two = struct(min = _min),
+ three = struct(does_nothing = _does_nothing),
+ ),
+)
diff --git a/src/test/java/com/google/devtools/build/skydoc/testdata/multi_level_namespace_test_with_whitelist/golden.txt b/src/test/java/com/google/devtools/build/skydoc/testdata/multi_level_namespace_test_with_whitelist/golden.txt
new file mode 100644
index 0000000..3eaf093
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/skydoc/testdata/multi_level_namespace_test_with_whitelist/golden.txt
@@ -0,0 +1,70 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_namespace.min"></a>
+
+## my_namespace.min
+
+<pre>
+my_namespace.min(<a href="#my_namespace.min-integers">integers</a>)
+</pre>
+
+Returns the minimum of given elements.
+
+### Parameters
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="my_namespace.min-integers">
+ <td><code>integers</code></td>
+ <td>
+ required.
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+
+<a name="#my_namespace.math.min"></a>
+
+## my_namespace.math.min
+
+<pre>
+my_namespace.math.min(<a href="#my_namespace.math.min-integers">integers</a>)
+</pre>
+
+Returns the minimum of given elements.
+
+### Parameters
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="my_namespace.math.min-integers">
+ <td><code>integers</code></td>
+ <td>
+ required.
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+
+<a name="#other_namespace.foo.nothing"></a>
+
+## other_namespace.foo.nothing
+
+<pre>
+other_namespace.foo.nothing()
+</pre>
+
+This function does nothing.
+
+
+
diff --git a/src/test/java/com/google/devtools/build/skydoc/testdata/multi_level_namespace_test_with_whitelist/input.bzl b/src/test/java/com/google/devtools/build/skydoc/testdata/multi_level_namespace_test_with_whitelist/input.bzl
new file mode 100644
index 0000000..34eb87b
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/skydoc/testdata/multi_level_namespace_test_with_whitelist/input.bzl
@@ -0,0 +1,23 @@
+"""A test that verifies documenting a multi-leveled namespace of functions with whitelist symbols.
+The whitelist symbols should cause everything in my_namespace to to be documented, but only a
+specific symbol in other_namespace to be documented."""
+
+def _min(integers):
+ """Returns the minimum of given elements."""
+ _ignore = [integers]
+ return 42
+
+def _does_nothing():
+ """This function does nothing."""
+ pass
+
+my_namespace = struct(
+ dropped_field = "Note this field should not be documented",
+ min = _min,
+ math = struct(min = _min),
+)
+
+other_namespace = struct(
+ foo = struct(nothing = _does_nothing),
+ min = _min,
+)
diff --git a/src/test/java/com/google/devtools/build/skydoc/testdata/module_test/golden.txt b/src/test/java/com/google/devtools/build/skydoc/testdata/namespace_test/golden.txt
similarity index 64%
rename from src/test/java/com/google/devtools/build/skydoc/testdata/module_test/golden.txt
rename to src/test/java/com/google/devtools/build/skydoc/testdata/namespace_test/golden.txt
index ca95f87..6fd6bdd 100644
--- a/src/test/java/com/google/devtools/build/skydoc/testdata/module_test/golden.txt
+++ b/src/test/java/com/google/devtools/build/skydoc/testdata/namespace_test/golden.txt
@@ -1,11 +1,11 @@
<!-- Generated with Stardoc: http://skydoc.bazel.build -->
-<a name="#my_module.assert_non_empty"></a>
+<a name="#my_namespace.assert_non_empty"></a>
-## my_module.assert_non_empty
+## my_namespace.assert_non_empty
<pre>
-my_module.assert_non_empty(<a href="#my_module.assert_non_empty-some_list">some_list</a>, <a href="#my_module.assert_non_empty-other_list">other_list</a>)
+my_namespace.assert_non_empty(<a href="#my_namespace.assert_non_empty-some_list">some_list</a>, <a href="#my_namespace.assert_non_empty-other_list">other_list</a>)
</pre>
Asserts the two given lists are not empty.
@@ -18,7 +18,7 @@
<col class="col-description" />
</colgroup>
<tbody>
- <tr id="my_module.assert_non_empty-some_list">
+ <tr id="my_namespace.assert_non_empty-some_list">
<td><code>some_list</code></td>
<td>
required.
@@ -27,7 +27,7 @@
</p>
</td>
</tr>
- <tr id="my_module.assert_non_empty-other_list">
+ <tr id="my_namespace.assert_non_empty-other_list">
<td><code>other_list</code></td>
<td>
required.
@@ -40,12 +40,12 @@
</table>
-<a name="#my_module.min"></a>
+<a name="#my_namespace.min"></a>
-## my_module.min
+## my_namespace.min
<pre>
-my_module.min(<a href="#my_module.min-integers">integers</a>)
+my_namespace.min(<a href="#my_namespace.min-integers">integers</a>)
</pre>
Returns the minimum of given elements.
@@ -58,7 +58,7 @@
<col class="col-description" />
</colgroup>
<tbody>
- <tr id="my_module.min-integers">
+ <tr id="my_namespace.min-integers">
<td><code>integers</code></td>
<td>
required.
@@ -71,12 +71,12 @@
</table>
-<a name="#my_module.join_strings"></a>
+<a name="#my_namespace.join_strings"></a>
-## my_module.join_strings
+## my_namespace.join_strings
<pre>
-my_module.join_strings(<a href="#my_module.join_strings-strings">strings</a>, <a href="#my_module.join_strings-delimiter">delimiter</a>)
+my_namespace.join_strings(<a href="#my_namespace.join_strings-strings">strings</a>, <a href="#my_namespace.join_strings-delimiter">delimiter</a>)
</pre>
Joins the given strings with a delimiter.
@@ -89,7 +89,7 @@
<col class="col-description" />
</colgroup>
<tbody>
- <tr id="my_module.join_strings-strings">
+ <tr id="my_namespace.join_strings-strings">
<td><code>strings</code></td>
<td>
required.
@@ -98,7 +98,7 @@
</p>
</td>
</tr>
- <tr id="my_module.join_strings-delimiter">
+ <tr id="my_namespace.join_strings-delimiter">
<td><code>delimiter</code></td>
<td>
optional. default is <code>", "</code>
diff --git a/src/test/java/com/google/devtools/build/skydoc/testdata/module_test/input.bzl b/src/test/java/com/google/devtools/build/skydoc/testdata/namespace_test/input.bzl
similarity index 91%
rename from src/test/java/com/google/devtools/build/skydoc/testdata/module_test/input.bzl
rename to src/test/java/com/google/devtools/build/skydoc/testdata/namespace_test/input.bzl
index d1b04b3..69cad64 100644
--- a/src/test/java/com/google/devtools/build/skydoc/testdata/module_test/input.bzl
+++ b/src/test/java/com/google/devtools/build/skydoc/testdata/namespace_test/input.bzl
@@ -1,4 +1,4 @@
-"""A test that verifies documenting a module of functions."""
+"""A test that verifies documenting a namespace of functions."""
def _min(integers):
"""Returns the minimum of given elements.
@@ -35,7 +35,7 @@
_ignore = [strings, delimiter]
return ""
-my_module = struct(
+my_namespace = struct(
dropped_field = "Note this field should not be documented",
assert_non_empty = _assert_non_empty,
min = _min,