Create flag to turn down named-ness of certain Starlark params
This change creates --experimental_restrict_named_params to make all builtin Starlark parameters which are named via legacyNamed = true to instead be treated as positional-only.
This is a step on the path to cleaning up namedness of builtin Starlark parameters.
When we finalize the list of parameters which should truly be positional-only, we can change this flag to be prefixed with --incompatible instead of --experimental, as per the incompatible change policies.
Progress toward #5010.
RELNOTES: None.
PiperOrigin-RevId: 236898018
diff --git a/src/main/java/com/google/devtools/build/lib/packages/StarlarkSemanticsOptions.java b/src/main/java/com/google/devtools/build/lib/packages/StarlarkSemanticsOptions.java
index 4231d1f..1ef10cb 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/StarlarkSemanticsOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/StarlarkSemanticsOptions.java
@@ -126,6 +126,19 @@
+ "debugging.")
public boolean experimentalPlatformsApi;
+ // TODO(cparsons): Change this flag to --incompatible instead of --experimental when it is
+ // fully implemented.
+ @Option(
+ name = "experimental_restrict_named_params",
+ defaultValue = "false",
+ documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
+ effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
+ metadataTags = {OptionMetadataTag.EXPERIMENTAL},
+ help =
+ "If set to true, restricts a number of Starlark built-in function parameters to be "
+ + "only specifiable positionally (and not by keyword).")
+ public boolean experimentalRestrictNamedParams;
+
// TODO(cparsons): Resolve and finalize the transition() API. The transition implementation
// function should accept two mandatory parameters, 'settings' and 'attr'.
@Option(
@@ -551,6 +564,7 @@
.experimentalJavaCommonCreateProviderEnabledPackages(
experimentalJavaCommonCreateProviderEnabledPackages)
.experimentalPlatformsApi(experimentalPlatformsApi)
+ .experimentalRestrictNamedParams(experimentalRestrictNamedParams)
.experimentalStarlarkConfigTransitions(experimentalStarlarkConfigTransitions)
.experimentalTransitionWhitelistLocation(experimentalTransitionWhitelistLocation)
.incompatibleBzlDisallowLoadAfterStatement(incompatibleBzlDisallowLoadAfterStatement)
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java b/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java
index 6cff955..344ba77 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java
@@ -221,7 +221,7 @@
boolean allowNonDefaultPositionalNext = true;
for (Param parameter : annotation.parameters()) {
- if ((!parameter.positional()) && (!isParamNamed(parameter))) {
+ if (!parameter.positional() && !parameter.named()) {
throw new SkylarkCallableProcessorException(
methodElement,
String.format("Parameter '%s' must be either positional or named",
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
index 33104a3..3785595 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
@@ -553,10 +553,7 @@
}
} else { // Param not specified by user. Use default value.
if (param.getDefaultValue().isEmpty()) {
- throw argumentMismatchException(
- String.format("parameter '%s' has no default value", param.getName()),
- method,
- objClass);
+ throw unspecifiedParameterException(param, method, objClass, kwargs);
}
value =
SkylarkSignatureProcessor.getDefaultValue(
@@ -613,6 +610,22 @@
return builder.build().toArray();
}
+ private EvalException unspecifiedParameterException(
+ ParamDescriptor param,
+ MethodDescriptor method,
+ Class<?> objClass,
+ Map<String, Object> kwargs) {
+ if (kwargs.containsKey(param.getName())) {
+ return argumentMismatchException(
+ String.format("parameter '%s' may not be specified by name", param.getName()),
+ method,
+ objClass);
+ } else {
+ return argumentMismatchException(
+ String.format("parameter '%s' has no default value", param.getName()), method, objClass);
+ }
+ }
+
private EvalException unexpectedKeywordArgumentException(
Set<String> unexpectedKeywords,
MethodDescriptor method,
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/ParamDescriptor.java b/src/main/java/com/google/devtools/build/lib/syntax/ParamDescriptor.java
index ec10c19..7a47cc1 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/ParamDescriptor.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/ParamDescriptor.java
@@ -56,7 +56,6 @@
Class<?> generic1,
boolean noneable,
boolean named,
- boolean legacyNamed,
boolean positional,
SkylarkType skylarkType,
@Nullable String valueOverride,
@@ -67,7 +66,7 @@
this.allowedTypes = allowedTypes;
this.generic1 = generic1;
this.noneable = noneable;
- this.named = named || legacyNamed;
+ this.named = named;
this.positional = positional;
this.skylarkType = skylarkType;
this.valueOverride = valueOverride;
@@ -103,14 +102,20 @@
allowedTypes,
generic,
noneable,
- param.named(),
- param.legacyNamed(),
+ isNamed(param, starlarkSemantics),
param.positional(),
getType(type, generic, allowedTypes, noneable),
valueOverride,
flagResponsibleForDisable);
}
+ private static boolean isNamed(Param param, StarlarkSemantics starlarkSemantics) {
+ if (param.named()) {
+ return true;
+ }
+ return param.legacyNamed() && !starlarkSemantics.experimentalRestrictNamedParams();
+ }
+
/** @see Param#name() */
public String getName() {
return name;
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkSemantics.java b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkSemantics.java
index b2a879c..857d7b9 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkSemantics.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkSemantics.java
@@ -127,6 +127,8 @@
public abstract boolean experimentalPlatformsApi();
+ public abstract boolean experimentalRestrictNamedParams();
+
public abstract boolean experimentalStarlarkConfigTransitions();
public abstract String experimentalTransitionWhitelistLocation();
@@ -208,6 +210,7 @@
.experimentalEnableAndroidMigrationApis(false)
.experimentalJavaCommonCreateProviderEnabledPackages(ImmutableList.of())
.experimentalPlatformsApi(false)
+ .experimentalRestrictNamedParams(false)
.experimentalStarlarkConfigTransitions(false)
.experimentalTransitionWhitelistLocation("")
.incompatibleUseToolchainProvidersInJavaCommon(false)
@@ -257,6 +260,8 @@
public abstract Builder experimentalPlatformsApi(boolean value);
+ public abstract Builder experimentalRestrictNamedParams(boolean value);
+
public abstract Builder experimentalStarlarkConfigTransitions(boolean value);
public abstract Builder experimentalTransitionWhitelistLocation(String value);
diff --git a/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java b/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
index 9ac3941..563c864 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
@@ -130,6 +130,7 @@
+ ","
+ rand.nextDouble(),
"--experimental_platforms_api=" + rand.nextBoolean(),
+ "--experimental_restrict_named_params=" + rand.nextBoolean(),
"--experimental_starlark_config_transitions=" + rand.nextBoolean(),
"--experimental_transition_whitelist_location=" + rand.nextDouble(),
"--incompatible_bzl_disallow_load_after_statement=" + rand.nextBoolean(),
@@ -177,6 +178,7 @@
.experimentalJavaCommonCreateProviderEnabledPackages(
ImmutableList.of(String.valueOf(rand.nextDouble()), String.valueOf(rand.nextDouble())))
.experimentalPlatformsApi(rand.nextBoolean())
+ .experimentalRestrictNamedParams(rand.nextBoolean())
.experimentalStarlarkConfigTransitions(rand.nextBoolean())
.experimentalTransitionWhitelistLocation(String.valueOf(rand.nextDouble()))
.incompatibleBzlDisallowLoadAfterStatement(rand.nextBoolean())
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java b/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
index a7a74fc..af019ff 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
@@ -745,4 +745,13 @@
.testIfErrorContains("None", "fail(msg=None)")
.testEval("type(None)", "'NoneType'");
}
+
+ @Test
+ public void testExperimentalStarlarkConfig() throws Exception {
+ new SkylarkTest("--experimental_restrict_named_params")
+ .testIfErrorContains(
+ "parameter 'elements' may not be specified by name, "
+ + "for call to method join(elements) of 'string'",
+ "','.join(elements=['foo', 'bar'])");
+ }
}