Annotation of pkgsrc/pkgtools/pkglint/files/redundantscope_test.go, Revision 1.10
1.1 rillig 1: package pkglint
2:
3: import "gopkg.in/check.v1"
4:
5: // In a single file, five variables get a default value and are later overridden
6: // with the same value using the five different assignments operators.
7: func (s *Suite) Test_RedundantScope__single_file_default(c *check.C) {
8: t := s.Init(c)
9:
10: mklines := t.NewMkLines("file.mk",
1.2 rillig 11: "VAR.def?= value",
12: "VAR.asg?= value",
13: "VAR.app?= value",
14: "VAR.evl?= value",
15: "VAR.shl?= value",
1.1 rillig 16: "",
1.2 rillig 17: "VAR.def?= value",
18: "VAR.asg= value",
19: "VAR.app+= value",
20: "VAR.evl:= value",
21: "VAR.shl!= value")
1.1 rillig 22:
23: NewRedundantScope().Check(mklines)
24:
25: t.CheckOutputLines(
1.2 rillig 26: "NOTE: file.mk:7: Default assignment of VAR.def has no effect because of line 1.",
27: "NOTE: file.mk:8: Definition of VAR.asg is redundant because of line 2.",
1.10 ! rillig 28: "NOTE: file.mk:10: Definition of VAR.evl is redundant because of line 4.")
1.2 rillig 29: // TODO: "VAR.shl: is overwritten later"
1.1 rillig 30: }
31:
32: // In a single file, five variables get assigned are value and are later overridden
33: // with the same value using the five different assignments operators.
34: func (s *Suite) Test_RedundantScope__single_file_assign(c *check.C) {
35: t := s.Init(c)
36:
37: mklines := t.NewMkLines("file.mk",
1.2 rillig 38: "VAR.def= value",
39: "VAR.asg= value",
40: "VAR.app= value",
41: "VAR.evl= value",
42: "VAR.shl= value",
1.1 rillig 43: "",
1.2 rillig 44: "VAR.def?= value",
45: "VAR.asg= value",
46: "VAR.app+= value",
47: "VAR.evl:= value",
48: "VAR.shl!= value")
1.1 rillig 49:
50: NewRedundantScope().Check(mklines)
51:
52: t.CheckOutputLines(
1.2 rillig 53: "NOTE: file.mk:7: Default assignment of VAR.def has no effect because of line 1.",
54: "NOTE: file.mk:8: Definition of VAR.asg is redundant because of line 2.",
1.10 ! rillig 55: "NOTE: file.mk:10: Definition of VAR.evl is redundant because of line 4.")
1.2 rillig 56: // TODO: "VAR.shl: is overwritten later"
1.1 rillig 57: }
58:
59: // In a single file, five variables get appended a value and are later overridden
60: // with the same value using the five different assignments operators.
61: func (s *Suite) Test_RedundantScope__single_file_append(c *check.C) {
62: t := s.Init(c)
63:
64: mklines := t.NewMkLines("file.mk",
1.2 rillig 65: "VAR.def+= value",
66: "VAR.asg+= value",
67: "VAR.app+= value",
68: "VAR.evl+= value",
69: "VAR.shl+= value",
1.1 rillig 70: "",
1.2 rillig 71: "VAR.def?= value",
72: "VAR.asg= value",
73: "VAR.app+= value",
74: "VAR.evl:= value",
75: "VAR.shl!= value")
1.1 rillig 76:
77: NewRedundantScope().Check(mklines)
78:
79: t.CheckOutputLines(
1.2 rillig 80: "NOTE: file.mk:7: Default assignment of VAR.def has no effect because of line 1.",
81: "WARN: file.mk:2: Variable VAR.asg is overwritten in line 8.",
82: "WARN: file.mk:4: Variable VAR.evl is overwritten in line 10.")
83: // TODO: "VAR.shl: is overwritten later"
1.1 rillig 84: }
85:
86: // In a single file, five variables get assigned a value using the := operator,
87: // which in this simple case is equivalent to the = operator. The variables are
88: // later overridden with the same value using the five different assignments operators.
89: func (s *Suite) Test_RedundantScope__single_file_eval(c *check.C) {
90: t := s.Init(c)
91:
92: mklines := t.NewMkLines("file.mk",
1.2 rillig 93: "VAR.def:= value",
94: "VAR.asg:= value",
95: "VAR.app:= value",
96: "VAR.evl:= value",
97: "VAR.shl:= value",
1.1 rillig 98: "",
1.2 rillig 99: "VAR.def?= value",
100: "VAR.asg= value",
101: "VAR.app+= value",
102: "VAR.evl:= value",
103: "VAR.shl!= value")
1.1 rillig 104:
105: NewRedundantScope().Check(mklines)
106:
107: t.CheckOutputLines(
1.2 rillig 108: "NOTE: file.mk:7: Default assignment of VAR.def has no effect because of line 1.",
109: "NOTE: file.mk:8: Definition of VAR.asg is redundant because of line 2.",
1.10 ! rillig 110: "NOTE: file.mk:10: Definition of VAR.evl is redundant because of line 4.")
1.2 rillig 111: // TODO: "VAR.shl: is overwritten later"
1.1 rillig 112: }
113:
114: // In a single file, five variables get assigned a value using the != operator,
115: // which runs a shell command. As of March 2019 pkglint doesn't try to evaluate
116: // the shell commands, therefore the variable values are unknown. The variables
117: // are later overridden using the five different assignments operators.
118: func (s *Suite) Test_RedundantScope__single_file_shell(c *check.C) {
119: t := s.Init(c)
120:
121: mklines := t.NewMkLines("file.mk",
1.2 rillig 122: "VAR.def!= value",
123: "VAR.asg!= value",
124: "VAR.app!= value",
125: "VAR.evl!= value",
126: "VAR.shl!= value",
1.1 rillig 127: "",
1.2 rillig 128: "VAR.def?= value",
129: "VAR.asg= value",
130: "VAR.app+= value",
131: "VAR.evl:= value",
132: "VAR.shl!= value")
1.1 rillig 133:
134: NewRedundantScope().Check(mklines)
135:
136: t.CheckOutputLines(
1.2 rillig 137: "NOTE: file.mk:7: Default assignment of VAR.def has no effect because of line 1.",
138: "WARN: file.mk:2: Variable VAR.asg is overwritten in line 8.",
139: "WARN: file.mk:4: Variable VAR.evl is overwritten in line 10.")
140: // TODO: "VAR.shl: is overwritten later"
1.1 rillig 141: }
142:
143: // In a single file, five variables get a default value and are later overridden
144: // with the same value using the five different assignments operators.
145: func (s *Suite) Test_RedundantScope__single_file_default_ref(c *check.C) {
146: t := s.Init(c)
147:
148: mklines := t.NewMkLines("file.mk",
1.2 rillig 149: "VAR.def?= ${OTHER}",
150: "VAR.asg?= ${OTHER}",
151: "VAR.app?= ${OTHER}",
152: "VAR.evl?= ${OTHER}",
153: "VAR.shl?= ${OTHER}",
1.1 rillig 154: "",
1.2 rillig 155: "VAR.def?= ${OTHER}",
156: "VAR.asg= ${OTHER}",
157: "VAR.app+= ${OTHER}",
158: "VAR.evl:= ${OTHER}",
159: "VAR.shl!= ${OTHER}")
1.1 rillig 160:
161: NewRedundantScope().Check(mklines)
162:
163: t.CheckOutputLines(
1.2 rillig 164: "NOTE: file.mk:7: Default assignment of VAR.def has no effect because of line 1.",
165: "NOTE: file.mk:8: Definition of VAR.asg is redundant because of line 2.")
166: // TODO: "VAR.evl: is overwritten later",
167: // TODO: "VAR.shl: is overwritten later"
1.1 rillig 168: }
169:
170: // In a single file, five variables get assigned are value and are later overridden
171: // with the same value using the five different assignments operators.
172: func (s *Suite) Test_RedundantScope__single_file_assign_ref(c *check.C) {
173: t := s.Init(c)
174:
175: mklines := t.NewMkLines("file.mk",
1.2 rillig 176: "VAR.def= ${OTHER}",
177: "VAR.asg= ${OTHER}",
178: "VAR.app= ${OTHER}",
179: "VAR.evl= ${OTHER}",
180: "VAR.shl= ${OTHER}",
1.1 rillig 181: "",
1.2 rillig 182: "VAR.def?= ${OTHER}",
183: "VAR.asg= ${OTHER}",
184: "VAR.app+= ${OTHER}",
185: "VAR.evl:= ${OTHER}",
186: "VAR.shl!= ${OTHER}")
1.1 rillig 187:
188: NewRedundantScope().Check(mklines)
189:
190: t.CheckOutputLines(
1.2 rillig 191: "NOTE: file.mk:7: Default assignment of VAR.def has no effect because of line 1.",
192: "NOTE: file.mk:8: Definition of VAR.asg is redundant because of line 2.")
193: // TODO: "VAR.evl: is overwritten later",
194: // TODO: "VAR.shl: is overwritten later"
1.1 rillig 195: }
196:
197: // In a single file, five variables get appended a value and are later overridden
198: // with the same value using the five different assignments operators.
199: func (s *Suite) Test_RedundantScope__single_file_append_ref(c *check.C) {
200: t := s.Init(c)
201:
202: mklines := t.NewMkLines("file.mk",
1.2 rillig 203: "VAR.def+= ${OTHER}",
204: "VAR.asg+= ${OTHER}",
205: "VAR.app+= ${OTHER}",
206: "VAR.evl+= ${OTHER}",
207: "VAR.shl+= ${OTHER}",
1.1 rillig 208: "",
1.2 rillig 209: "VAR.def?= ${OTHER}",
210: "VAR.asg= ${OTHER}",
211: "VAR.app+= ${OTHER}",
212: "VAR.evl:= ${OTHER}",
213: "VAR.shl!= ${OTHER}")
1.1 rillig 214:
215: NewRedundantScope().Check(mklines)
216:
217: t.CheckOutputLines(
1.2 rillig 218: "NOTE: file.mk:7: Default assignment of VAR.def has no effect because of line 1.",
219: "WARN: file.mk:2: Variable VAR.asg is overwritten in line 8.")
220: // TODO: "VAR.evl: is overwritten later",
221: // TODO: "VAR.shl: is overwritten later"
1.1 rillig 222: }
223:
224: // In a single file, five variables get assigned a value using the := operator,
225: // which in this simple case is equivalent to the = operator. The variables are
226: // later overridden with the same value using the five different assignments operators.
227: func (s *Suite) Test_RedundantScope__single_file_eval_ref(c *check.C) {
228: t := s.Init(c)
229:
230: mklines := t.NewMkLines("file.mk",
1.2 rillig 231: "VAR.def:= ${OTHER}",
232: "VAR.asg:= ${OTHER}",
233: "VAR.app:= ${OTHER}",
234: "VAR.evl:= ${OTHER}",
235: "VAR.shl:= ${OTHER}",
1.1 rillig 236: "",
1.2 rillig 237: "VAR.def?= ${OTHER}",
238: "VAR.asg= ${OTHER}",
239: "VAR.app+= ${OTHER}",
240: "VAR.evl:= ${OTHER}",
241: "VAR.shl!= ${OTHER}")
1.1 rillig 242:
243: NewRedundantScope().Check(mklines)
244:
245: t.CheckOutputLines(
1.2 rillig 246: "NOTE: file.mk:7: Default assignment of VAR.def has no effect because of line 1.",
247: "NOTE: file.mk:8: Definition of VAR.asg is redundant because of line 2.")
248: // TODO: "VAR.evl: is overwritten later",
249: // TODO: "VAR.shl: is overwritten later"
1.1 rillig 250: }
251:
252: // In a single file, five variables get assigned a value using the != operator,
253: // which runs a shell command. As of March 2019 pkglint doesn't try to evaluate
254: // the shell commands, therefore the variable values are unknown. The variables
255: // are later overridden using the five different assignments operators.
256: func (s *Suite) Test_RedundantScope__single_file_shell_ref(c *check.C) {
257: t := s.Init(c)
258:
259: mklines := t.NewMkLines("file.mk",
1.2 rillig 260: "VAR.def!= ${OTHER}",
261: "VAR.asg!= ${OTHER}",
262: "VAR.app!= ${OTHER}",
263: "VAR.evl!= ${OTHER}",
264: "VAR.shl!= ${OTHER}",
1.1 rillig 265: "",
1.2 rillig 266: "VAR.def?= ${OTHER}",
267: "VAR.asg= ${OTHER}",
268: "VAR.app+= ${OTHER}",
269: "VAR.evl:= ${OTHER}",
270: "VAR.shl!= ${OTHER}")
1.1 rillig 271:
272: NewRedundantScope().Check(mklines)
273:
274: t.CheckOutputLines(
1.2 rillig 275: "NOTE: file.mk:7: Default assignment of VAR.def has no effect because of line 1.",
276: "WARN: file.mk:2: Variable VAR.asg is overwritten in line 8.")
277: // TODO: "VAR.evl: is overwritten later",
278: // TODO: "VAR.shl: is overwritten later"
1.1 rillig 279: }
280:
281: func (s *Suite) Test_RedundantScope__after_including_same_value(c *check.C) {
282: t := s.Init(c)
283:
1.2 rillig 284: // including.mk:1: include "included.mk"
285: // included.mk:1: VAR.x.y op1 ${OTHER}
286: // including.mk:2: VAR.x.y op2 ${OTHER}
287: //
288: test := func(includedOp, includingOp string, diagnostics ...string) {
289: opName := [...]string{"asg", "shl", "evl", "app", "def"}
290: varname := sprintf("VAR.%s.%s",
291: opName[NewMkOperator(includedOp)],
292: opName[NewMkOperator(includingOp)])
293:
294: include, get := t.SetUpHierarchy()
295: include("including.mk",
296: include("included.mk",
297: sprintf("%s%s ${OTHER}", varname, includedOp)),
298: sprintf("%s%s ${OTHER}", varname, includingOp))
299:
300: NewRedundantScope().Check(get("including.mk"))
301:
302: t.CheckOutput(diagnostics)
303: }
304:
305: // As of March 2019, the != operator is ignored for the redundancy check.
306: // TODO: Add the != operator.
307:
308: test("?=", "?=",
309: "NOTE: including.mk:2: Default assignment of VAR.def.def has no effect because of included.mk:1.")
310:
311: test("?=", "=",
312: "NOTE: including.mk:2: Definition of VAR.def.asg is redundant because of included.mk:1.")
313:
314: // VAR.def.app defines a default value and then appends to it. This is a common pattern.
315: // Appending the same value feels redundant but probably doesn't happen in practice.
316: // If it does, there should be a note for it.
317: test("?=", "+=")
318:
319: // VAR.def.evl introduces a subtle difference since := evaluates the variable immediately.
320: // Therefore the assignment is not redundant.
321: test("?=", ":=")
322:
323: test("=", "?=",
324: "NOTE: including.mk:2: Default assignment of VAR.asg.def has no effect because of included.mk:1.")
325:
326: test("=", "=",
327: "NOTE: including.mk:2: Definition of VAR.asg.asg is redundant because of included.mk:1.")
328:
329: // VAR.asg.app defines a variable and later appends to it. This is a common pattern.
330: // Appending the same value feels redundant but probably doesn't happen in practice.
331: // If it does, there should be a note for it.
332: test("=", "+=")
333:
334: // VAR.asg.evl evaluates the variable immediately and is thus not redundant.
335: test("=", ":=")
336:
337: test("+=", "?=",
338: "NOTE: including.mk:2: Default assignment of VAR.app.def has no effect because of included.mk:1.")
339:
340: // VAR.app.asg first appends and then overwrites. This might be a mistake.
341: // TODO: Find out whether this case happens in actual pkgsrc and if it's accidental.
342: // VAR.app.app first appends and then appends one more. This is a common pattern.
343: test("+=", "=")
344:
345: test("+=", "+=")
346:
347: test("+=", ":=")
348:
349: test(":=", "?=",
350: "NOTE: including.mk:2: Default assignment of VAR.evl.def has no effect because of included.mk:1.")
351:
352: test(":=", "=",
353: "NOTE: including.mk:2: Definition of VAR.evl.asg is redundant because of included.mk:1.")
1.1 rillig 354:
1.2 rillig 355: test(":=", "+=")
1.1 rillig 356:
1.2 rillig 357: test(":=", ":=")
1.1 rillig 358: }
359:
360: func (s *Suite) Test_RedundantScope__after_including_different_value(c *check.C) {
361: t := s.Init(c)
362:
363: // Only test the ?=, = and += operators since the others are ignored,
364: // as of March 2019.
365: include, get := t.SetUpHierarchy()
366: include("including.mk",
367: include("included.mk",
1.2 rillig 368: "VAR.def.def?= ${VALUE}",
369: "VAR.def.asg?= ${VALUE}",
370: "VAR.def.app?= ${VALUE}",
371: "VAR.asg.def= ${VALUE}",
372: "VAR.asg.asg= ${VALUE}",
373: "VAR.asg.app= ${VALUE}",
374: "VAR.app.def+= ${VALUE}",
375: "VAR.app.asg+= ${VALUE}",
376: "VAR.app.app+= ${VALUE}"),
377: "VAR.def.def?= ${OTHER}",
378: "VAR.def.asg= ${OTHER}",
379: "VAR.def.app+= ${OTHER}",
380: "VAR.asg.def?= ${OTHER}",
381: "VAR.asg.asg= ${OTHER}",
382: "VAR.asg.app+= ${OTHER}",
383: "VAR.app.def?= ${OTHER}",
384: "VAR.app.asg= ${OTHER}",
385: "VAR.app.app+= ${OTHER}")
1.1 rillig 386: mklines := get("including.mk")
387:
388: NewRedundantScope().Check(mklines)
389:
390: t.CheckOutputLines(
391: "NOTE: including.mk:2: Default assignment of VAR.def.def has no effect because of included.mk:1.",
392: "NOTE: including.mk:5: Default assignment of VAR.asg.def has no effect because of included.mk:4.",
393: "NOTE: including.mk:8: Default assignment of VAR.app.def has no effect because of included.mk:7.")
394: }
395:
396: func (s *Suite) Test_RedundantScope__before_including_same_value(c *check.C) {
397: t := s.Init(c)
398:
399: // Only test the ?=, = and += operators since the others are ignored,
400: // as of March 2019.
401: include, get := t.SetUpHierarchy()
402: include("including.mk",
1.2 rillig 403: "VAR.def.def?= ${OTHER}",
404: "VAR.def.asg?= ${OTHER}",
405: "VAR.def.app?= ${OTHER}",
406: "VAR.asg.def= ${OTHER}",
407: "VAR.asg.asg= ${OTHER}",
408: "VAR.asg.app= ${OTHER}",
409: "VAR.app.def+= ${OTHER}",
410: "VAR.app.asg+= ${OTHER}",
411: "VAR.app.app+= ${OTHER}",
1.1 rillig 412: include("included.mk",
1.2 rillig 413: "VAR.def.def?= ${OTHER}",
414: "VAR.def.asg= ${OTHER}",
415: "VAR.def.app+= ${OTHER}",
416: "VAR.asg.def?= ${OTHER}",
417: "VAR.asg.asg= ${OTHER}",
418: "VAR.asg.app+= ${OTHER}",
419: "VAR.app.def?= ${OTHER}",
420: "VAR.app.asg= ${OTHER}",
421: "VAR.app.app+= ${OTHER}"))
1.1 rillig 422: mklines := get("including.mk")
423:
424: NewRedundantScope().Check(mklines)
425:
426: t.CheckOutputLines(
427: "NOTE: including.mk:1: Default assignment of VAR.def.def has no effect because of included.mk:1.",
428: "NOTE: including.mk:2: Default assignment of VAR.def.asg has no effect because of included.mk:2.",
429: "NOTE: including.mk:4: Definition of VAR.asg.def is redundant because of included.mk:4.",
430: "NOTE: including.mk:5: Definition of VAR.asg.asg is redundant because of included.mk:5.",
431: "WARN: including.mk:8: Variable VAR.app.asg is overwritten in included.mk:8.")
432: }
433:
434: func (s *Suite) Test_RedundantScope__before_including_different_value(c *check.C) {
435: t := s.Init(c)
436:
437: // Only test the ?=, = and += operators since the others are ignored,
438: // as of March 2019.
439: include, get := t.SetUpHierarchy()
440: include("including.mk",
1.2 rillig 441: "VAR.def.def?= ${VALUE}",
442: "VAR.def.asg?= ${VALUE}",
443: "VAR.def.app?= ${VALUE}",
444: "VAR.asg.def= ${VALUE}",
445: "VAR.asg.asg= ${VALUE}",
446: "VAR.asg.app= ${VALUE}",
447: "VAR.app.def+= ${VALUE}",
448: "VAR.app.asg+= ${VALUE}",
449: "VAR.app.app+= ${VALUE}",
1.1 rillig 450: include("included.mk",
1.2 rillig 451: "VAR.def.def?= ${OTHER}",
452: "VAR.def.asg= ${OTHER}",
453: "VAR.def.app+= ${OTHER}",
454: "VAR.asg.def?= ${OTHER}",
455: "VAR.asg.asg= ${OTHER}",
456: "VAR.asg.app+= ${OTHER}",
457: "VAR.app.def?= ${OTHER}",
458: "VAR.app.asg= ${OTHER}",
459: "VAR.app.app+= ${OTHER}"))
1.1 rillig 460: mklines := get("including.mk")
461:
462: NewRedundantScope().Check(mklines)
463:
464: t.CheckOutputLines(
465: "WARN: including.mk:2: Variable VAR.def.asg is overwritten in included.mk:2.",
466: "WARN: including.mk:5: Variable VAR.asg.asg is overwritten in included.mk:5.",
467: "WARN: including.mk:8: Variable VAR.app.asg is overwritten in included.mk:8.")
468: }
469:
470: func (s *Suite) Test_RedundantScope__independent_same_value(c *check.C) {
471: t := s.Init(c)
472:
473: // Only test the ?=, = and += operators since the others are ignored,
474: // as of March 2019.
475: include, get := t.SetUpHierarchy()
476: include("including.mk",
477: include("included1.mk",
1.2 rillig 478: "VAR.def.def?= ${OTHER}",
479: "VAR.def.asg?= ${OTHER}",
480: "VAR.def.app?= ${OTHER}",
481: "VAR.asg.def= ${OTHER}",
482: "VAR.asg.asg= ${OTHER}",
483: "VAR.asg.app= ${OTHER}",
484: "VAR.app.def+= ${OTHER}",
485: "VAR.app.asg+= ${OTHER}",
486: "VAR.app.app+= ${OTHER}"),
1.1 rillig 487: include("included2.mk",
1.2 rillig 488: "VAR.def.def?= ${OTHER}",
489: "VAR.def.asg= ${OTHER}",
490: "VAR.def.app+= ${OTHER}",
491: "VAR.asg.def?= ${OTHER}",
492: "VAR.asg.asg= ${OTHER}",
493: "VAR.asg.app+= ${OTHER}",
494: "VAR.app.def?= ${OTHER}",
495: "VAR.app.asg= ${OTHER}",
496: "VAR.app.app+= ${OTHER}"))
1.1 rillig 497: mklines := get("including.mk")
498:
499: NewRedundantScope().Check(mklines)
500:
501: // Since the two included files are independent, there cannot be any
502: // redundancies between them. These redundancies can only be discovered
503: // when one of them includes the other.
504: t.CheckOutputEmpty()
505: }
506:
507: func (s *Suite) Test_RedundantScope__independent_different_value(c *check.C) {
508: t := s.Init(c)
509:
510: // Only test the ?=, = and += operators since the others are ignored,
511: // as of March 2019.
512: include, get := t.SetUpHierarchy()
513: include("including.mk",
514: include("included1.mk",
1.2 rillig 515: "VAR.def.def?= ${VALUE}",
516: "VAR.def.asg?= ${VALUE}",
517: "VAR.def.app?= ${VALUE}",
518: "VAR.asg.def= ${VALUE}",
519: "VAR.asg.asg= ${VALUE}",
520: "VAR.asg.app= ${VALUE}",
521: "VAR.app.def+= ${VALUE}",
522: "VAR.app.asg+= ${VALUE}",
523: "VAR.app.app+= ${VALUE}"),
1.1 rillig 524: include("included2.mk",
1.2 rillig 525: "VAR.def.def?= ${OTHER}",
526: "VAR.def.asg= ${OTHER}",
527: "VAR.def.app+= ${OTHER}",
528: "VAR.asg.def?= ${OTHER}",
529: "VAR.asg.asg= ${OTHER}",
530: "VAR.asg.app+= ${OTHER}",
531: "VAR.app.def?= ${OTHER}",
532: "VAR.app.asg= ${OTHER}",
533: "VAR.app.app+= ${OTHER}"))
1.1 rillig 534: mklines := get("including.mk")
535:
536: NewRedundantScope().Check(mklines)
537:
538: // Since the two included files are independent, there cannot be any
539: // redundancies between them. Redundancies can only be discovered
540: // when one of them includes the other.
541: t.CheckOutputEmpty()
542: }
543:
544: func (s *Suite) Test_RedundantScope__file_hierarchy(c *check.C) {
545: t := s.Init(c)
546:
547: include, get := t.SetUpHierarchy()
548:
549: include("including.mk",
550: include("other.mk",
1.2 rillig 551: "VAR= other"),
1.1 rillig 552: include("module.mk",
1.2 rillig 553: "VAR= module",
1.1 rillig 554: include("version.mk",
1.2 rillig 555: "VAR= version"),
1.1 rillig 556: include("env.mk",
1.2 rillig 557: "VAR= env")))
1.1 rillig 558:
559: NewRedundantScope().Check(get("including.mk"))
560:
561: // No output since the included files are independent.
562: t.CheckOutputEmpty()
563:
564: NewRedundantScope().Check(get("other.mk"))
565:
566: // No output since the file by itself in neither redundant nor
567: // does it include any other file.
568: t.CheckOutputEmpty()
569:
570: NewRedundantScope().Check(get("module.mk"))
571:
572: // No warning about env.mk because it is independent from version.mk.
573: // Pkglint only produces warnings when it is very sure that the variable
574: // definition is really redundant in all cases.
575: //
576: // One reason to not warn is that at the point where env.mk is evaluated,
577: // version.mk had last written to the variable. Since version.mk is
578: // independent from env.mk, there is nothing redundant here.
579: // Pkglint doesn't do this, but it could.
580: //
581: // Another reason not to warn is that all locations where the variable has
582: // ever been accessed are saved. And if the current location neither includes
583: // all of the others nor is included by all of the others, there is at least
584: // one access that is in an unrelated file. This is what pkglint does.
585: t.CheckOutputLines(
586: "WARN: module.mk:1: Variable VAR is overwritten in version.mk:1.")
587: }
588:
1.2 rillig 589: // The RedundantScope keeps track of the variable values. As a consequence,
590: // it reports the variable assignment in the last line as being redundant,
591: // instead of warning that it destroys the previous value.
592: func (s *Suite) Test_RedundantScope__assign_and_append_followed_by_assign(c *check.C) {
593: t := s.Init(c)
594:
595: mklines := t.NewMkLines("redundant.mk",
596: "VAR= first",
597: "VAR+= second",
598: "VAR= first second")
599:
600: NewRedundantScope().Check(mklines)
601:
602: t.CheckOutputLines(
603: "NOTE: redundant.mk:3: Definition of VAR is redundant because of line 2.")
604: }
605:
606: // The redundancy analysis for a variable VAR is influenced by changes to
607: // each variable that is referenced by VAR. The exact details also depend
608: // on the assignment operators being used for VAR and OTHER.
609: func (s *Suite) Test_RedundantScope__referenced_variable_is_modified(c *check.C) {
610: t := s.Init(c)
611:
612: test := func(line1, line2, line3, line4 string, diagnostics ...string) {
613: mklines := t.NewMkLines("filename.mk",
614: line1, line2, line3, line4)
615:
616: NewRedundantScope().Check(mklines)
617:
618: t.CheckOutput(diagnostics)
619: }
620:
621: test(
622: "OTHER= other-before",
623: "VAR= ${OTHER}",
624: "OTHER?= other-after",
625: "VAR= ${OTHER}",
626:
627: // TODO: "3: has no effect"
628: "NOTE: filename.mk:4: Definition of VAR is redundant because of line 2.")
629:
630: test(
631: "OTHER= other-before",
632: "VAR= ${OTHER}",
633: "OTHER= other-after",
634: "VAR= ${OTHER}",
635:
636: // TODO: "3: overwrites",
637: "NOTE: filename.mk:4: Definition of VAR is redundant because of line 2.")
638:
639: test(
640: "OTHER= other-before",
641: "VAR= ${OTHER}",
642: "OTHER+= other-after",
643: "VAR= ${OTHER}",
644:
645: "NOTE: filename.mk:4: Definition of VAR is redundant because of line 2.")
646:
647: test(
648: "OTHER= other-before",
649: "VAR= ${OTHER}",
650: "OTHER:= other-after",
651: "VAR= ${OTHER}",
652:
653: // TODO: "3: overwrites line 1"
654: "NOTE: filename.mk:4: Definition of VAR is redundant because of line 2.")
655:
656: test(
657: "OTHER= other-before",
658: "VAR= ${OTHER}",
659: "OTHER!= other-after",
660: "VAR= ${OTHER}",
661:
662: // TODO: "3: overwrites line 1",
663: "NOTE: filename.mk:4: Definition of VAR is redundant because of line 2.")
664: }
665:
666: // The redundancy analysis for a variable VAR is influenced by changes to
667: // each variable that is referenced by VAR. The exact details also depend
668: // on the assignment operators being used for VAR and OTHER.
669: func (s *Suite) Test_RedundantScope__variable_referencing_another_is_modified(c *check.C) {
670: t := s.Init(c)
671:
672: test := func(line1, line2, line3, line4 string, diagnostics ...string) {
673: mklines := t.NewMkLines("filename.mk",
674: line1, line2, line3, line4)
675:
676: NewRedundantScope().Check(mklines)
677:
678: t.CheckOutput(diagnostics)
679: }
680:
681: // In this test, the second line is tested for each operator.
682:
683: test(
684: "OTHER= other-before",
685: "VAR?= ${OTHER}",
686: "OTHER= other-after",
687: "VAR= ${OTHER}",
688:
689: // TODO: "3: overwrites line 1"
690: "NOTE: filename.mk:4: Definition of VAR is redundant because of line 2.")
691:
692: test(
693: "OTHER= other-before",
694: "VAR= ${OTHER}",
695: "OTHER= other-after",
696: "VAR= ${OTHER}",
697:
698: // TODO: "3: overwrites",
699: "NOTE: filename.mk:4: Definition of VAR is redundant because of line 2.")
700:
701: test(
702: "OTHER= other-before",
703: "VAR+= ${OTHER}",
704: "OTHER= other-after",
705: "VAR= ${OTHER}",
706:
707: // TODO: "3: overwrites",
708: // The value from line 2 is prefixed by a space, therefore pkglint
709: // issues a warning here instead of an "is redundant" note.
710: "WARN: filename.mk:2: Variable VAR is overwritten in line 4.")
711:
712: test(
713: "OTHER= other-before",
714: "VAR:= ${OTHER}",
715: "OTHER= other-after",
716: "VAR= ${OTHER}",
717:
718: // As of March 2019, pkglint only looks at each variable in isolation.
719: // In this case, to detect that the assignment in line 1 has no effect,
720: // it's necessary to trace the assignment in line 2 and then see that
721: // the VAR from line 2 is immediately overwritten in line 4.
722: "NOTE: filename.mk:4: Definition of VAR is redundant because of line 2.")
723:
724: test(
725: "OTHER= other-before",
726: "VAR= ${OTHER}",
727: "OTHER!= other-after",
728: "VAR= ${OTHER}",
729:
730: "NOTE: filename.mk:4: Definition of VAR is redundant because of line 2.")
731: }
732:
1.4 rillig 733: func (s *Suite) Test_RedundantScope__incomplete_then_default(c *check.C) {
734: t := s.Init(c)
735:
736: include, get := t.SetUpHierarchy()
737:
738: include("including.mk",
739: ".if ${OPSYS} == NetBSD",
740: "VAR=\tNetBSD",
741: ".elif ${OPSYS} == FreeBSD",
742: "VAR=\tFreeBSD",
743: ".endif",
744: "",
745: "VAR?=\tdefault")
746:
747: mklines := get("including.mk")
748:
749: NewRedundantScope().Check(mklines)
750:
751: t.CheckOutputEmpty()
752: }
753:
754: func (s *Suite) Test_RedundantScope__complete_then_default(c *check.C) {
755: t := s.Init(c)
756:
757: include, get := t.SetUpHierarchy()
758:
759: include("including.mk",
760: ".if ${OPSYS} == NetBSD",
761: "VAR=\tNetBSD",
762: ".else",
763: "VAR=\tFreeBSD",
764: ".endif",
765: "",
766: "VAR?=\tdefault")
767:
768: mklines := get("including.mk")
769:
770: NewRedundantScope().Check(mklines)
771:
772: // TODO: Pkglint could know that the ?= is redundant because VAR is
773: // definitely assigned.
774: t.CheckOutputEmpty()
775: }
776:
777: func (s *Suite) Test_RedundantScope__conditional_then_override(c *check.C) {
778: t := s.Init(c)
779:
780: include, get := t.SetUpHierarchy()
781:
782: include("including.mk",
783: ".if ${OPSYS} == NetBSD",
784: "VAR=\tNetBSD",
785: ".else",
786: "VAR=\tFreeBSD",
787: ".endif",
788: "",
789: "VAR=\tdefault")
790:
791: mklines := get("including.mk")
792:
793: NewRedundantScope().Check(mklines)
794:
795: // TODO: Pkglint could know that no matter which branch is taken,
796: // the variable will be overwritten in the last line.
797: t.CheckOutputEmpty()
798: }
799:
800: func (s *Suite) Test_RedundantScope__set_then_conditional(c *check.C) {
801: t := s.Init(c)
802:
803: include, get := t.SetUpHierarchy()
804:
805: include("including.mk",
806: "VAR=\tdefault",
807: "",
808: ".if ${OPSYS} == NetBSD",
809: "VAR=\tNetBSD",
810: ".else",
811: "VAR=\tFreeBSD",
812: ".endif")
813:
814: mklines := get("including.mk")
815:
816: NewRedundantScope().Check(mklines)
817:
818: // TODO: Pkglint could know that no matter which branch is taken,
819: // one of the branches will overwrite the assignment from line 1.
820: t.CheckOutputEmpty()
821: }
822:
823: func (s *Suite) Test_RedundantScope__branch_with_set_then_set(c *check.C) {
824: t := s.Init(c)
825:
826: include, get := t.SetUpHierarchy()
827:
828: include("including.mk",
829: ".if ${OPSYS} == NetBSD",
830: "VAR=\tfirst",
831: "VAR=\tsecond",
832: ".endif")
833:
834: mklines := get("including.mk")
835:
836: NewRedundantScope().Check(mklines)
837:
838: // TODO: Pkglint could know that the second assignment overwrites the
839: // first assignment since they are in the same basic block.
840: t.CheckOutputEmpty()
841: }
842:
1.1 rillig 843: // FIXME: Continue the systematic redundancy tests.
844: //
845: // Tests where the variables are defined in a .for loop that might not be
846: // evaluated at all.
847: //
848: // Tests where files are included conditionally and additionally have conditional
849: // sections, arbitrarily nested.
850: //
851: // Tests that show how to suppress the notes about redundant assignments
852: // and overwritten variables. The explanation must be helpful.
853: //
854: // Tests for dynamic variable assignments. For example BUILD_DIRS.NetBSD may
855: // be modified by any assignment of the form BUILD_DIRS.${var} or even ${var}.
856: // Without further analysis, pkglint cannot report redundancy warnings for any
857: // package that uses such variable assignments.
1.2 rillig 858: //
859: // Tests for variables with modifiers, such as ${VAR:Uundef}, ${VAR:Mpattern},
860: // ${command:sh}, ${command::=value}.
861: //
862: // A test that compares a package with the default values from mk/defaults/mk.conf.
863: // A package doesn't need to override these defaults, and the redundancy check
864: // should notify the package author of this redundancy.
1.1 rillig 865:
866: func (s *Suite) Test_RedundantScope__override_after_including(c *check.C) {
867: t := s.Init(c)
868:
1.2 rillig 869: include, get := t.SetUpHierarchy()
870: include("including.mk",
871: include("included.mk",
872: "OVERRIDE= previous value",
873: "REDUNDANT= redundant"),
874: "OVERRIDE= overridden value",
875: "REDUNDANT= redundant")
876:
877: NewRedundantScope().Check(get("including.mk"))
1.1 rillig 878:
879: t.CheckOutputLines(
880: "NOTE: including.mk:3: Definition of REDUNDANT is redundant because of included.mk:2.")
881: }
882:
883: func (s *Suite) Test_RedundantScope__redundant_assign_after_including(c *check.C) {
884: t := s.Init(c)
885:
1.2 rillig 886: include, get := t.SetUpHierarchy()
887: include("including.mk",
888: include("included.mk",
889: "REDUNDANT= redundant"),
890: "REDUNDANT= redundant")
891:
892: NewRedundantScope().Check(get("including.mk"))
1.1 rillig 893:
894: t.CheckOutputLines(
895: "NOTE: including.mk:2: Definition of REDUNDANT is redundant because of included.mk:1.")
896: }
897:
898: func (s *Suite) Test_RedundantScope__override_in_Makefile_after_including(c *check.C) {
899: t := s.Init(c)
900:
1.2 rillig 901: include, get := t.SetUpHierarchy()
902: include("Makefile",
903: include("module.mk",
904: "VAR= value ${OTHER}",
905: "VAR?= value ${OTHER}",
906: "VAR= new value"),
907: "VAR= the package may overwrite variables from other files")
1.1 rillig 908:
909: // XXX: The warnings from here are not in the same order as the other warnings.
910: // XXX: There may be some warnings for the same file separated by warnings for other files.
1.2 rillig 911: NewRedundantScope().Check(get("Makefile"))
1.1 rillig 912:
913: // No warning for VAR=... in Makefile since it makes sense to have common files
914: // with default values for variables, overriding some of them in each package.
915: t.CheckOutputLines(
916: "NOTE: module.mk:2: Default assignment of VAR has no effect because of line 1.",
917: "WARN: module.mk:2: Variable VAR is overwritten in line 3.")
918: }
919:
920: func (s *Suite) Test_RedundantScope__default_value_definitely_unused(c *check.C) {
921: t := s.Init(c)
1.2 rillig 922:
1.1 rillig 923: mklines := t.NewMkLines("module.mk",
1.2 rillig 924: "VAR= value ${OTHER}",
925: "VAR?= different value")
1.1 rillig 926:
927: NewRedundantScope().Check(mklines)
928:
929: // A default assignment after an unconditional assignment is redundant.
930: // Even more so when the variable is not used between the two assignments.
931: t.CheckOutputLines(
932: "NOTE: module.mk:2: Default assignment of VAR has no effect because of line 1.")
933: }
934:
935: func (s *Suite) Test_RedundantScope__default_value_overridden(c *check.C) {
936: t := s.Init(c)
1.2 rillig 937:
1.1 rillig 938: mklines := t.NewMkLines("module.mk",
1.2 rillig 939: "VAR?= default value",
940: "VAR= overridden value")
1.1 rillig 941:
942: NewRedundantScope().Check(mklines)
943:
944: t.CheckOutputLines(
945: "WARN: module.mk:1: Variable VAR is overwritten in line 2.")
946: }
947:
948: func (s *Suite) Test_RedundantScope__overwrite_same_value(c *check.C) {
949: t := s.Init(c)
1.2 rillig 950:
1.1 rillig 951: mklines := t.NewMkLines("module.mk",
1.2 rillig 952: "VAR= value ${OTHER}",
953: "VAR= value ${OTHER}")
1.1 rillig 954:
955: NewRedundantScope().Check(mklines)
956:
957: t.CheckOutputLines(
958: "NOTE: module.mk:2: Definition of VAR is redundant because of line 1.")
959: }
960:
961: func (s *Suite) Test_RedundantScope__conditional_overwrite(c *check.C) {
962: t := s.Init(c)
1.2 rillig 963:
1.1 rillig 964: mklines := t.NewMkLines("module.mk",
1.2 rillig 965: "VAR= default",
1.1 rillig 966: ".if ${OPSYS} == NetBSD",
1.2 rillig 967: "VAR= opsys",
1.1 rillig 968: ".endif")
969:
970: NewRedundantScope().Check(mklines)
971:
972: t.CheckOutputEmpty()
973: }
974:
975: func (s *Suite) Test_RedundantScope__overwrite_inside_conditional(c *check.C) {
976: t := s.Init(c)
1.2 rillig 977:
1.1 rillig 978: mklines := t.NewMkLines("module.mk",
1.2 rillig 979: "VAR= generic",
1.1 rillig 980: ".if ${OPSYS} == NetBSD",
1.2 rillig 981: "VAR= ignored",
982: "VAR= overwritten",
1.1 rillig 983: ".endif")
984:
985: NewRedundantScope().Check(mklines)
986:
987: // TODO: expected a warning "WARN: module.mk:4: line 3 is ignored"
988: // Since line 3 and line 4 are in the same basic block, line 3 is definitely ignored.
989: t.CheckOutputEmpty()
990: }
991:
992: func (s *Suite) Test_RedundantScope__conditionally_include(c *check.C) {
993: t := s.Init(c)
1.2 rillig 994:
995: include, get := t.SetUpHierarchy()
996: include("module.mk",
997: "VAR= generic",
1.1 rillig 998: ".if ${OPSYS} == NetBSD",
1.2 rillig 999: include("included.mk",
1000: "VAR= ignored",
1001: "VAR= overwritten"),
1.1 rillig 1002: ".endif")
1003:
1.2 rillig 1004: NewRedundantScope().Check(get("module.mk"))
1.1 rillig 1005:
1006: // TODO: expected a warning "WARN: module.mk:4: line 3 is ignored"
1007: // Since line 3 and line 4 are in the same basic block, line 3 is definitely ignored.
1008: t.CheckOutputEmpty()
1009: }
1010:
1011: func (s *Suite) Test_RedundantScope__conditional_default(c *check.C) {
1012: t := s.Init(c)
1.2 rillig 1013:
1.1 rillig 1014: mklines := t.NewMkLines("module.mk",
1.2 rillig 1015: "VAR= default",
1.1 rillig 1016: ".if ${OPSYS} == NetBSD",
1.2 rillig 1017: "VAR?= opsys",
1.1 rillig 1018: ".endif")
1019:
1020: NewRedundantScope().Check(mklines)
1021:
1.2 rillig 1022: // TODO: WARN: module.mk:3: The value \"opsys\" will never be assigned
1023: // to VAR because it is defined unconditionally in line 1.
1.1 rillig 1024: t.CheckOutputEmpty()
1025: }
1026:
1027: // These warnings are precise and accurate since the value of VAR is not used between line 2 and 4.
1028: func (s *Suite) Test_RedundantScope__overwrite_same_variable_different_value(c *check.C) {
1029: t := s.Init(c)
1.2 rillig 1030:
1.1 rillig 1031: mklines := t.NewMkLines("module.mk",
1.2 rillig 1032: "OTHER= value before",
1033: "VAR= value ${OTHER}",
1034: "OTHER= value after",
1035: "VAR= value ${OTHER}")
1.1 rillig 1036:
1037: NewRedundantScope().Check(mklines)
1038:
1039: // Strictly speaking, line 1 is redundant because OTHER is not evaluated
1040: // at load time and then immediately overwritten in line 3. If the operator
1041: // in line 2 were a := instead of a =, the situation would be clear.
1042: // Pkglint doesn't warn about the redundancy in line 1 because it prefers
1043: // to omit warnings instead of giving wrong advice.
1044: t.CheckOutputLines(
1045: "NOTE: module.mk:4: Definition of VAR is redundant because of line 2.")
1046: }
1047:
1048: func (s *Suite) Test_RedundantScope__overwrite_different_value_used_between(c *check.C) {
1049: t := s.Init(c)
1.2 rillig 1050:
1.1 rillig 1051: mklines := t.NewMkLines("module.mk",
1.2 rillig 1052: "OTHER= value before",
1053: "VAR= value ${OTHER}",
1.1 rillig 1054:
1055: // VAR is used here at load time, therefore it must be defined at this point.
1056: // At this point, VAR uses the \"before\" value of OTHER.
1.2 rillig 1057: "RESULT1:= ${VAR}",
1.1 rillig 1058:
1.2 rillig 1059: "OTHER= value after",
1.1 rillig 1060:
1061: // VAR is used here again at load time, this time using the \"after\" value of OTHER.
1.2 rillig 1062: "RESULT2:= ${VAR}",
1.1 rillig 1063:
1064: // Still this definition is redundant.
1.2 rillig 1065: "VAR= value ${OTHER}")
1.1 rillig 1066:
1067: NewRedundantScope().Check(mklines)
1068:
1069: // There is nothing redundant here. Each write is followed by a
1070: // corresponding read, except for the last one. That is ok though
1071: // because in pkgsrc the last action of a package is to include
1072: // bsd.pkg.mk, which reads almost all variables.
1073: t.CheckOutputEmpty()
1074: }
1075:
1076: func (s *Suite) Test_RedundantScope__procedure_call_to_noop(c *check.C) {
1077: t := s.Init(c)
1078:
1079: include, get := t.SetUpHierarchy()
1080: include("mk/pthread.buildlink3.mk",
1081: "CHECK_BUILTIN.pthread:= yes",
1082: include("pthread.builtin.mk",
1083: "# Nothing happens here."),
1084: "CHECK_BUILTIN.pthread:= no")
1085:
1086: NewRedundantScope().Check(get("mk/pthread.buildlink3.mk"))
1087:
1088: t.CheckOutputLines(
1089: "WARN: mk/pthread.buildlink3.mk:1: Variable CHECK_BUILTIN.pthread is overwritten in line 3.")
1090: }
1091:
1092: func (s *Suite) Test_RedundantScope__procedure_call_implemented(c *check.C) {
1093: t := s.Init(c)
1094:
1095: include, get := t.SetUpHierarchy()
1096: include("mk/pthread.buildlink3.mk",
1097: "CHECK_BUILTIN.pthread:= yes",
1098: include("pthread.builtin.mk",
1099: "CHECK_BUILTIN.pthread?= no",
1100: ".if !empty(CHECK_BUILTIN.pthread:M[Nn][Oo])",
1101: ".endif"),
1102: "CHECK_BUILTIN.pthread:= no")
1103:
1104: NewRedundantScope().Check(get("mk/pthread.buildlink3.mk"))
1105:
1106: // This test is a bit unrealistic. It wrongly assumes that all files from
1107: // an .include directive are actually included by pkglint.
1108: //
1109: // See Package.readMakefile/handleIncludeLine/skip.
1110: t.CheckOutputEmpty()
1111: }
1112:
1113: func (s *Suite) Test_RedundantScope__procedure_call_implemented_package(c *check.C) {
1114: t := s.Init(c)
1115:
1116: t.SetUpPkgsrc()
1117: t.SetUpPackage("devel/gettext-lib")
1118: t.SetUpPackage("x11/Xaos",
1119: ".include \"../../devel/gettext-lib/buildlink3.mk\"")
1120: t.CreateFileLines("devel/gettext-lib/builtin.mk",
1.5 rillig 1121: MkCvsID,
1.1 rillig 1122: "",
1123: ".include \"../../mk/bsd.fast.prefs.mk\"",
1124: "",
1.2 rillig 1125: "CHECK_BUILTIN.gettext?= no",
1.1 rillig 1126: ".if !empty(CHECK_BUILTIN.gettext:M[nN][oO])",
1127: ".endif")
1128: t.CreateFileLines("devel/gettext-lib/buildlink3.mk",
1.5 rillig 1129: MkCvsID,
1.2 rillig 1130: "CHECK_BUILTIN.gettext:= yes",
1.1 rillig 1131: ".include \"builtin.mk\"",
1.2 rillig 1132: "CHECK_BUILTIN.gettext:= no")
1.3 rillig 1133: t.FinishSetUp()
1.1 rillig 1134:
1135: // Checking x11/Xaos instead of devel/gettext-lib avoids warnings
1136: // about the minimal buildlink3.mk file.
1137: G.Check(t.File("x11/Xaos"))
1138:
1139: // There is nothing redundant here.
1140: // Up to March 2019, pkglint didn't pass the correct pathnames to Package.included,
1141: // which triggered a wrong note here.
1142: t.CheckOutputEmpty()
1143: }
1144:
1145: func (s *Suite) Test_RedundantScope__procedure_call_infrastructure(c *check.C) {
1146: t := s.Init(c)
1147:
1148: t.SetUpPackage("x11/alacarte",
1149: ".include \"../../mk/pthread.buildlink3.mk\"")
1150: t.CreateFileLines("mk/pthread.buildlink3.mk",
1.5 rillig 1151: MkCvsID,
1.2 rillig 1152: "CHECK_BUILTIN.gettext:= yes",
1.1 rillig 1153: ".include \"pthread.builtin.mk\"",
1.2 rillig 1154: "CHECK_BUILTIN.gettext:= no")
1.1 rillig 1155: t.CreateFileLines("mk/pthread.builtin.mk",
1.5 rillig 1156: MkCvsID,
1.2 rillig 1157: "CHECK_BUILTIN.gettext?= no",
1.1 rillig 1158: ".if !empty(CHECK_BUILTIN.gettext:M[nN][oO])",
1159: ".endif")
1.3 rillig 1160: t.FinishSetUp()
1.1 rillig 1161:
1162: G.Check(t.File("x11/alacarte"))
1163:
1164: // There is nothing redundant here.
1165: //
1166: // 1. pthread.buildlink3.mk sets the variable
1167: // 2. pthread.builtin.mk assigns it a default value
1168: // (which is common practice)
1169: // 3. pthread.builtin.mk then reads it
1170: // (which marks the next write as non-redundant)
1171: // 4. pthread.buildlink3.mk sets the variable again
1172: // (this is considered neither overwriting nor redundant)
1173: //
1174: // Up to March 2019, pkglint complained:
1175: //
1176: // WARN: ~/mk/pthread.buildlink3.mk:2:
1177: // Variable CHECK_BUILTIN.gettext is overwritten in line 4.
1178: //
1179: // The cause for the warning is that when including files from the
1180: // infrastructure, pkglint only includes the outermost level of files.
1181: // If an infrastructure file includes another infrastructure file,
1182: // pkglint skips that, for performance reasons.
1183: //
1184: // This optimization effectively made the .include for pthread.builtin.mk
1185: // a no-op, therefore it was correct to issue a warning here.
1186: //
1187: // Since this warning is wrong, in March 2019 another special rule has
1188: // been added to Package.readMakefile.handleIncludeLine.skip saying that
1189: // including a buildlink3.mk file also includes the corresponding
1190: // builtin.mk file.
1191: t.CheckOutputEmpty()
1192: }
1193:
1194: func (s *Suite) Test_RedundantScope__shell_and_eval(c *check.C) {
1195: t := s.Init(c)
1.2 rillig 1196:
1.1 rillig 1197: mklines := t.NewMkLines("module.mk",
1.2 rillig 1198: "VAR:= value ${OTHER}",
1199: "VAR!= value ${OTHER}")
1.1 rillig 1200:
1201: NewRedundantScope().Check(mklines)
1202:
1203: // As of November 2018, pkglint doesn't check redundancies that involve the := or != operators.
1204: //
1205: // What happens here is:
1206: //
1207: // Line 1 evaluates OTHER at load time.
1208: // Line 1 assigns its value to VAR.
1209: // Line 2 evaluates OTHER at load time.
1210: // Line 2 passes its value through the shell and assigns the result to VAR.
1211: //
1212: // Since VAR is defined in line 1, not used afterwards and overwritten in line 2, it is redundant.
1213: // Well, not quite, because evaluating ${OTHER} might have side-effects from :sh or ::= modifiers,
1214: // but these are so rare that they are frowned upon and are not considered by pkglint.
1215: //
1216: // Expected result:
1217: // WARN: module.mk:2: Previous definition of VAR in line 1 is unused.
1218:
1219: t.CheckOutputEmpty()
1220: }
1221:
1222: func (s *Suite) Test_RedundantScope__shell_and_eval_literal(c *check.C) {
1223: t := s.Init(c)
1.2 rillig 1224:
1.1 rillig 1225: mklines := t.NewMkLines("module.mk",
1.2 rillig 1226: "VAR:= value",
1227: "VAR!= value")
1.1 rillig 1228:
1229: NewRedundantScope().Check(mklines)
1230:
1231: // Even when := is used with a literal value (which is usually
1232: // only done for procedure calls), the shell evaluation can have
1233: // so many different side effects that pkglint cannot reliably
1234: // help in this situation.
1235: //
1236: // TODO: Why not? The evaluation in line 1 is trivial to analyze.
1237: t.CheckOutputEmpty()
1238: }
1239:
1240: func (s *Suite) Test_RedundantScope__included_OPSYS_variable(c *check.C) {
1241: t := s.Init(c)
1242:
1243: t.SetUpPackage("category/package",
1244: ".include \"../../category/dependency/buildlink3.mk\"",
1.2 rillig 1245: "CONFIGURE_ARGS+= one",
1246: "CONFIGURE_ARGS= two",
1247: "CONFIGURE_ARGS+= three")
1.1 rillig 1248: t.SetUpPackage("category/dependency")
1.10 ! rillig 1249: t.CreateFileBuildlink3("category/dependency/buildlink3.mk")
1.1 rillig 1250: t.CreateFileLines("category/dependency/builtin.mk",
1.5 rillig 1251: MkCvsID,
1.2 rillig 1252: "CONFIGURE_ARGS.Darwin+= darwin")
1.3 rillig 1253: t.FinishSetUp()
1.1 rillig 1254:
1255: G.Check(t.File("category/package"))
1256:
1257: t.CheckOutputLines(
1258: "WARN: ~/category/package/Makefile:21: Variable CONFIGURE_ARGS is overwritten in line 22.")
1259: }
1260:
1261: func (s *Suite) Test_RedundantScope__if_then_else(c *check.C) {
1262: t := s.Init(c)
1263:
1.2 rillig 1264: mklines := t.NewMkLines("if-then-else.mk",
1.1 rillig 1265: ".if exists(${FILE})",
1.2 rillig 1266: "OS= NetBSD",
1.1 rillig 1267: ".else",
1.2 rillig 1268: "OS= OTHER",
1.1 rillig 1269: ".endif")
1270:
1271: NewRedundantScope().Check(mklines)
1272:
1273: // These two definitions are of course not redundant since they happen in
1274: // different branches of the same .if statement.
1275: t.CheckOutputEmpty()
1276: }
1277:
1278: func (s *Suite) Test_RedundantScope__if_then_else_without_variable(c *check.C) {
1279: t := s.Init(c)
1280:
1.2 rillig 1281: mklines := t.NewMkLines("if-then-else.mk",
1.1 rillig 1282: ".if exists(/nonexistent)",
1.2 rillig 1283: "IT= exists",
1.1 rillig 1284: ".else",
1.2 rillig 1285: "IT= doesn't exist",
1.1 rillig 1286: ".endif")
1287:
1288: NewRedundantScope().Check(mklines)
1289:
1290: // These two definitions are of course not redundant since they happen in
1291: // different branches of the same .if statement.
1292: // Even though the .if condition does not refer to any variables,
1293: // this still means that the variable assignments are conditional.
1294: t.CheckOutputEmpty()
1295: }
1296:
1297: func (s *Suite) Test_RedundantScope__append_then_default(c *check.C) {
1298: t := s.Init(c)
1299:
1.2 rillig 1300: mklines := t.NewMkLines("append-then-default.mk",
1301: "VAR+= value",
1302: "VAR?= value")
1.1 rillig 1303:
1304: NewRedundantScope().Check(mklines)
1305:
1306: t.CheckOutputLines(
1.2 rillig 1307: "NOTE: append-then-default.mk:2: Default assignment of VAR has no effect because of line 1.")
1.1 rillig 1308: }
1309:
1310: func (s *Suite) Test_RedundantScope__assign_then_default_in_same_file(c *check.C) {
1311: t := s.Init(c)
1312:
1.2 rillig 1313: mklines := t.NewMkLines("assign-then-default.mk",
1314: "VAR= value",
1315: "VAR?= value")
1.1 rillig 1316:
1317: NewRedundantScope().Check(mklines)
1318:
1319: t.CheckOutputLines(
1.2 rillig 1320: "NOTE: assign-then-default.mk:2: " +
1321: "Default assignment of VAR has no effect because of line 1.")
1.1 rillig 1322: }
1323:
1324: func (s *Suite) Test_RedundantScope__eval_then_eval(c *check.C) {
1325: t := s.Init(c)
1326:
1.2 rillig 1327: mklines := t.NewMkLines("filename.mk",
1328: "VAR:= value",
1329: "VAR:= value",
1330: "VAR:= other")
1.1 rillig 1331:
1332: NewRedundantScope().Check(mklines)
1333:
1334: t.CheckOutputLines(
1.10 ! rillig 1335: "NOTE: filename.mk:2: Definition of VAR is redundant because of line 1.",
1.2 rillig 1336: "WARN: filename.mk:2: Variable VAR is overwritten in line 3.")
1.1 rillig 1337: }
1338:
1339: func (s *Suite) Test_RedundantScope__shell_then_assign(c *check.C) {
1340: t := s.Init(c)
1341:
1.2 rillig 1342: mklines := t.NewMkLines("filename.mk",
1343: "VAR!= echo echo",
1344: "VAR= echo echo")
1.1 rillig 1345:
1346: NewRedundantScope().Check(mklines)
1347:
1348: // Although the two variable assignments look very similar, they do
1349: // something entirely different. The first executes the echo command,
1350: // and the second just assigns a string. Therefore the actual variable
1351: // values are different, and the second assignment is not redundant.
1352: // It assigns a different value. Nevertheless, the shell command is
1353: // redundant and can be removed since its result is never used.
1354: t.CheckOutputLines(
1.2 rillig 1355: "WARN: filename.mk:1: Variable VAR is overwritten in line 2.")
1.1 rillig 1356: }
1357:
1358: func (s *Suite) Test_RedundantScope__shell_then_read_then_assign(c *check.C) {
1359: t := s.Init(c)
1360:
1361: mklines := t.SetUpFileMkLines("filename.mk",
1.2 rillig 1362: "VAR!= echo echo",
1363: "OUTPUT:= ${VAR}",
1364: "VAR= echo echo")
1.1 rillig 1365:
1366: NewRedundantScope().Check(mklines)
1367:
1368: // No warning since the value is used in-between.
1369: t.CheckOutputEmpty()
1370: }
1371:
1372: func (s *Suite) Test_RedundantScope__assign_then_default_in_included_file(c *check.C) {
1373: t := s.Init(c)
1374:
1.2 rillig 1375: include, get := t.SetUpHierarchy()
1376: include("assign-then-default.mk",
1377: "VAR= value",
1378: include("included.mk",
1379: "VAR?= value"))
1.1 rillig 1380:
1.2 rillig 1381: NewRedundantScope().Check(get("assign-then-default.mk"))
1.1 rillig 1382:
1.2 rillig 1383: // If assign-then-default.mk:1 were deleted, VAR would still have the same value.
1.1 rillig 1384: t.CheckOutputLines(
1.2 rillig 1385: "NOTE: assign-then-default.mk:1: Definition of VAR is redundant because of included.mk:1.")
1.1 rillig 1386: }
1387:
1388: func (s *Suite) Test_RedundantScope__conditionally_included_file(c *check.C) {
1389: t := s.Init(c)
1390:
1.2 rillig 1391: include, get := t.SetUpHierarchy()
1392: include("including.mk",
1393: "VAR= value",
1.1 rillig 1394: ".if ${COND}",
1.2 rillig 1395: include("included.mk",
1396: "VAR?= value"),
1.1 rillig 1397: ".endif")
1398:
1.2 rillig 1399: NewRedundantScope().Check(get("including.mk"))
1.1 rillig 1400:
1401: // The assignment in including.mk:2 is only redundant if included.mk is actually included.
1402: // Therefore both included.mk:2 nor including.mk:2 are relevant.
1403: t.CheckOutputEmpty()
1404: }
1405:
1406: func (s *Suite) Test_RedundantScope__procedure_parameters(c *check.C) {
1407: t := s.Init(c)
1408:
1.2 rillig 1409: // TODO: make Tester.SetUpHierarchy accept a file multiple times.
1.1 rillig 1410: t.CreateFileLines("mk/pkg-build-options.mk",
1.2 rillig 1411: "USED:= ${pkgbase}")
1.1 rillig 1412: t.CreateFileLines("including.mk",
1.2 rillig 1413: "pkgbase= package1",
1.1 rillig 1414: ".include \"mk/pkg-build-options.mk\"",
1415: "",
1.2 rillig 1416: "pkgbase= package2",
1.1 rillig 1417: ".include \"mk/pkg-build-options.mk\"",
1418: "",
1.2 rillig 1419: "pkgbase= package3",
1.1 rillig 1420: ".include \"mk/pkg-build-options.mk\"")
1421: mklines := t.LoadMkInclude("including.mk")
1422:
1423: NewRedundantScope().Check(mklines)
1424:
1425: // This variable is not overwritten since it is used in-between
1426: // by the included file.
1427: t.CheckOutputEmpty()
1428: }
1429:
1.10 ! rillig 1430: func (s *Suite) Test_RedundantScope__infra(c *check.C) {
! 1431: t := s.Init(c)
! 1432:
! 1433: t.CreateFileLines("mk/bsd.options.mk",
! 1434: "PKG_OPTIONS:=\t# empty",
! 1435: "PKG_OPTIONS=\t# empty")
! 1436: t.CreateFileLines("options.mk",
! 1437: "OUTSIDE:=\t# empty",
! 1438: "OUTSIDE=\t# empty",
! 1439: ".include \"mk/bsd.options.mk\"")
! 1440:
! 1441: test := func(diagnostics ...string) {
! 1442: mklines := t.LoadMkInclude("options.mk")
! 1443: scope := NewRedundantScope()
! 1444: scope.IsRelevant = func(mkline *MkLine) bool {
! 1445: // See checkfilePackageMakefile.
! 1446: if !G.Infrastructure && !G.Opts.CheckGlobal {
! 1447: return !G.Pkgsrc.IsInfra(mkline.Filename)
! 1448: }
! 1449: return true
! 1450: }
! 1451:
! 1452: scope.Check(mklines)
! 1453:
! 1454: // No note about the redundant variable assignment in bsd.options.mk
! 1455: // because it is part of the infrastructure, which is filtered out.
! 1456: t.CheckOutput(diagnostics)
! 1457: }
! 1458:
! 1459: test(
! 1460: "NOTE: ~/options.mk:2: " +
! 1461: "Definition of OUTSIDE is redundant because of line 1.")
! 1462:
! 1463: t.SetUpCommandLine("-Cglobal")
! 1464:
! 1465: test(
! 1466: "NOTE: ~/options.mk:2: "+
! 1467: "Definition of OUTSIDE is redundant because of line 1.",
! 1468: "NOTE: ~/mk/bsd.options.mk:2: "+
! 1469: "Definition of PKG_OPTIONS is redundant because of line 1.")
! 1470: }
! 1471:
1.7 rillig 1472: // Branch coverage for info.vari.IsConstant(). The other tests typically
1.1 rillig 1473: // make a variable non-constant by adding conditional assignments between
1474: // .if/.endif. But there are other ways. The output of shell commands is
1475: // unpredictable for pkglint (as of March 2019), therefore it treats these
1476: // variables as non-constant.
1477: func (s *Suite) Test_RedundantScope_handleVarassign__shell_followed_by_default(c *check.C) {
1478: t := s.Init(c)
1479:
1480: include, get := t.SetUpHierarchy()
1481: include("including.mk",
1.2 rillig 1482: "VAR!= echo 'hello, world'",
1.1 rillig 1483: include("included.mk",
1.2 rillig 1484: "VAR?= hello world"))
1.1 rillig 1485:
1486: NewRedundantScope().Check(get("including.mk"))
1487:
1488: // If pkglint should ever learn to interpret simple shell commands, there
1489: // should be a warning for including.mk:2 that the shell command generates
1490: // the default value.
1491: t.CheckOutputEmpty()
1492: }
1493:
1.7 rillig 1494: func (s *Suite) Test_RedundantScope_handleVarassign__overwrite_definition_from_included_file(c *check.C) {
1.1 rillig 1495: t := s.Init(c)
1496:
1.2 rillig 1497: include, get := t.SetUpHierarchy()
1498: include("including.mk",
1499: "SUBDIR= ${WRKSRC}",
1500: include("included.mk",
1501: "WRKSRC= ${WRKDIR}/${PKGBASE}"),
1502: "WRKSRC= ${WRKDIR}/overwritten")
1.1 rillig 1503:
1.2 rillig 1504: NewRedundantScope().Check(get("including.mk"))
1.1 rillig 1505:
1506: // Before pkglint 5.7.2 (2019-03-10), the above setup generated a warning:
1507: //
1508: // WARN: ~/included.mk:2: Variable WRKSRC is overwritten in including.mk:4.
1509: //
1510: // This warning is obviously wrong since the included file must never
1511: // receive a warning. Of course this default definition may be overridden
1512: // by the including file.
1513: //
1514: // The warning was generated because in including.mk:2 the variable WRKSRC
1515: // was used for the first time. Back then, each variable had only a single
1516: // include path. That include path marks where the variable is used and
1517: // defined.
1518: //
1519: // The variable definition at included.mk didn't modify this include path.
1520: // Therefore pkglint wrongly assumed that this variable was only ever
1521: // accessed in including.mk and issued a warning.
1522: //
1523: // To fix this, the RedundantScope now remembers every access to the
1524: // variable, and the redundancy warnings are only issued in cases where
1525: // either all variable accesses are in files including the current file,
1526: // or all variable accesses are in files included by the current file.
1527: t.CheckOutputEmpty()
1528: }
1529:
1530: func (s *Suite) Test_RedundantScope_handleVarassign__conditional(c *check.C) {
1531: t := s.Init(c)
1532:
1533: mklines := t.NewMkLines("filename.mk",
1.2 rillig 1534: "VAR= value",
1.1 rillig 1535: ".if 1",
1.2 rillig 1536: "VAR= conditional",
1.1 rillig 1537: ".endif")
1538:
1.2 rillig 1539: scope := NewRedundantScope()
1540: scope.Check(mklines)
1541: writeLocations := scope.get("VAR").vari.WriteLocations()
1.1 rillig 1542:
1.6 rillig 1543: t.CheckDeepEquals(
1.2 rillig 1544: writeLocations,
1.5 rillig 1545: []*MkLine{mklines.mklines[0], mklines.mklines[2]})
1.2 rillig 1546: }
1547:
1548: // Ensures that commented variables do not influence the redundancy check.
1.7 rillig 1549: func (s *Suite) Test_RedundantScope_handleVarassign__commented_variable_assignment(c *check.C) {
1.2 rillig 1550: t := s.Init(c)
1551:
1552: include, get := t.SetUpHierarchy()
1553: include("main.mk",
1554: include("redundant.mk",
1555: "VAR= value"),
1556: include("doc.mk",
1557: "#OTHER= ${VAR}"),
1558: "VAR= value",
1559: "OTHER= value")
1560:
1561: NewRedundantScope().Check(get("main.mk"))
1562:
1563: t.CheckOutputLines(
1564: "NOTE: main.mk:3: Definition of VAR is redundant because of redundant.mk:1.")
1.1 rillig 1565: }
1566:
1.10 ! rillig 1567: func (s *Suite) Test_RedundantScope_handleVarassign__assign_then_eval(c *check.C) {
! 1568: t := s.Init(c)
! 1569:
! 1570: mklines := t.NewMkLines("mk/bsd.options.mk",
! 1571: "PKG_OPTIONS=\t# empty",
! 1572: "PKG_OPTIONS:=\t# empty")
! 1573:
! 1574: scope := NewRedundantScope()
! 1575: scope.Check(mklines)
! 1576:
! 1577: t.CheckOutputLines(
! 1578: "NOTE: mk/bsd.options.mk:2: " +
! 1579: "Definition of PKG_OPTIONS is redundant because of line 1.")
! 1580: }
! 1581:
1.1 rillig 1582: func (s *Suite) Test_includePath_includes(c *check.C) {
1583: t := s.Init(c)
1584:
1.9 rillig 1585: path := func(locations ...CurrPath) includePath {
1.1 rillig 1586: return includePath{locations}
1587: }
1588:
1589: var (
1590: m = path("Makefile")
1591: mc = path("Makefile", "Makefile.common")
1592: mco = path("Makefile", "Makefile.common", "other.mk")
1593: mo = path("Makefile", "other.mk")
1594: )
1595:
1.6 rillig 1596: t.CheckEquals(m.includes(m), false)
1.1 rillig 1597:
1.6 rillig 1598: t.CheckEquals(m.includes(mc), true)
1599: t.CheckEquals(m.includes(mco), true)
1600: t.CheckEquals(mc.includes(mco), true)
1601:
1602: t.CheckEquals(mc.includes(m), false)
1603: t.CheckEquals(mc.includes(mo), false)
1604: t.CheckEquals(mo.includes(mc), false)
1.1 rillig 1605: }
1606:
1607: func (s *Suite) Test_includePath_equals(c *check.C) {
1608: t := s.Init(c)
1609:
1.9 rillig 1610: path := func(locations ...CurrPath) includePath {
1.1 rillig 1611: return includePath{locations}
1612: }
1613:
1614: var (
1615: m = path("Makefile")
1616: mc = path("Makefile", "Makefile.common")
1617: mco = path("Makefile", "Makefile.common", "other.mk")
1618: mo = path("Makefile", "other.mk")
1619: )
1620:
1.6 rillig 1621: t.CheckEquals(m.equals(m), true)
1.1 rillig 1622:
1.6 rillig 1623: t.CheckEquals(m.equals(mc), false)
1624: t.CheckEquals(m.equals(mco), false)
1625: t.CheckEquals(mc.equals(mco), false)
1626:
1627: t.CheckEquals(mc.equals(m), false)
1628: t.CheckEquals(mc.equals(mo), false)
1629: t.CheckEquals(mo.equals(mc), false)
1.1 rillig 1630: }
CVSweb <webmaster@jp.NetBSD.org>