1
16
17 package warn
18
19 import "testing"
20
21 func TestUnnamedMacroNoReaderSameFile(t *testing.T) {
22 checkFindings(t, "unnamed-macro", `
23 load(":foo.bzl", "foo")
24
25 my_rule = rule()
26
27 def macro(x):
28 foo()
29 my_rule(name = x)
30
31 def not_macro(x):
32 foo()
33 native.glob()
34 native.existing_rule()
35 native.existing_rules()
36 native.package_name()
37 native.repository_name()
38 native.exports_files()
39 return my_rule
40
41 def another_macro(x):
42 foo()
43 [native.cc_library() for i in x]
44 `,
45 []string{
46 `5: The macro "macro" should have a keyword argument called "name".
47
48 It is considered a macro because it calls a rule or another macro "my_rule" on line 7.`,
49 `19: The macro "another_macro" should have a keyword argument called "name".
50
51 It is considered a macro because it calls a rule or another macro "native.cc_library" on line 21.`,
52 },
53 scopeBzl)
54
55 checkFindings(t, "unnamed-macro", `
56 my_rule = rule()
57
58 def macro1(foo, name, bar):
59 my_rule()
60
61 def macro2(foo, *, name):
62 my_rule()
63
64 def macro3(foo, *args, **kwargs):
65 my_rule()
66 `,
67 []string{},
68 scopeBzl)
69
70 checkFindings(t, "unnamed-macro", `
71 my_rule = rule()
72
73 def macro(name):
74 my_rule(name = name)
75
76 alias = macro
77
78 def bad_macro():
79 for x in y:
80 alias(x)
81 `,
82 []string{
83 `8: The macro "bad_macro" should have a keyword argument called "name".
84
85 It is considered a macro because it calls a rule or another macro "alias" on line 10.`,
86 },
87 scopeBzl)
88
89 checkFindings(t, "unnamed-macro", `
90 my_rule = rule()
91
92 def macro1():
93 my_rule(name = x)
94
95 def macro2():
96 macro1()
97
98 def macro3():
99 macro2()
100
101 def macro4():
102 my_rule()
103 `,
104 []string{
105 `3: The macro "macro1" should have a keyword argument called "name".
106
107 It is considered a macro because it calls a rule or another macro "my_rule" on line 4.`,
108 `6: The macro "macro2" should have a keyword argument called "name".
109
110 It is considered a macro because it calls a rule or another macro "macro1" on line 7`,
111 `9: The macro "macro3" should have a keyword argument called "name".
112
113 It is considered a macro because it calls a rule or another macro "macro2" on line 10.`,
114 `12: The macro "macro4" should have a keyword argument called "name".
115
116 It is considered a macro because it calls a rule or another macro "my_rule" on line 13.`,
117 },
118 scopeBzl)
119 }
120
121 func TestUnnamedMacroRecursion(t *testing.T) {
122
123
124 checkFindings(t, "unnamed-macro", `
125 my_rule = rule()
126
127 def macro():
128 macro()
129 `, []string{}, scopeBzl)
130
131 checkFindings(t, "unnamed-macro", `
132 my_rule = rule()
133
134 def macro():
135 macro()
136 `, []string{}, scopeBzl)
137
138 checkFindings(t, "unnamed-macro", `
139 my_rule = rule()
140
141 def macro1():
142 macro2()
143
144 def macro2():
145 macro3()
146
147 def macro3():
148 macro1()
149 `, []string{}, scopeBzl)
150
151 checkFindings(t, "unnamed-macro", `
152 my_rule = rule()
153
154 def foo():
155 bar()
156
157 def bar():
158 foo()
159 my_rule()
160 `,
161 []string{
162 `3: The macro "foo" should have a keyword argument called "name".
163
164 It is considered a macro because it calls a rule or another macro "bar" on line 4.`,
165 `6: The macro "bar" should have a keyword argument called "name".
166
167 It is considered a macro because it calls a rule or another macro "my_rule" on line 8.`,
168 },
169 scopeBzl)
170 }
171
172 func TestUnnamedMacroWithReader(t *testing.T) {
173 defer setUpFileReader(map[string]string{
174 "test/package/subdir1/foo.bzl": `
175 def foo():
176 native.foo_binary()
177
178 def bar():
179 foo()
180
181 my_rule = rule()
182 `,
183 "test/package/subdir2/baz.bzl": `
184 load(":subdir1/foo.bzl", "bar", your_rule = "my_rule")
185 load("//does/not:exist.bzl", "something")
186
187 def baz():
188 if False:
189 bar()
190
191 def qux():
192 your_rule()
193
194 def f():
195 something()
196 `,
197 })()
198
199 checkFindings(t, "unnamed-macro", `
200 load("//test/package:subdir1/foo.bzl", abc = "bar")
201 load(":subdir2/baz.bzl", "baz", "qux", "f")
202
203 def macro1(surname):
204 abc()
205
206 def macro2(surname):
207 baz()
208
209 def macro3(surname):
210 qux()
211
212 def not_macro(x):
213 f()
214 `,
215 []string{
216 `4: The macro "macro1" should have a keyword argument called "name".
217
218 It is considered a macro because it calls a rule or another macro "abc" on line 5.
219
220 By convention, every public macro needs a "name" argument (even if it doesn't use it).
221 This is important for tooling and automation.
222
223 * If this function is a helper function that's not supposed to be used outside of this file,
224 please make it private (e.g. rename it to "_macro1").
225 * Otherwise, add a "name" argument. If possible, use that name when calling other macros/rules.`,
226 `7: The macro "macro2" should have a keyword argument called "name".
227
228 It is considered a macro because it calls a rule or another macro "baz" on line 8.
229
230 By convention, every public macro needs a "name" argument (even if it doesn't use it).
231 This is important for tooling and automation.
232
233 * If this function is a helper function that's not supposed to be used outside of this file,
234 please make it private (e.g. rename it to "_macro2").
235 * Otherwise, add a "name" argument. If possible, use that name when calling other macros/rules.`,
236 `10: The macro "macro3" should have a keyword argument called "name".
237
238 It is considered a macro because it calls a rule or another macro "qux" on line 11.
239
240 By convention, every public macro needs a "name" argument (even if it doesn't use it).
241 This is important for tooling and automation.
242
243 * If this function is a helper function that's not supposed to be used outside of this file,
244 please make it private (e.g. rename it to "_macro3").
245 * Otherwise, add a "name" argument. If possible, use that name when calling other macros/rules.`,
246 },
247 scopeBzl)
248 }
249
250 func TestUnnamedMacroRecursionWithReader(t *testing.T) {
251
252
253 defer setUpFileReader(map[string]string{
254 "test/package/foo.bzl": `
255 load(":bar.bzl", "bar")
256
257 def foo():
258 foo()
259
260 def baz():
261 bar()
262
263 def qux():
264 native.cc_library()
265
266 `,
267 "test/package/bar.bzl": `
268 load(":foo.bzl", "foo", "baz", quuux = "qux")
269
270 def bar():
271 baz()
272
273 def qux():
274 foo()
275 baz()
276 quuux()
277 `,
278 })()
279
280 checkFindings(t, "unnamed-macro", `
281 load(":foo.bzl", "foo", "baz")
282 load(":bar.bzl", quux = "qux")
283
284 def macro():
285 foo()
286 baz()
287 quux()
288 `, []string{
289 `4: The macro "macro" should have a keyword argument called "name".
290
291 It is considered a macro because it calls a rule or another macro "quux" on line 7.`,
292 }, scopeBzl)
293 }
294
295 func TestUnnamedMacroLoadCycle(t *testing.T) {
296 defer setUpFileReader(map[string]string{
297 "test/package/foo.bzl": `
298 load(":test_file.bzl", some_rule = "my_rule")
299
300 def foo():
301 some_rule()
302 `,
303 })()
304
305 checkFindings(t, "unnamed-macro", `
306 load(":foo.bzl", bar = "foo")
307
308 my_rule = rule()
309
310 def macro():
311 bar()
312 `, []string{
313 `5: The macro "macro" should have a keyword argument called "name".
314
315 It is considered a macro because it calls a rule or another macro "bar" on line 6.`,
316 }, scopeBzl)
317 }
318
319 func TestUnnamedMacroLoadedFiles(t *testing.T) {
320
321
322 defer setUpFileReader(map[string]string{
323 "a.bzl": "a = rule()",
324 "b.bzl": "b = rule()",
325 "c.bzl": "c = rule()",
326 "d.bzl": "d = rule()",
327 })()
328
329 checkFindings(t, "unnamed-macro", `
330 load("//:a.bzl", "a")
331 load("//:b.bzl", "b")
332 load("//:c.bzl", "c")
333 load("//:d.bzl", "d")
334
335 def macro1():
336 a() # has to load a.bzl to analyze
337
338 def macro2():
339 b() # can skip b.bzl because there's a native rule
340 native.cc_library()
341
342 def macro3():
343 c() # can skip c.bzl because a.bzl has already been loaded
344 a()
345
346 def macro4():
347 d() # can skip d.bzl because there's a rule or another macro defined in the same file
348 r()
349
350 r = rule()
351 `, []string{
352 `6: The macro "macro1" should have a keyword argument called "name".
353
354 It is considered a macro because it calls a rule or another macro "a" on line 7.`,
355 `9: The macro "macro2" should have a keyword argument called "name".
356
357 It is considered a macro because it calls a rule or another macro "native.cc_library" on line 11.`,
358 `13: The macro "macro3" should have a keyword argument called "name".
359
360 It is considered a macro because it calls a rule or another macro "a" on line 15.`,
361 `17: The macro "macro4" should have a keyword argument called "name".
362
363 It is considered a macro because it calls a rule or another macro "r" on line 19.`,
364 }, scopeBzl)
365
366 if len(fileReaderRequests) == 1 && fileReaderRequests[0] == "a.bzl" {
367 return
368 }
369 t.Errorf("expected to load only a.bzl, instead loaded %d files: %v", len(fileReaderRequests), fileReaderRequests)
370 }
371
372 func TestUnnamedMacroAliases(t *testing.T) {
373 defer setUpFileReader(map[string]string{
374 "test/package/foo.bzl": `
375 load(":bar.bzl", _bar = "bar")
376
377 bar = _bar`,
378 "test/package/bar.bzl": `
379 my_rule = rule()
380
381 bar = my_rule`,
382 })()
383
384 checkFindings(t, "unnamed-macro", `
385 load(":bar.bzl", "bar")
386
387 baz = bar
388
389 def macro1():
390 baz()
391
392 def macro2(name):
393 baz()
394 `, []string{
395 `5: The macro "macro1" should have a keyword argument called "name".
396
397 It is considered a macro because it calls a rule or another macro "baz" on line 6.`,
398 }, scopeBzl)
399 }
400
401 func TestUnnamedMacroPrivate(t *testing.T) {
402 checkFindings(t, "unnamed-macro", `
403 my_rule = rule()
404
405 def _not_macro(x):
406 my_rule(name = x)
407
408 def macro(x):
409 _not_macro(x)
410 `,
411 []string{
412 `6: The macro "macro" should have a keyword argument called "name".
413
414 It is considered a macro because it calls a rule or another macro "_not_macro" on line 7.`,
415 },
416 scopeBzl)
417 }
418
419 func TestUnnamedMacroTypeAnnotation(t *testing.T) {
420 checkFindings(t, "unnamed-macro", `
421 my_rule = rule()
422
423 def macro(name: string):
424 my_rule(name)
425 `,
426 []string{},
427 scopeBzl)
428
429 checkFindings(t, "unnamed-macro", `
430 my_rule = rule()
431
432 def macro(name: string = "default"):
433 my_rule(name)
434 `,
435 []string{},
436 scopeBzl)
437 }
438
View as plain text