pkgsrc-Changes archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
CVS commit: pkgsrc/pkgtools/pkglint
Module Name: pkgsrc
Committed By: rillig
Date: Wed Mar 29 07:12:36 UTC 2023
Modified Files:
pkgsrc/pkgtools/pkglint: Makefile
pkgsrc/pkgtools/pkglint/files: check_test.go homepage.go
logging_test.go mkassignchecker.go mkcondchecker.go
mkcondchecker_test.go mkcondsimplifier.go mkcondsimplifier_test.go
mkline.go mkline_test.go mklines.go mkvarusechecker.go
mkvarusechecker_test.go pkglint.go pkgsrc.go scope.go var.go
varalignblock_test.go vardefs.go vardefs_test.go vartype.go
vartype_test.go vartypecheck.go vartypecheck_test.go
pkgsrc/pkgtools/pkglint/files/makepat: pat.go pat_test.go
pkgsrc/pkgtools/pkglint/files/textproc: lexer.go
Log Message:
Update pkgtools/pkglint to 23.1.0
Changes since 22.4.1:
In makefiles outside pkgsrc, don't require the first line to contain the
CVS Id.
When simplifying conditions, correctly handle the edge case that a
single-word value may evaluate numerically to zero.
In dependency lines, parse '#' signs correctly.
In error messages about malformed patch files, use the correct plural
form.
To generate a diff of this commit:
cvs rdiff -u -r1.741 -r1.742 pkgsrc/pkgtools/pkglint/Makefile
cvs rdiff -u -r1.82 -r1.83 pkgsrc/pkgtools/pkglint/files/check_test.go
cvs rdiff -u -r1.7 -r1.8 pkgsrc/pkgtools/pkglint/files/homepage.go
cvs rdiff -u -r1.29 -r1.30 pkgsrc/pkgtools/pkglint/files/logging_test.go
cvs rdiff -u -r1.17 -r1.18 pkgsrc/pkgtools/pkglint/files/mkassignchecker.go
cvs rdiff -u -r1.16 -r1.17 pkgsrc/pkgtools/pkglint/files/mkcondchecker.go \
pkgsrc/pkgtools/pkglint/files/mkvarusechecker_test.go
cvs rdiff -u -r1.15 -r1.16 \
pkgsrc/pkgtools/pkglint/files/mkcondchecker_test.go \
pkgsrc/pkgtools/pkglint/files/mkvarusechecker.go
cvs rdiff -u -r1.4 -r1.5 pkgsrc/pkgtools/pkglint/files/mkcondsimplifier.go
cvs rdiff -u -r1.2 -r1.3 \
pkgsrc/pkgtools/pkglint/files/mkcondsimplifier_test.go
cvs rdiff -u -r1.87 -r1.88 pkgsrc/pkgtools/pkglint/files/mkline.go
cvs rdiff -u -r1.86 -r1.87 pkgsrc/pkgtools/pkglint/files/mkline_test.go
cvs rdiff -u -r1.77 -r1.78 pkgsrc/pkgtools/pkglint/files/mklines.go
cvs rdiff -u -r1.88 -r1.89 pkgsrc/pkgtools/pkglint/files/pkglint.go
cvs rdiff -u -r1.70 -r1.71 pkgsrc/pkgtools/pkglint/files/pkgsrc.go
cvs rdiff -u -r1.3 -r1.4 pkgsrc/pkgtools/pkglint/files/scope.go
cvs rdiff -u -r1.11 -r1.12 pkgsrc/pkgtools/pkglint/files/var.go
cvs rdiff -u -r1.19 -r1.20 \
pkgsrc/pkgtools/pkglint/files/varalignblock_test.go
cvs rdiff -u -r1.107 -r1.108 pkgsrc/pkgtools/pkglint/files/vardefs.go
cvs rdiff -u -r1.30 -r1.31 pkgsrc/pkgtools/pkglint/files/vardefs_test.go
cvs rdiff -u -r1.55 -r1.56 pkgsrc/pkgtools/pkglint/files/vartype.go
cvs rdiff -u -r1.28 -r1.29 pkgsrc/pkgtools/pkglint/files/vartype_test.go
cvs rdiff -u -r1.103 -r1.104 pkgsrc/pkgtools/pkglint/files/vartypecheck.go
cvs rdiff -u -r1.95 -r1.96 pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go
cvs rdiff -u -r1.4 -r1.5 pkgsrc/pkgtools/pkglint/files/makepat/pat.go \
pkgsrc/pkgtools/pkglint/files/makepat/pat_test.go
cvs rdiff -u -r1.12 -r1.13 pkgsrc/pkgtools/pkglint/files/textproc/lexer.go
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: pkgsrc/pkgtools/pkglint/Makefile
diff -u pkgsrc/pkgtools/pkglint/Makefile:1.741 pkgsrc/pkgtools/pkglint/Makefile:1.742
--- pkgsrc/pkgtools/pkglint/Makefile:1.741 Wed Mar 8 13:38:53 2023
+++ pkgsrc/pkgtools/pkglint/Makefile Wed Mar 29 07:12:36 2023
@@ -1,7 +1,6 @@
-# $NetBSD: Makefile,v 1.741 2023/03/08 13:38:53 bsiegert Exp $
+# $NetBSD: Makefile,v 1.742 2023/03/29 07:12:36 rillig Exp $
-PKGNAME= pkglint-22.4.1
-PKGREVISION= 1
+PKGNAME= pkglint-23.1.0
CATEGORIES= pkgtools
MAINTAINER= rillig%NetBSD.org@localhost
Index: pkgsrc/pkgtools/pkglint/files/check_test.go
diff -u pkgsrc/pkgtools/pkglint/files/check_test.go:1.82 pkgsrc/pkgtools/pkglint/files/check_test.go:1.83
--- pkgsrc/pkgtools/pkglint/files/check_test.go:1.82 Thu Mar 2 08:58:29 2023
+++ pkgsrc/pkgtools/pkglint/files/check_test.go Wed Mar 29 07:12:36 2023
@@ -63,6 +63,7 @@ func (s *Suite) SetUpTest(c *check.C) {
trace.Out = &t.stdout
G.Pkgsrc = NewPkgsrc(t.File("."))
+ G.Project = G.Pkgsrc
t.c = c
t.SetUpCommandLine("-Wall") // To catch duplicate warnings
@@ -231,11 +232,11 @@ func (t *Tester) SetUpCommandLine(args .
//
// See SetUpTool for registering tools like echo, awk, perl.
func (t *Tester) SetUpVartypes() {
- G.Pkgsrc.vartypes.Init(G.Pkgsrc)
+ G.Pkgsrc.Types().Init(G.Pkgsrc)
}
func (t *Tester) SetUpMasterSite(varname string, urls ...string) {
- if !G.Pkgsrc.vartypes.IsDefinedExact(varname) {
+ if !G.Pkgsrc.Types().IsDefinedExact(varname) {
t.SetUpVarType(varname, BtFetchURL,
List|SystemProvided,
"buildlink3.mk: none",
@@ -272,7 +273,7 @@ func (t *Tester) SetUpVarType(varname st
aclEntries = []string{"Makefile, *.mk: default, set, append, use, use-loadtime"}
}
- G.Pkgsrc.vartypes.acl(varname, basicType, options, aclEntries...)
+ G.Project.Types().acl(varname, basicType, options, aclEntries...)
// Make sure that registering the type succeeds.
// This is necessary for BtUnknown and guessed types.
Index: pkgsrc/pkgtools/pkglint/files/homepage.go
diff -u pkgsrc/pkgtools/pkglint/files/homepage.go:1.7 pkgsrc/pkgtools/pkglint/files/homepage.go:1.8
--- pkgsrc/pkgtools/pkglint/files/homepage.go:1.7 Sun Jan 29 13:36:31 2023
+++ pkgsrc/pkgtools/pkglint/files/homepage.go Wed Mar 29 07:12:36 2023
@@ -33,9 +33,9 @@ import (
// - http://sf.net/p/$project/
//
// TODO: implement complete homepage migration for SourceForge.
-// TODO: allow to suppress the automatic migration for SourceForge,
//
-// even if it is not about https vs. http.
+// TODO: allow to suppress the automatic migration for SourceForge,
+// even if it is not about https vs. http.
type HomepageChecker struct {
Value string
ValueNoVar string
Index: pkgsrc/pkgtools/pkglint/files/logging_test.go
diff -u pkgsrc/pkgtools/pkglint/files/logging_test.go:1.29 pkgsrc/pkgtools/pkglint/files/logging_test.go:1.30
--- pkgsrc/pkgtools/pkglint/files/logging_test.go:1.29 Sun Jan 29 13:36:31 2023
+++ pkgsrc/pkgtools/pkglint/files/logging_test.go Wed Mar 29 07:12:36 2023
@@ -597,8 +597,7 @@ func (s *Suite) Test_Logger_writeSource_
// output lines.
//
// TODO: Giving the diagnostics again would be useful, but the warning and
-//
-// error counters should not be affected, as well as the exitcode.
+// error counters should not be affected, as well as the exitcode.
func (s *Suite) Test_Logger_writeSource__separator_autofix(c *check.C) {
t := s.Init(c)
Index: pkgsrc/pkgtools/pkglint/files/mkassignchecker.go
diff -u pkgsrc/pkgtools/pkglint/files/mkassignchecker.go:1.17 pkgsrc/pkgtools/pkglint/files/mkassignchecker.go:1.18
--- pkgsrc/pkgtools/pkglint/files/mkassignchecker.go:1.17 Thu Mar 2 08:58:29 2023
+++ pkgsrc/pkgtools/pkglint/files/mkassignchecker.go Wed Mar 29 07:12:36 2023
@@ -25,7 +25,7 @@ func (ck *MkAssignChecker) check() {
// checkLeft checks everything to the left of the assignment operator.
func (ck *MkAssignChecker) checkLeft() {
varname := ck.MkLine.Varname()
- if !ck.mayBeDefined(varname) {
+ if G.Pkgsrc != nil && !ck.mayBeDefined(varname) {
ck.MkLine.Warnf("Variable names starting with an underscore (%s) are reserved for internal pkgsrc use.", varname)
}
@@ -72,13 +72,12 @@ func (ck *MkAssignChecker) checkLeftNotU
return
}
- vartypes := G.Pkgsrc.vartypes
+ vartypes := G.Project.Types()
if vartypes.IsDefinedExact(varname) || vartypes.IsDefinedExact(varcanon) {
return
}
- deprecated := G.Pkgsrc.Deprecated
- if deprecated[varname] != "" || deprecated[varcanon] != "" {
+ if G.Project.Deprecated(varname) != "" {
return
}
@@ -125,11 +124,8 @@ func (ck *MkAssignChecker) checkLeftOpsy
}
func (ck *MkAssignChecker) checkLeftDeprecated() {
- varname := ck.MkLine.Varname()
- if fix := G.Pkgsrc.Deprecated[varname]; fix != "" {
- ck.MkLine.Warnf("Definition of %s is deprecated. %s", varname, fix)
- } else if fix = G.Pkgsrc.Deprecated[varnameCanon(varname)]; fix != "" {
- ck.MkLine.Warnf("Definition of %s is deprecated. %s", varname, fix)
+ if instead := G.Project.Deprecated(ck.MkLine.Varname()); instead != "" {
+ ck.MkLine.Warnf("Definition of %s is deprecated. %s", ck.MkLine.Varname(), instead)
}
}
@@ -703,7 +699,7 @@ func (ck *MkAssignChecker) mayBeDefined(
if G.Infrastructure {
return true
}
- if G.Pkgsrc == nil || G.Pkgsrc.vartypes.Canon(varname) != nil {
+ if G.Pkgsrc.Types().Canon(varname) != nil {
return true
}
Index: pkgsrc/pkgtools/pkglint/files/mkcondchecker.go
diff -u pkgsrc/pkgtools/pkglint/files/mkcondchecker.go:1.16 pkgsrc/pkgtools/pkglint/files/mkcondchecker.go:1.17
--- pkgsrc/pkgtools/pkglint/files/mkcondchecker.go:1.16 Thu Mar 2 08:58:29 2023
+++ pkgsrc/pkgtools/pkglint/files/mkcondchecker.go Wed Mar 29 07:12:36 2023
@@ -241,8 +241,10 @@ func (ck *MkCondChecker) checkCompareWit
op != "==" && op != "!=" &&
matches(num, `^\d+$`) {
- ck.MkLine.Errorf("The Python version must not be compared numerically.")
- ck.MkLine.Explain(
+ fixedNum := replaceAll(num, `^([0-9])([0-9])$`, `${1}0$2`)
+ fix := ck.MkLine.Autofix()
+ fix.Errorf("_PYTHON_VERSION must not be compared numerically.")
+ fix.Explain(
"The variable _PYTHON_VERSION must not be compared",
"against an integer number, as these comparisons are",
"not meaningful.",
@@ -252,6 +254,8 @@ func (ck *MkCondChecker) checkCompareWit
"",
"In addition, _PYTHON_VERSION can be \"none\",",
"which is not a number.")
+ fix.Replace("${_PYTHON_VERSION} "+op+" "+num, "${PYTHON_VERSION} "+op+" "+fixedNum)
+ fix.Apply()
}
}
Index: pkgsrc/pkgtools/pkglint/files/mkvarusechecker_test.go
diff -u pkgsrc/pkgtools/pkglint/files/mkvarusechecker_test.go:1.16 pkgsrc/pkgtools/pkglint/files/mkvarusechecker_test.go:1.17
--- pkgsrc/pkgtools/pkglint/files/mkvarusechecker_test.go:1.16 Fri Jun 24 07:16:23 2022
+++ pkgsrc/pkgtools/pkglint/files/mkvarusechecker_test.go Wed Mar 29 07:12:36 2023
@@ -157,7 +157,7 @@ func (s *Suite) Test_MkVarUseChecker_Che
// do this. In packages though, LOCALBASE is deprecated.
// There is no warning about DEFAULT_PREFIX being "defined but not used"
- // since Pkgsrc.loadUntypedVars calls Pkgsrc.vartypes.DefineType, which
+ // since Pkgsrc.loadUntypedVars calls Pkgsrc.Types().DefineType, which
// registers that variable globally.
t.CheckOutputEmpty()
}
Index: pkgsrc/pkgtools/pkglint/files/mkcondchecker_test.go
diff -u pkgsrc/pkgtools/pkglint/files/mkcondchecker_test.go:1.15 pkgsrc/pkgtools/pkglint/files/mkcondchecker_test.go:1.16
--- pkgsrc/pkgtools/pkglint/files/mkcondchecker_test.go:1.15 Thu Mar 2 08:58:29 2023
+++ pkgsrc/pkgtools/pkglint/files/mkcondchecker_test.go Wed Mar 29 07:12:36 2023
@@ -256,6 +256,14 @@ func (s *Suite) Test_MkCondChecker_Check
"!empty(MACHINE_PLATFORM:MNetBSD-9.99.*) && "+
"!empty(MACHINE_PLATFORM:MNetBSD-[1-9][0-9].*)",
".endif"),
+ "NOTE: filename.mk:5:"+
+ " \"!empty(MACHINE_PLATFORM:MNetBSD-9.99.*)\" "+
+ "can be simplified to "+
+ "\"${MACHINE_PLATFORM:MNetBSD-9.99.*}\".",
+ "NOTE: filename.mk:5: "+
+ "\"!empty(MACHINE_PLATFORM:MNetBSD-[1-9][0-9].*)\" "+
+ "can be simplified to "+
+ "\"${MACHINE_PLATFORM:MNetBSD-[1-9][0-9].*}\".",
"ERROR: filename.mk:5: The patterns \"NetBSD-9.99.*\" "+
"and \"NetBSD-[1-9][0-9].*\" cannot match at the same time.")
@@ -620,7 +628,7 @@ func (s *Suite) Test_MkCondChecker_check
t.CheckOutputLines(
"WARN: filename.mk:4: Numeric comparison > 6.5.",
- "ERROR: filename.mk:4: The Python version must not be compared numerically.",
+ "ERROR: filename.mk:4: _PYTHON_VERSION must not be compared numerically.",
"WARN: filename.mk:4: _PYTHON_VERSION is used but not defined.")
}
@@ -648,6 +656,7 @@ func (s *Suite) Test_MkCondChecker_check
func (s *Suite) Test_MkCondChecker_checkCompareWithNumPython(c *check.C) {
t := s.Init(c)
+ t.SetUpCommandLine("--show-autofix")
mklines := t.NewMkLines("filename.mk",
MkCvsID,
"",
@@ -661,14 +670,16 @@ func (s *Suite) Test_MkCondChecker_check
mklines.Check()
t.CheckOutputLines(
- "WARN: filename.mk:3: "+
- "Variable names starting with an underscore "+
- "(_PYTHON_VERSION) are reserved "+
- "for internal pkgsrc use.",
"ERROR: filename.mk:5: "+
- "The Python version must not be compared numerically.",
+ "_PYTHON_VERSION must not be compared numerically.",
+ "AUTOFIX: filename.mk:5: "+
+ "Replacing \"${_PYTHON_VERSION} < 38\" "+
+ "with \"${PYTHON_VERSION} < 308\".",
"ERROR: filename.mk:6: "+
- "The Python version must not be compared numerically.")
+ "_PYTHON_VERSION must not be compared numerically.",
+ "AUTOFIX: filename.mk:6: "+
+ "Replacing \"${_PYTHON_VERSION} < 310\" "+
+ "with \"${PYTHON_VERSION} < 310\".")
}
func (s *Suite) Test_MkCondChecker_checkCompareVarStrCompiler(c *check.C) {
Index: pkgsrc/pkgtools/pkglint/files/mkvarusechecker.go
diff -u pkgsrc/pkgtools/pkglint/files/mkvarusechecker.go:1.15 pkgsrc/pkgtools/pkglint/files/mkvarusechecker.go:1.16
--- pkgsrc/pkgtools/pkglint/files/mkvarusechecker.go:1.15 Thu Mar 2 08:58:29 2023
+++ pkgsrc/pkgtools/pkglint/files/mkvarusechecker.go Wed Mar 29 07:12:36 2023
@@ -61,7 +61,7 @@ func (ck *MkVarUseChecker) checkUndefine
ck.MkLines.allVars.Mentioned(varname) != nil,
ck.MkLines.pkg != nil && ck.MkLines.pkg.vars.IsDefinedSimilar(varname),
containsVarUse(varname),
- G.Pkgsrc.vartypes.IsDefinedCanon(varname),
+ G.Project.Types().IsDefinedCanon(varname),
varname == "":
return
}
@@ -837,10 +837,7 @@ func (ck *MkVarUseChecker) checkBuildDef
func (ck *MkVarUseChecker) checkDeprecated() {
varname := ck.use.varname
- instead := G.Pkgsrc.Deprecated[varname]
- if instead == "" {
- instead = G.Pkgsrc.Deprecated[varnameCanon(varname)]
- }
+ instead := G.Project.Deprecated(varname)
if instead == "" {
return
}
Index: pkgsrc/pkgtools/pkglint/files/mkcondsimplifier.go
diff -u pkgsrc/pkgtools/pkglint/files/mkcondsimplifier.go:1.4 pkgsrc/pkgtools/pkglint/files/mkcondsimplifier.go:1.5
--- pkgsrc/pkgtools/pkglint/files/mkcondsimplifier.go:1.4 Sun Jan 29 13:36:31 2023
+++ pkgsrc/pkgtools/pkglint/files/mkcondsimplifier.go Wed Mar 29 07:12:36 2023
@@ -1,6 +1,7 @@
package pkglint
import (
+ "netbsd.org/pkglint/makepat"
"netbsd.org/pkglint/textproc"
"strings"
)
@@ -282,13 +283,18 @@ func (s *MkCondSimplifier) simplifyMatch
//
// The same reasoning applies to the variable name, even though the
// variable name typically only uses a restricted character set.
- if !matches(varuse.Mod(), `^[*.:\w\[\]]+$`) {
+ if !matches(varuse.Mod(), `^[*+\-.:\w\[\]]+$`) {
+ return
+ }
+
+ mayMatchNumber, err := s.mayMatchNumber(pattern)
+ if err != nil {
return
}
fixedPart := varname + modsExceptLast + ":M" + pattern
from := condStr(neg, "!", "") + "empty(" + fixedPart + ")"
- to := condStr(neg, "", "!") + "${" + fixedPart + "}"
+ to := condStr(neg, "", "!") + "${" + fixedPart + "}" + condStr(mayMatchNumber, " != \"\"", "")
fix := s.MkLine.Autofix()
fix.Notef("%q can be simplified to %q.", from, to)
@@ -318,3 +324,28 @@ func (s *MkCondSimplifier) isDefined(var
vartype.Union().Contains(aclpUseLoadtime) &&
vartype.IsDefinedIfInScope()
}
+
+var numeric = makepat.Number()
+
+// In the conditional '.if ${EXPR}', the condition '${EXPR}' may evaluate to
+// a single word. If that word is numeric and evaluates to zero, the condition
+// evaluates to false.
+//
+// There are several words that evaluate to zero, such as 0, .0, 0.0, 1e-400,
+// -1e-400, +1e400.
+//
+// When simplifying the condition '!empty(EXPR:Mpattern)' to
+// '${EXPR:Mpattern}', if the pattern can result in a numeric word, the
+// result must be compared with an empty string by adding '!= ""', to
+// preserve the exact behavior.
+func (*MkCondSimplifier) mayMatchNumber(pattern string) (bool, error) {
+ if pattern == "" {
+ return false, nil
+ }
+ p, err := makepat.Compile(pattern)
+ if err != nil {
+ return true, err
+ }
+ both := makepat.Intersect(p, numeric)
+ return both.CanMatch(), nil
+}
Index: pkgsrc/pkgtools/pkglint/files/mkcondsimplifier_test.go
diff -u pkgsrc/pkgtools/pkglint/files/mkcondsimplifier_test.go:1.2 pkgsrc/pkgtools/pkglint/files/mkcondsimplifier_test.go:1.3
--- pkgsrc/pkgtools/pkglint/files/mkcondsimplifier_test.go:1.2 Sat Nov 19 10:51:07 2022
+++ pkgsrc/pkgtools/pkglint/files/mkcondsimplifier_test.go Wed Mar 29 07:12:36 2023
@@ -3,6 +3,7 @@ package pkglint
import (
"gopkg.in/check.v1"
"netbsd.org/pkglint/regex"
+ "testing"
)
type MkCondSimplifierTester struct {
@@ -877,6 +878,61 @@ func (s *Suite) Test_MkCondSimplifier_si
"AUTOFIX: filename.mk:6: "+
"Replacing \"!empty(IN_SCOPE_DEFINED:M[Nn][Oo])\" "+
"with \"${IN_SCOPE_DEFINED:tl} == no\".")
+
+ // When replacing the '!empty' with a plain expression, there's an
+ // edge case that differs in behavior. A condition evaluates to
+ // false if its expression value has a single word, that word is
+ // numeric and evaluates to 0.0. Since make parses scientific
+ // notation such as 12345e-400, even numbers that contain nonzero
+ // digits may evaluate to false.
+ t.testBeforeAndAfterPrefs(
+ ".if !empty(IN_SCOPE_DEFINED:M[0-9]*)",
+ ".if ${IN_SCOPE_DEFINED:M[0-9]*} != \"\"",
+ "NOTE: filename.mk:6: "+
+ "\"!empty(IN_SCOPE_DEFINED:M[0-9]*)\" "+
+ "can be simplified to "+
+ "\"${IN_SCOPE_DEFINED:M[0-9]*} != \\\"\\\"\".",
+ "AUTOFIX: filename.mk:6: "+
+ "Replacing \"!empty(IN_SCOPE_DEFINED:M[0-9]*)\" "+
+ "with \"${IN_SCOPE_DEFINED:M[0-9]*} != \\\"\\\"\".")
+
+ // The pattern '[0123456789]' is equivalent to '[0-9]'.
+ t.testBeforeAndAfterPrefs(
+ ".if !empty(IN_SCOPE_DEFINED:M[0123456789]*)",
+ ".if ${IN_SCOPE_DEFINED:M[0123456789]*} != \"\"",
+ "NOTE: filename.mk:6: "+
+ "\"!empty(IN_SCOPE_DEFINED:M[0123456789]*)\" "+
+ "can be simplified to "+
+ "\"${IN_SCOPE_DEFINED:M[0123456789]*} != \\\"\\\"\".",
+ "AUTOFIX: filename.mk:6: "+
+ "Replacing \"!empty(IN_SCOPE_DEFINED:M[0123456789]*)\" "+
+ "with \"${IN_SCOPE_DEFINED:M[0123456789]*} != \\\"\\\"\".")
+
+ // The 'e' may be part of a number, but there is no number that
+ // consists of letters only, therefore the '!= ""' is not necessary.
+ t.testBeforeAndAfterPrefs(
+ ".if !empty(IN_SCOPE_DEFINED:M[abcdef]*)",
+ ".if ${IN_SCOPE_DEFINED:M[abcdef]*}",
+ "NOTE: filename.mk:6: "+
+ "\"!empty(IN_SCOPE_DEFINED:M[abcdef]*)\" "+
+ "can be simplified to "+
+ "\"${IN_SCOPE_DEFINED:M[abcdef]*}\".",
+ "AUTOFIX: filename.mk:6: "+
+ "Replacing \"!empty(IN_SCOPE_DEFINED:M[abcdef]*)\" "+
+ "with \"${IN_SCOPE_DEFINED:M[abcdef]*}\".")
+
+ // The letters 'abcd' may form part of a hex number, but there is no
+ // number that starts with any of these letters.
+ t.testBeforeAndAfterPrefs(
+ ".if !empty(IN_SCOPE_DEFINED:M[abcd]*)",
+ ".if ${IN_SCOPE_DEFINED:M[abcd]*}",
+ "NOTE: filename.mk:6: "+
+ "\"!empty(IN_SCOPE_DEFINED:M[abcd]*)\" "+
+ "can be simplified to "+
+ "\"${IN_SCOPE_DEFINED:M[abcd]*}\".",
+ "AUTOFIX: filename.mk:6: "+
+ "Replacing \"!empty(IN_SCOPE_DEFINED:M[abcd]*)\" "+
+ "with \"${IN_SCOPE_DEFINED:M[abcd]*}\".")
}
func (s *Suite) Test_MkCondSimplifier_isDefined(c *check.C) {
@@ -902,3 +958,43 @@ func (s *Suite) Test_MkCondSimplifier_is
t.CheckOutputLines(
"WARN: filename.mk:3: UNDEFINED is used but not defined.")
}
+
+func Test_MkCondSimplifier_mayMatchNumber(t *testing.T) {
+ tests := []struct {
+ pattern string
+ want bool
+ }{
+ // The empty pattern matches only a single word, namely the
+ // empty string, and that word is not numeric. Except for the
+ // workaround in make's TryParseNumber, which treats the
+ // empty string as zero, for undocumented reasons.
+ {"", false},
+ // Can only match '0', whose numeric value is zero.
+ {"0", true},
+ // Can only match '1', whose numeric value is not zero.
+ {"1", true},
+ // Can match '0', '00000', and so on.
+ {"[0-9]", true},
+ // A pattern consisting of only nonzero digits cannot match a
+ // number whose numeric value is zero.
+ {"[1-9]", true},
+ // Can match '0', '0x0', '0e0', '0e1000'.
+ {"[0-9a-z]", true},
+ // Each number has at least one digit, for example, '.e+' is
+ // not syntactically valid.
+ {"[^0-9]", false},
+ // No word that ends in '.c' is numeric.
+ {"*.c", false},
+ }
+ for _, tt := range tests {
+ t.Run(tt.pattern, func(t *testing.T) {
+ got, err := (*MkCondSimplifier).mayMatchNumber(nil, tt.pattern)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got != tt.want {
+ t.Errorf("got %v for %q, want %v", got, tt.pattern, tt.want)
+ }
+ })
+ }
+}
Index: pkgsrc/pkgtools/pkglint/files/mkline.go
diff -u pkgsrc/pkgtools/pkglint/files/mkline.go:1.87 pkgsrc/pkgtools/pkglint/files/mkline.go:1.88
--- pkgsrc/pkgtools/pkglint/files/mkline.go:1.87 Thu Mar 2 08:58:29 2023
+++ pkgsrc/pkgtools/pkglint/files/mkline.go Wed Mar 29 07:12:36 2023
@@ -1342,8 +1342,7 @@ func (ind *Indentation) CheckFinish(file
// which would confuse the devel/bmake parser.
//
// TODO: The allowed characters differ between the basename and the parameter
-//
-// of the variable. The square bracket is only allowed in the parameter part.
+// of the variable. The square bracket is only allowed in the parameter part.
var (
// TODO: remove the ','
VarbaseBytes = textproc.NewByteSet("A-Za-z_0-9-+,")
Index: pkgsrc/pkgtools/pkglint/files/mkline_test.go
diff -u pkgsrc/pkgtools/pkglint/files/mkline_test.go:1.86 pkgsrc/pkgtools/pkglint/files/mkline_test.go:1.87
--- pkgsrc/pkgtools/pkglint/files/mkline_test.go:1.86 Thu Mar 2 08:58:29 2023
+++ pkgsrc/pkgtools/pkglint/files/mkline_test.go Wed Mar 29 07:12:36 2023
@@ -643,8 +643,8 @@ func (s *Suite) Test_MkLine_VariableNeed
"MASTER_SITES=\t${HOMEPAGE}")
mkline := mklines.mklines[1]
- vuc := VarUseContext{G.Pkgsrc.vartypes.Canon("MASTER_SITES"), VucRunTime, VucQuotPlain, false}
- nq := mkline.VariableNeedsQuoting(nil, NewMkVarUse("HOMEPAGE"), G.Pkgsrc.vartypes.Canon("HOMEPAGE"), &vuc)
+ vuc := VarUseContext{G.Pkgsrc.Types().Canon("MASTER_SITES"), VucRunTime, VucQuotPlain, false}
+ nq := mkline.VariableNeedsQuoting(nil, NewMkVarUse("HOMEPAGE"), G.Pkgsrc.Types().Canon("HOMEPAGE"), &vuc)
t.CheckEquals(nq, no)
@@ -1025,8 +1025,7 @@ func (s *Suite) Test_MkLine_VariableNeed
}
// TODO: COMPILER_RPATH_FLAG and LINKER_RPATH_FLAG have different types
-//
-// defined in vardefs.go; examine why.
+// defined in vardefs.go; examine why.
func (s *Suite) Test_MkLine_VariableNeedsQuoting__shellword_part(c *check.C) {
t := s.Init(c)
Index: pkgsrc/pkgtools/pkglint/files/mklines.go
diff -u pkgsrc/pkgtools/pkglint/files/mklines.go:1.77 pkgsrc/pkgtools/pkglint/files/mklines.go:1.78
--- pkgsrc/pkgtools/pkglint/files/mklines.go:1.77 Thu Mar 2 08:58:29 2023
+++ pkgsrc/pkgtools/pkglint/files/mklines.go Wed Mar 29 07:12:36 2023
@@ -426,7 +426,9 @@ func (mklines *MkLines) checkAll() {
"pre-package": true, "do-package": true, "post-package": true,
"pre-clean": true, "do-clean": true, "post-clean": true}
- mklines.lines.CheckCvsID(0, `#[\t ]+`, "# ")
+ if G.Pkgsrc != nil {
+ mklines.lines.CheckCvsID(0, `#[\t ]+`, "# ")
+ }
substContext := NewSubstContext(mklines.pkg)
var varalign VaralignBlock
Index: pkgsrc/pkgtools/pkglint/files/pkglint.go
diff -u pkgsrc/pkgtools/pkglint/files/pkglint.go:1.88 pkgsrc/pkgtools/pkglint/files/pkglint.go:1.89
--- pkgsrc/pkgtools/pkglint/files/pkglint.go:1.88 Thu Mar 2 08:58:29 2023
+++ pkgsrc/pkgtools/pkglint/files/pkglint.go Wed Mar 29 07:12:36 2023
@@ -32,7 +32,8 @@ type Pkglint struct {
Network,
Recursive bool
- Pkgsrc *Pkgsrc // Global data, mostly extracted from mk/*.
+ Project Project
+ Pkgsrc *Pkgsrc // Global data, mostly extracted from mk/*.
Todo CurrPathQueue // The files or directories that still need to be checked.
@@ -205,10 +206,12 @@ func (p *Pkglint) prepareMainLoop() {
} else {
G.Logger.TechFatalf(firstDir, "Must be inside a pkgsrc tree.")
}
+ p.Project = NewNetBSDProject()
} else {
p.Pkgsrc = NewPkgsrc(firstDir.JoinNoClean(relTopdir))
p.Wip = p.Pkgsrc.IsWip(firstDir) // See Pkglint.checkMode.
p.Pkgsrc.LoadInfrastructure()
+ p.Project = p.Pkgsrc
}
currentUser, err := user.Current()
Index: pkgsrc/pkgtools/pkglint/files/pkgsrc.go
diff -u pkgsrc/pkgtools/pkglint/files/pkgsrc.go:1.70 pkgsrc/pkgtools/pkglint/files/pkgsrc.go:1.71
--- pkgsrc/pkgtools/pkglint/files/pkgsrc.go:1.70 Thu Mar 2 08:58:29 2023
+++ pkgsrc/pkgtools/pkglint/files/pkgsrc.go Wed Mar 29 07:12:36 2023
@@ -43,8 +43,8 @@ type Pkgsrc struct {
// to BUILD_DEFS.
UserDefinedVars Scope
- Deprecated map[string]string
- vartypes VarTypeRegistry
+ deprecated map[string]string
+ types VarTypeRegistry
}
func NewPkgsrc(dir CurrPath) *Pkgsrc {
@@ -72,7 +72,7 @@ func NewPkgsrc(dir CurrPath) *Pkgsrc {
// simple, since setting up a realistic pkgsrc environment requires
// a lot of files.
func (src *Pkgsrc) LoadInfrastructure() {
- src.vartypes.Init(src)
+ src.Types().Init(src)
src.loadMasterSites()
src.loadPkgOptions()
src.changes.load(src)
@@ -339,7 +339,7 @@ func (src *Pkgsrc) addBuildDefs(varnames
}
func (src *Pkgsrc) initDeprecatedVars() {
- src.Deprecated = map[string]string{
+ src.deprecated = map[string]string{
// December 2003
"FIX_RPATH": "It has been removed from pkgsrc in 2003.",
@@ -513,7 +513,7 @@ func (src *Pkgsrc) loadUntypedVars() {
define := func(varcanon string, mkline *MkLine) {
switch {
- case src.vartypes.IsDefinedCanon(varcanon):
+ case src.Types().IsDefinedCanon(varcanon):
// Already defined, can also be a tool.
case !matches(varcanon, `^[A-Z]`):
@@ -534,7 +534,7 @@ func (src *Pkgsrc) loadUntypedVars() {
if trace.Tracing {
trace.Stepf("Untyped variable %q in %s", varcanon, mkline)
}
- src.vartypes.DefineType(varcanon, unknownType)
+ src.Types().DefineType(varcanon, unknownType)
}
}
@@ -640,6 +640,18 @@ func (src *Pkgsrc) loadDefaultBuildDefs(
"USE_ABI_DEPENDS")
}
+func (src *Pkgsrc) Deprecated(varname string) string {
+ deprecated := src.deprecated
+ if instead := deprecated[varname]; instead != "" {
+ return instead
+ }
+ return deprecated[varnameCanon(varname)]
+}
+
+func (src *Pkgsrc) Types() *VarTypeRegistry {
+ return &src.types
+}
+
// Latest returns the latest package matching the given pattern.
// It searches the category for subdirectories matching the given
// regular expression, takes the latest of them and replaces its
@@ -746,7 +758,7 @@ func (src *Pkgsrc) VariableType(mklines
// When scanning mk/** for otherwise unknown variables, their type
// is set to BtUnknown. These variables must not override the guess
// based on the variable name.
- vartype = src.vartypes.Canon(varname)
+ vartype = src.Types().Canon(varname)
if vartype != nil && vartype.basicType != BtUnknown {
return vartype
}
@@ -832,7 +844,7 @@ func (src *Pkgsrc) guessVariableType(var
// must take precedence over this rule, because otherwise, list
// variables from the infrastructure would be guessed to be plain
// variables.
- vartype = src.vartypes.Canon(varname)
+ vartype = src.Types().Canon(varname)
if vartype != nil {
return vartype
}
Index: pkgsrc/pkgtools/pkglint/files/scope.go
diff -u pkgsrc/pkgtools/pkglint/files/scope.go:1.3 pkgsrc/pkgtools/pkglint/files/scope.go:1.4
--- pkgsrc/pkgtools/pkglint/files/scope.go:1.3 Sun Jan 29 13:36:31 2023
+++ pkgsrc/pkgtools/pkglint/files/scope.go Wed Mar 29 07:12:36 2023
@@ -6,13 +6,11 @@ import "sort"
// in a certain scope, such as a package or a file.
//
// TODO: Decide whether the scope should consider variable assignments
-//
-// from the pkgsrc infrastructure. For Package.checkGnuConfigureUseLanguages
-// it would be better to ignore them completely.
+// from the pkgsrc infrastructure. For Package.checkGnuConfigureUseLanguages
+// it would be better to ignore them completely.
//
// TODO: Merge this code with Var, which defines essentially the
-//
-// same features.
+// same features.
//
// See also substScope, which already analyzes the possible variable values
// based on the conditional code paths.
Index: pkgsrc/pkgtools/pkglint/files/var.go
diff -u pkgsrc/pkgtools/pkglint/files/var.go:1.11 pkgsrc/pkgtools/pkglint/files/var.go:1.12
--- pkgsrc/pkgtools/pkglint/files/var.go:1.11 Thu Mar 2 08:58:29 2023
+++ pkgsrc/pkgtools/pkglint/files/var.go Wed Mar 29 07:12:36 2023
@@ -92,9 +92,8 @@ func (v *Var) AddRef(varname string) {
// cases may be implemented later.
//
// TODO: Simple .for loops that append to the variable are ok as well.
-//
-// (This needs to be worded more precisely since that part potentially
-// adds a lot of complexity to the whole data structure.)
+// (This needs to be worded more precisely since that part potentially
+// adds a lot of complexity to the whole data structure.)
//
// Variable assignments in the pkgsrc infrastructure are taken into account
// for determining whether a variable is constant.
Index: pkgsrc/pkgtools/pkglint/files/varalignblock_test.go
diff -u pkgsrc/pkgtools/pkglint/files/varalignblock_test.go:1.19 pkgsrc/pkgtools/pkglint/files/varalignblock_test.go:1.20
--- pkgsrc/pkgtools/pkglint/files/varalignblock_test.go:1.19 Sun Jan 29 13:36:31 2023
+++ pkgsrc/pkgtools/pkglint/files/varalignblock_test.go Wed Mar 29 07:12:36 2023
@@ -1963,8 +1963,7 @@ func (s *Suite) Test_VaralignBlock__var_
// initial line, this is intentional.
//
// TODO: Make this rule more general: if the indentation of the continuation
-//
-// lines is more than the initial line, it is intentional.
+// lines is more than the initial line, it is intentional.
func (s *Suite) Test_VaralignBlock__var_tab24_value_space_cont_tabs32_value_space_cont_tabs32_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
Index: pkgsrc/pkgtools/pkglint/files/vardefs.go
diff -u pkgsrc/pkgtools/pkglint/files/vardefs.go:1.107 pkgsrc/pkgtools/pkglint/files/vardefs.go:1.108
--- pkgsrc/pkgtools/pkglint/files/vardefs.go:1.107 Sun Jan 29 13:36:31 2023
+++ pkgsrc/pkgtools/pkglint/files/vardefs.go Wed Mar 29 07:12:36 2023
@@ -36,6 +36,8 @@ func NewVarTypeRegistry() VarTypeRegistr
return VarTypeRegistry{make(map[string]*Vartype), make(map[string][]ACLEntry)}
}
+// Canon looks up the type of the variable, first by trying the exact
+// variable name, then by trying the canonicalized variable name.
func (reg *VarTypeRegistry) Canon(varname string) *Vartype {
vartype := reg.types[varname]
if vartype == nil {
@@ -101,9 +103,8 @@ func (reg *VarTypeRegistry) DefineName(v
// - which packages need this custom permission set.
//
// TODO: When prefixed with "infra:", the entry should only
-//
-// apply to files within the pkgsrc infrastructure. Without this prefix,
-// the pattern should only apply to files outside the pkgsrc infrastructure.
+// apply to files within the pkgsrc infrastructure. Without this prefix,
+// the pattern should only apply to files outside the pkgsrc infrastructure.
func (reg *VarTypeRegistry) acl(varname string, basicType *BasicType, options vartypeOptions, aclEntries ...string) {
parsedEntries := reg.parseACLEntries(varname, aclEntries...)
reg.Define(varname, basicType, options, parsedEntries)
@@ -209,8 +210,7 @@ func (reg *VarTypeRegistry) pkglistbl3ra
// when these files are included.
//
// TODO: These timing issues should be handled separately from the permissions.
-//
-// They can be made more precise.
+// They can be made more precise.
func (reg *VarTypeRegistry) sys(varname string, basicType *BasicType, options ...vartypeOptions) {
reg.DefineName(varname, basicType, reg.options(SystemProvided, options), "sys")
}
Index: pkgsrc/pkgtools/pkglint/files/vardefs_test.go
diff -u pkgsrc/pkgtools/pkglint/files/vardefs_test.go:1.30 pkgsrc/pkgtools/pkglint/files/vardefs_test.go:1.31
--- pkgsrc/pkgtools/pkglint/files/vardefs_test.go:1.30 Thu Mar 2 08:58:29 2023
+++ pkgsrc/pkgtools/pkglint/files/vardefs_test.go Wed Mar 29 07:12:36 2023
@@ -161,7 +161,7 @@ func (s *Suite) Test_VarTypeRegistry_enu
t.ExpectFatal(
func() {
- G.Pkgsrc.vartypes.enumFromDirs(
+ G.Project.Types().enumFromDirs(
G.Pkgsrc, "category", `^pack.*`, "$0", "default")
},
"FATAL: ~/category: Must contain at least 1 "+
@@ -193,7 +193,7 @@ func (s *Suite) Test_VarTypeRegistry_enu
t.ExpectFatal(
func() {
- G.Pkgsrc.vartypes.enumFromFiles(G.Pkgsrc,
+ G.Project.Types().enumFromFiles(G.Pkgsrc,
"mk/platform", `^(\w+)\.mk$`, "$1", "default")
},
"FATAL: ~/mk/platform: Must contain at least 1 "+
@@ -216,10 +216,10 @@ func (s *Suite) Test_VarTypeRegistry_Ini
t := s.Init(c)
src := NewPkgsrc(t.File("."))
- src.vartypes.Init(src)
+ src.Types().Init(src)
- t.CheckEquals(src.vartypes.Canon("BSD_MAKE_ENV").basicType.name, "ShellWord")
- t.CheckEquals(src.vartypes.Canon("USE_BUILTIN.*").basicType.name, "YesNoIndirectly")
+ t.CheckEquals(src.Types().Canon("BSD_MAKE_ENV").basicType.name, "ShellWord")
+ t.CheckEquals(src.Types().Canon("USE_BUILTIN.*").basicType.name, "YesNoIndirectly")
}
func (s *Suite) Test_VarTypeRegistry_Init__LP64PLATFORMS(c *check.C) {
Index: pkgsrc/pkgtools/pkglint/files/vartype.go
diff -u pkgsrc/pkgtools/pkglint/files/vartype.go:1.55 pkgsrc/pkgtools/pkglint/files/vartype.go:1.56
--- pkgsrc/pkgtools/pkglint/files/vartype.go:1.55 Sun Jan 29 13:36:31 2023
+++ pkgsrc/pkgtools/pkglint/files/vartype.go Wed Mar 29 07:12:36 2023
@@ -508,8 +508,7 @@ func init() {
}
// TODO: Move these values to VarTypeRegistry.Init and read them from the
-//
-// pkgsrc infrastructure files, as far as possible.
+// pkgsrc infrastructure files, as far as possible.
const (
// See mk/emulator/emulator-vars.mk.
emulOpsysValues = "" +
Index: pkgsrc/pkgtools/pkglint/files/vartype_test.go
diff -u pkgsrc/pkgtools/pkglint/files/vartype_test.go:1.28 pkgsrc/pkgtools/pkglint/files/vartype_test.go:1.29
--- pkgsrc/pkgtools/pkglint/files/vartype_test.go:1.28 Sat Jan 1 12:44:25 2022
+++ pkgsrc/pkgtools/pkglint/files/vartype_test.go Wed Mar 29 07:12:36 2023
@@ -54,14 +54,14 @@ func (s *Suite) Test_Vartype_EffectivePe
t.SetUpVartypes()
- if typ := G.Pkgsrc.vartypes.Canon("PREFIX"); t.CheckNotNil(typ) {
+ if typ := G.Pkgsrc.Types().Canon("PREFIX"); t.CheckNotNil(typ) {
t.CheckEquals(typ.basicType.name, "Pathname")
t.CheckDeepEquals(typ.aclEntries, []ACLEntry{NewACLEntry("*", aclpUse)})
t.CheckEquals(typ.EffectivePermissions("Makefile"), aclpUse)
t.CheckEquals(typ.EffectivePermissions("buildlink3.mk"), aclpUse)
}
- if typ := G.Pkgsrc.vartypes.Canon("EXTRACT_OPTS"); t.CheckNotNil(typ) {
+ if typ := G.Pkgsrc.Types().Canon("EXTRACT_OPTS"); t.CheckNotNil(typ) {
t.CheckEquals(typ.basicType.name, "ShellWord")
t.CheckEquals(typ.EffectivePermissions("Makefile"), aclpAllWrite|aclpUse)
t.CheckEquals(typ.EffectivePermissions("options.mk"), aclpAllWrite|aclpUse)
Index: pkgsrc/pkgtools/pkglint/files/vartypecheck.go
diff -u pkgsrc/pkgtools/pkglint/files/vartypecheck.go:1.103 pkgsrc/pkgtools/pkglint/files/vartypecheck.go:1.104
--- pkgsrc/pkgtools/pkglint/files/vartypecheck.go:1.103 Sun Jan 29 13:36:31 2023
+++ pkgsrc/pkgtools/pkglint/files/vartypecheck.go Wed Mar 29 07:12:36 2023
@@ -847,7 +847,7 @@ func (cv *VartypeCheck) MachinePlatformP
// provided in a type registry where they could be looked up
// by a name. The following line therefore assumes that OPSYS
// is an operating system name, which sounds like a safe bet.
- btOpsys := G.Pkgsrc.vartypes.types["OPSYS"].basicType
+ btOpsys := G.Pkgsrc.Types().types["OPSYS"].basicType
opsysCv := cv.WithVarnameValueMatch("the operating system part of "+cv.Varname, opsysPattern)
btOpsys.checker(opsysCv)
Index: pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go
diff -u pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go:1.95 pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go:1.96
--- pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go:1.95 Sun Jan 29 13:36:31 2023
+++ pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go Wed Mar 29 07:12:36 2023
@@ -821,7 +821,7 @@ func (s *Suite) Test_VartypeCheck_EmulPl
func (s *Suite) Test_VartypeCheck_Enum(c *check.C) {
basicType := enum("jdk1 jdk2 jdk4")
- G.Pkgsrc.vartypes.Define("JDK", basicType, UserSettable, []ACLEntry{})
+ G.Pkgsrc.Types().Define("JDK", basicType, UserSettable, []ACLEntry{})
vt := NewVartypeCheckTester(s.Init(c), basicType)
vt.Varname("JDK")
Index: pkgsrc/pkgtools/pkglint/files/makepat/pat.go
diff -u pkgsrc/pkgtools/pkglint/files/makepat/pat.go:1.4 pkgsrc/pkgtools/pkglint/files/makepat/pat.go:1.5
--- pkgsrc/pkgtools/pkglint/files/makepat/pat.go:1.4 Sun Jan 29 13:36:32 2023
+++ pkgsrc/pkgtools/pkglint/files/makepat/pat.go Wed Mar 29 07:12:36 2023
@@ -19,16 +19,16 @@ type state struct {
type transition struct {
min, max byte
- to StateID
+ to stateID
}
-type StateID uint16
+type stateID uint16
// Compile parses a pattern, including the error checking that is missing
// from bmake.
func Compile(pattern string) (*Pattern, error) {
var p Pattern
- s := p.AddState(false)
+ s := p.addState(false)
lex := textproc.NewLexer(pattern)
for !lex.EOF() {
@@ -36,18 +36,18 @@ func Compile(pattern string) (*Pattern,
switch ch {
case '*':
- p.AddTransition(s, 0, 255, s)
+ p.addTransition(s, 0, 255, s)
case '?':
- next := p.AddState(false)
- p.AddTransition(s, 0, 255, next)
+ next := p.addState(false)
+ p.addTransition(s, 0, 255, next)
s = next
case '\\':
if lex.EOF() {
return nil, errors.New("unfinished escape sequence")
}
ch := lex.NextByte()
- next := p.AddState(false)
- p.AddTransition(s, ch, ch, next)
+ next := p.addState(false)
+ p.addTransition(s, ch, ch, next)
s = next
case '[':
next, err := compileCharClass(&p, lex, ch, s)
@@ -56,8 +56,8 @@ func Compile(pattern string) (*Pattern,
}
s = next
default:
- next := p.AddState(false)
- p.AddTransition(s, ch, ch, next)
+ next := p.addState(false)
+ p.addTransition(s, ch, ch, next)
s = next
}
}
@@ -66,10 +66,10 @@ func Compile(pattern string) (*Pattern,
return &p, nil
}
-func compileCharClass(p *Pattern, lex *textproc.Lexer, ch byte, s StateID) (StateID, error) {
+func compileCharClass(p *Pattern, lex *textproc.Lexer, ch byte, s stateID) (stateID, error) {
negate := lex.SkipByte('^')
- chars := make([]bool, 256)
- next := p.AddState(false)
+ var chars [256]bool
+ next := p.addState(false)
for {
if lex.EOF() {
return 0, errors.New("unfinished character class")
@@ -99,6 +99,11 @@ func compileCharClass(p *Pattern, lex *t
}
}
+ p.addTransitions(s, &chars, next)
+ return next, nil
+}
+
+func (p *Pattern) addTransitions(from stateID, chars *[256]bool, to stateID) {
start := 0
for start < len(chars) && !chars[start] {
start++
@@ -111,7 +116,7 @@ func compileCharClass(p *Pattern, lex *t
}
if start < end {
- p.AddTransition(s, byte(start), byte(end-1), next)
+ p.addTransition(from, byte(start), byte(end-1), to)
}
start = end
@@ -119,15 +124,14 @@ func compileCharClass(p *Pattern, lex *t
start++
}
}
- return next, nil
}
-func (p *Pattern) AddState(end bool) StateID {
+func (p *Pattern) addState(end bool) stateID {
p.states = append(p.states, state{nil, end})
- return StateID(len(p.states) - 1)
+ return stateID(len(p.states) - 1)
}
-func (p *Pattern) AddTransition(from StateID, min, max byte, to StateID) {
+func (p *Pattern) addTransition(from stateID, min, max byte, to stateID) {
state := &p.states[from]
state.transitions = append(state.transitions, transition{min, max, to})
}
@@ -178,15 +182,15 @@ func (p *Pattern) Match(s string) bool {
func Intersect(p1, p2 *Pattern) *Pattern {
var res Pattern
- newState := make(map[[2]StateID]StateID)
+ newState := make(map[[2]stateID]stateID)
// stateFor returns the state ID in the intersection,
// creating it if necessary.
- stateFor := func(s1, s2 StateID) StateID {
- key := [2]StateID{s1, s2}
+ stateFor := func(s1, s2 stateID) stateID {
+ key := [2]stateID{s1, s2}
ns, ok := newState[key]
if !ok {
- ns = res.AddState(p1.states[s1].end && p2.states[s2].end)
+ ns = res.addState(p1.states[s1].end && p2.states[s2].end)
newState[key] = ns
}
return ns
@@ -202,16 +206,18 @@ func Intersect(p1, p2 *Pattern) *Pattern
min := bmax(t1.min, t2.min)
max := bmin(t1.max, t2.max)
if min <= max {
- from := stateFor(StateID(i1), StateID(i2))
+ from := stateFor(stateID(i1), stateID(i2))
to := stateFor(t1.to, t2.to)
- res.AddTransition(from, min, max, to)
+ res.addTransition(from, min, max, to)
}
}
}
}
}
- return res.optimized()
+ // If the returned pattern is used more than once,
+ // consider calling .optimize first.
+ return &res
}
func (p *Pattern) optimized() *Pattern {
@@ -252,7 +258,7 @@ func (p *Pattern) reachable() []bool {
return reachable
}
-// relevant returns all states from which and end state is reachable.
+// relevant returns all states from which an end state is reachable.
// In optimized patterns, each state is relevant.
func (p *Pattern) relevant(reachable []bool) []bool {
relevant := make([]bool, len(p.states))
@@ -276,7 +282,7 @@ func (p *Pattern) relevant(reachable []b
changed = true
for from, st := range p.states {
for _, tr := range st.transitions {
- if tr.to == StateID(to) && reachable[from] &&
+ if tr.to == stateID(to) && reachable[from] &&
progress[from] == 0 {
progress[from] = 1
}
@@ -296,17 +302,17 @@ func (p *Pattern) relevant(reachable []b
func (p *Pattern) compressed(relevant []bool) *Pattern {
var opt Pattern
- newIDs := make([]StateID, len(p.states))
+ newIDs := make([]stateID, len(p.states))
for i, r := range relevant {
if r {
- newIDs[i] = opt.AddState(p.states[i].end)
+ newIDs[i] = opt.addState(p.states[i].end)
}
}
for from, s := range p.states {
for _, t := range s.transitions {
if relevant[from] && relevant[t.to] {
- opt.AddTransition(newIDs[from], t.min, t.max, newIDs[t.to])
+ opt.addTransition(newIDs[from], t.min, t.max, newIDs[t.to])
}
}
}
@@ -335,6 +341,145 @@ func (p *Pattern) CanMatch() bool {
return false
}
+// Number creates a pattern that matches integer or floating point constants,
+// as in C99, both decimal and hex.
+func Number() *Pattern {
+ // The states and transitions are taken from a manually constructed
+ // hand-drawn state diagram, based on the syntax rules from C99 6.4.4.
+
+ const (
+ start stateID = iota
+ sign
+
+ dec
+ decDot
+ decFrac
+
+ zero
+ zeroX
+
+ hex
+ hexDot
+ hexFrac
+
+ exp
+ expSign
+ expDec
+ )
+
+ return &Pattern{
+ states: []state{
+ start: {
+ []transition{
+ {'+', '+', sign},
+ {'-', '-', sign},
+ {'0', '9', dec},
+ {'.', '.', decDot},
+ {'0', '9', decFrac},
+ {'0', '0', zero},
+ },
+ false,
+ },
+ sign: {
+ []transition{
+ {'0', '9', dec},
+ {'.', '.', decDot},
+ {'0', '9', decFrac},
+ {'0', '0', zero},
+ },
+ false,
+ },
+ dec: {
+ []transition{
+ {'0', '9', dec},
+ {'.', '.', decFrac},
+ },
+ true,
+ },
+ decDot: {
+ []transition{
+ {'0', '9', decFrac},
+ },
+ false,
+ },
+ decFrac: {
+ []transition{
+ {'0', '9', decFrac},
+ {'E', 'E', exp},
+ {'e', 'e', exp},
+ },
+ true,
+ },
+ zero: {
+ []transition{
+ {'X', 'X', zeroX},
+ {'x', 'x', zeroX},
+ },
+ false,
+ },
+ zeroX: {
+ []transition{
+ {'0', '9', hex},
+ {'A', 'F', hex},
+ {'a', 'f', hex},
+ {'.', '.', hexDot},
+ {'0', '9', hexFrac},
+ {'A', 'F', hexFrac},
+ {'a', 'f', hexFrac},
+ },
+ false,
+ },
+ hex: {
+ []transition{
+ {'0', '9', hex},
+ {'A', 'F', hex},
+ {'a', 'f', hex},
+ {'.', '.', hexFrac},
+ },
+ true,
+ },
+ hexDot: {
+ []transition{
+ {'0', '9', hexFrac},
+ {'A', 'F', hexFrac},
+ {'a', 'f', hexFrac},
+ },
+ false,
+ },
+ hexFrac: {
+ []transition{
+ {'0', '9', hexFrac},
+ {'A', 'F', hexFrac},
+ {'a', 'f', hexFrac},
+ {'P', 'P', exp},
+ {'p', 'p', exp},
+ },
+ false,
+ },
+ exp: {
+ []transition{
+ {'+', '+', expSign},
+ {'-', '-', expSign},
+ {'0', '9', expDec},
+ },
+ false,
+ },
+ expSign: {
+ []transition{
+ {'0', '9', expDec},
+ },
+ false,
+ },
+ expDec: {
+ []transition{
+ {'0', '9', expDec},
+ },
+ true,
+ },
+ },
+ }
+}
+
func bmin(a, b byte) byte {
if a < b {
return a
Index: pkgsrc/pkgtools/pkglint/files/makepat/pat_test.go
diff -u pkgsrc/pkgtools/pkglint/files/makepat/pat_test.go:1.4 pkgsrc/pkgtools/pkglint/files/makepat/pat_test.go:1.5
--- pkgsrc/pkgtools/pkglint/files/makepat/pat_test.go:1.4 Sun Aug 8 22:04:15 2021
+++ pkgsrc/pkgtools/pkglint/files/makepat/pat_test.go Wed Mar 29 07:12:36 2023
@@ -2,6 +2,7 @@ package makepat
import (
"netbsd.org/pkglint/intqa"
+ "netbsd.org/pkglint/textproc"
"reflect"
"testing"
)
@@ -54,11 +55,57 @@ func Test_compileCharClass(t *testing.T)
}
}
-func Test_Pattern_AddState(t *testing.T) {
+func Test_Pattern_addTransitions(t *testing.T) {
+ none := textproc.NewByteSet("")
+ numeric := textproc.NewByteSet("-+0-9.Ee")
+ all := none.Inverse()
+
+ tests := []struct {
+ name string
+ bs *textproc.ByteSet
+ example byte
+ want bool
+ }{
+ {"none min", none, 0, false},
+ {"none max", none, 255, false},
+ {"all min", all, 0, true},
+ {"all max", all, 255, true},
+ {"numeric", numeric, '*', false},
+ {"numeric", numeric, '+', true},
+ {"numeric", numeric, ',', false},
+ {"numeric", numeric, '-', true},
+ {"numeric", numeric, '.', true},
+ {"numeric", numeric, '/', false},
+ {"numeric", numeric, '0', true},
+ {"numeric", numeric, '9', true},
+ {"numeric", numeric, ':', false},
+ {"numeric", numeric, 'D', false},
+ {"numeric", numeric, 'E', true},
+ {"numeric", numeric, 'F', false},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ var p Pattern
+ s0 := p.addState(false)
+ s1 := p.addState(true)
+ var chars [256]bool
+ for i := 0; i < 256; i++ {
+ chars[i] = tt.bs.Contains(byte(i))
+ }
+ p.addTransitions(s0, &chars, s1)
+ got := p.Match(string([]byte{tt.example}))
+ if got != tt.want {
+ t.Errorf("got %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func Test_Pattern_addState(t *testing.T) {
var p Pattern
- p.AddState(false)
- p.AddState(true)
+ p.addState(false)
+ p.addState(true)
expected := Pattern{states: []state{{nil, false}, {nil, true}}}
if !reflect.DeepEqual(p, expected) {
@@ -66,13 +113,13 @@ func Test_Pattern_AddState(t *testing.T)
}
}
-func Test_Pattern_AddTransition(t *testing.T) {
+func Test_Pattern_addTransition(t *testing.T) {
var p Pattern
- p.AddState(false)
- p.AddState(true)
- p.AddTransition(0, '0', '9', 1)
- p.AddTransition(1, '0', '9', 0)
+ p.addState(false)
+ p.addState(true)
+ p.addTransition(0, '0', '9', 1)
+ p.addTransition(1, '0', '9', 0)
expected := Pattern{states: []state{
{[]transition{{'0', '9', 1}}, false},
@@ -166,38 +213,63 @@ func Test_Pattern_Match(t *testing.T) {
}
func Test_Intersect(t *testing.T) {
+ // The state machine of the compiled patterns is more powerful than
+ // the string representation of the patterns, therefore the
+ // intersected pattern is not visualized. Instead, it is tested using
+ // a single example string.
tests := []struct {
pattern1 string
pattern2 string
- str string
- matches bool
canMatch bool
+ example string
+ matches bool
}{
- {"N-*", "N-*", "N-*", true, true},
- {"N-9.99.*", "N-[1-9].*", "", false, true},
- {"N-9.99.*", "N-[1-9][0-9].*", "", false, false},
- {"*.c", "*.h", "", false, false},
- {"a*", "*b", "ab", true, true},
- {"a*bc", "ab*c", "abc", true, true},
+ {"N-*", "N-*", true, "N-*", true},
+ {"N-9.99.*", "N-[1-9].*", true, "", false},
+ {"N-9.99.*", "N-[1-9][0-9].*", false, "", false},
+ {"*.c", "*.h", false, "", false},
+ {"a*", "*b", true, "ab", true},
+ {"a*bc", "ab*c", true, "abc", true},
+ {"*1*", "*2*", true, "asdf", false},
+ {"*1*", "*2*", true, "a1a", false},
+ {"*1*", "*2*", true, "a2a", false},
+ {"*1*", "*2*", true, "a12a", true},
+ {"*1*", "*2*", true, "a21a", true},
+ {"*[^0-9-+eE.]*", "*.c", true, ".c", true},
+ {"*[^0-9-+eE.]*", "*.c", true, "0.c", true},
+ {"*[^0-9-+eE.]*", "*.e", true, "0.e", false},
+ {"*[^0-9-+eE.]*", "*.e", true, "a.e", true},
+ {"*[^0-9-+eE.]*", "*.0", true, "000a0.0", true},
+ {"*[^0-9-+eE.]*", "*.0", true, "0000.0", false},
+ {"[0-9]", "[a-f]", false, "0", false},
+ {"[0-9]", "[0a-f]", true, "0", true},
+ {"[0-9]", "[0a-f]", true, "1", false},
+ {"[0-9]", "[0a-f]", true, "00", false},
}
for _, tt := range tests {
- t.Run(tt.str, func(t *testing.T) {
- a1, err1 := Compile(tt.pattern1)
- a2, err2 := Compile(tt.pattern2)
+ t.Run(tt.example, func(t *testing.T) {
+ p1, err1 := Compile(tt.pattern1)
+ p2, err2 := Compile(tt.pattern2)
if err1 != nil {
t.Fatal(err1)
}
if err2 != nil {
t.Fatal(err2)
}
- a := Intersect(a1, a2)
- matches := a.Match(tt.str)
+ both := Intersect(p1, p2)
+ canMatch := both.CanMatch()
+ if canMatch != tt.canMatch {
+ t.Errorf("CanMatch() = %v, want %v", canMatch, tt.canMatch)
+ }
+ matches := both.Match(tt.example)
if matches != tt.matches {
t.Errorf("Match() = %v, want %v", matches, tt.matches)
}
- canMatch := a.CanMatch()
- if canMatch != tt.canMatch {
- t.Errorf("CanMatch() = %v, want %v", canMatch, tt.canMatch)
+ if matches && !p1.Match(tt.example) {
+ t.Errorf("example %q doesn't match pattern1 %q", tt.example, tt.pattern1)
+ }
+ if matches && !p2.Match(tt.example) {
+ t.Errorf("example %q doesn't match pattern2 %q", tt.example, tt.pattern2)
}
})
}
@@ -205,12 +277,12 @@ func Test_Intersect(t *testing.T) {
func Test_Pattern_optimized(t *testing.T) {
var p Pattern
- p.AddState(false)
- p.AddState(false)
- p.AddState(false)
- p.AddState(true)
- p.AddTransition(0, '1', '1', 1)
- p.AddTransition(1, '2', '2', 3)
+ p.addState(false)
+ p.addState(false)
+ p.addState(false)
+ p.addState(true)
+ p.addTransition(0, '1', '1', 1)
+ p.addTransition(1, '2', '2', 3)
opt := p.optimized()
@@ -297,6 +369,49 @@ func Test_Pattern_CanMatch(t *testing.T)
}
+func Test_Number(t *testing.T) {
+ tests := []struct {
+ example string
+ want bool
+ }{
+ {"", false},
+ {".", false},
+ {"0x", false},
+ {"0xa", true},
+ {"+0xa", true},
+ {"-0xa", true},
+ {"0xa.", false},
+ {"0xa.a", false},
+ {"0xa.aa", false},
+ {"0xa.p", false},
+ {"0xa.p-1", true},
+ {"0xa.p1", true},
+ {"0xa.p11", true},
+ {"0xaa.", false},
+ {"1", true},
+ {"+1", true},
+ {"-1", true},
+ {"1.", true},
+ {"1.1", true},
+ {"1.11", true},
+ {"1.1e", false},
+ {"1.1e+", false},
+ {"1.1e+1", true},
+ {"1.1e+11", true},
+ {"1.1e-1", true},
+ {"1.e+1", true},
+ {"11", true},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.example, func(t *testing.T) {
+ if got := Number().Match(tt.example); got != tt.want {
+ t.Errorf("got %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
func Test_bmin(t *testing.T) {
if bmin(0, 255) != 0 {
t.Error()
Index: pkgsrc/pkgtools/pkglint/files/textproc/lexer.go
diff -u pkgsrc/pkgtools/pkglint/files/textproc/lexer.go:1.12 pkgsrc/pkgtools/pkglint/files/textproc/lexer.go:1.13
--- pkgsrc/pkgtools/pkglint/files/textproc/lexer.go:1.12 Sun Aug 8 22:04:15 2021
+++ pkgsrc/pkgtools/pkglint/files/textproc/lexer.go Wed Mar 29 07:12:36 2023
@@ -265,7 +265,8 @@ func NewByteSet(chars string) *ByteSet {
return &set
}
-// Inverse returns a byte set that matches the inverted set of bytes.
+// Inverse returns a byte set that contains exactly those bytes that
+// are not in the given set.
func (bs *ByteSet) Inverse() *ByteSet {
var inv ByteSet
for i := 0; i < 256; i++ {
Home |
Main Index |
Thread Index |
Old Index