blob: 98aec8a05dc3cfb919388e2b4e619ba38f65501b [file] [log] [blame]
kendalllane9ca07442019-06-10 09:18:14 -07001// Copyright 2019 The Bazel Authors. All rights reserved.
cparsons2b98ef72018-11-07 14:05:11 -08002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.skydoc.rendering;
16
cparsons2b98ef72018-11-07 14:05:11 -080017import com.google.common.collect.ImmutableList;
18import com.google.common.collect.Lists;
19import com.google.common.collect.Maps;
kendalllane267df732019-06-05 13:40:19 -070020import com.google.devtools.build.lib.syntax.Printer;
21import com.google.devtools.build.lib.syntax.Printer.BasePrinter;
Googler3fcfbe12019-08-28 08:10:11 -070022import com.google.devtools.build.lib.syntax.StarlarkFunction;
kendalllane267df732019-06-05 13:40:19 -070023import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.FunctionParamInfo;
Googler3fcfbe12019-08-28 08:10:11 -070024import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.StarlarkFunctionInfo;
gregce3ec8d812020-06-18 08:05:01 -070025import com.google.devtools.starlark.common.DocstringUtils;
26import com.google.devtools.starlark.common.DocstringUtils.DocstringInfo;
27import com.google.devtools.starlark.common.DocstringUtils.DocstringParseError;
28import com.google.devtools.starlark.common.DocstringUtils.ParameterDoc;
cparsons2b98ef72018-11-07 14:05:11 -080029import java.util.List;
30import java.util.Map;
kendalllane267df732019-06-05 13:40:19 -070031import javax.annotation.Nullable;
cparsons2b98ef72018-11-07 14:05:11 -080032
kendalllane9ca07442019-06-10 09:18:14 -070033/** Contains a number of utility methods for functions and parameters. */
34public final class FunctionUtil {
cparsons2b98ef72018-11-07 14:05:11 -080035 /**
Googler3fcfbe12019-08-28 08:10:11 -070036 * Create and return a {@link StarlarkFunctionInfo} object encapsulating information obtained from
37 * the given function and from its parsed docstring.
cparsons2b98ef72018-11-07 14:05:11 -080038 *
39 * @param functionName the name of the function in the target scope. (Note this is not necessarily
40 * the original exported function name; the function may have been renamed in the target
41 * Starlark file's scope)
adonovanf34b8052020-01-16 09:42:22 -080042 * @param fn the function object
kendalllane9ca07442019-06-10 09:18:14 -070043 * @throws com.google.devtools.build.skydoc.rendering.DocstringParseException if the function's
44 * docstring is malformed
cparsons2b98ef72018-11-07 14:05:11 -080045 */
adonovanf34b8052020-01-16 09:42:22 -080046 public static StarlarkFunctionInfo fromNameAndFunction(String functionName, StarlarkFunction fn)
47 throws DocstringParseException {
cparsons2b98ef72018-11-07 14:05:11 -080048 String functionDescription = "";
49 Map<String, String> paramNameToDocMap = Maps.newLinkedHashMap();
50
adonovanf34b8052020-01-16 09:42:22 -080051 String doc = fn.getDocumentation();
52 if (doc != null) {
cparsons2b98ef72018-11-07 14:05:11 -080053 List<DocstringParseError> parseErrors = Lists.newArrayList();
adonovanf34b8052020-01-16 09:42:22 -080054 DocstringInfo docstringInfo = DocstringUtils.parseDocstring(doc, parseErrors);
cparsons2b98ef72018-11-07 14:05:11 -080055 if (!parseErrors.isEmpty()) {
adonovanf34b8052020-01-16 09:42:22 -080056 throw new DocstringParseException(functionName, fn.getLocation(), parseErrors);
cparsons2b98ef72018-11-07 14:05:11 -080057 }
58 functionDescription += docstringInfo.getSummary();
59 if (!docstringInfo.getSummary().isEmpty() && !docstringInfo.getLongDescription().isEmpty()) {
60 functionDescription += "\n\n";
61 }
62 functionDescription += docstringInfo.getLongDescription();
63 for (ParameterDoc paramDoc : docstringInfo.getParameters()) {
64 paramNameToDocMap.put(paramDoc.getParameterName(), paramDoc.getDescription());
65 }
66 }
adonovanf34b8052020-01-16 09:42:22 -080067 List<FunctionParamInfo> paramsInfo = parameterInfos(fn, paramNameToDocMap);
Googler3fcfbe12019-08-28 08:10:11 -070068 return StarlarkFunctionInfo.newBuilder()
kendalllane9ca07442019-06-10 09:18:14 -070069 .setFunctionName(functionName)
70 .setDocString(functionDescription)
kendalllane1dbbc0142019-06-14 07:50:24 -070071 .addAllParameter(paramsInfo)
kendalllane9ca07442019-06-10 09:18:14 -070072 .build();
cparsons2b98ef72018-11-07 14:05:11 -080073 }
74
kendalllane267df732019-06-05 13:40:19 -070075 /** Constructor to be used for normal parameters. */
76 public static FunctionParamInfo forParam(
77 String name, String docString, @Nullable Object defaultValue) {
78 FunctionParamInfo.Builder paramBuilder =
79 FunctionParamInfo.newBuilder().setName(name).setDocString(docString);
80 if (defaultValue == null) {
81 paramBuilder.setMandatory(true);
82 } else {
83 BasePrinter printer = Printer.getSimplifiedPrinter();
84 printer.repr(defaultValue);
85 String defaultValueString = printer.toString();
86
87 if (defaultValueString.isEmpty()) {
88 defaultValueString = "{unknown object}";
89 }
90 paramBuilder.setDefaultValue(defaultValueString).setMandatory(false);
91 }
92 return paramBuilder.build();
93 }
94
95 /** Constructor to be used for *args or **kwargs. */
96 public static FunctionParamInfo forSpecialParam(String name, String docString) {
97 return FunctionParamInfo.newBuilder()
98 .setName(name)
99 .setDocString(docString)
100 .setMandatory(false)
101 .build();
102 }
103
cparsons2b98ef72018-11-07 14:05:11 -0800104 private static List<FunctionParamInfo> parameterInfos(
adonovancac48fe2020-04-23 12:31:21 -0700105 StarlarkFunction fn, Map<String, String> parameterDoc) {
106 List<String> names = fn.getParameterNames();
107 int nparams = names.size();
108 int kwargsIndex = fn.hasKwargs() ? --nparams : -1;
109 int varargsIndex = fn.hasVarargs() ? --nparams : -1;
110 // Inv: nparams is number of regular parameters.
cparsons2b98ef72018-11-07 14:05:11 -0800111
adonovancac48fe2020-04-23 12:31:21 -0700112 ImmutableList.Builder<FunctionParamInfo> infos = ImmutableList.builder();
113 for (int i = 0; i < names.size(); i++) {
114 String name = names.get(i);
115 FunctionParamInfo info;
116 if (i == varargsIndex) {
117 // *args
118 String doc = parameterDoc.getOrDefault("*" + name, "");
119 info = forSpecialParam(name, doc);
120 } else if (i == kwargsIndex) {
121 // **kwargs
122 String doc = parameterDoc.getOrDefault("**" + name, "");
123 info = forSpecialParam(name, doc);
124 } else {
125 // regular parameter
126 String doc = parameterDoc.getOrDefault(name, "");
127 info = forParam(name, doc, fn.getDefaultValue(i));
cparsons2b98ef72018-11-07 14:05:11 -0800128 }
adonovancac48fe2020-04-23 12:31:21 -0700129 infos.add(info);
cparsonsa249de12019-02-25 12:00:10 -0800130 }
adonovancac48fe2020-04-23 12:31:21 -0700131 return infos.build();
cparsons2b98ef72018-11-07 14:05:11 -0800132 }
cparsons2b98ef72018-11-07 14:05:11 -0800133}