Sat Jun 20 07:00:44 2020 UTC ()
pkgtools/pkglint: update to 20.1.18

Changes since 20.1.17:

Fixed the algorithm for checking whether two patterns overlap, such as
MACHINE_PLATFORM:MNetBSD-[0-9].*-*.

Fixed wrong warning about foreign variable in SUBST block, as seen in
geography/qgis.


(rillig)
diff -r1.657 -r1.658 pkgsrc/pkgtools/pkglint/Makefile
diff -r1.73 -r1.74 pkgsrc/pkgtools/pkglint/files/check_test.go
diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/scope.go
diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/scope_test.go
diff -r1.38 -r1.39 pkgsrc/pkgtools/pkglint/files/substcontext.go
diff -r1.36 -r1.37 pkgsrc/pkgtools/pkglint/files/substcontext_test.go
diff -r1.99 -r1.100 pkgsrc/pkgtools/pkglint/files/vardefs.go
diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/makepat/pat.go
diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/makepat/pat_test.go

cvs diff -r1.657 -r1.658 pkgsrc/pkgtools/pkglint/Makefile (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/Makefile 2020/06/17 09:54:12 1.657
+++ pkgsrc/pkgtools/pkglint/Makefile 2020/06/20 07:00:44 1.658
@@ -1,17 +1,16 @@ @@ -1,17 +1,16 @@
1# $NetBSD: Makefile,v 1.657 2020/06/17 09:54:12 bsiegert Exp $ 1# $NetBSD: Makefile,v 1.658 2020/06/20 07:00:44 rillig Exp $
2 2
3PKGNAME= pkglint-20.1.17 3PKGNAME= pkglint-20.1.18
4PKGREVISION= 1 
5CATEGORIES= pkgtools 4CATEGORIES= pkgtools
6DISTNAME= tools 5DISTNAME= tools
7MASTER_SITES= ${MASTER_SITE_GITHUB:=golang/} 6MASTER_SITES= ${MASTER_SITE_GITHUB:=golang/}
8GITHUB_PROJECT= tools 7GITHUB_PROJECT= tools
9GITHUB_TAG= 92d8274bd7b8a4c65f24bafe401a029e58392704 8GITHUB_TAG= 92d8274bd7b8a4c65f24bafe401a029e58392704
10 9
11MAINTAINER= rillig@NetBSD.org 10MAINTAINER= rillig@NetBSD.org
12HOMEPAGE= https://github.com/rillig/pkglint 11HOMEPAGE= https://github.com/rillig/pkglint
13COMMENT= Verifier for NetBSD packages 12COMMENT= Verifier for NetBSD packages
14LICENSE= 2-clause-bsd 13LICENSE= 2-clause-bsd
15CONFLICTS+= pkglint4-[0-9]* 14CONFLICTS+= pkglint4-[0-9]*
16 15
17USE_TOOLS+= pax 16USE_TOOLS+= pax

cvs diff -r1.73 -r1.74 pkgsrc/pkgtools/pkglint/files/Attic/check_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/check_test.go 2020/06/14 11:35:54 1.73
+++ pkgsrc/pkgtools/pkglint/files/Attic/check_test.go 2020/06/20 07:00:44 1.74
@@ -118,27 +118,26 @@ func Test__qa(t *testing.T) { @@ -118,27 +118,26 @@ func Test__qa(t *testing.T) {
118 ck.Configure("mkparser.go", "*", "*", -intqa.EMissingTest) // TODO 118 ck.Configure("mkparser.go", "*", "*", -intqa.EMissingTest) // TODO
119 ck.Configure("mkshparser.go", "*", "*", -intqa.EMissingTest) // TODO 119 ck.Configure("mkshparser.go", "*", "*", -intqa.EMissingTest) // TODO
120 ck.Configure("mkshtypes.go", "*", "*", -intqa.EMissingTest) // TODO 120 ck.Configure("mkshtypes.go", "*", "*", -intqa.EMissingTest) // TODO
121 ck.Configure("mkshwalker.go", "*", "*", -intqa.EMissingTest) // TODO 121 ck.Configure("mkshwalker.go", "*", "*", -intqa.EMissingTest) // TODO
122 ck.Configure("mktokenslexer.go", "*", "*", -intqa.EMissingTest) // TODO 122 ck.Configure("mktokenslexer.go", "*", "*", -intqa.EMissingTest) // TODO
123 ck.Configure("mktypes.go", "*", "*", -intqa.EMissingTest) // TODO 123 ck.Configure("mktypes.go", "*", "*", -intqa.EMissingTest) // TODO
124 ck.Configure("options.go", "*", "*", -intqa.EMissingTest) // TODO 124 ck.Configure("options.go", "*", "*", -intqa.EMissingTest) // TODO
125 ck.Configure("package.go", "*", "*", -intqa.EMissingTest) // TODO 125 ck.Configure("package.go", "*", "*", -intqa.EMissingTest) // TODO
126 ck.Configure("paragraph.go", "*", "*", -intqa.EMissingTest) // TODO 126 ck.Configure("paragraph.go", "*", "*", -intqa.EMissingTest) // TODO
127 ck.Configure("patches.go", "*", "*", -intqa.EMissingTest) // TODO 127 ck.Configure("patches.go", "*", "*", -intqa.EMissingTest) // TODO
128 ck.Configure("pkglint.go", "*", "*", -intqa.EMissingTest) // TODO 128 ck.Configure("pkglint.go", "*", "*", -intqa.EMissingTest) // TODO
129 ck.Configure("pkgsrc.go", "*", "*", -intqa.EMissingTest) // TODO 129 ck.Configure("pkgsrc.go", "*", "*", -intqa.EMissingTest) // TODO
130 ck.Configure("redundantscope.go", "*", "*", -intqa.EMissingTest) // TODO 130 ck.Configure("redundantscope.go", "*", "*", -intqa.EMissingTest) // TODO
131 ck.Configure("scope.go", "*", "*", -intqa.EMissingTest) // TODO 
132 ck.Configure("shell.go", "*", "*", -intqa.EMissingTest) // TODO 131 ck.Configure("shell.go", "*", "*", -intqa.EMissingTest) // TODO
133 ck.Configure("shtokenizer.go", "*", "*", -intqa.EMissingTest) // TODO 132 ck.Configure("shtokenizer.go", "*", "*", -intqa.EMissingTest) // TODO
134 ck.Configure("shtypes.go", "*", "*", -intqa.EMissingTest) // TODO 133 ck.Configure("shtypes.go", "*", "*", -intqa.EMissingTest) // TODO
135 ck.Configure("substcontext.go", "*", "*", -intqa.EMissingTest) // TODO 134 ck.Configure("substcontext.go", "*", "*", -intqa.EMissingTest) // TODO
136 ck.Configure("tools.go", "*", "*", -intqa.EMissingTest) // TODO 135 ck.Configure("tools.go", "*", "*", -intqa.EMissingTest) // TODO
137 ck.Configure("util.go", "*", "*", -intqa.EMissingTest) // TODO 136 ck.Configure("util.go", "*", "*", -intqa.EMissingTest) // TODO
138 ck.Configure("var.go", "*", "*", -intqa.EMissingTest) // TODO 137 ck.Configure("var.go", "*", "*", -intqa.EMissingTest) // TODO
139 138
140 ck.Configure("varalignblock.go", "*", "*", -intqa.EMissingTest) // TODO 139 ck.Configure("varalignblock.go", "*", "*", -intqa.EMissingTest) // TODO
141 ck.Configure("varalignblock.go", "varalignLine", "*", +intqa.EMissingTest) // TODO: remove as redundant 140 ck.Configure("varalignblock.go", "varalignLine", "*", +intqa.EMissingTest) // TODO: remove as redundant
142 141
143 ck.Configure("vardefs.go", "*", "*", -intqa.EMissingTest) // TODO 142 ck.Configure("vardefs.go", "*", "*", -intqa.EMissingTest) // TODO
144 ck.Configure("vargroups.go", "*", "*", -intqa.EMissingTest) // TODO 143 ck.Configure("vargroups.go", "*", "*", -intqa.EMissingTest) // TODO

cvs diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/Attic/scope.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/scope.go 2020/06/14 11:35:54 1.1
+++ pkgsrc/pkgtools/pkglint/files/Attic/scope.go 2020/06/20 07:00:44 1.2
@@ -104,33 +104,33 @@ func (s *Scope) def(name string, mkline  @@ -104,33 +104,33 @@ func (s *Scope) def(name string, mkline
104 case opAssignShell: 104 case opAssignShell:
105 v.value = "" 105 v.value = ""
106 v.indeterminate = true 106 v.indeterminate = true
107 default: 107 default:
108 v.value = mkline.Value() 108 v.value = mkline.Value()
109 } 109 }
110} 110}
111 111
112func (s *Scope) Fallback(varname string, value string) { 112func (s *Scope) Fallback(varname string, value string) {
113 s.create(varname).fallback = value 113 s.create(varname).fallback = value
114} 114}
115 115
116// Use marks the variable and its canonicalized form as used. 116// Use marks the variable and its canonicalized form as used.
117func (s *Scope) Use(varname string, line *MkLine, time VucTime) { 117func (s *Scope) Use(varname string, mkline *MkLine, time VucTime) {
118 use := func(name string) { 118 use := func(name string) {
119 v := s.create(name) 119 v := s.create(name)
120 if v.used == nil { 120 if v.used == nil {
121 v.used = line 121 v.used = mkline
122 if trace.Tracing { 122 if trace.Tracing {
123 trace.Step2("Using %q in %s", name, line.String()) 123 trace.Step2("Using %q in %s", name, mkline.String())
124 } 124 }
125 } 125 }
126 if time == VucLoadTime { 126 if time == VucLoadTime {
127 v.usedAtLoadTime = true 127 v.usedAtLoadTime = true
128 } 128 }
129 } 129 }
130 130
131 use(varname) 131 use(varname)
132 use(varnameCanon(varname)) 132 use(varnameCanon(varname))
133} 133}
134 134
135// Mentioned returns the first line in which the variable is either: 135// Mentioned returns the first line in which the variable is either:
136// - defined, 136// - defined,

cvs diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/Attic/scope_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/scope_test.go 2020/06/14 11:35:54 1.1
+++ pkgsrc/pkgtools/pkglint/files/Attic/scope_test.go 2020/06/20 07:00:44 1.2
@@ -24,26 +24,63 @@ func (s *Suite) Test_Scope__commented_va @@ -24,26 +24,63 @@ func (s *Suite) Test_Scope__commented_va
24 t.CheckEquals(scope.IsDefined("VAR"), false) 24 t.CheckEquals(scope.IsDefined("VAR"), false)
25 t.Check(scope.FirstDefinition("VAR"), check.IsNil) 25 t.Check(scope.FirstDefinition("VAR"), check.IsNil)
26 t.Check(scope.LastDefinition("VAR"), check.IsNil) 26 t.Check(scope.LastDefinition("VAR"), check.IsNil)
27 27
28 t.CheckEquals(scope.Mentioned("VAR"), mkline) 28 t.CheckEquals(scope.Mentioned("VAR"), mkline)
29 t.CheckEquals(scope.Commented("VAR"), mkline) 29 t.CheckEquals(scope.Commented("VAR"), mkline)
30 30
31 value, found, indeterminate := scope.LastValueFound("VAR") 31 value, found, indeterminate := scope.LastValueFound("VAR")
32 t.CheckEquals(value, "") 32 t.CheckEquals(value, "")
33 t.CheckEquals(found, false) 33 t.CheckEquals(found, false)
34 t.CheckEquals(indeterminate, false) 34 t.CheckEquals(indeterminate, false)
35} 35}
36 36
 37func (s *Suite) Test_NewScope(c *check.C) {
 38 t := s.Init(c)
 39
 40 scope := NewScope()
 41
 42 t.Check(scope.names, check.IsNil)
 43 t.Check(scope.vs, check.NotNil)
 44 t.Check(scope.vs, check.HasLen, 0)
 45}
 46
 47func (s *Suite) Test_Scope_varnames(c *check.C) {
 48 t := s.Init(c)
 49
 50 scope := NewScope()
 51 mkline := t.NewMkLine("filename.mk", 3, "DEFINED=\t${USED}")
 52
 53 t.Check(scope.varnames(), check.IsNil)
 54
 55 scope.Define("DEFINED", mkline)
 56 scope.Use("USED", mkline, VucRunTime)
 57
 58 t.CheckDeepEquals(scope.varnames(), []string{"DEFINED", "USED"})
 59 scope.varnames()[0] = "modified" // just to demonstrate the caching
 60 t.CheckDeepEquals(scope.varnames(), []string{"modified", "USED"})
 61}
 62
 63func (s *Suite) Test_Scope_create(c *check.C) {
 64 t := s.Init(c)
 65
 66 scope := NewScope()
 67
 68 v1 := scope.create("VAR")
 69 v2 := scope.create("VAR")
 70
 71 t.CheckEquals(v1, v2)
 72}
 73
