bazel syntax: rationalize the type check operators
This change introduces {Dict,Sequence}.{,noneable}Cast.
These four conversion operators cast an arbitrary value
to a Sequence<T> or Dict<K,V>, replacing these 9 previous functions:
- Dict.{castSkylarkDictOrNoneToDict,getContents}
- Sequence.{castList,castSkylarkListOrNoneToList,getContents}
- SkylarkType.{cast,cast',checkType,castMap}
The functions don't allocate an unmodifiable wrapper,
as Dict and List are already unmodifiable through the java.util
interfaces, so this is just wasteful allocation and indirection.
Also, there is no a priori allocation of Formattable error messages.
A number of messes (e.g. unsound casts) were cleaned up throughout.
The new operators do not accept a Location, and report error
using Starlark.errorf. (This whole CL started as a subtask of a
subtask to eliminate the Location parameter of EvalException,
and this sub-sub-task is an experiment to see whether removing
Location parameters that don't correspond to program counter
locations is a UI regression. SRCTU.createTarget is the only
place where this appears to be a problem. For now I've added a
small kludge, but in a follow-up I will change it to report
events, not throw exceptions, so that it can report multiple
errors at arbitrary locations.
Depset might benefit from a similar {noneable,}Cast treatment,
but this too is left for a follow-up.
This is a breaking API change for copybara.
RELNOTES: N/A
PiperOrigin-RevId: 306925434
diff --git a/src/test/java/com/google/devtools/build/lib/packages/WorkspaceFactoryTest.java b/src/test/java/com/google/devtools/build/lib/packages/WorkspaceFactoryTest.java
index ad60ded..7d1e676 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/WorkspaceFactoryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/WorkspaceFactoryTest.java
@@ -175,10 +175,10 @@
@Test
public void testRepoMappingNotAStringStringDict() throws Exception {
helper.parse("local_repository(name='foo', path='/foo', repo_mapping=1)");
- assertThat(helper.getParserError()).contains("got 'int' for 'repo_mapping', want 'dict'");
+ assertThat(helper.getParserError()).contains("got int for 'repo_mapping', want dict");
helper.parse("local_repository(name='foo', path='/foo', repo_mapping='hello')");
- assertThat(helper.getParserError()).contains("got 'string' for 'repo_mapping', want 'dict'");
+ assertThat(helper.getParserError()).contains("got string for 'repo_mapping', want dict");
helper.parse("local_repository(name='foo', path='/foo', repo_mapping={1: 1})");
assertThat(helper.getParserError())
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTest.java
index 43efec1..58464d6 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTest.java
@@ -2376,9 +2376,7 @@
EvalException e =
assertThrows(
EvalException.class, () -> CcModule.withFeatureSetFromSkylark(withFeatureSetProvider));
- assertThat(e)
- .hasMessageThat()
- .contains("Illegal argument: 'features' is not of expected type list or NoneType");
+ assertThat(e).hasMessageThat().contains("for features, got struct, want sequence");
}
@Test
@@ -2392,9 +2390,7 @@
EvalException e =
assertThrows(
EvalException.class, () -> CcModule.withFeatureSetFromSkylark(withFeatureSetProvider));
- assertThat(e)
- .hasMessageThat()
- .contains("Illegal argument: 'not_features' is not of expected type list or NoneType");
+ assertThat(e).hasMessageThat().contains("for not_features, got struct, want sequence");
}
@Test
@@ -2410,7 +2406,7 @@
EvalException.class, () -> CcModule.withFeatureSetFromSkylark(withFeatureSetProvider));
assertThat(e)
.hasMessageThat()
- .contains("expected type 'string' for 'features' element but got type 'struct' instead");
+ .contains("at index 0 of features, got element of type struct, want string");
}
@Test
@@ -2426,8 +2422,7 @@
EvalException.class, () -> CcModule.withFeatureSetFromSkylark(withFeatureSetProvider));
assertThat(e)
.hasMessageThat()
- .contains(
- "expected type 'string' for 'not_features' element but got type 'struct' instead");
+ .contains("at index 0 of not_features, got element of type struct, want string");
}
private void createCustomWithFeatureSetRule(String pkg, String features, String notFeatures)
@@ -2588,9 +2583,7 @@
assertThat(envSetProvider).isNotNull();
EvalException e =
assertThrows(EvalException.class, () -> CcModule.envSetFromSkylark(envSetProvider));
- assertThat(e)
- .hasMessageThat()
- .contains("'env_entries' is not of expected type list or NoneType");
+ assertThat(e).hasMessageThat().contains("for env_entries, got struct, want sequence");
}
@Test
@@ -2624,9 +2617,7 @@
assertThat(envSetProvider).isNotNull();
EvalException e =
assertThrows(EvalException.class, () -> CcModule.envSetFromSkylark(envSetProvider));
- assertThat(e)
- .hasMessageThat()
- .contains("'with_features' is not of expected type list or NoneType");
+ assertThat(e).hasMessageThat().contains("for with_features, got string, want sequence");
}
@Test
@@ -3170,9 +3161,7 @@
EvalException e = assertThrows(EvalException.class, () -> CcModule.toolFromSkylark(toolStruct));
assertThat(e)
.hasMessageThat()
- .contains(
- "expected type 'string' for 'execution_requirements' "
- + "element but got type 'struct' instead");
+ .contains("at index 0 of execution_requirements, got element of type struct, want string");
}
@Test
@@ -3245,9 +3234,7 @@
SkylarkInfo toolStruct = (SkylarkInfo) getMyInfoFromTarget(t).getValue("tool");
assertThat(toolStruct).isNotNull();
EvalException e = assertThrows(EvalException.class, () -> CcModule.toolFromSkylark(toolStruct));
- assertThat(e)
- .hasMessageThat()
- .contains("Illegal argument: 'with_features' is not of expected type list or NoneType");
+ assertThat(e).hasMessageThat().contains("for with_features, got struct, want sequence");
}
@Test
@@ -3280,8 +3267,7 @@
EvalException e = assertThrows(EvalException.class, () -> CcModule.toolFromSkylark(toolStruct));
assertThat(e)
.hasMessageThat()
- .contains(
- "llegal argument: 'execution_requirements' is not of expected type list or NoneType");
+ .contains("for execution_requirements, got string, want sequence");
}
@Test
@@ -3296,9 +3282,7 @@
EvalException e = assertThrows(EvalException.class, () -> CcModule.toolFromSkylark(toolStruct));
assertThat(e)
.hasMessageThat()
- .contains(
- "expected type 'string' for 'execution_requirements' "
- + "element but got type 'struct' instead");
+ .contains("at index 0 of execution_requirements, got element of type struct, want string");
}
private void createCustomToolRule(
@@ -3359,7 +3343,7 @@
() -> CcModule.flagSetFromSkylark(flagSetStruct, /* actionName= */ null));
assertThat(e)
.hasMessageThat()
- .contains("expected type 'string' for 'actions' element but got type 'struct' instead");
+ .contains("at index 0 of actions, got element of type struct, want string");
}
@Test
@@ -3499,9 +3483,7 @@
assertThrows(
EvalException.class,
() -> CcModule.flagSetFromSkylark(flagSetStruct, /* actionName */ null));
- assertThat(e)
- .hasMessageThat()
- .contains("Illegal argument: 'flag_groups' is not of expected type list or NoneType");
+ assertThat(e).hasMessageThat().contains("for flag_groups, got struct, want sequence");
}
@Test
@@ -3517,9 +3499,7 @@
assertThrows(
EvalException.class,
() -> CcModule.flagSetFromSkylark(flagSetStruct, /* actionName */ null));
- assertThat(e)
- .hasMessageThat()
- .contains("Illegal argument: 'with_features' is not of expected type list or NoneType");
+ assertThat(e).hasMessageThat().contains("for with_features, got struct, want sequence");
}
@Test
@@ -3535,9 +3515,7 @@
assertThrows(
EvalException.class,
() -> CcModule.flagSetFromSkylark(flagSetStruct, /* actionName */ null));
- assertThat(e)
- .hasMessageThat()
- .contains("Illegal argument: 'actions' is not of expected type list or NoneType");
+ assertThat(e).hasMessageThat().contains("for actions, got struct, want sequence");
}
private void createCustomFlagSetRule(
@@ -3669,7 +3647,7 @@
EvalException.class, () -> CcModule.actionConfigFromSkylark(actionConfigStruct));
assertThat(e)
.hasMessageThat()
- .contains("expected type 'string' for 'implies' element but got type 'struct' instead");
+ .contains("at index 0 of implies, got element of type struct, want string");
}
@Test
@@ -3691,7 +3669,7 @@
EvalException.class, () -> CcModule.actionConfigFromSkylark(actionConfigStruct));
assertThat(e)
.hasMessageThat()
- .contains("expected type 'string' for 'implies' element but got type 'struct' instead");
+ .contains("at index 0 of implies, got element of type struct, want string");
}
@Test
@@ -3843,9 +3821,7 @@
EvalException e =
assertThrows(
EvalException.class, () -> CcModule.actionConfigFromSkylark(actionConfigStruct));
- assertThat(e)
- .hasMessageThat()
- .contains("Illegal argument: 'tools' is not of expected type list or NoneType");
+ assertThat(e).hasMessageThat().contains("for tools, got struct, want sequence");
}
@Test
@@ -3865,9 +3841,7 @@
EvalException e =
assertThrows(
EvalException.class, () -> CcModule.actionConfigFromSkylark(actionConfigStruct));
- assertThat(e)
- .hasMessageThat()
- .contains("Illegal argument: 'flag_sets' is not of expected type list or NoneType");
+ assertThat(e).hasMessageThat().contains("for flag_sets, got bool, want sequence");
}
@Test
@@ -3887,9 +3861,7 @@
EvalException e =
assertThrows(
EvalException.class, () -> CcModule.actionConfigFromSkylark(actionConfigStruct));
- assertThat(e)
- .hasMessageThat()
- .contains("Illegal argument: 'implies' is not of expected type list or NoneType");
+ assertThat(e).hasMessageThat().contains("for implies, got struct, want sequence");
}
private void createCustomActionConfigRule(
@@ -4040,7 +4012,7 @@
assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(featureStruct));
assertThat(e)
.hasMessageThat()
- .contains("expected type 'string' for 'implies' element but got type 'struct' instead");
+ .contains("at index 0 of implies, got element of type struct, want string");
}
@Test
@@ -4064,7 +4036,7 @@
assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(featureStruct));
assertThat(e)
.hasMessageThat()
- .contains("expected type 'string' for 'provides' element but got type 'struct' instead");
+ .contains("at index 0 of provides, got element of type struct, want string");
}
@Test
@@ -4226,9 +4198,7 @@
assertThat(featureStruct).isNotNull();
EvalException e =
assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(featureStruct));
- assertThat(e)
- .hasMessageThat()
- .contains("Illegal argument: 'flag_sets' is not of expected type list or NoneType");
+ assertThat(e).hasMessageThat().contains("for flag_sets, got struct, want sequence");
}
@Test
@@ -4249,9 +4219,7 @@
assertThat(featureStruct).isNotNull();
EvalException e =
assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(featureStruct));
- assertThat(e)
- .hasMessageThat()
- .contains("Illegal argument: 'env_sets' is not of expected type list or NoneType");
+ assertThat(e).hasMessageThat().contains("for env_sets, got struct, want sequence");
}
@Test
@@ -4272,9 +4240,7 @@
assertThat(featureStruct).isNotNull();
EvalException e =
assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(featureStruct));
- assertThat(e)
- .hasMessageThat()
- .contains("Illegal argument: 'requires' is not of expected type list or NoneType");
+ assertThat(e).hasMessageThat().contains("for requires, got struct, want sequence");
}
@Test
@@ -4295,9 +4261,7 @@
assertThat(featureStruct).isNotNull();
EvalException e =
assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(featureStruct));
- assertThat(e)
- .hasMessageThat()
- .contains("Illegal argument: 'implies' is not of expected type list or NoneType");
+ assertThat(e).hasMessageThat().contains("for implies, got struct, want sequence");
}
@Test
@@ -4318,9 +4282,7 @@
assertThat(featureStruct).isNotNull();
EvalException e =
assertThrows(EvalException.class, () -> CcModule.featureFromSkylark(featureStruct));
- assertThat(e)
- .hasMessageThat()
- .contains("Illegal argument: 'provides' is not of expected type list or NoneType");
+ assertThat(e).hasMessageThat().contains("for provides, got struct, want sequence");
}
@Test
diff --git a/src/test/java/com/google/devtools/build/lib/rules/python/PyStructUtilsTest.java b/src/test/java/com/google/devtools/build/lib/rules/python/PyStructUtilsTest.java
index 2fe94e3..b77aadf 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/python/PyStructUtilsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/python/PyStructUtilsTest.java
@@ -91,8 +91,7 @@
ThrowingRunnable access, String fieldName, String expectedType) {
assertThrowsEvalExceptionContaining(
access,
- String.format(
- "\'py' provider's '%s' field should be a %s (got a 'int')", fieldName, expectedType));
+ String.format("\'py' provider's '%s' field was int, want %s", fieldName, expectedType));
}
/** We need this because {@code NestedSet}s don't have value equality. */
@@ -154,7 +153,7 @@
public void getUsesSharedLibraries_WrongType() {
StructImpl info = makeStruct(ImmutableMap.of(PyStructUtils.USES_SHARED_LIBRARIES, 123));
assertHasWrongTypeMessage(
- () -> PyStructUtils.getUsesSharedLibraries(info), "uses_shared_libraries", "boolean");
+ () -> PyStructUtils.getUsesSharedLibraries(info), "uses_shared_libraries", "bool");
}
@Test
@@ -191,7 +190,7 @@
public void getHasPy2OnlySources_WrongType() {
StructImpl info = makeStruct(ImmutableMap.of(PyStructUtils.HAS_PY2_ONLY_SOURCES, 123));
assertHasWrongTypeMessage(
- () -> PyStructUtils.getHasPy2OnlySources(info), "has_py2_only_sources", "boolean");
+ () -> PyStructUtils.getHasPy2OnlySources(info), "has_py2_only_sources", "bool");
}
@Test
@@ -209,7 +208,7 @@
public void getHasPy3OnlySources_WrongType() {
StructImpl info = makeStruct(ImmutableMap.of(PyStructUtils.HAS_PY3_ONLY_SOURCES, 123));
assertHasWrongTypeMessage(
- () -> PyStructUtils.getHasPy3OnlySources(info), "has_py3_only_sources", "boolean");
+ () -> PyStructUtils.getHasPy3OnlySources(info), "has_py3_only_sources", "bool");
}
/** Checks values set by the builder. */
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 11ef9a4..8a59f59 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
@@ -401,7 +401,7 @@
@Test
public void testLabelListWithAspectsError() throws Exception {
checkEvalErrorContains(
- "expected type 'Aspect' for 'aspects' element but got type 'int' instead",
+ "at index 0 of aspects, got element of type int, want Aspect",
"def _impl(target, ctx):",
" pass",
"my_aspect = aspect(implementation = _impl)",
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java
index 65637e4..77bdd6d 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java
@@ -401,7 +401,7 @@
public void testCreateSpawnActionBadGenericArg() throws Exception {
setRuleContext(createRuleContext("//foo:foo"));
checkEvalErrorContains(
- "expected type 'File' for 'outputs' element but got type 'string' instead",
+ "at index 0 of outputs, got element of type string, want File",
"l = ['a', 'b']",
"ruleContext.actions.run_shell(",
" outputs = l,",
@@ -841,7 +841,7 @@
public void testRunfilesBadListGenericType() throws Exception {
setRuleContext(createRuleContext("//foo:foo"));
checkEvalErrorContains(
- "expected type 'File' for 'files' element but got type 'string' instead",
+ "at index 0 of files, got element of type string, want File",
"ruleContext.runfiles(files = ['some string'])");
}
@@ -857,16 +857,16 @@
public void testRunfilesBadMapGenericType() throws Exception {
setRuleContext(createRuleContext("//foo:foo"));
checkEvalErrorContains(
- "expected type 'string' for 'symlinks' key but got type 'int' instead",
+ "got dict<int, File> for 'symlinks', want dict<string, File>",
"ruleContext.runfiles(symlinks = {123: ruleContext.files.srcs[0]})");
checkEvalErrorContains(
- "expected type 'File' for 'symlinks' value but got type 'int' instead",
+ "got dict<string, int> for 'symlinks', want dict<string, File>",
"ruleContext.runfiles(symlinks = {'some string': 123})");
checkEvalErrorContains(
- "expected type 'string' for 'root_symlinks' key but got type 'int' instead",
+ "got dict<int, File> for 'root_symlinks', want dict<string, File>",
"ruleContext.runfiles(root_symlinks = {123: ruleContext.files.srcs[0]})");
checkEvalErrorContains(
- "expected type 'File' for 'root_symlinks' value but got type 'int' instead",
+ "got dict<string, int> for 'root_symlinks', want dict<string, File>",
"ruleContext.runfiles(root_symlinks = {'some string': 123})");
}
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/DepsetTest.java b/src/test/java/com/google/devtools/build/lib/syntax/DepsetTest.java
index f1592ca..431cbee 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/DepsetTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/DepsetTest.java
@@ -234,7 +234,7 @@
public void testItemsAndTransitive() throws Exception {
new Scenario()
.testIfExactError(
- "expected type 'sequence' for items but got type 'depset' instead",
+ "for items, got depset, want sequence",
"depset(items = depset(), transitive = [depset()])");
}
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 428a7b1..78aeeac 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
@@ -716,9 +716,7 @@
@Test
public void testDepsetDirectInvalidType() throws Exception {
new Scenario()
- .testIfErrorContains(
- "expected type 'sequence' for direct but got type 'string' instead",
- "depset(direct='hello')");
+ .testIfErrorContains("for direct, got string, want sequence", "depset(direct='hello')");
}
@Test