blob: c8e1be5803f8c287b182aa9ac18a7ca36f8b65c9 [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
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000030 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000031 public void testAssignmentNotValidLValue() {
Michajlo Matijkiw8c539ea2017-02-22 23:02:46 +000032 checkError("cannot assign to '\"a\"'", "'a' = 1");
Ulf Adams89f012d2015-02-26 13:39:28 +000033 }
34
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000035 @Test
laurentlbe3684492017-08-21 12:02:46 +020036 public void testAugmentedAssignmentWithMultipleLValues() {
37 checkError("cannot perform augmented assignment on a list or tuple expression", "a, b += 2, 3");
38 }
39
40 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000041 public void testReturnOutsideFunction() throws Exception {
laurentlba9b9aea2017-09-04 17:39:09 +020042 checkError("return statements must be inside a function", "return 2\n");
Ulf Adams89f012d2015-02-26 13:39:28 +000043 }
44
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000045 @Test
laurentlba0fd7662017-05-10 12:26:15 -040046 public void testLoadAfterStatement() throws Exception {
47 env = newEnvironmentWithSkylarkOptions("--incompatible_bzl_disallow_load_after_statement=true");
48 checkError(
49 "load() statements must be called before any other statement",
50 "a = 5",
51 "load(':b.bzl', 'c')");
52 }
53
54 @Test
55 public void testAllowLoadAfterStatement() throws Exception {
56 env =
57 newEnvironmentWithSkylarkOptions("--incompatible_bzl_disallow_load_after_statement=false");
58 parse("a = 5", "load(':b.bzl', 'c')");
59 }
60
61 @Test
laurentlb77ffee32017-06-07 12:16:45 -040062 public void testForbiddenToplevelIfStatement() throws Exception {
laurentlb77ffee32017-06-07 12:16:45 -040063 checkError("if statements are not allowed at the top level", "if True: a = 2");
64 }
65
66 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000067 public void testTwoFunctionsWithTheSameName() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000068 checkError(
69 "Variable foo is read only", "def foo():", " return 1", "def foo(x, y):", " return 1");
Ulf Adams89f012d2015-02-26 13:39:28 +000070 }
71
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000072 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000073 public void testFunctionLocalVariable() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000074 checkError(
75 "name 'a' is not defined",
Ulf Adams89f012d2015-02-26 13:39:28 +000076 "def func2(b):",
77 " c = b",
78 " c = a",
79 "def func1():",
80 " a = 1",
81 " func2(2)");
82 }
83
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000084 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000085 public void testFunctionLocalVariableDoesNotEffectGlobalValidationEnv() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000086 checkError("name 'a' is not defined", "def func1():", " a = 1", "def func2(b):", " b = a");
Ulf Adams89f012d2015-02-26 13:39:28 +000087 }
88
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000089 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000090 public void testFunctionParameterDoesNotEffectGlobalValidationEnv() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000091 checkError("name 'a' is not defined", "def func1(a):", " return a", "def func2():", " b = a");
Ulf Adams89f012d2015-02-26 13:39:28 +000092 }
93
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000094 @Test
fzaiser6ccff292017-08-28 17:50:51 +020095 public void testDefinitionByItself() throws Exception {
laurentlb09ec2612018-08-27 12:35:56 -070096 // Variables are assumed to be statically visible in the block (even if they might not be
97 // initialized).
98 parse("a = a");
99 parse("a += a");
100 parse("[[] for a in a]");
101 parse("def f():", " for a in a: pass");
102 }
103
104 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000105 public void testLocalValidationEnvironmentsAreSeparated() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000106 parse("def func1():", " a = 1", "def func2():", " a = 'abc'\n");
Ulf Adams89f012d2015-02-26 13:39:28 +0000107 }
108
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000109 @Test
laurentlb09ec2612018-08-27 12:35:56 -0700110 public void testBuiltinsCanBeShadowed() throws Exception {
laurentlb09ec2612018-08-27 12:35:56 -0700111 parse("repr = 1");
112 }
113
114 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000115 public void testSkylarkGlobalVariablesAreReadonly() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000116 checkError("Variable a is read only", "a = 1", "a = 2");
Ulf Adams89f012d2015-02-26 13:39:28 +0000117 }
118
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000119 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000120 public void testFunctionDefRecursion() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000121 parse("def func():", " func()\n");
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 testMutualRecursion() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000126 parse("def foo(i):", " bar(i)", "def bar(i):", " foo(i)", "foo(4)");
Ulf Adams89f012d2015-02-26 13:39:28 +0000127 }
128
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000129 @Test
Laurent Le Brun68743162015-05-13 13:18:09 +0000130 public void testFunctionDefinedBelow() {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000131 parse("def bar(): a = foo() + 'a'", "def foo(): return 1\n");
Ulf Adams89f012d2015-02-26 13:39:28 +0000132 }
133
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000134 @Test
laurentlb09ec2612018-08-27 12:35:56 -0700135 public void testGlobalDefinedBelow() throws Exception {
136 env = newEnvironmentWithSkylarkOptions("--incompatible_static_name_resolution=true");
137 parse("def bar(): return x", "x = 5\n");
138 }
139
140 @Test
141 public void testLocalVariableDefinedBelow() throws Exception {
142 env = newEnvironmentWithSkylarkOptions("--incompatible_static_name_resolution=true");
143 parse(
144 "def bar():",
145 " for i in range(5):",
146 " if i > 2: return x",
147 " x = i" // x is visible in the entire function block
148 );
149 }
150
151 @Test
Laurent Le Brun68743162015-05-13 13:18:09 +0000152 public void testFunctionDoesNotExist() {
Laurent Le Brune102a2d2017-01-02 12:06:18 +0000153 checkError("name 'foo' is not defined", "def bar(): a = foo() + 'a'");
Laurent Le Brun68743162015-05-13 13:18:09 +0000154 }
155
156 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000157 public void testTupleLiteralWorksForDifferentTypes() throws Exception {
158 parse("('a', 1)");
159 }
160
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000161 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000162 public void testDictLiteralDifferentValueTypeWorks() throws Exception {
163 parse("{'a': 1, 'b': 'c'}");
164 }
165
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000166 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000167 public void testNoneAssignment() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000168 parse("def func():", " a = None", " a = 2", " a = None\n");
Ulf Adams89f012d2015-02-26 13:39:28 +0000169 }
170
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000171 @Test
Laurent Le Brun65ec9572015-03-16 10:45:07 +0000172 public void testNoneIsAnyType() throws Exception {
173 parse("None + None");
174 parse("2 == None");
175 parse("None > 'a'");
176 parse("[] in None");
177 parse("5 * None");
178 }
179
Ulf Adams89f012d2015-02-26 13:39:28 +0000180 // Skylark built-in functions specific tests
181
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000182 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000183 public void testFuncReturningDictAssignmentAsLValue() throws Exception {
Vladimir Moskva10770382016-08-23 15:04:54 +0000184 parse(
Florian Weikerta6dae6b2015-08-04 20:17:23 +0000185 "def my_dict():",
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000186 " return {'a': 1}",
187 "def func():",
Vladimir Moskva10770382016-08-23 15:04:54 +0000188 " my_dict()['b'] = 2");
Ulf Adams89f012d2015-02-26 13:39:28 +0000189 }
190
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000191 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000192 public void testEmptyLiteralGenericIsSetInLaterConcatWorks() {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000193 parse("def func():", " s = {}", " s['a'] = 'b'\n");
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 testModulesReadOnlyInFuncDefBody() {
Vladimir Moskvad200daf2016-12-23 16:35:37 +0000198 parse("def func():", " cmd_helper = depset()");
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 testBuiltinGlobalFunctionsReadOnlyInFuncDefBody() {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000203 parse("def func():", " rule = 'abc'");
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 testBuiltinGlobalFunctionsReadOnlyAsFuncDefArg() {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000208 parse("def func(rule):", " return rule");
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 testFunctionReturnsFunction() {
213 parse(
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000214 "def rule(*, implementation): return None",
215 "def impl(ctx): return None",
Ulf Adams89f012d2015-02-26 13:39:28 +0000216 "",
217 "skylark_rule = rule(implementation = impl)",
218 "",
219 "def macro(name):",
220 " skylark_rule(name = name)");
221 }
222
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000223 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000224 public void testTypeForBooleanLiterals() {
225 parse("len([1, 2]) == 0 and True");
226 parse("len([1, 2]) == 0 and False");
227 }
228
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000229 @Test
Laurent Le Bruna5cdb19e2015-04-01 17:19:57 +0000230 public void testDollarErrorDoesNotLeak() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000231 setFailFast(false);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000232 parseFile(
233 "def GenerateMapNames():", " a = 2", " b = [3, 4]", " if a not b:", " print(a)");
laurentlb566ef5a2018-05-22 10:35:06 -0700234 assertContainsError("syntax error at 'b': expected 'in'");
Laurent Le Bruna5cdb19e2015-04-01 17:19:57 +0000235 // Parser uses "$error" symbol for error recovery.
236 // It should not be used in error messages.
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000237 for (Event event : getEventCollector()) {
Laurent Le Bruna5cdb19e2015-04-01 17:19:57 +0000238 assertThat(event.getMessage()).doesNotContain("$error$");
239 }
240 }
241
laurentlb1e5352d2018-11-06 12:25:55 -0800242 @Test
243 public void testPositionalAfterStarArg() throws Exception {
244 env = newEnvironmentWithSkylarkOptions("--incompatible_strict_argument_ordering=true");
245 checkError(
246 "positional argument is misplaced (positional arguments come first)",
247 "def fct(*args, **kwargs): pass",
248 "fct(1, *[2], 3)");
249 }
250
251 @Test
252 public void testTwoStarArgs() throws Exception {
253 env = newEnvironmentWithSkylarkOptions("--incompatible_strict_argument_ordering=true");
254 checkError(
255 "*arg argument is misplaced",
256 "def fct(*args, **kwargs):",
257 " pass",
258 "fct(1, 2, 3, *[], *[])");
259 }
260
261 @Test
262 public void testKeywordArgAfterStarArg() throws Exception {
263 env = newEnvironmentWithSkylarkOptions("--incompatible_strict_argument_ordering=true");
264 checkError(
265 "keyword argument is misplaced (keyword arguments must be before any *arg or **kwarg)",
266 "def fct(*args, **kwargs): pass",
267 "fct(1, *[2], a=3)");
268 }
269
Ulf Adams89f012d2015-02-26 13:39:28 +0000270 private void parse(String... lines) {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000271 parseFile(lines);
Ulf Adamsb089c8d2015-10-26 12:20:33 +0000272 assertNoWarningsOrErrors();
Ulf Adams89f012d2015-02-26 13:39:28 +0000273 }
274
275 private void checkError(String errorMsg, String... lines) {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000276 setFailFast(false);
277 parseFile(lines);
Ulf Adamsc708f962015-10-22 12:02:28 +0000278 assertContainsError(errorMsg);
Ulf Adams89f012d2015-02-26 13:39:28 +0000279 }
280}