blob: c71ff2c6ccc1e0da5594b51e128018450fb55cfb [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2015 The Bazel Authors. All rights reserved.
Ulf Adams89f012d2015-02-26 13:39:28 +00002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.syntax;
15
Francois-Rene Rideaub6ab6f22015-03-10 16:05:12 +000016import static com.google.common.truth.Truth.assertThat;
Francois-Rene Rideau3e9bab32015-03-06 13:41:00 +000017
Laurent Le Bruna5cdb19e2015-04-01 17:19:57 +000018import com.google.devtools.build.lib.events.Event;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000019import com.google.devtools.build.lib.syntax.util.EvaluationTestCase;
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000020import org.junit.Test;
21import org.junit.runner.RunWith;
22import org.junit.runners.JUnit4;
23
Ulf Adams89f012d2015-02-26 13:39:28 +000024/**
25 * Tests for the validation process of Skylark files.
26 */
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000027@RunWith(JUnit4.class)
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000028public class ValidationTest extends EvaluationTestCase {
Ulf Adams89f012d2015-02-26 13:39:28 +000029
Googler83439e62019-09-24 12:11:30 -070030 // TODO(adonovan): break dependency on EvaluationTestCase.
31
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000032 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000033 public void testAssignmentNotValidLValue() {
Michajlo Matijkiw8c539ea2017-02-22 23:02:46 +000034 checkError("cannot assign to '\"a\"'", "'a' = 1");
Ulf Adams89f012d2015-02-26 13:39:28 +000035 }
36
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000037 @Test
laurentlbe3684492017-08-21 12:02:46 +020038 public void testAugmentedAssignmentWithMultipleLValues() {
39 checkError("cannot perform augmented assignment on a list or tuple expression", "a, b += 2, 3");
40 }
41
42 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000043 public void testReturnOutsideFunction() throws Exception {
laurentlba9b9aea2017-09-04 17:39:09 +020044 checkError("return statements must be inside a function", "return 2\n");
Ulf Adams89f012d2015-02-26 13:39:28 +000045 }
46
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000047 @Test
laurentlba0fd7662017-05-10 12:26:15 -040048 public void testLoadAfterStatement() throws Exception {
Googlera3421e22019-09-26 06:48:32 -070049 thread =
50 newStarlarkThreadWithSkylarkOptions(
51 "--incompatible_bzl_disallow_load_after_statement=true");
laurentlba0fd7662017-05-10 12:26:15 -040052 checkError(
53 "load() statements must be called before any other statement",
54 "a = 5",
55 "load(':b.bzl', 'c')");
56 }
57
58 @Test
59 public void testAllowLoadAfterStatement() throws Exception {
Googlera3421e22019-09-26 06:48:32 -070060 thread =
61 newStarlarkThreadWithSkylarkOptions(
62 "--incompatible_bzl_disallow_load_after_statement=false");
laurentlba0fd7662017-05-10 12:26:15 -040063 parse("a = 5", "load(':b.bzl', 'c')");
64 }
65
66 @Test
laurentlb77ffee32017-06-07 12:16:45 -040067 public void testForbiddenToplevelIfStatement() throws Exception {
laurentlb77ffee32017-06-07 12:16:45 -040068 checkError("if statements are not allowed at the top level", "if True: a = 2");
69 }
70
71 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000072 public void testTwoFunctionsWithTheSameName() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000073 checkError(
74 "Variable foo is read only", "def foo():", " return 1", "def foo(x, y):", " return 1");
Ulf Adams89f012d2015-02-26 13:39:28 +000075 }
76
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000077 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000078 public void testFunctionLocalVariable() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000079 checkError(
80 "name 'a' is not defined",
Ulf Adams89f012d2015-02-26 13:39:28 +000081 "def func2(b):",
82 " c = b",
83 " c = a",
84 "def func1():",
85 " a = 1",
86 " func2(2)");
87 }
88
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000089 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000090 public void testFunctionLocalVariableDoesNotEffectGlobalValidationEnv() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000091 checkError("name 'a' is not defined", "def func1():", " a = 1", "def func2(b):", " b = a");
Ulf Adams89f012d2015-02-26 13:39:28 +000092 }
93
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000094 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000095 public void testFunctionParameterDoesNotEffectGlobalValidationEnv() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000096 checkError("name 'a' is not defined", "def func1(a):", " return a", "def func2():", " b = a");
Ulf Adams89f012d2015-02-26 13:39:28 +000097 }
98
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000099 @Test
fzaiser6ccff292017-08-28 17:50:51 +0200100 public void testDefinitionByItself() throws Exception {
laurentlb09ec2612018-08-27 12:35:56 -0700101 // Variables are assumed to be statically visible in the block (even if they might not be
102 // initialized).
103 parse("a = a");
104 parse("a += a");
105 parse("[[] for a in a]");
106 parse("def f():", " for a in a: pass");
107 }
108
109 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000110 public void testLocalValidationEnvironmentsAreSeparated() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000111 parse("def func1():", " a = 1", "def func2():", " a = 'abc'\n");
Ulf Adams89f012d2015-02-26 13:39:28 +0000112 }
113
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000114 @Test
laurentlb09ec2612018-08-27 12:35:56 -0700115 public void testBuiltinsCanBeShadowed() throws Exception {
laurentlb09ec2612018-08-27 12:35:56 -0700116 parse("repr = 1");
117 }
118
119 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000120 public void testSkylarkGlobalVariablesAreReadonly() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000121 checkError("Variable a is read only", "a = 1", "a = 2");
Ulf Adams89f012d2015-02-26 13:39:28 +0000122 }
123
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000124 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000125 public void testFunctionDefRecursion() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000126 parse("def func():", " func()\n");
Ulf Adams89f012d2015-02-26 13:39:28 +0000127 }
128
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000129 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000130 public void testMutualRecursion() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000131 parse("def foo(i):", " bar(i)", "def bar(i):", " foo(i)", "foo(4)");
Ulf Adams89f012d2015-02-26 13:39:28 +0000132 }
133
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000134 @Test
Laurent Le Brun68743162015-05-13 13:18:09 +0000135 public void testFunctionDefinedBelow() {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000136 parse("def bar(): a = foo() + 'a'", "def foo(): return 1\n");
Ulf Adams89f012d2015-02-26 13:39:28 +0000137 }
138
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000139 @Test
laurentlb09ec2612018-08-27 12:35:56 -0700140 public void testGlobalDefinedBelow() throws Exception {
Googlera3421e22019-09-26 06:48:32 -0700141 thread = newStarlarkThreadWithSkylarkOptions();
laurentlb09ec2612018-08-27 12:35:56 -0700142 parse("def bar(): return x", "x = 5\n");
143 }
144
145 @Test
146 public void testLocalVariableDefinedBelow() throws Exception {
Googlera3421e22019-09-26 06:48:32 -0700147 thread = newStarlarkThreadWithSkylarkOptions();
laurentlb09ec2612018-08-27 12:35:56 -0700148 parse(
149 "def bar():",
150 " for i in range(5):",
151 " if i > 2: return x",
152 " x = i" // x is visible in the entire function block
153 );
154 }
155
156 @Test
Laurent Le Brun68743162015-05-13 13:18:09 +0000157 public void testFunctionDoesNotExist() {
Laurent Le Brune102a2d2017-01-02 12:06:18 +0000158 checkError("name 'foo' is not defined", "def bar(): a = foo() + 'a'");
Laurent Le Brun68743162015-05-13 13:18:09 +0000159 }
160
161 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000162 public void testTupleLiteralWorksForDifferentTypes() throws Exception {
163 parse("('a', 1)");
164 }
165
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000166 @Test
Googler39ef7d02019-09-16 06:37:04 -0700167 public void testDictExpressionDifferentValueTypeWorks() throws Exception {
Ulf Adams89f012d2015-02-26 13:39:28 +0000168 parse("{'a': 1, 'b': 'c'}");
169 }
170
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000171 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000172 public void testNoneAssignment() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000173 parse("def func():", " a = None", " a = 2", " a = None\n");
Ulf Adams89f012d2015-02-26 13:39:28 +0000174 }
175
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000176 @Test
Laurent Le Brun65ec9572015-03-16 10:45:07 +0000177 public void testNoneIsAnyType() throws Exception {
178 parse("None + None");
179 parse("2 == None");
180 parse("None > 'a'");
181 parse("[] in None");
182 parse("5 * None");
183 }
184
Ulf Adams89f012d2015-02-26 13:39:28 +0000185 // Skylark built-in functions specific tests
186
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000187 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000188 public void testFuncReturningDictAssignmentAsLValue() throws Exception {
Vladimir Moskva10770382016-08-23 15:04:54 +0000189 parse(
Florian Weikerta6dae6b2015-08-04 20:17:23 +0000190 "def my_dict():",
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000191 " return {'a': 1}",
192 "def func():",
Vladimir Moskva10770382016-08-23 15:04:54 +0000193 " my_dict()['b'] = 2");
Ulf Adams89f012d2015-02-26 13:39:28 +0000194 }
195
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000196 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000197 public void testEmptyLiteralGenericIsSetInLaterConcatWorks() {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000198 parse("def func():", " s = {}", " s['a'] = 'b'\n");
Ulf Adams89f012d2015-02-26 13:39:28 +0000199 }
200
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000201 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000202 public void testModulesReadOnlyInFuncDefBody() {
Vladimir Moskvad200daf2016-12-23 16:35:37 +0000203 parse("def func():", " cmd_helper = depset()");
Ulf Adams89f012d2015-02-26 13:39:28 +0000204 }
205
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000206 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000207 public void testBuiltinGlobalFunctionsReadOnlyInFuncDefBody() {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000208 parse("def func():", " rule = 'abc'");
Ulf Adams89f012d2015-02-26 13:39:28 +0000209 }
210
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000211 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000212 public void testBuiltinGlobalFunctionsReadOnlyAsFuncDefArg() {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000213 parse("def func(rule):", " return rule");
Ulf Adams89f012d2015-02-26 13:39:28 +0000214 }
215
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000216 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000217 public void testFunctionReturnsFunction() {
218 parse(
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000219 "def rule(*, implementation): return None",
220 "def impl(ctx): return None",
Ulf Adams89f012d2015-02-26 13:39:28 +0000221 "",
222 "skylark_rule = rule(implementation = impl)",
223 "",
224 "def macro(name):",
225 " skylark_rule(name = name)");
226 }
227
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000228 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000229 public void testTypeForBooleanLiterals() {
230 parse("len([1, 2]) == 0 and True");
231 parse("len([1, 2]) == 0 and False");
232 }
233
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000234 @Test
Laurent Le Bruna5cdb19e2015-04-01 17:19:57 +0000235 public void testDollarErrorDoesNotLeak() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000236 setFailFast(false);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000237 parseFile(
238 "def GenerateMapNames():", " a = 2", " b = [3, 4]", " if a not b:", " print(a)");
laurentlb566ef5a2018-05-22 10:35:06 -0700239 assertContainsError("syntax error at 'b': expected 'in'");
Laurent Le Bruna5cdb19e2015-04-01 17:19:57 +0000240 // Parser uses "$error" symbol for error recovery.
241 // It should not be used in error messages.
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000242 for (Event event : getEventCollector()) {
Laurent Le Bruna5cdb19e2015-04-01 17:19:57 +0000243 assertThat(event.getMessage()).doesNotContain("$error$");
244 }
245 }
246
laurentlb1e5352d2018-11-06 12:25:55 -0800247 @Test
248 public void testPositionalAfterStarArg() throws Exception {
laurentlb1e5352d2018-11-06 12:25:55 -0800249 checkError(
250 "positional argument is misplaced (positional arguments come first)",
251 "def fct(*args, **kwargs): pass",
252 "fct(1, *[2], 3)");
253 }
254
255 @Test
256 public void testTwoStarArgs() throws Exception {
laurentlb1e5352d2018-11-06 12:25:55 -0800257 checkError(
258 "*arg argument is misplaced",
259 "def fct(*args, **kwargs):",
260 " pass",
261 "fct(1, 2, 3, *[], *[])");
262 }
263
264 @Test
265 public void testKeywordArgAfterStarArg() throws Exception {
laurentlb1e5352d2018-11-06 12:25:55 -0800266 checkError(
267 "keyword argument is misplaced (keyword arguments must be before any *arg or **kwarg)",
268 "def fct(*args, **kwargs): pass",
269 "fct(1, *[2], a=3)");
270 }
271
Googler83439e62019-09-24 12:11:30 -0700272 @Test
273 public void testTopLevelForFails() throws Exception {
274 setFailFast(false);
275 parseFile("for i in []: 0\n");
276 assertContainsError("for loops are not allowed at the top level");
277 }
278
279 @Test
280 public void testNestedFunctionFails() throws Exception {
281 setFailFast(false);
282 parseFile(
283 "def func(a):", //
284 " def bar(): return 0",
285 " return bar()",
286 "");
287 assertContainsError("nested functions are not allowed. Move the function to the top level");
288 }
289
Ulf Adams89f012d2015-02-26 13:39:28 +0000290 private void parse(String... lines) {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000291 parseFile(lines);
Ulf Adamsb089c8d2015-10-26 12:20:33 +0000292 assertNoWarningsOrErrors();
Ulf Adams89f012d2015-02-26 13:39:28 +0000293 }
294
295 private void checkError(String errorMsg, String... lines) {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000296 setFailFast(false);
297 parseFile(lines);
Ulf Adamsc708f962015-10-22 12:02:28 +0000298 assertContainsError(errorMsg);
Ulf Adams89f012d2015-02-26 13:39:28 +0000299 }
300}