Create infrastructure for convenient control of Starlark Params by semantic flag Also migrate ~3 examples of parameter control to use the new infrastructure, as a manner of demonstration. RELNOTES: None. PiperOrigin-RevId: 221332133
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java index fb2831b..b332f98 100644 --- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java +++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
@@ -1081,7 +1081,9 @@ reporter.removeHandler(failFastHandler); getConfiguredTarget("//test:r"); - assertContainsEvent("'default' is no longer a supported parameter for attr.output"); + assertContainsEvent( + "parameter 'default' is deprecated and will be removed soon. It may be " + + "temporarily re-enabled by setting --incompatible_no_output_attr_default=false"); } @Test @@ -1129,7 +1131,9 @@ reporter.removeHandler(failFastHandler); getConfiguredTarget("//test:r"); - assertContainsEvent("'default' is no longer a supported parameter for attr.output_list"); + assertContainsEvent( + "parameter 'default' is deprecated and will be removed soon. It may be " + + "temporarily re-enabled by setting --incompatible_no_output_attr_default=false"); } @Test
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java index 60dc76f..7579775 100644 --- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java +++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
@@ -771,15 +771,14 @@ public void testRuleUnknownKeyword() throws Exception { registerDummyUserDefinedFunction(); checkErrorContains( - "unexpected keyword 'bad_keyword' in call to " + "rule(implementation: function, ", + "unexpected keyword 'bad_keyword', in call to rule(function, string bad_keyword)", "rule(impl, bad_keyword = 'some text')"); } @Test public void testRuleImplementationMissing() throws Exception { checkErrorContains( - "missing mandatory positional argument 'implementation' while calling " - + "rule(implementation", + "parameter 'implementation' has no default value, in call to rule(dict attrs)", "rule(attrs = {})"); } @@ -787,8 +786,8 @@ public void testRuleBadTypeForAdd() throws Exception { registerDummyUserDefinedFunction(); checkErrorContains( - "expected dict or NoneType for 'attrs' while calling rule but got string instead: " - + "some text", + "expected value of type 'dict or NoneType' for parameter 'attrs', " + + "in call to rule(function, string attrs)", "rule(impl, attrs = 'some text')"); } @@ -804,7 +803,7 @@ public void testRuleBadTypeForDoc() throws Exception { registerDummyUserDefinedFunction(); checkErrorContains( - "expected string for 'doc' while calling rule but got int instead", + "expected value of type 'string' for parameter 'doc', in call to rule(function, int doc)", "rule(impl, doc = 1)"); } @@ -1416,7 +1415,7 @@ @Test public void declaredProvidersBadTypeForDoc() throws Exception { checkErrorContains( - "expected string for 'doc' while calling provider but got int instead", + "expected value of type 'string' for parameter 'doc', in call to provider(int doc)", "provider(doc = 1)"); } @@ -1559,7 +1558,7 @@ public void aspectBadTypeForDoc() throws Exception { registerDummyUserDefinedFunction(); checkErrorContains( - "expected string for 'doc' while calling aspect but got int instead", + "expected value of type 'string' for parameter 'doc', in call to aspect(function, int doc)", "aspect(impl, doc = 1)"); }
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessorTest.java b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessorTest.java index 98bd519..aa78222 100644 --- a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessorTest.java +++ b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessorTest.java
@@ -317,6 +317,17 @@ } @Test + public void testEnablingAndDisablingFlag_param() throws Exception { + assertAbout(javaSource()) + .that(getFile("EnablingAndDisablingFlagParam.java")) + .processedWith(new SkylarkCallableProcessor()) + .failsToCompile() + .withErrorContaining( + "Parameter 'two' has enableOnlyWithFlag and disableWithFlag set. " + + "At most one may be set"); + } + + @Test public void testConflictingMethodNames() throws Exception { assertAbout(javaSource()) .that(getFile("ConflictingMethodNames.java")) @@ -325,4 +336,33 @@ .withErrorContaining("Containing class has more than one method with name " + "'conflicting_method' defined"); } + + @Test + public void testDisabledValueParamNoToggle() throws Exception { + assertAbout(javaSource()) + .that(getFile("DisabledValueParamNoToggle.java")) + .processedWith(new SkylarkCallableProcessor()) + .failsToCompile() + .withErrorContaining("Parameter 'two' has valueWhenDisabled set, but is always enabled"); + } + + @Test + public void testToggledKwargsParam() throws Exception { + assertAbout(javaSource()) + .that(getFile("ToggledKwargsParam.java")) + .processedWith(new SkylarkCallableProcessor()) + .failsToCompile() + .withErrorContaining("The extraKeywords parameter may not be toggled by semantic flag"); + } + + @Test + public void testToggledParamNoDisabledValue() throws Exception { + assertAbout(javaSource()) + .that(getFile("ToggledParamNoDisabledValue.java")) + .processedWith(new SkylarkCallableProcessor()) + .failsToCompile() + .withErrorContaining( + "Parameter 'two' may be disabled by semantic flag, " + + "thus valueWhenDisabled must be set"); + } }
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/DisabledValueParamNoToggle.java b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/DisabledValueParamNoToggle.java new file mode 100644 index 0000000..16e4edb --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/DisabledValueParamNoToggle.java
@@ -0,0 +1,36 @@ +// Copyright 2018 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.lib.skylarkinterface.processor.testsources; + +import com.google.devtools.build.lib.skylarkinterface.Param; +import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; + +/** + * Test case for a SkylarkCallable method which has a parameter which has a "disabled value" set but + * is always-on. + */ +public class DisabledValueParamNoToggle { + + @SkylarkCallable( + name = "no_toggle_method", + documented = false, + parameters = { + @Param(name = "one", named = true, positional = true), + @Param(name = "two", named = true, valueWhenDisabled = "3", positional = true) + }) + public Integer noToggleMethod(Integer one, Integer two) { + return 42; + } +}
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/EnablingAndDisablingFlagParam.java b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/EnablingAndDisablingFlagParam.java new file mode 100644 index 0000000..83274df --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/EnablingAndDisablingFlagParam.java
@@ -0,0 +1,42 @@ +// Copyright 2018 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.lib.skylarkinterface.processor.testsources; + +import com.google.devtools.build.lib.skylarkinterface.Param; +import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; +import com.google.devtools.build.lib.syntax.SkylarkSemantics.FlagIdentifier; + +/** + * Test case for a SkylarkCallable method which has a parameter with both enableOnlyWithFlag and + * disableWithFlag specified. + */ +public class EnablingAndDisablingFlagParam { + + @SkylarkCallable( + name = "someMethod", + documented = false, + parameters = { + @Param(name = "one", type = String.class, named = true), + @Param( + name = "two", + type = Integer.class, + named = true, + enableOnlyWithFlag = FlagIdentifier.EXPERIMENTAL_BUILD_SETTING_API, + disableWithFlag = FlagIdentifier.EXPERIMENTAL_BUILD_SETTING_API), + }) + public String someMethod(String one, Integer two) { + return "foo"; + } +}
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/ToggledKwargsParam.java b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/ToggledKwargsParam.java new file mode 100644 index 0000000..243b15a --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/ToggledKwargsParam.java
@@ -0,0 +1,51 @@ +// Copyright 2018 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.lib.skylarkinterface.processor.testsources; + +import com.google.devtools.build.lib.skylarkinterface.Param; +import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; +import com.google.devtools.build.lib.syntax.Environment; +import com.google.devtools.build.lib.syntax.SkylarkDict; +import com.google.devtools.build.lib.syntax.SkylarkList; +import com.google.devtools.build.lib.syntax.SkylarkSemantics.FlagIdentifier; + +/** + * Test case for a SkylarkCallable method which has a "extraKeywords" parameter which has + * enableOnlyWithFlag set. (This is unsupported.) + */ +public class ToggledKwargsParam { + + @SkylarkCallable( + name = "toggled_kwargs_method", + documented = false, + parameters = { + @Param(name = "one", type = String.class, named = true), + @Param(name = "two", type = Integer.class, named = true), + }, + extraPositionals = @Param(name = "args"), + extraKeywords = + @Param( + name = "kwargs", + enableOnlyWithFlag = FlagIdentifier.EXPERIMENTAL_BUILD_SETTING_API), + useEnvironment = true) + public String toggledKwargsMethod( + String one, + Integer two, + SkylarkList<?> args, + SkylarkDict<?, ?> kwargs, + Environment environment) { + return "cat"; + } +}
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/ToggledParamNoDisabledValue.java b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/ToggledParamNoDisabledValue.java new file mode 100644 index 0000000..eebbd28 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/ToggledParamNoDisabledValue.java
@@ -0,0 +1,41 @@ +// Copyright 2018 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.lib.skylarkinterface.processor.testsources; + +import com.google.devtools.build.lib.skylarkinterface.Param; +import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; +import com.google.devtools.build.lib.syntax.SkylarkSemantics.FlagIdentifier; + +/** + * Test case for a SkylarkCallable method which has a parameter which may be disabled with semantic + * flag but has no "disabled value". + */ +public class ToggledParamNoDisabledValue { + + @SkylarkCallable( + name = "no_disabled_value_method", + documented = false, + parameters = { + @Param(name = "one", named = true, positional = true), + @Param( + name = "two", + named = true, + enableOnlyWithFlag = FlagIdentifier.EXPERIMENTAL_BUILD_SETTING_API, + positional = true) + }) + public Integer noDisabledValueMethod(Integer one, Integer two) { + return 42; + } +}
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/StarlarkFlagGuardingTest.java b/src/test/java/com/google/devtools/build/lib/syntax/StarlarkFlagGuardingTest.java new file mode 100644 index 0000000..9839d9a --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/syntax/StarlarkFlagGuardingTest.java
@@ -0,0 +1,248 @@ +// Copyright 2018 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.lib.syntax; + +import com.google.devtools.build.lib.skylarkinterface.Param; +import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; +import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; +import com.google.devtools.build.lib.syntax.SkylarkSemantics.FlagIdentifier; +import com.google.devtools.build.lib.syntax.util.EvaluationTestCase; +import com.google.devtools.build.lib.testutil.TestMode; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Starlark evaluation tests which verify the infrastructure which toggles build API methods and + * parameters with semantic flags. + */ +@RunWith(JUnit4.class) +public class StarlarkFlagGuardingTest extends EvaluationTestCase { + + @Before + public final void setup() throws Exception { + setMode(TestMode.SKYLARK); + } + + /** Mock containing exposed methods for flag-guarding tests. */ + @SkylarkModule(name = "Mock", doc = "") + public static class Mock { + + @SkylarkCallable( + name = "positionals_only_method", + documented = false, + parameters = { + @Param(name = "a", positional = true, named = false, type = Integer.class), + @Param( + name = "b", + positional = true, + named = false, + type = Boolean.class, + enableOnlyWithFlag = FlagIdentifier.EXPERIMENTAL_ANALYSIS_TESTING_IMPROVEMENTS, + valueWhenDisabled = "False"), + @Param(name = "c", positional = true, named = false, type = Integer.class), + }, + useEnvironment = true) + public String positionalsOnlyMethod(Integer a, boolean b, Integer c, Environment env) { + return "positionals_only_method(" + a + ", " + b + ", " + c + ")"; + } + + @SkylarkCallable( + name = "keywords_only_method", + documented = false, + parameters = { + @Param(name = "a", positional = false, named = true, type = Integer.class), + @Param( + name = "b", + positional = false, + named = true, + type = Boolean.class, + enableOnlyWithFlag = FlagIdentifier.EXPERIMENTAL_ANALYSIS_TESTING_IMPROVEMENTS, + valueWhenDisabled = "False"), + @Param(name = "c", positional = false, named = true, type = Integer.class), + }, + useEnvironment = true) + public String keywordsOnlyMethod(Integer a, boolean b, Integer c, Environment env) { + return "keywords_only_method(" + a + ", " + b + ", " + c + ")"; + } + + @SkylarkCallable( + name = "mixed_params_method", + documented = false, + parameters = { + @Param(name = "a", positional = true, named = false, type = Integer.class), + @Param( + name = "b", + positional = true, + named = false, + type = Boolean.class, + enableOnlyWithFlag = FlagIdentifier.EXPERIMENTAL_ANALYSIS_TESTING_IMPROVEMENTS, + valueWhenDisabled = "False"), + @Param( + name = "c", + positional = false, + named = true, + type = Integer.class, + enableOnlyWithFlag = FlagIdentifier.EXPERIMENTAL_ANALYSIS_TESTING_IMPROVEMENTS, + valueWhenDisabled = "3"), + @Param(name = "d", positional = false, named = true, type = Boolean.class), + }, + useEnvironment = true) + public String mixedParamsMethod(Integer a, boolean b, Integer c, boolean d, Environment env) { + return "mixed_params_method(" + a + ", " + b + ", " + c + ", " + d + ")"; + } + + @SkylarkCallable( + name = "keywords_multiple_flags", + documented = false, + parameters = { + @Param(name = "a", positional = false, named = true, type = Integer.class), + @Param( + name = "b", + positional = false, + named = true, + type = Boolean.class, + disableWithFlag = FlagIdentifier.INCOMPATIBLE_NO_ATTR_LICENSE, + valueWhenDisabled = "False"), + @Param( + name = "c", + positional = false, + named = true, + type = Integer.class, + enableOnlyWithFlag = FlagIdentifier.EXPERIMENTAL_ANALYSIS_TESTING_IMPROVEMENTS, + valueWhenDisabled = "3"), + }, + useEnvironment = true) + public String keywordsMultipleFlags(Integer a, boolean b, Integer c, Environment env) { + return "keywords_multiple_flags(" + a + ", " + b + ", " + c + ")"; + } + } + + @Test + public void testPositionalsOnlyGuardedMethod() throws Exception { + new SkylarkTest("--experimental_analysis_testing_improvements=true") + .update("mock", new Mock()) + .testEval( + "mock.positionals_only_method(1, True, 3)", "'positionals_only_method(1, true, 3)'"); + + new SkylarkTest("--experimental_analysis_testing_improvements=true") + .update("mock", new Mock()) + .testIfErrorContains( + "expected value of type 'bool' for parameter 'b', " + + "in method call positionals_only_method(int, int) of 'Mock'", + "mock.positionals_only_method(1, 3)"); + + new SkylarkTest("--experimental_analysis_testing_improvements=false") + .update("mock", new Mock()) + .testEval("mock.positionals_only_method(1, 3)", "'positionals_only_method(1, false, 3)'"); + + new SkylarkTest("--experimental_analysis_testing_improvements=false") + .update("mock", new Mock()) + .testIfErrorContains( + "expected value of type 'int' for parameter 'c', " + + "in method call positionals_only_method(int, bool, int) of 'Mock'", + "mock.positionals_only_method(1, True, 3)"); + } + + @Test + public void testKeywordOnlyGuardedMethod() throws Exception { + new SkylarkTest("--experimental_analysis_testing_improvements=true") + .update("mock", new Mock()) + .testEval( + "mock.keywords_only_method(a=1, b=True, c=3)", "'keywords_only_method(1, true, 3)'"); + + new SkylarkTest("--experimental_analysis_testing_improvements=true") + .update("mock", new Mock()) + .testIfErrorContains( + "parameter 'b' has no default value, in method call " + + "keywords_only_method(int a, int c) of 'Mock'", + "mock.keywords_only_method(a=1, c=3)"); + + new SkylarkTest("--experimental_analysis_testing_improvements=false") + .update("mock", new Mock()) + .testEval("mock.keywords_only_method(a=1, c=3)", "'keywords_only_method(1, false, 3)'"); + + new SkylarkTest("--experimental_analysis_testing_improvements=false") + .update("mock", new Mock()) + .testIfErrorContains( + "parameter 'b' is experimental and thus unavailable with the current " + + "flags. It may be enabled by setting " + + "--experimental_analysis_testing_improvements", + "mock.keywords_only_method(a=1, b=True, c=3)"); + } + + @Test + public void testMixedParamsMethod() throws Exception { + new SkylarkTest("--experimental_analysis_testing_improvements=true") + .update("mock", new Mock()) + .testEval( + "mock.mixed_params_method(1, True, c=3, d=True)", + "'mixed_params_method(1, true, 3, true)'"); + + new SkylarkTest("--experimental_analysis_testing_improvements=true") + .update("mock", new Mock()) + .testIfErrorContains( + "parameter 'b' has no default value, in method call " + + "mixed_params_method(int, int c) of 'Mock'", + "mock.mixed_params_method(1, c=3)"); + + new SkylarkTest("--experimental_analysis_testing_improvements=false") + .update("mock", new Mock()) + .testEval( + "mock.mixed_params_method(1, d=True)", "'mixed_params_method(1, false, 3, true)'"); + + new SkylarkTest("--experimental_analysis_testing_improvements=false") + .update("mock", new Mock()) + .testIfErrorContains( + "expected no more than 1 positional arguments, but got 2, " + + "in method call mixed_params_method(int, bool, int c, bool d) of 'Mock'", + "mock.mixed_params_method(1, True, c=3, d=True)"); + } + + @Test + public void testKeywordsMultipleFlags() throws Exception { + new SkylarkTest( + "--experimental_analysis_testing_improvements=true", + "--incompatible_no_attr_license=false") + .update("mock", new Mock()) + .testEval( + "mock.keywords_multiple_flags(a=42, b=True, c=0)", + "'keywords_multiple_flags(42, true, 0)'"); + + new SkylarkTest( + "--experimental_analysis_testing_improvements=true", + "--incompatible_no_attr_license=false") + .update("mock", new Mock()) + .testIfErrorContains( + "parameter 'b' has no default value, in method call " + + "keywords_multiple_flags(int a) of 'Mock'", + "mock.keywords_multiple_flags(a=42)"); + + new SkylarkTest( + "--experimental_analysis_testing_improvements=false", + "--incompatible_no_attr_license=true") + .update("mock", new Mock()) + .testEval("mock.keywords_multiple_flags(a=42)", "'keywords_multiple_flags(42, false, 3)'"); + + new SkylarkTest( + "--experimental_analysis_testing_improvements=false", + "--incompatible_no_attr_license=true") + .update("mock", new Mock()) + .testIfErrorContains( + "parameter 'b' is deprecated and will be removed soon. It may be " + + "temporarily re-enabled by setting --incompatible_no_attr_license=false", + "mock.keywords_multiple_flags(a=42, b=True, c=0)"); + } +}