Refactor UserDefinedFunctionInfo into a proto.

PiperOrigin-RevId: 252418282
diff --git a/src/main/java/com/google/devtools/build/skydoc/BUILD b/src/main/java/com/google/devtools/build/skydoc/BUILD
index f34fda2..453b998 100644
--- a/src/main/java/com/google/devtools/build/skydoc/BUILD
+++ b/src/main/java/com/google/devtools/build/skydoc/BUILD
@@ -84,7 +84,9 @@
         "//src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository",
         "//src/main/java/com/google/devtools/build/skydoc/fakebuildapi/test",
         "//src/main/java/com/google/devtools/build/skydoc/rendering",
+        "//src/main/java/com/google/devtools/build/skydoc/rendering/proto:stardoc_output_java_proto",
         "//src/main/java/com/google/devtools/common/options",
         "//third_party:guava",
+        "//third_party:jsr305",
     ],
 )
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 bb8a6ea..f794be5 100644
--- a/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
+++ b/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
@@ -102,11 +102,12 @@
 import com.google.devtools.build.skydoc.fakebuildapi.test.FakeAnalysisTestResultInfoProvider;
 import com.google.devtools.build.skydoc.fakebuildapi.test.FakeCoverageCommon;
 import com.google.devtools.build.skydoc.fakebuildapi.test.FakeTestingModule;
+import com.google.devtools.build.skydoc.rendering.DocstringParseException;
+import com.google.devtools.build.skydoc.rendering.FunctionUtil;
 import com.google.devtools.build.skydoc.rendering.MarkdownRenderer;
 import com.google.devtools.build.skydoc.rendering.ProviderInfo;
 import com.google.devtools.build.skydoc.rendering.RuleInfo;