37func (s *Suite) Test_Scope_Define(c *check.C) { 74func (s *Suite) Test_Scope_Define(c *check.C) {
38 t := s.Init(c) 75 t := s.Init(c)
39 76
40 scope := NewScope() 77 scope := NewScope()
41 78
42 test := func(line string, found, indeterminate bool, value string) { 79 test := func(line string, found, indeterminate bool, value string) {
43 mkline := t.NewMkLine("file.mk", 123, line) 80 mkline := t.NewMkLine("file.mk", 123, line)
44 scope.Define("BUILD_DIRS", mkline) 81 scope.Define("BUILD_DIRS", mkline)
45 82
46 actualValue, actualFound, actualIndeterminate := scope.LastValueFound("BUILD_DIRS") 83 actualValue, actualFound, actualIndeterminate := scope.LastValueFound("BUILD_DIRS")
47 84
48 t.CheckDeepEquals( 85 t.CheckDeepEquals(
49 []interface{}{actualFound, actualIndeterminate, actualValue}, 86 []interface{}{actualFound, actualIndeterminate, actualValue},
@@ -65,26 +102,65 @@ func (s *Suite) Test_Scope_Define(c *che @@ -65,26 +102,65 @@ func (s *Suite) Test_Scope_Define(c *che
65 // Later default assignments do not have an effect. 102 // Later default assignments do not have an effect.
66 test("BUILD_DIRS?=\tdefault", 103 test("BUILD_DIRS?=\tdefault",
67 true, false, "one two three four") 104 true, false, "one two three four")
68 105
69 test("BUILD_DIRS!=\techo dynamic", 106 test("BUILD_DIRS!=\techo dynamic",
70 true, true, "") 107 true, true, "")
71 108
72 // The shell assignment above sets the variable to an indeterminate 109 // The shell assignment above sets the variable to an indeterminate
73 // value, after which all further default assignments are ignored. 110 // value, after which all further default assignments are ignored.
74 test("BUILD_DIRS?=\tdefault after shell assignment", 111 test("BUILD_DIRS?=\tdefault after shell assignment",
75 true, true, "") 112 true, true, "")
76} 113}
77 114
 115func (s *Suite) Test_Scope_def(c *check.C) {
 116 t := s.Init(c)
 117
 118 mkline := t.NewMkLine("filename.mk", 3, "VAR=\tvalue")
 119 scope := NewScope()
 120
 121 scope.def("VAR.param", mkline)
 122
 123 t.Check(scope.FirstDefinition("VAR"), check.IsNil)
 124 t.CheckEquals(scope.FirstDefinition("VAR.param"), mkline)
 125 t.Check(scope.FirstDefinition("VAR.*"), check.IsNil)
 126}
 127
 128func (s *Suite) Test_Scope_Fallback(c *check.C) {
 129 t := s.Init(c)
 130
 131 mkline := t.NewMkLine("filename.mk", 3, "VAR=\tvalue")
 132 scope := NewScope()
 133 scope.def("VAR.param", mkline)
 134
 135 scope.Fallback("FALLBACK", "fallback")
 136
 137 t.CheckEquals(scope.LastValue("VAR.param"), "value")
 138 t.CheckEquals(scope.LastValue("FALLBACK"), "fallback")
 139 t.CheckEquals(scope.LastValue("UNDEFINED"), "")
 140}
 141
 142func (s *Suite) Test_Scope_Use(c *check.C) {
 143 t := s.Init(c)
 144
 145 mkline := t.NewMkLine("filename.mk", 3, "VAR=\t${USED}")
 146 scope := NewScope()
 147 scope.Define("VAR", mkline)
 148 scope.Use("USED", mkline, VucRunTime)
 149
 150 t.CheckEquals(scope.LastValue("VAR"), "${USED}")
 151 t.CheckEquals(scope.LastValue("USED"), "")
 152}
 153
78func (s *Suite) Test_Scope_Mentioned(c *check.C) { 154func (s *Suite) Test_Scope_Mentioned(c *check.C) {
79 t := s.Init(c) 155 t := s.Init(c)
80 156
81 assigned := t.NewMkLine("filename.mk", 3, "VAR=\tvalue") 157 assigned := t.NewMkLine("filename.mk", 3, "VAR=\tvalue")
82 commented := t.NewMkLine("filename.mk", 4, "#COMMENTED=\tvalue") 158 commented := t.NewMkLine("filename.mk", 4, "#COMMENTED=\tvalue")
83 documented := t.NewMkLine("filename.mk", 5, "# DOCUMENTED is a variable.") 159 documented := t.NewMkLine("filename.mk", 5, "# DOCUMENTED is a variable.")
84 160
85 scope := NewScope() 161 scope := NewScope()
86 scope.Define("VAR", assigned) 162 scope.Define("VAR", assigned)
87 scope.Define("COMMENTED", commented) 163 scope.Define("COMMENTED", commented)
88 scope.Define("DOCUMENTED", documented) 164 scope.Define("DOCUMENTED", documented)
89 165
90 t.CheckEquals(scope.Mentioned("VAR"), assigned) 166 t.CheckEquals(scope.Mentioned("VAR"), assigned)
@@ -92,87 +168,159 @@ func (s *Suite) Test_Scope_Mentioned(c * @@ -92,87 +168,159 @@ func (s *Suite) Test_Scope_Mentioned(c *
92 t.CheckEquals(scope.Mentioned("DOCUMENTED"), documented) 168 t.CheckEquals(scope.Mentioned("DOCUMENTED"), documented)
93 t.Check(scope.Mentioned("UNKNOWN"), check.IsNil) 169 t.Check(scope.Mentioned("UNKNOWN"), check.IsNil)
94} 170}
95 171
96func (s *Suite) Test_Scope_IsDefined(c *check.C) { 172func (s *Suite) Test_Scope_IsDefined(c *check.C) {
97 t := s.Init(c) 173 t := s.Init(c)
98 174
99 scope := NewScope() 175 scope := NewScope()
100 scope.Define("VAR.param", t.NewMkLine("file.mk", 1, "VAR.param=value")) 176 scope.Define("VAR.param", t.NewMkLine("file.mk", 1, "VAR.param=value"))
101 177
102 t.CheckEquals(scope.IsDefined("VAR.param"), true) 178 t.CheckEquals(scope.IsDefined("VAR.param"), true)
103 t.CheckEquals(scope.IsDefined("VAR.other"), false) 179 t.CheckEquals(scope.IsDefined("VAR.other"), false)
104 t.CheckEquals(scope.IsDefined("VARIABLE.*"), false) 180 t.CheckEquals(scope.IsDefined("VARIABLE.*"), false)
 181}
 182
 183func (s *Suite) Test_Scope_IsDefinedSimilar(c *check.C) {
 184 t := s.Init(c)
 185
 186 scope := NewScope()
 187 scope.Define("VAR.param", t.NewMkLine("file.mk", 1, "VAR.param=value"))
105 188
106 t.CheckEquals(scope.IsDefinedSimilar("VAR.param"), true) 189 t.CheckEquals(scope.IsDefinedSimilar("VAR.param"), true)
107 t.CheckEquals(scope.IsDefinedSimilar("VAR.other"), true) 190 t.CheckEquals(scope.IsDefinedSimilar("VAR.other"), true)
108 t.CheckEquals(scope.IsDefinedSimilar("VARIABLE.*"), false) 191 t.CheckEquals(scope.IsDefinedSimilar("VARIABLE.*"), false)
109} 192}
110 193
111func (s *Suite) Test_Scope_IsUsed(c *check.C) { 194func (s *Suite) Test_Scope_IsUsed(c *check.C) {
112 t := s.Init(c) 195 t := s.Init(c)
113 196
114 scope := NewScope() 197 scope := NewScope()
115 mkline := t.NewMkLine("file.mk", 1, "\techo ${VAR.param}") 198 mkline := t.NewMkLine("file.mk", 1, "\techo ${VAR.param}")
116 scope.Use("VAR.param", mkline, VucRunTime) 199 scope.Use("VAR.param", mkline, VucRunTime)
117 200
118 t.CheckEquals(scope.IsUsed("VAR.param"), true) 201 t.CheckEquals(scope.IsUsed("VAR.param"), true)
119 t.CheckEquals(scope.IsUsed("VAR.other"), false) 202 t.CheckEquals(scope.IsUsed("VAR.other"), false)
120 t.CheckEquals(scope.IsUsed("VARIABLE.*"), false) 203 t.CheckEquals(scope.IsUsed("VARIABLE.*"), false)
 204}
 205
 206func (s *Suite) Test_Scope_IsUsedSimilar(c *check.C) {
 207 t := s.Init(c)
 208
 209 scope := NewScope()
 210 mkline := t.NewMkLine("file.mk", 1, "\techo ${VAR.param}")
 211 scope.Use("VAR.param", mkline, VucRunTime)
121 212
122 t.CheckEquals(scope.IsUsedSimilar("VAR.param"), true) 213 t.CheckEquals(scope.IsUsedSimilar("VAR.param"), true)
123 t.CheckEquals(scope.IsUsedSimilar("VAR.other"), true) 214 t.CheckEquals(scope.IsUsedSimilar("VAR.other"), true)
124 t.CheckEquals(scope.IsUsedSimilar("VARIABLE.*"), false) 215 t.CheckEquals(scope.IsUsedSimilar("VARIABLE.*"), false)
125} 216}
126 217
 218func (s *Suite) Test_Scope_IsUsedAtLoadTime(c *check.C) {
 219 t := s.Init(c)
 220
 221 scope := NewScope()
 222 mkline := t.NewMkLine("file.mk", 1, "\techo ${VAR.param}")
 223
 224 scope.Use("LOAD_TIME", mkline, VucLoadTime)
 225 scope.Use("RUN_TIME", mkline, VucRunTime)
 226
 227 t.CheckEquals(scope.IsUsedAtLoadTime("LOAD_TIME"), true)
 228 t.CheckEquals(scope.IsUsedAtLoadTime("RUN_TIME"), false)
 229 t.CheckEquals(scope.IsUsedAtLoadTime("UNDEFINED"), false)
 230}
 231
127func (s *Suite) Test_Scope_FirstDefinition(c *check.C) { 232func (s *Suite) Test_Scope_FirstDefinition(c *check.C) {
128 t := s.Init(c) 233 t := s.Init(c)
129 234
130 mkline1 := t.NewMkLine("fname.mk", 3, "VAR=\tvalue") 235 mkline3 := t.NewMkLine("fname.mk", 3, "VAR=\tvalue")
131 mkline2 := t.NewMkLine("fname.mk", 3, ".if ${SNEAKY::=value}") 236 mkline4 := t.NewMkLine("fname.mk", 4, ".if ${SNEAKY::=value}")
 237 mkline5 := t.NewMkLine("fname.mk", 5, ".if ${USED}")
132 238
133 scope := NewScope() 239 scope := NewScope()
134 scope.Define("VAR", mkline1) 240 scope.Define("VAR", mkline3)
135 scope.Define("SNEAKY", mkline2) 241 scope.Define("SNEAKY", mkline4)
 242 scope.Use("USED", mkline5, VucLoadTime)
136 243
137 t.CheckEquals(scope.FirstDefinition("VAR"), mkline1) 244 t.CheckEquals(scope.FirstDefinition("VAR"), mkline3)
138 245
139 // This call returns nil because it's not a variable assignment 246 // This call returns nil because it's not a variable assignment
140 // and the calling code typically assumes a variable definition. 247 // and the calling code typically assumes a variable definition.
141 // These sneaky variables with implicit definition are an edge 248 // These sneaky variables with implicit definition are an edge
142 // case that only few people actually know. It's better that way. 249 // case that only few people actually know. It's better that way.
143 t.Check(scope.FirstDefinition("SNEAKY"), check.IsNil) 250 t.Check(scope.FirstDefinition("SNEAKY"), check.IsNil)
144 251
 252 t.Check(scope.FirstDefinition("USED"), check.IsNil)
 253
145 t.CheckOutputLines( 254 t.CheckOutputLines(
146 "ERROR: fname.mk:3: Assignment modifiers like \":=\" " + 255 "ERROR: fname.mk:4: Assignment modifiers like \":=\" " +
147 "must not be used at all.") 256 "must not be used at all.")
148} 257}
149 258
 259func (s *Suite) Test_Scope_LastDefinition(c *check.C) {
 260 t := s.Init(c)
 261
 262 mkline3 := t.NewMkLine("fname.mk", 3, "VAR=\tfirst")
 263 mkline4 := t.NewMkLine("fname.mk", 4, "VAR=\t${USED}")
 264
 265 scope := NewScope()
 266 scope.Define("VAR", mkline3)
 267 scope.Define("VAR", mkline4)
 268 scope.Use("USED", mkline4, VucRunTime)
 269
 270 t.CheckEquals(scope.LastDefinition("VAR"), mkline4)
 271 t.Check(scope.LastDefinition("UNDEFINED"), check.IsNil)
 272 t.Check(scope.LastDefinition("USED"), check.IsNil)
 273}
 274
150func (s *Suite) Test_Scope_Commented(c *check.C) { 275func (s *Suite) Test_Scope_Commented(c *check.C) {
151 t := s.Init(c) 276 t := s.Init(c)
152 277
153 assigned := t.NewMkLine("filename.mk", 3, "VAR=\tvalue") 278 assigned := t.NewMkLine("filename.mk", 3, "VAR=\tvalue")
154 commented := t.NewMkLine("filename.mk", 4, "#COMMENTED=\tvalue") 279 commented := t.NewMkLine("filename.mk", 4, "#COMMENTED=\tvalue")
155 documented := t.NewMkLine("filename.mk", 5, "# DOCUMENTED is a variable.") 280 documented := t.NewMkLine("filename.mk", 5, "# DOCUMENTED is a variable.")
 281 used := t.NewMkLine("filename.mk", 6, "ANY=\t${USED}")
156 282
157 scope := NewScope() 283 scope := NewScope()
158 scope.Define("VAR", assigned) 284 scope.Define("VAR", assigned)
159 scope.Define("COMMENTED", commented) 285 scope.Define("COMMENTED", commented)
160 scope.Define("DOCUMENTED", documented) 286 scope.Define("DOCUMENTED", documented)
 287 scope.Use("USED", used, VucRunTime)
161 288
162 t.Check(scope.Commented("VAR"), check.IsNil) 289 t.Check(scope.Commented("VAR"), check.IsNil)
163 t.CheckEquals(scope.Commented("COMMENTED"), commented) 290 t.CheckEquals(scope.Commented("COMMENTED"), commented)
164 t.Check(scope.Commented("DOCUMENTED"), check.IsNil) 291 t.Check(scope.Commented("DOCUMENTED"), check.IsNil)
165 t.Check(scope.Commented("UNKNOWN"), check.IsNil) 292 t.Check(scope.Commented("UNKNOWN"), check.IsNil)
 293 t.Check(scope.Commented("USED"), check.IsNil)
 294}
 295
 296func (s *Suite) Test_Scope_FirstUse(c *check.C) {
 297 t := s.Init(c)
 298
 299 mklines := t.NewMkLines("file.mk",
 300 MkCvsID,
 301 "VAR1=\t${USED}",
 302 "VAR2=\t${USED}")
 303
 304 mklines.Check()
 305
 306 scope := mklines.allVars
 307 t.CheckEquals(scope.FirstUse("USED"), mklines.mklines[1])
 308 t.Check(scope.FirstUse("UNUSED"), check.IsNil)
 309
 310 t.CheckOutputLines(
 311 "WARN: file.mk:2: VAR1 is defined but not used.",
 312 "WARN: file.mk:2: USED is used but not defined.",
 313 "WARN: file.mk:3: VAR2 is defined but not used.")
166} 314}
167 315
168func (s *Suite) Test_Scope_LastValue(c *check.C) { 316func (s *Suite) Test_Scope_LastValue(c *check.C) {
169 t := s.Init(c) 317 t := s.Init(c)
170 318
171 mklines := t.NewMkLines("file.mk", 319 mklines := t.NewMkLines("file.mk",
172 MkCvsID, 320 MkCvsID,
173 "VAR=\tfirst", 321 "VAR=\tfirst",
174 "VAR=\tsecond", 322 "VAR=\tsecond",
175 ".if 1", 323 ".if 1",
176 "VAR=\tthird (conditional)", 324 "VAR=\tthird (conditional)",
177 ".endif") 325 ".endif")
178 326
@@ -200,28 +348,74 @@ func (s *Suite) Test_Scope_LastValue__ap @@ -200,28 +348,74 @@ func (s *Suite) Test_Scope_LastValue__ap
200 "${PLIST.one}${PLIST.two}bin/program") 348 "${PLIST.one}${PLIST.two}bin/program")
201 t.CreateFileLines("Makefile.common", 349 t.CreateFileLines("Makefile.common",
202 MkCvsID, 350 MkCvsID,
203 "PLIST_VARS=\tone", 351 "PLIST_VARS=\tone",
204 "PLIST.one=\tyes") 352 "PLIST.one=\tyes")
205 pkg := NewPackage(".") 353 pkg := NewPackage(".")
206 t.FinishSetUp() 354 t.FinishSetUp()
207 355
208 pkg.Check() 356 pkg.Check()
209 357
210 t.CheckEquals(pkg.vars.LastValue("PLIST_VARS"), "one two") 358 t.CheckEquals(pkg.vars.LastValue("PLIST_VARS"), "one two")
211} 359}
212 360
 361func (s *Suite) Test_Scope_LastValueFound(c *check.C) {
 362 t := s.Init(c)
 363
 364 mklines := t.NewMkLines("file.mk",
 365 MkCvsID,
 366 "VAR=\tfirst",
 367 "VAR=\tsecond",
 368 ".if 1",
 369 "VAR=\tthird (conditional)",
 370 ".endif")
 371
 372 mklines.Check()
 373
 374 value, found, indeterminate := mklines.allVars.LastValueFound("VAR")
 375 t.CheckEquals(value, "third (conditional)")
 376 t.CheckEquals(found, true)
 377 t.CheckEquals(indeterminate, false) // TODO: why?
 378
 379 t.CheckOutputLines(
 380 "WARN: file.mk:2: VAR is defined but not used.")
 381}
 382
 383// Scope.DefineAll copies only the variable definitions,
 384// but not the uses of variables.
213func (s *Suite) Test_Scope_DefineAll(c *check.C) { 385func (s *Suite) Test_Scope_DefineAll(c *check.C) {
214 t := s.Init(c) 386 t := s.Init(c)
215 387
 388 mkline := t.NewMkLine("filename.mk", 123, "VAR=\t${USED}")
 389
216 src := NewScope() 390 src := NewScope()
 391 src.Use("USED", mkline, VucRunTime)
217 392
218 dst := NewScope() 393 dst := NewScope()
219 dst.DefineAll(&src) 394 dst.DefineAll(&src)
220 395
221 c.Check(dst.vs, check.HasLen, 0) 396 c.Check(dst.vs, check.HasLen, 0)
222 397
223 src.Define("VAR", t.NewMkLine("file.mk", 1, "VAR=value")) 398 src.Define("VAR", t.NewMkLine("file.mk", 1, "VAR=value"))
224 dst.DefineAll(&src) 399 dst.DefineAll(&src)
225 400
226 t.CheckEquals(dst.IsDefined("VAR"), true) 401 t.CheckEquals(dst.IsDefined("VAR"), true)
227} 402}
 403
 404func (s *Suite) Test_Scope_forEach(c *check.C) {
 405 t := s.Init(c)
 406
 407 mkline := t.NewMkLine("filename.mk", 123, "VAR=\t${USED}")
 408
 409 scope := NewScope()
 410 scope.Define("VAR", mkline)
 411 scope.Use("USED", mkline, VucRunTime)
 412
 413 var result []string
 414 scope.forEach(func(varname string, data *scopeVar) {
 415 result = append(result, varname+"="+data.value)
 416 })
 417
 418 t.CheckDeepEquals(result, []string{
 419 "USED=",
 420 "VAR=${USED}"})
 421}

cvs diff -r1.38 -r1.39 pkgsrc/pkgtools/pkglint/files/Attic/substcontext.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/substcontext.go 2020/05/24 19:12:29 1.38
+++ pkgsrc/pkgtools/pkglint/files/Attic/substcontext.go 2020/06/20 07:00:44 1.39
@@ -410,26 +410,28 @@ func (b *substBlock) varassign(mkline *M @@ -410,26 +410,28 @@ func (b *substBlock) varassign(mkline *M
410 case "SUBST_STAGE.*": 410 case "SUBST_STAGE.*":
411 b.varassignStage(mkline, pkg) 411 b.varassignStage(mkline, pkg)
412 case "SUBST_MESSAGE.*": 412 case "SUBST_MESSAGE.*":
413 b.varassignMessages(mkline) 413 b.varassignMessages(mkline)
414 case "SUBST_FILES.*": 414 case "SUBST_FILES.*":
415 b.varassignFiles(mkline) 415 b.varassignFiles(mkline)
416 case "SUBST_SED.*": 416 case "SUBST_SED.*":
417 b.varassignSed(mkline) 417 b.varassignSed(mkline)
418 case "SUBST_VARS.*": 418 case "SUBST_VARS.*":
419 b.varassignVars(mkline) 419 b.varassignVars(mkline)
420 case "SUBST_FILTER_CMD.*": 420 case "SUBST_FILTER_CMD.*":
421 b.varassignFilterCmd(mkline) 421 b.varassignFilterCmd(mkline)
422 } 422 }
 423
 424 b.varassignAllowForeign(mkline)
423} 425}
424 426
425func (b *substBlock) varassignStage(mkline *MkLine, pkg *Package) { 427func (b *substBlock) varassignStage(mkline *MkLine, pkg *Package) {
426 if b.isConditional() { 428 if b.isConditional() {
427 mkline.Warnf("%s should not be defined conditionally.", mkline.Varname()) 429 mkline.Warnf("%s should not be defined conditionally.", mkline.Varname())
428 } 430 }
429 431
430 b.dupString(mkline, ssStage) 432 b.dupString(mkline, ssStage)
431 433
432 value := mkline.Value() 434 value := mkline.Value()
433 if value == "pre-patch" || value == "post-patch" { 435 if value == "pre-patch" || value == "post-patch" {
434 fix := mkline.Autofix() 436 fix := mkline.Autofix()
435 fix.Warnf("Substitutions should not happen in the patch phase.") 437 fix.Warnf("Substitutions should not happen in the patch phase.")
@@ -481,26 +483,32 @@ func (b *substBlock) varassignVars(mklin @@ -481,26 +483,32 @@ func (b *substBlock) varassignVars(mklin
481 b.dupList(mkline, ssVars, ssVarsAutofix) 483 b.dupList(mkline, ssVars, ssVarsAutofix)
482 b.addSeen(ssTransform) 484 b.addSeen(ssTransform)
483 485
484 for _, substVar := range mkline.Fields() { 486 for _, substVar := range mkline.Fields() {
485 b.allowVar(substVar) 487 b.allowVar(substVar)
486 } 488 }
487} 489}
488 490
489func (b *substBlock) varassignFilterCmd(mkline *MkLine) { 491func (b *substBlock) varassignFilterCmd(mkline *MkLine) {
490 b.dupString(mkline, ssFilterCmd) 492 b.dupString(mkline, ssFilterCmd)
491 b.addSeen(ssTransform) 493 b.addSeen(ssTransform)
492} 494}
493 495
 496func (b *substBlock) varassignAllowForeign(mkline *MkLine) {
 497 mkline.ForEachUsed(func(varUse *MkVarUse, time VucTime) {
 498 b.allowVar(varUse.varname)
 499 })
 500}
 501
494func (b *substBlock) suggestSubstVars(mkline *MkLine) { 502func (b *substBlock) suggestSubstVars(mkline *MkLine) {
495 503
496 tokens, _ := splitIntoShellTokens(mkline.Line, mkline.Value()) 504 tokens, _ := splitIntoShellTokens(mkline.Line, mkline.Value())
497 for _, token := range tokens { 505 for _, token := range tokens {
498 varname := b.extractVarname(mkline.UnquoteShell(token, false)) 506 varname := b.extractVarname(mkline.UnquoteShell(token, false))
499 if varname == "" { 507 if varname == "" {
500 continue 508 continue
501 } 509 }
502 510
503 id := b.id 511 id := b.id
504 varop := sprintf("SUBST_VARS.%s%s%s", 512 varop := sprintf("SUBST_VARS.%s%s%s",
505 id, 513 id,
506 condStr(hasSuffix(id, "+"), " ", ""), 514 condStr(hasSuffix(id, "+"), " ", ""),

cvs diff -r1.36 -r1.37 pkgsrc/pkgtools/pkglint/files/Attic/substcontext_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/substcontext_test.go 2020/05/24 19:12:29 1.36
+++ pkgsrc/pkgtools/pkglint/files/Attic/substcontext_test.go 2020/06/20 07:00:44 1.37
@@ -968,26 +968,42 @@ func (s *Suite) Test_substScope_finish__ @@ -968,26 +968,42 @@ func (s *Suite) Test_substScope_finish__
968 "VAR2= value2", 968 "VAR2= value2",
969 "SUBST_FILES.1= files", 969 "SUBST_FILES.1= files",
970 "SUBST_VARS.1= VAR1", 970 "SUBST_VARS.1= VAR1",
971 "SUBST_STAGE.2= pre-configure", 971 "SUBST_STAGE.2= pre-configure",
972 "SUBST_FILES.2= files", 972 "SUBST_FILES.2= files",
973 "VAR1= value1", 973 "VAR1= value1",
974 "SUBST_VARS.2= VAR2") 974 "SUBST_VARS.2= VAR2")
975 975
976 t.CheckOutputLines( 976 t.CheckOutputLines(
977 "NOTE: filename.mk:1: " + 977 "NOTE: filename.mk:1: " +
978 "Please add only one class at a time to SUBST_CLASSES.") 978 "Please add only one class at a time to SUBST_CLASSES.")
979} 979}
980 980
 981func (s *Suite) Test_substScope_finish__indirect_SUBST_FILES(c *check.C) {
 982 t := s.Init(c)
 983
 984 t.RunSubst(
 985 "SUBST_CLASSES+= 1",
 986 "SUBST_STAGE.1= pre-configure",
 987 "S1_CMD= echo file",
 988 "SUBST_FILES.1= ${S1_CMD:sh}",
 989 "SUBST_VARS.1= PREFIX")
 990
 991 // Since S1_CMD is used in SUBST_FILES, it is not foreign
 992 // but obviously belongs to the SUBST block.
 993 // Before 2020-06-20, pkglint had warned about this.
 994 t.CheckOutputEmpty()
 995}
 996
981func (s *Suite) Test_substScope_prepareSubstClasses(c *check.C) { 997func (s *Suite) Test_substScope_prepareSubstClasses(c *check.C) {
982 t := s.Init(c) 998 t := s.Init(c)
983 999
984 t.RunSubst( 1000 t.RunSubst(
985 "SUBST_CLASSES+= 1", 1001 "SUBST_CLASSES+= 1",
986 "SUBST_STAGE.1= post-configure", 1002 "SUBST_STAGE.1= post-configure",
987 ".if 0") 1003 ".if 0")
988 1004
989 // There's no need to warn about unbalanced conditionals 1005 // There's no need to warn about unbalanced conditionals
990 // since that is already done by MkLines.Check. 1006 // since that is already done by MkLines.Check.
991 t.CheckOutputLines( 1007 t.CheckOutputLines(
992 "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_FILES.1 missing.", 1008 "WARN: filename.mk:EOF: Incomplete SUBST block: SUBST_FILES.1 missing.",
993 "WARN: filename.mk:EOF: Incomplete SUBST block: "+ 1009 "WARN: filename.mk:EOF: Incomplete SUBST block: "+

cvs diff -r1.99 -r1.100 pkgsrc/pkgtools/pkglint/files/Attic/vardefs.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/Attic/vardefs.go 2020/06/07 15:49:23 1.99
+++ pkgsrc/pkgtools/pkglint/files/Attic/vardefs.go 2020/06/20 07:00:44 1.100
@@ -1145,27 +1145,27 @@ func (reg *VarTypeRegistry) Init(src *Pk @@ -1145,27 +1145,27 @@ func (reg *VarTypeRegistry) Init(src *Pk
1145 reg.sys("EMUL_OPSYS", enum("darwin freebsd hpux irix linux osf1 solaris sunos none")) 1145 reg.sys("EMUL_OPSYS", enum("darwin freebsd hpux irix linux osf1 solaris sunos none"))
1146 reg.pkg("EMUL_PKG_FMT", enum("plain rpm")) 1146 reg.pkg("EMUL_PKG_FMT", enum("plain rpm"))
1147 reg.usr("EMUL_PLATFORM", BtEmulPlatform) 1147 reg.usr("EMUL_PLATFORM", BtEmulPlatform)
1148 reg.pkglist("EMUL_PLATFORMS", BtEmulPlatform) 1148 reg.pkglist("EMUL_PLATFORMS", BtEmulPlatform)
1149 reg.usrlist("EMUL_PREFER", BtEmulPlatform) 1149 reg.usrlist("EMUL_PREFER", BtEmulPlatform)
1150 reg.pkglist("EMUL_REQD", BtDependencyPattern) 1150 reg.pkglist("EMUL_REQD", BtDependencyPattern)
1151 reg.usr("EMUL_TYPE.*", enum("native builtin suse suse-10.0 suse-12.1 suse-13.1")) 1151 reg.usr("EMUL_TYPE.*", enum("native builtin suse suse-10.0 suse-12.1 suse-13.1"))
1152 reg.sys("ERROR_CAT", BtShellCommand) 1152 reg.sys("ERROR_CAT", BtShellCommand)
1153 reg.sys("ERROR_MSG", BtShellCommand) 1153 reg.sys("ERROR_MSG", BtShellCommand)
1154 reg.syslist("EXPORT_SYMBOLS_LDFLAGS", BtLdFlag) 1154 reg.syslist("EXPORT_SYMBOLS_LDFLAGS", BtLdFlag)
1155 reg.sys("EXTRACT_CMD", BtShellCommand) 1155 reg.sys("EXTRACT_CMD", BtShellCommand)
1156 reg.pkg("EXTRACT_DIR", BtPathname) 1156 reg.pkg("EXTRACT_DIR", BtPathname)
1157 reg.pkg("EXTRACT_DIR.*", BtPathname) 1157 reg.pkg("EXTRACT_DIR.*", BtPathname)
1158 reg.pkglist("EXTRACT_ELEMENTS", BtPathPattern) // TODO: No slashes allowed 1158 reg.pkglist("EXTRACT_ELEMENTS", BtPathPattern)
1159 reg.pkglist("EXTRACT_ENV", BtShellWord) 1159 reg.pkglist("EXTRACT_ENV", BtShellWord)
1160 reg.pkglist("EXTRACT_ONLY", BtPathname) 1160 reg.pkglist("EXTRACT_ONLY", BtPathname)
1161 reg.pkglist("EXTRACT_OPTS", BtShellWord) 1161 reg.pkglist("EXTRACT_OPTS", BtShellWord)
1162 reg.pkglist("EXTRACT_OPTS_BIN", BtShellWord) 1162 reg.pkglist("EXTRACT_OPTS_BIN", BtShellWord)
1163 reg.pkglist("EXTRACT_OPTS_LHA", BtShellWord) 1163 reg.pkglist("EXTRACT_OPTS_LHA", BtShellWord)
1164 reg.pkglist("EXTRACT_OPTS_PAX", BtShellWord) 1164 reg.pkglist("EXTRACT_OPTS_PAX", BtShellWord)
1165 reg.pkglist("EXTRACT_OPTS_RAR", BtShellWord) 1165 reg.pkglist("EXTRACT_OPTS_RAR", BtShellWord)
1166 reg.pkglist("EXTRACT_OPTS_TAR", BtShellWord) 1166 reg.pkglist("EXTRACT_OPTS_TAR", BtShellWord)
1167 reg.pkglist("EXTRACT_OPTS_ZIP", BtShellWord) 1167 reg.pkglist("EXTRACT_OPTS_ZIP", BtShellWord)
1168 reg.pkglist("EXTRACT_OPTS_ZOO", BtShellWord) 1168 reg.pkglist("EXTRACT_OPTS_ZOO", BtShellWord)
1169 reg.pkg("EXTRACT_SUFX", BtDistSuffix) 1169 reg.pkg("EXTRACT_SUFX", BtDistSuffix)
1170 reg.sys("FAIL_MSG", BtShellCommand) 1170 reg.sys("FAIL_MSG", BtShellCommand)
1171 reg.sys("FAMBASE", BtPathname) 1171 reg.sys("FAMBASE", BtPathname)

cvs diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/makepat/Attic/pat.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/makepat/Attic/pat.go 2020/06/14 11:35:54 1.1
+++ pkgsrc/pkgtools/pkglint/files/makepat/Attic/pat.go 2020/06/20 07:00:44 1.2
@@ -17,185 +17,265 @@ type state struct { @@ -17,185 +17,265 @@ type state struct {
17 end bool 17 end bool
18} 18}
19 19
20type transition struct { 20type transition struct {
21 min, max byte 21 min, max byte
22 to StateID 22 to StateID
23} 23}
24 24
25type StateID uint16 25type StateID uint16
26 26
27// Compile parses a pattern, including the error checking that is missing 27// Compile parses a pattern, including the error checking that is missing
28// from bmake. 28// from bmake.
29func Compile(pattern string) (*Pattern, error) { 29func Compile(pattern string) (*Pattern, error) {
30 var a Pattern 30 var p Pattern
31 s := a.AddState(false) 31 s := p.AddState(false)
32 
33 var deadEnd StateID 
34 32
35 lex := textproc.NewLexer(pattern) 33 lex := textproc.NewLexer(pattern)
36 for !lex.EOF() { 34 for !lex.EOF() {
37 35
38 if lex.SkipByte('*') { 36 if lex.SkipByte('*') {
39 a.AddTransition(s, 0, 255, s) 37 p.AddTransition(s, 0, 255, s)
40 continue 38 continue
41 } 39 }
42 40
43 if lex.SkipByte('?') { 41 if lex.SkipByte('?') {
44 next := a.AddState(false) 42 next := p.AddState(false)
45 a.AddTransition(s, 0, 255, next) 43 p.AddTransition(s, 0, 255, next)
46 s = next 44 s = next
47 continue 45 continue
48 } 46 }
49 47
50 if lex.SkipByte('\\') { 48 if lex.SkipByte('\\') {
51 if lex.EOF() { 49 if lex.EOF() {
52 return nil, errors.New("unfinished escape sequence") 50 return nil, errors.New("unfinished escape sequence")
53 } 51 }
54 ch := lex.NextByte() 52 ch := lex.NextByte()
55 next := a.AddState(false) 53 next := p.AddState(false)
56 a.AddTransition(s, ch, ch, next) 54 p.AddTransition(s, ch, ch, next)
57 s = next 55 s = next
58 continue 56 continue
59 } 57 }
60 58
61 ch := lex.NextByte() 59 ch := lex.NextByte()
62 if ch != '[' { 60 if ch != '[' {
63 next := a.AddState(false) 61 next := p.AddState(false)
64 a.AddTransition(s, ch, ch, next) 62 p.AddTransition(s, ch, ch, next)
65 s = next 63 s = next
66 continue 64 continue
67 } 65 }
68 66
69 negate := lex.SkipByte('^') 67 next, err := compileCharClass(&p, lex, ch, s)
70 if negate && deadEnd == 0 { 68 if err != nil {
71 deadEnd = a.AddState(false) 69 return nil, err
 70 }
 71
 72 s = next
 73 }
 74
 75 p.states[s].end = true
 76 return &p, nil
 77}
 78
 79func compileCharClass(p *Pattern, lex *textproc.Lexer, ch byte, s StateID) (StateID, error) {
 80 negate := lex.SkipByte('^')
 81 chars := make([]bool, 256)
 82 next := p.AddState(false)
 83 for {
 84 if lex.EOF() {
 85 return 0, errors.New("unfinished character class")
 86 }
 87 ch = lex.NextByte()
 88 if ch == ']' {
 89 break
72 } 90 }
73 next := a.AddState(false) 91 if lex.SkipByte('-') {
74 for { 
75 if lex.EOF() { 92 if lex.EOF() {
76 return nil, errors.New("unfinished character class") 93 return 0, errors.New("unfinished character range")
77 } 94 }
78 ch = lex.NextByte() 95 max := lex.NextByte()
79 if ch == ']' { 96 if ch > max {
80 break 97 ch, max = max, ch
81 } 
82 max := ch 
83 if lex.SkipByte('-') { 
84 if lex.EOF() { 
85 return nil, errors.New("unfinished character range") 
86 } 
87 max = lex.NextByte() 
88 } 98 }
89 99 for i := int(ch); i <= int(max); i++ {
90 to := next 100 chars[i] = true
91 if negate { 
92 to = deadEnd 
93 } 101 }
94 a.AddTransition(s, bmin(ch, max), bmax(ch, max), to) 102 } else {
 103 chars[ch] = true
95 } 104 }
96 if negate { 105 }
97 a.AddTransition(s, 0, 255, next) 106 if negate {
 107 for i, b := range chars {
 108 chars[i] = !b
98 } 109 }
99 s = next 
100 } 110 }
101 111
102 a.states[s].end = true 112 start := 0
103 return &a, nil 113 for start < len(chars) && !chars[start] {
 114 start++
 115 }
 116
 117 for start < len(chars) {
 118 end := start
 119 for end < len(chars) && chars[end] {
 120 end++
 121 }
 122
 123 if start < end {
 124 p.AddTransition(s, byte(start), byte(end-1), next)
 125 }
 126
 127 start = end
 128 for start < len(chars) && !chars[start] {
 129 start++
 130 }
 131 }
 132 return next, nil
104} 133}
105 134
106func (a *Pattern) AddState(end bool) StateID { 135func (p *Pattern) AddState(end bool) StateID {
107 a.states = append(a.states, state{nil, end}) 136 p.states = append(p.states, state{nil, end})
108 return StateID(len(a.states) - 1) 137 return StateID(len(p.states) - 1)
109} 138}
110 139
111func (a *Pattern) AddTransition(from StateID, min, max byte, to StateID) { 140func (p *Pattern) AddTransition(from StateID, min, max byte, to StateID) {
112 state := &a.states[from] 141 state := &p.states[from]
113 state.transitions = append(state.transitions, transition{min, max, to}) 142 state.transitions = append(state.transitions, transition{min, max, to})
114} 143}
115 144
116// Match tests whether a pattern matches the given string. 145// Match tests whether a pattern matches the given string.
117func (a *Pattern) Match(s string) bool { 146func (p *Pattern) Match(s string) bool {
118 state := StateID(0) 147 curr := make([]bool, len(p.states))
 148 next := make([]bool, len(p.states))
 149
 150 curr[0] = true
119 for _, ch := range []byte(s) { 151 for _, ch := range []byte(s) {
120 for _, tr := range a.states[state].transitions { 152 ok := false
121 if tr.min <= ch && ch <= tr.max { 153 for i, _ := range next {
122 state = tr.to 154 next[i] = false
123 goto nextByte 155 }
 156
 157 for si := range curr {
 158 if !curr[si] {
 159 continue
 160 }
 161 for _, tr := range p.states[si].transitions {
 162 if tr.min <= ch && ch <= tr.max {
 163 next[tr.to] = true
 164 ok = true
 165 }
124 } 166 }
125 } 167 }
126 return false 168 if !ok {
127 nextByte: 169 return false
 170 }
 171 curr, next = next, curr
128 } 172 }
129 return a.states[state].end 173
 174 for i, curr := range curr {
 175 if curr && p.states[i].end {
 176 return true
 177 }
 178 }
 179 return false
130} 180}
131 181
132// Intersect computes a pattern that only matches if both given patterns 182// Intersect computes a pattern that only matches if both given patterns
133// match at the same time. 183// match at the same time.
134func Intersect(a, b *Pattern) *Pattern { 184func Intersect(p1, p2 *Pattern) *Pattern {
135 var is Pattern 185 var res Pattern
136 for i := 0; i < len(a.states); i++ { 186 for i1 := 0; i1 < len(p1.states); i1++ {
137 for j := 0; j < len(b.states); j++ { 187 for i2 := 0; i2 < len(p2.states); i2++ {
138 is.AddState(a.states[i].end && b.states[j].end) 188 res.AddState(p1.states[i1].end && p2.states[i2].end)
139 } 189 }
140 } 190 }
141 191
142 for i := 0; i < len(a.states); i++ { 192 for i1 := 0; i1 < len(p1.states); i1++ {
143 for j := 0; j < len(b.states); j++ { 193 for i2 := 0; i2 < len(p2.states); i2++ {
144 for _, at := range a.states[i].transitions { 194 for _, t1 := range p1.states[i1].transitions {
145 for _, bt := range b.states[j].transitions { 195 for _, t2 := range p2.states[i2].transitions {
146 min := bmax(at.min, bt.min) 196 min := bmax(t1.min, t2.min)
147 max := bmin(at.max, bt.max) 197 max := bmin(t1.max, t2.max)
148 if min <= max { 198 if min <= max {
149 from := StateID(i*len(b.states) + j) 199 from := StateID(i1*len(p2.states) + i2)
150 to := at.to*StateID(len(b.states)) + bt.to 200 to := t1.to*StateID(len(p2.states)) + t2.to
151 is.AddTransition(from, min, max, to) 201 res.AddTransition(from, min, max, to)
152 } 202 }
153 } 203 }
154 } 204 }
155 } 205 }
156 } 206 }
157 207
158 // TODO: optimize: remove transitions that point to a dead end 208 return res.optimized()
 209}
 210
 211func (p *Pattern) optimized() *Pattern {
 212 var opt Pattern
 213
 214 var todo []StateID
 215 hasNewID := make([]bool, len(p.states))
 216 newIDs := make([]StateID, len(p.states))
 217
 218 todo = append(todo, 0)
 219 newIDs[0] = opt.AddState(p.states[0].end)
 220 hasNewID[0] = true
 221
 222 for len(todo) > 0 {
 223 oldStateID := todo[len(todo)-1]
 224 todo = todo[:len(todo)-1]
 225
 226 oldState := p.states[oldStateID]
 227
 228 for _, t := range oldState.transitions {
 229 if !hasNewID[t.to] {
 230 hasNewID[t.to] = true
 231 newIDs[t.to] = opt.AddState(p.states[t.to].end)
 232 todo = append(todo, t.to)
 233 }
 234 opt.AddTransition(newIDs[oldStateID], t.min, t.max, newIDs[t.to])
 235 }
 236 }
 237
 238 // TODO: remove transitions that point to a dead end
159 239
160 return &is 240 return &opt
161} 241}
162 242
163// CanMatch tests whether the pattern can match some string. 243// CanMatch tests whether the pattern can match some string.
164// Most patterns can do that. 244// Most patterns can do that.
165// Typical counterexamples are: 245// Typical counterexamples are:
166// [^] 246// [^]
167// Intersect("*.c", "*.h") 247// Intersect("*.c", "*.h")
168func (a *Pattern) CanMatch() bool { 248func (p *Pattern) CanMatch() bool {
169 reachable := make([]bool, len(a.states)) 249 reachable := make([]bool, len(p.states))
170 reachable[0] = true 250 reachable[0] = true
171 251
172again: 252again:
173 changed := false 253 changed := false
174 for i, s := range a.states { 254 for i, s := range p.states {
175 if reachable[i] { 255 if reachable[i] {
176 for _, t := range s.transitions { 256 for _, t := range s.transitions {
177 if !reachable[t.to] { 257 if !reachable[t.to] {
178 reachable[t.to] = true 258 reachable[t.to] = true
179 changed = true 259 changed = true
180 } 260 }
181 } 261 }
182 } 262 }
183 } 263 }
184 if changed { 264 if changed {
185 goto again 265 goto again
186 } 266 }
187 267
188 for i, s := range a.states { 268 for i, s := range p.states {
189 if reachable[i] && s.end { 269 if reachable[i] && s.end {
190 return true 270 return true
191 } 271 }
192 } 272 }
193 return false 273 return false
194} 274}
195 275
196func bmin(a, b byte) byte { 276func bmin(a, b byte) byte {
197 if a < b { 277 if a < b {
198 return a 278 return a
199 } 279 }
200 return b 280 return b
201} 281}

cvs diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/makepat/Attic/pat_test.go (expand / switch to unified diff)

--- pkgsrc/pkgtools/pkglint/files/makepat/Attic/pat_test.go 2020/06/14 11:35:54 1.1
+++ pkgsrc/pkgtools/pkglint/files/makepat/Attic/pat_test.go 2020/06/20 07:00:44 1.2
@@ -1,20 +1,97 @@ @@ -1,20 +1,97 @@
1package makepat 1package makepat
2 2
3import ( 3import (
 4 "netbsd.org/pkglint/intqa"
 5 "reflect"
4 "testing" 6 "testing"
5) 7)
6 8
7func Test_Automaton_Match(t *testing.T) { 9func Test_Compile__errors(t *testing.T) {
 10 tests := []struct {
 11 pattern string
 12 msg string
 13 }{
 14 {"\\", "unfinished escape sequence"},
 15 {"[", "unfinished character class"},
 16 {"[a-", "unfinished character range"},
 17 {"[\\", "unfinished character class"},
 18 }
 19 for _, tt := range tests {
 20 t.Run(tt.pattern, func(t *testing.T) {
 21 _, err := Compile(tt.pattern)
 22 if err == nil {
 23 t.Fail()
 24 } else if err.Error() != tt.msg {
 25 t.Errorf("err = %v, want %v", err, tt.msg)
 26 }
 27 })
 28 }
 29}
 30
 31func Test_compileCharClass(t *testing.T) {
 32 tests := []struct {
 33 pattern string
 34 str string
 35 want bool
 36 }{
 37 {"[0-9]", "/", false},
 38 {"[0-9]", "0", true},
 39 {"[0-9]", "9", true},
 40 {"[0-9]", ":", false},
 41 }
 42 for _, tt := range tests {
 43 t.Run(tt.pattern, func(t *testing.T) {
 44 p, err := Compile(tt.pattern)
 45 if err != nil {
 46 t.Fail()
 47 }
 48 got := p.Match(tt.str)
 49 if got != tt.want {
 50 t.Errorf("got %v, want %v", got, tt.want)
 51 }
 52 })
 53 }
 54}
 55
 56func Test_Pattern_AddState(t *testing.T) {
 57 var p Pattern
 58
 59 p.AddState(false)
 60 p.AddState(true)
 61
 62 expected := Pattern{states: []state{{nil, false}, {nil, true}}}
 63 if !reflect.DeepEqual(p, expected) {
 64 t.Errorf("%#v", p)
 65 }
 66}
 67
 68func Test_Pattern_AddTransition(t *testing.T) {
 69 var p Pattern
 70
 71 p.AddState(false)
 72 p.AddState(true)
 73 p.AddTransition(0, '0', '9', 1)
 74 p.AddTransition(1, '0', '9', 0)
 75
 76 expected := Pattern{states: []state{
 77 {[]transition{{'0', '9', 1}}, false},
 78 {[]transition{{'0', '9', 0}}, true}}}
 79 if !reflect.DeepEqual(p, expected) {
 80 t.Errorf("%#v", p)
 81 }
 82}
 83
 84func Test_Pattern_Match(t *testing.T) {
8 tests := []struct { 85 tests := []struct {
9 pattern string 86 pattern string
10 str string 87 str string
11 want bool 88 want bool
12 }{ 89 }{
13 {"a-[0-9]*", "", false}, 90 {"a-[0-9]*", "", false},
14 {"a-[0-9]*", "a", false}, 91 {"a-[0-9]*", "a", false},
15 {"a-[0-9]*", "b", false}, 92 {"a-[0-9]*", "b", false},
16 {"a-[0-9]*", "a-", false}, 93 {"a-[0-9]*", "a-", false},
17 {"a-[0-9]*", "a-0", true}, 94 {"a-[0-9]*", "a-0", true},
18 {"a-[0-9]*", "a-13", true}, 95 {"a-[0-9]*", "a-13", true},
19 {"a-[0-9]*", "a-3* matches arbitrary text", true}, 96 {"a-[0-9]*", "a-3* matches arbitrary text", true},
20 {"a-[0-9]*", "a+", false}, 97 {"a-[0-9]*", "a+", false},
@@ -57,62 +134,46 @@ func Test_Automaton_Match(t *testing.T)  @@ -57,62 +134,46 @@ func Test_Automaton_Match(t *testing.T)
57 {"[0-9A-Za-z]", "z", true}, 134 {"[0-9A-Za-z]", "z", true},
58 {"[0-9A-Za-z]", "{", false}, 135 {"[0-9A-Za-z]", "{", false},
59 {"[\\-]]", "{", false}, 136 {"[\\-]]", "{", false},
60 {"[\\-]]", "\\", true}, 137 {"[\\-]]", "\\", true},
61 {"[\\-]]", "]", true}, 138 {"[\\-]]", "]", true},
62 {"[\\-]]", "^", false}, 139 {"[\\-]]", "^", false},
63 {"[9-0]", "", false}, 140 {"[9-0]", "", false},
64 {"[9-0]", "55", false}, 141 {"[9-0]", "55", false},
65 {"[9-0]", "/", false}, 142 {"[9-0]", "/", false},
66 {"[9-0]", "0", true}, 143 {"[9-0]", "0", true},
67 {"[9-0]", "5", true}, 144 {"[9-0]", "5", true},
68 {"[9-0]", "9", true}, 145 {"[9-0]", "9", true},
69 {"[9-0]", ":", false}, 146 {"[9-0]", ":", false},
 147 {"*.c", ".c", true},
 148 {"*.c", "a.c", true},
 149 {"*.c", "c.c", true},
 150 {"*.c", "..c", true},
 151 {"*.c", ".c.c", true},
 152 {"*.c", "a.c.c", true},
70 } 153 }
71 for _, tt := range tests { 154 for _, tt := range tests {
72 t.Run(tt.pattern+" "+tt.str, func(t *testing.T) { 155 t.Run(tt.pattern+" "+tt.str, func(t *testing.T) {
73 a, err := Compile(tt.pattern) 156 a, err := Compile(tt.pattern)
74 if err != nil { 157 if err != nil {
75 t.Fatal(err) 158 t.Fatal(err)
76 } 159 }
77 if got := a.Match(tt.str); got != tt.want { 160 if got := a.Match(tt.str); got != tt.want {
78 t.Errorf("Match() = %v, want %v", got, tt.want) 161 t.Errorf("Match() = %v, want %v", got, tt.want)
79 } 162 }
80 }) 163 })
81 } 164 }
82} 165}
83 166
84func Test_Automaton_Compile__errors(t *testing.T) { 
85 tests := []struct { 
86 pattern string 
87 msg string 
88 }{ 
89 {"\\", "unfinished escape sequence"}, 
90 {"[", "unfinished character class"}, 
91 {"[a-", "unfinished character range"}, 
92 {"[\\", "unfinished character class"}, 
93 } 
94 for _, tt := range tests { 
95 t.Run(tt.pattern, func(t *testing.T) { 
96 _, err := Compile(tt.pattern) 
97 if err == nil { 
98 t.Fail() 
99 } else if err.Error() != tt.msg { 
100 t.Errorf("err = %v, want %v", err, tt.msg) 
101 } 
102 }) 
103 } 
104} 
105 
106func Test_Intersect(t *testing.T) { 167func Test_Intersect(t *testing.T) {
107 tests := []struct { 168 tests := []struct {
108 pattern1 string 169 pattern1 string
109 pattern2 string 170 pattern2 string
110 str string 171 str string
111 matches bool 172 matches bool
112 canMatch bool 173 canMatch bool
113 }{ 174 }{
114 {"N-*", "N-*", "N-*", true, true}, 175 {"N-*", "N-*", "N-*", true, true},
115 {"N-9.99.*", "N-[1-9].*", "", false, true}, 176 {"N-9.99.*", "N-[1-9].*", "", false, true},
116 {"N-9.99.*", "N-[1-9][0-9].*", "", false, false}, 177 {"N-9.99.*", "N-[1-9][0-9].*", "", false, false},
117 } 178 }
118 for _, tt := range tests { 179 for _, tt := range tests {
@@ -127,13 +188,89 @@ func Test_Intersect(t *testing.T) { @@ -127,13 +188,89 @@ func Test_Intersect(t *testing.T) {
127 } 188 }
128 a := Intersect(a1, a2) 189 a := Intersect(a1, a2)
129 matches := a.Match(tt.str) 190 matches := a.Match(tt.str)
130 if matches != tt.matches { 191 if matches != tt.matches {
131 t.Errorf("Match() = %v, want %v", matches, tt.matches) 192 t.Errorf("Match() = %v, want %v", matches, tt.matches)
132 } 193 }
133 canMatch := a.CanMatch() 194 canMatch := a.CanMatch()
134 if canMatch != tt.canMatch { 195 if canMatch != tt.canMatch {
135 t.Errorf("CanMatch() = %v, want %v", canMatch, tt.canMatch) 196 t.Errorf("CanMatch() = %v, want %v", canMatch, tt.canMatch)
136 } 197 }
137 }) 198 })
138 } 199 }
139} 200}
 201
 202func Test_Pattern_optimized(t *testing.T) {
 203 var p Pattern
 204 p.AddState(false)
 205 p.AddState(false)
 206 p.AddState(false)
 207 p.AddState(true)
 208 p.AddTransition(0, '1', '1', 1)
 209 p.AddTransition(1, '2', '2', 3)
 210
 211 opt := p.optimized()
 212
 213 expected := Pattern{[]state{
 214 {[]transition{{'1', '1', 1}}, false},
 215 {[]transition{{'2', '2', 2}}, false},
 216 {nil, true}}}
 217 if !reflect.DeepEqual(opt, &expected) {
 218 t.Errorf("%#v", p)
 219 }
 220}
 221
 222func Test_Pattern_CanMatch(t *testing.T) {
 223 tests := []struct {
 224 p1 string
 225 p2 string
 226 want bool
 227 }{
 228 {"*.c", "*.h", false},
 229 {"*.c", "????.?", true},
 230 {"[1-9]", "5", true},
 231 {"[1-9]", ":", false},
 232 {"[1-9A-Za-z]", "[ -/]", false},
 233 }
 234 for _, tt := range tests {
 235 t.Run(tt.p1+" "+tt.p2, func(t *testing.T) {
 236 p1, err1 := Compile(tt.p1)
 237 p2, err2 := Compile(tt.p2)
 238 if err1 != nil {
 239 t.Fatal(err1)
 240 }
 241 if err2 != nil {
 242 t.Fatal(err2)
 243 }
 244 both := Intersect(p1, p2)
 245 got := both.CanMatch()
 246 if got != tt.want {
 247 t.Errorf("CanMatch() = %v, want %v", got, tt.want)
 248 }
 249 })
 250 }
 251
 252}
 253
 254func Test_bmin(t *testing.T) {
 255 if bmin(0, 255) != 0 {
 256 t.Error()
 257 }
 258 if bmin(128, 127) != 127 {
 259 t.Error()
 260 }
 261}
 262
 263func Test_bmax(t *testing.T) {
 264 if bmax(0, 255) != 255 {
 265 t.Error()
 266 }
 267 if bmax(128, 127) != 128 {
 268 t.Error()
 269 }
 270}
 271
 272func Test(t *testing.T) {
 273 ck := intqa.NewQAChecker(t.Errorf)
 274 ck.Configure("*", "*", "", -intqa.EMissingTest)
 275 ck.Check()
 276}