From bf5504a8c8f2d5e91acb18db864b2dae0d90ca7f Mon Sep 17 00:00:00 2001 From: Googler Date: Tue, 22 Aug 2023 07:57:16 -0700 Subject: [PATCH] starlark_doc_extract: expose providers' custom init callback in extracted docs Note that init's signature may have little relation to the list of fields of the constructed provider, so exposing a full StarlarkFunctionInfo for the init is appropriate. Working towards https://github.com/bazelbuild/stardoc/issues/182 PiperOrigin-RevId: 559112284 Change-Id: I5a4821cde492676553a40400ff1e7831a069294f --- .../build/lib/packages/StarlarkProvider.java | 6 +-- .../ModuleInfoExtractor.java | 22 +++++++- .../rendering/proto/stardoc_output.proto | 7 ++- .../ModuleInfoExtractorTest.java | 54 +++++++++++++++++++ 4 files changed, 82 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/packages/StarlarkProvider.java b/src/main/java/com/google/devtools/build/lib/packages/StarlarkProvider.java index 096647a0719a7c..ab43efe2be93f3 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/StarlarkProvider.java +++ b/src/main/java/com/google/devtools/build/lib/packages/StarlarkProvider.java @@ -14,8 +14,6 @@ package com.google.devtools.build.lib.packages; - -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -306,8 +304,10 @@ public StarlarkCallable createRawConstructor() { return new RawConstructor(this); } + /** + * Returns the provider's custom initializer callback, or null if the provider doesn't have one. + */ @Nullable - @VisibleForTesting public StarlarkCallable getInit() { return init; } diff --git a/src/main/java/com/google/devtools/build/lib/rules/starlarkdocextract/ModuleInfoExtractor.java b/src/main/java/com/google/devtools/build/lib/rules/starlarkdocextract/ModuleInfoExtractor.java index 3aac9268968f28..f52ae7afafa628 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/starlarkdocextract/ModuleInfoExtractor.java +++ b/src/main/java/com/google/devtools/build/lib/rules/starlarkdocextract/ModuleInfoExtractor.java @@ -218,7 +218,8 @@ private void maybeVisit( protected void visitRule(String qualifiedName, StarlarkRuleFunction value) throws ExtractionException {} - protected void visitProvider(String qualifiedName, StarlarkProvider value) {} + protected void visitProvider(String qualifiedName, StarlarkProvider value) + throws ExtractionException {} protected void visitFunction(String qualifiedName, StarlarkFunction value) throws ExtractionException {} @@ -376,7 +377,8 @@ protected void visitRule(String qualifiedName, StarlarkRuleFunction ruleFunction } @Override - protected void visitProvider(String qualifiedName, StarlarkProvider provider) { + protected void visitProvider(String qualifiedName, StarlarkProvider provider) + throws ExtractionException { ProviderInfo.Builder providerInfoBuilder = ProviderInfo.newBuilder(); // Record the name under which this symbol is made accessible, which may differ from the // symbol's exported name. @@ -408,6 +410,22 @@ protected void visitProvider(String qualifiedName, StarlarkProvider provider) { } } } + // TODO(b/276733504): if init is a dict-returning native method (e.g. `dict`), do we document + // it? (This is very unlikely to be useful at present, and would require parsing annotations + // on the native method.) + if (provider.getInit() instanceof StarlarkFunction) { + try { + providerInfoBuilder.setInit( + StarlarkFunctionInfoExtractor.fromNameAndFunction( + qualifiedName, + (StarlarkFunction) provider.getInit(), + /* withOriginKey= */ true, + labelRenderer)); + } catch (DocstringParseException e) { + throw new ExtractionException(e); + } + } + moduleInfoBuilder.addProviderInfo(providerInfoBuilder); } 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 bb1bdf5d98036d..a165a33581414a 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 @@ -241,11 +241,14 @@ message ProviderInfo { // The fields of the provider. repeated ProviderFieldInfo field_info = 3; + // Note: legacy Stardoc (0.5.x and earlier) does not set any fields below. + // The module where and the name under which the provider was originally // declared. - // - // Note: legacy Stardoc (0.5.x and earlier) does not set this field. OriginKey origin_key = 4; + + // The provider's init callback. + StarlarkFunctionInfo init = 5; } // Representation of a Starlark aspect definition. diff --git a/src/test/java/com/google/devtools/build/lib/rules/starlarkdocextract/ModuleInfoExtractorTest.java b/src/test/java/com/google/devtools/build/lib/rules/starlarkdocextract/ModuleInfoExtractorTest.java index 6e75c633fdf81e..40deba3f9cbe26 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/starlarkdocextract/ModuleInfoExtractorTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/starlarkdocextract/ModuleInfoExtractorTest.java @@ -389,6 +389,60 @@ public void providerFields() throws Exception { .build()); } + @Test + public void providerInit() throws Exception { + Module module = + exec( + "def _my_info_init(x_value, y_value = 0):", + " '''MyInfo constructor", + "", + " Args:", + " x_value: my x value", + " y_value: my y value", + " '''", + " return {'x': x_value, 'y': y_value}", + "", + "_MyInfo, _new_my_info = provider(", + " doc = '''My provider''',", + " fields = ['x', 'y'],", + " init = _my_info_init,", + ")", + "", + "namespace = struct(", + " MyInfo = _MyInfo,", + ")"); + ModuleInfo moduleInfo = getExtractor().extractFrom(module); + assertThat(moduleInfo.getProviderInfoList()) + .containsExactly( + ProviderInfo.newBuilder() + .setProviderName("namespace.MyInfo") + .setDocString("My provider") + .addFieldInfo(ProviderFieldInfo.newBuilder().setName("x")) + .addFieldInfo(ProviderFieldInfo.newBuilder().setName("y")) + .setInit( + StarlarkFunctionInfo.newBuilder() + .setFunctionName("namespace.MyInfo") + .setDocString("MyInfo constructor") + .addParameter( + FunctionParamInfo.newBuilder() + .setName("x_value") + .setDocString("my x value") + .setMandatory(true) + .build()) + .addParameter( + FunctionParamInfo.newBuilder() + .setName("y_value") + .setDocString("my y value") + .setDefaultValue("0") + .build()) + .setOriginKey( + OriginKey.newBuilder() + .setName("_my_info_init") + .setFile(fakeLabelString))) + .setOriginKey(OriginKey.newBuilder().setName("_MyInfo").setFile(fakeLabelString)) + .build()); + } + @Test public void ruleDocstring() throws Exception { Module module =