-import com.google.devtools.build.skydoc.rendering.UserDefinedFunctionInfo;
-import com.google.devtools.build.skydoc.rendering.UserDefinedFunctionInfo.DocstringParseException;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.UserDefinedFunctionInfo;
 import com.google.devtools.common.options.OptionsParser;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -287,7 +288,7 @@
     for (Entry<String, UserDefinedFunction> entry : userDefinedFunctions.entrySet()) {
       try {
         UserDefinedFunctionInfo functionInfo =
-            UserDefinedFunctionInfo.fromNameAndFunction(entry.getKey(), entry.getValue());
+            FunctionUtil.fromNameAndFunction(entry.getKey(), entry.getValue());
         printUserDefinedFunctionInfo(printWriter, renderer, functionInfo);
         printWriter.println();
       } catch (DocstringParseException exception) {
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/DocstringParseException.java b/src/main/java/com/google/devtools/build/skydoc/rendering/DocstringParseException.java
new file mode 100644
index 0000000..a7825d1
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/DocstringParseException.java
@@ -0,0 +1,50 @@
+// Copyright 2019 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.skydoc.rendering;
+
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.UserDefinedFunctionInfo;
+import com.google.devtools.skylark.common.DocstringUtils.DocstringParseError;
+import java.util.List;
+
+/**
+ * An exception that may be thrown during construction of {@link UserDefinedFunctionInfo} if the
+ * function's docstring is malformed.
+ */
+public class DocstringParseException extends Exception {
+  public DocstringParseException(
+      String functionName, Location definedLocation, List<DocstringParseError> parseErrors) {
+    super(getMessage(functionName, definedLocation, parseErrors));
+  }
+
+  private static String getMessage(
+      String functionName, Location definedLocation, List<DocstringParseError> parseErrors) {
+    StringBuilder message = new StringBuilder();
+    message.append(
+        String.format(
+            "Unable to generate documentation for function %s (defined at %s) "
+                + "due to malformed docstring. Parse errors:\n",
+            functionName, definedLocation));
+    for (DocstringParseError parseError : parseErrors) {
+      message.append(
+          String.format(
+              "  %s line %s: %s\n",
+              definedLocation,
+              parseError.getLineNumber(),
+              parseError.getMessage().replace('\n', ' ')));
+    }
+    return message.toString();
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/UserDefinedFunctionInfo.java b/src/main/java/com/google/devtools/build/skydoc/rendering/FunctionUtil.java
similarity index 70%
rename from src/main/java/com/google/devtools/build/skydoc/rendering/UserDefinedFunctionInfo.java
rename to src/main/java/com/google/devtools/build/skydoc/rendering/FunctionUtil.java
index 2e7c9df..2119b07 100644
--- a/src/main/java/com/google/devtools/build/skydoc/rendering/UserDefinedFunctionInfo.java
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/FunctionUtil.java
@@ -1,4 +1,4 @@
-// Copyright 2018 The Bazel Authors. All rights reserved.
+// Copyright 2019 The Bazel Authors. All rights reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.syntax.FunctionSignature;
 import com.google.devtools.build.lib.syntax.Printer;
 import com.google.devtools.build.lib.syntax.Printer.BasePrinter;
@@ -25,52 +24,17 @@
 import com.google.devtools.build.lib.syntax.StringLiteral;
 import com.google.devtools.build.lib.syntax.UserDefinedFunction;
 import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.FunctionParamInfo;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.UserDefinedFunctionInfo;
 import com.google.devtools.skylark.common.DocstringUtils;
 import com.google.devtools.skylark.common.DocstringUtils.DocstringInfo;
 import com.google.devtools.skylark.common.DocstringUtils.DocstringParseError;
 import com.google.devtools.skylark.common.DocstringUtils.ParameterDoc;
-import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import javax.annotation.Nullable;
 
-/** Encapsulates information about a user-defined Starlark function. */
-public class UserDefinedFunctionInfo {
-
-  private final String functionName;
-  private final Collection<FunctionParamInfo> parameters;
-  private final String docString;
-
-  /**
-   * An exception that may be thrown during construction of {@link UserDefinedFunctionInfo} if the
-   * function's docstring is malformed.
-   */
-  public static class DocstringParseException extends Exception {
-    public DocstringParseException(
-        String functionName, Location definedLocation, List<DocstringParseError> parseErrors) {
-      super(getMessage(functionName, definedLocation, parseErrors));
-    }
-
-    private static String getMessage(
-        String functionName, Location definedLocation, List<DocstringParseError> parseErrors) {
-      StringBuilder message = new StringBuilder();
-      message.append(
-          String.format(
-              "Unable to generate documentation for function %s (defined at %s) "
-                  + "due to malformed docstring. Parse errors:\n",
-              functionName, definedLocation));
-      for (DocstringParseError parseError : parseErrors) {
-        message.append(
-            String.format(
-                "  %s line %s: %s\n",
-                definedLocation,
-                parseError.getLineNumber(),
-                parseError.getMessage().replace('\n', ' ')));
-      }
-      return message.toString();
-    }
-  }
-
+/** Contains a number of utility methods for functions and parameters. */
+public final class FunctionUtil {
   /**
    * Create and return a {@link UserDefinedFunctionInfo} object encapsulating information obtained
    * from the given function and from its parsed docstring.
@@ -79,7 +43,8 @@
    *     the original exported function name; the function may have been renamed in the target
    *     Starlark file's scope)
    * @param userDefinedFunction the raw function object
-   * @throws DocstringParseException if the function's docstring is malformed
+   * @throws com.google.devtools.build.skydoc.rendering.DocstringParseException if the function's
+   *     docstring is malformed
    */
   public static UserDefinedFunctionInfo fromNameAndFunction(
       String functionName, UserDefinedFunction userDefinedFunction) throws DocstringParseException {
@@ -105,11 +70,12 @@
         paramNameToDocMap.put(paramDoc.getParameterName(), paramDoc.getDescription());
       }
     }
-
-    return new UserDefinedFunctionInfo(
-        functionName,
-        parameterInfos(userDefinedFunction, paramNameToDocMap),
-        functionDescription);
+    List<FunctionParamInfo> paramsInfo = parameterInfos(userDefinedFunction, paramNameToDocMap);
+    return UserDefinedFunctionInfo.newBuilder()
+        .setFunctionName(functionName)
+        .setDocString(functionDescription)
+        .addAllParameters(paramsInfo)
+        .build();
   }
 
   /** Constructor to be used for normal parameters. */
@@ -142,8 +108,7 @@
   }
 
   private static List<FunctionParamInfo> parameterInfos(
-      UserDefinedFunction userDefinedFunction,
-      Map<String, String> paramNameToDocMap)  {
+      UserDefinedFunction userDefinedFunction, Map<String, String> paramNameToDocMap) {
     FunctionSignature.WithValues<Object, SkylarkType> signature =
         userDefinedFunction.getSignature();
     ImmutableList.Builder<FunctionParamInfo> parameterInfos = ImmutableList.builder();
@@ -203,32 +168,4 @@
     }
     return parameterInfos.build();
   }
-
-  private UserDefinedFunctionInfo(
-      String functionName, Collection<FunctionParamInfo> parameters, String docString) {
-    this.functionName = functionName;
-    this.parameters = parameters;
-    this.docString = docString;
-  }
-
-  /** Returns the raw name of this function, for example, "my_function". */
-  public String getName() {
-    return functionName;
-  }
-
-  /**
-   * Returns a collection of {@link FunctionParamInfo} objects, where each encapsulates information
-   * about what of the function parameters. Ordering matches the actual Starlark function signature.
-   */
-  public Collection<FunctionParamInfo> getParameters() {
-    return parameters;
-  }
-
-  /**
-   * Returns the portion of the docstring that is not part of any special sections, such as "Args:"
-   * or "Returns:". Returns the empty string if there is no docstring literal for this function.
-   */
-  public String getDocString() {
-    return docString;
-  }
 }
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownRenderer.java b/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownRenderer.java
index 5591fe9..96dc91c 100644
--- a/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownRenderer.java
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownRenderer.java
@@ -14,6 +14,7 @@
 
 package com.google.devtools.build.skydoc.rendering;
 
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.UserDefinedFunctionInfo;
 import java.io.IOException;
 import java.io.StringWriter;
 import org.apache.velocity.VelocityContext;
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java b/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java
index f902e01..3e9010f 100644
--- a/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java
@@ -18,6 +18,7 @@
 import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeInfo;
 import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeType;
 import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.FunctionParamInfo;
+import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.UserDefinedFunctionInfo;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -62,10 +63,11 @@
    */
   @SuppressWarnings("unused") // Used by markdown template.
   public String funcSummary(UserDefinedFunctionInfo funcInfo) {
-    List<String> paramNames = funcInfo.getParameters().stream()
-        .map(param -> param.getName())
-        .collect(Collectors.toList());
-    return summary(funcInfo.getName(), paramNames);
+    List<String> paramNames =
+        funcInfo.getParametersList().stream()
+            .map(param -> param.getName())
+            .collect(Collectors.toList());
+    return summary(funcInfo.getFunctionName(), paramNames);
   }
 
   private String summary(String functionName, List<String> paramNames) {
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/proto/stardoc_output.proto b/src/main/java/com/google/devtools/build/skydoc/rendering/proto/stardoc_output.proto
index fc7feba..65b9061 100644
--- a/src/main/java/com/google/devtools/build/skydoc/rendering/proto/stardoc_output.proto
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/proto/stardoc_output.proto
@@ -65,6 +65,19 @@
   bool mandatory = 4;
 }
 
+// Representation of Starlark function definition.
+message UserDefinedFunctionInfo {
+  // The name of the function.
+  string function_name = 1;
+
+  // The parameters for the function.
+  repeated FunctionParamInfo parameters = 2;
+
+  // The documented description of the function (if specified in the function's
+  // docstring).
+  string doc_string = 3;
+}
+
 // Representation of a Starlark function parameter definition.
 message FunctionParamInfo {
   // The name of the parameter.
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/templates/func.vm b/src/main/java/com/google/devtools/build/skydoc/rendering/templates/func.vm
index 3046495..a6bd36e 100644
--- a/src/main/java/com/google/devtools/build/skydoc/rendering/templates/func.vm
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/templates/func.vm
@@ -1,6 +1,6 @@
-<a name="#${funcInfo.name}"></a>
+<a name="#${funcInfo.functionName}"></a>
 
-#[[##]]# ${funcInfo.name}
+#[[##]]# ${funcInfo.functionName}
 
 <pre>
 ${util.funcSummary($funcInfo)}
@@ -8,7 +8,7 @@
 
 ${funcInfo.docString}
 
-#if (!$funcInfo.parameters.isEmpty())
+#if (!$funcInfo.getParametersList().isEmpty())
 #[[###]]# Parameters
 
 <table class="params-table">
@@ -17,8 +17,8 @@
     <col class="col-description" />
   </colgroup>
   <tbody>
-#foreach ($param in $funcInfo.parameters)
-    <tr id="${funcInfo.name}-${param.name}">
+#foreach ($param in $funcInfo.getParametersList())
+    <tr id="${funcInfo.functionName}-${param.name}">
       <td><code>${param.name}</code></td>
       <td>
         ${util.mandatoryString($param)}.#if(!$param.getDefaultValue().isEmpty()) default is <code>$param.getDefaultValue()</code>#end
diff --git a/src/test/java/com/google/devtools/build/skydoc/SkydocTest.java b/src/test/java/com/google/devtools/build/skydoc/SkydocTest.java
index 0601cf6..0948690 100644
--- a/src/test/java/com/google/devtools/build/skydoc/SkydocTest.java
+++ b/src/test/java/com/google/devtools/build/skydoc/SkydocTest.java
@@ -28,9 +28,9 @@
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.skydoc.SkydocMain.StarlarkEvaluationException;
+import com.google.devtools.build.skydoc.rendering.DocstringParseException;
+import com.google.devtools.build.skydoc.rendering.FunctionUtil;
 import com.google.devtools.build.skydoc.rendering.RuleInfo;
-import com.google.devtools.build.skydoc.rendering.UserDefinedFunctionInfo;
-import com.google.devtools.build.skydoc.rendering.UserDefinedFunctionInfo.DocstringParseException;
 import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeType;
 import java.io.IOException;
 import java.util.Map;
@@ -377,7 +377,7 @@
     DocstringParseException expected =
         assertThrows(
             DocstringParseException.class,
-            () -> UserDefinedFunctionInfo.fromNameAndFunction("check_sources", checkSourcesFn));
+            () -> FunctionUtil.fromNameAndFunction("check_sources", checkSourcesFn));
     assertThat(expected)
         .hasMessageThat()
         .contains(