Skylark: implemented 'break' and 'continue'
Fixes #233.
https://github.com/google/bazel/issues/233
--
MOS_MIGRATED_REVID=95632067
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
index 43e5ac8..3dcbb9d4 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
@@ -248,7 +248,7 @@
" for i in ['hello', ' ', 'world']:",
" s = s + i",
" return s",
- "s = foo()\n");
+ "s = foo()");
assertEquals("hello world", lookup("s"));
}
@@ -260,7 +260,7 @@
" for i in 'abc':",
" s = s + [i]",
" return s",
- "s = foo()\n");
+ "s = foo()");
assertThat((Iterable<Object>) lookup("s")).containsExactly("a", "b", "c").inOrder();
}
@@ -273,7 +273,7 @@
" s = s + i",
" d = ['d', 'e', 'f']", // check that we use the old list
" return s",
- "s = foo()\n");
+ "s = foo()");
assertEquals("abc", lookup("s"));
}
@@ -296,7 +296,7 @@
checkEvalError("type 'int' is not iterable",
"def func():",
" for i in mock.value_of('1'): a = i",
- "func()\n");
+ "func()");
}
@@ -334,6 +334,131 @@
}
@Test
+ public void testForLoopBreak() throws Exception {
+ simpleFlowTest("break", 1);
+ }
+
+ @Test
+ public void testForLoopContinue() throws Exception {
+ simpleFlowTest("continue", 10);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void simpleFlowTest(String statement, int expected) throws Exception {
+ eval("def foo():",
+ " s = 0",
+ " hit = 0",
+ " for i in range(0, 10):",
+ " s = s + 1",
+ " " + statement + "",
+ " hit = 1",
+ " return [s, hit]",
+ "x = foo()");
+ assertThat((Iterable<Object>) lookup("x")).containsExactly(expected, 0).inOrder();
+ }
+
+ @Test
+ public void testForLoopBreakFromDeeperBlock() throws Exception {
+ flowFromDeeperBlock("break", 1);
+ flowFromNestedBlocks("break", 29);
+ }
+
+ @Test
+ public void testForLoopContinueFromDeeperBlock() throws Exception {
+ flowFromDeeperBlock("continue", 5);
+ flowFromNestedBlocks("continue", 39);
+ }
+
+ private void flowFromDeeperBlock(String statement, int expected) throws Exception {
+ eval("def foo():",
+ " s = 0",
+ " for i in range(0, 10):",
+ " if i % 2 != 0:",
+ " " + statement + "",
+ " s = s + 1",
+ " return s",
+ "x = foo()");
+ assertThat(lookup("x")).isEqualTo(expected);
+ }
+
+ private void flowFromNestedBlocks(String statement, int expected) throws Exception {
+ eval("def foo2():",
+ " s = 0",
+ " for i in range(1, 41):",
+ " if i % 2 == 0:",
+ " if i % 3 == 0:",
+ " if i % 5 == 0:",
+ " " + statement + "",
+ " s = s + 1",
+ " return s",
+ "y = foo2()");
+ assertThat(lookup("y")).isEqualTo(expected);
+ }
+
+ @Test
+ public void testNestedForLoopsMultipleBreaks() throws Exception {
+ nestedLoopsTest("break", 2, 6, 6);
+ }
+
+ @Test
+ public void testNestedForLoopsMultipleContinues() throws Exception {
+ nestedLoopsTest("continue", 4, 20, 20);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void nestedLoopsTest(String statement, Integer outerExpected, int firstExpected,
+ int secondExpected) throws Exception {
+ eval("def foo():",
+ " outer = 0",
+ " first = 0",
+ " second = 0",
+ " for i in range(0, 5):",
+ " for j in range(0, 5):",
+ " if j == 2:",
+ " " + statement + "",
+ " first = first + 1",
+ " for k in range(0, 5):",
+ " if k == 2:",
+ " " + statement + "",
+ " second = second + 1",
+ " if i == 2:",
+ " " + statement + "",
+ " outer = outer + 1",
+ " return [outer, first, second]",
+ "x = foo()");
+ assertThat((Iterable<Object>) lookup("x"))
+ .containsExactly(outerExpected, firstExpected, secondExpected).inOrder();
+ }
+
+ @Test
+ public void testForLoopBreakError() throws Exception {
+ flowStatementInsideFunction("break");
+ flowStatementAfterLoop("break");
+ }
+
+ @Test
+ public void testForLoopContinueError() throws Exception {
+ flowStatementInsideFunction("continue");
+ flowStatementAfterLoop("continue");
+ }
+
+ private void flowStatementInsideFunction(String statement) throws Exception {
+ checkEvalErrorContains(statement + " statement must be inside a for loop",
+ "def foo():",
+ " " + statement + "",
+ "x = foo()");
+ }
+
+ private void flowStatementAfterLoop(String statement) throws Exception {
+ checkEvalErrorContains(statement + " statement must be inside a for loop",
+ "def foo2():",
+ " for i in range(0, 3):",
+ " pass",
+ " " + statement + "",
+ "y = foo2()");
+ }
+
+ @Test
public void testNoneAssignment() throws Exception {
eval("def foo(x=None):",
" x = 1",