summaryrefslogtreecommitdiff
path: root/pkgtools
diff options
context:
space:
mode:
authorrillig <rillig@pkgsrc.org>2019-08-02 18:55:07 +0000
committerrillig <rillig@pkgsrc.org>2019-08-02 18:55:07 +0000
commit1ddaea84fb9bf40ade260577728c360caa61b973 (patch)
tree3481cb25413d2e459b05dfde03cbedfcfa99f67e /pkgtools
parent2fc7172ce82c096d3dd5e306094b202145d57c6b (diff)
downloadpkgsrc-1ddaea84fb9bf40ade260577728c360caa61b973.tar.gz
pkgtools/pkglint: update to 5.7.19
Changes since 5.7.18: * The tricky construct for generating case-items from a Make variable no longer produces parse errors. Example: case $$expr in ${PATTERNS:@p@ (${p}) action ;; @} esac
Diffstat (limited to 'pkgtools')
-rw-r--r--pkgtools/pkglint/Makefile4
-rw-r--r--pkgtools/pkglint/files/mkshparser.go15
-rw-r--r--pkgtools/pkglint/files/mkshparser_test.go56
-rw-r--r--pkgtools/pkglint/files/mkshtypes.go1
-rw-r--r--pkgtools/pkglint/files/mkshwalker.go4
-rw-r--r--pkgtools/pkglint/files/shell.y13
-rw-r--r--pkgtools/pkglint/files/shell_test.go9
7 files changed, 87 insertions, 15 deletions
diff --git a/pkgtools/pkglint/Makefile b/pkgtools/pkglint/Makefile
index 89838f9ec75..40aec1ec0c2 100644
--- a/pkgtools/pkglint/Makefile
+++ b/pkgtools/pkglint/Makefile
@@ -1,6 +1,6 @@
-# $NetBSD: Makefile,v 1.590 2019/08/01 22:38:49 rillig Exp $
+# $NetBSD: Makefile,v 1.591 2019/08/02 18:55:07 rillig Exp $
-PKGNAME= pkglint-5.7.18
+PKGNAME= pkglint-5.7.19
CATEGORIES= pkgtools
DISTNAME= tools
MASTER_SITES= ${MASTER_SITE_GITHUB:=golang/}
diff --git a/pkgtools/pkglint/files/mkshparser.go b/pkgtools/pkglint/files/mkshparser.go
index fd0297f2faf..b248c6b9dc9 100644
--- a/pkgtools/pkglint/files/mkshparser.go
+++ b/pkgtools/pkglint/files/mkshparser.go
@@ -243,6 +243,21 @@ func (lex *ShellLexer) Lex(lval *shyySymType) (ttype int) {
p := NewShTokenizer(dummyLine, token, false) // Just for converting the string to a ShToken
lval.Word = p.ShToken()
lex.atCommandStart = false
+
+ // Inside of a case statement, ${PATTERNS:@p@ (${p}) action ;; @} expands to
+ // a list of case-items, and after this list a new command starts.
+ // This is necessary to return a following "esac" as tkESAC instead of a
+ // simple word.
+ if lex.sinceCase >= 0 && len(lval.Word.Atoms) == 1 {
+ if varUse := lval.Word.Atoms[0].VarUse(); varUse != nil {
+ if len(varUse.modifiers) > 0 {
+ lastModifier := varUse.modifiers[len(varUse.modifiers)-1].Text
+ if hasPrefix(lastModifier, "@") {
+ lex.atCommandStart = true
+ }
+ }
+ }
+ }
}
return ttype
diff --git a/pkgtools/pkglint/files/mkshparser_test.go b/pkgtools/pkglint/files/mkshparser_test.go
index 0eab6b40489..31dc889e350 100644
--- a/pkgtools/pkglint/files/mkshparser_test.go
+++ b/pkgtools/pkglint/files/mkshparser_test.go
@@ -384,6 +384,23 @@ func (s *ShSuite) Test_ShellParser__case_clause(c *check.C) {
b.CaseItem(
b.Words("if", "then", "else"),
b.List(), sepNone))))
+
+ // This could be regarded an evil preprocessor hack, but it's used
+ // in practice and is somewhat established, even though it is
+ // difficult to parse and understand, even for humans.
+ s.test("case $$expr in ${PATTERNS:@p@ (${p}) action ;; @} (*) ;; esac",
+ b.List().AddCommand(b.Case(
+ b.Token("$$expr"),
+ b.CaseItemVar("${PATTERNS:@p@ (${p}) action ;; @}"),
+ b.CaseItem(
+ b.Words("*"),
+ b.List(), sepNone))))
+
+ // The default case may be omitted if PATTERNS can never be empty.
+ s.test("case $$expr in ${PATTERNS:@p@ (${p}) action ;; @} esac",
+ b.List().AddCommand(b.Case(
+ b.Token("$$expr"),
+ b.CaseItemVar("${PATTERNS:@p@ (${p}) action ;; @}"))))
}
func (s *ShSuite) Test_ShellParser__if_clause(c *check.C) {
@@ -555,11 +572,11 @@ func (s *ShSuite) test(program string, expected *MkShList) {
error: ""}
parser := shyyParserImpl{}
- succeeded := parser.Parse(&lexer)
+ zeroMeansSuccess := parser.Parse(&lexer)
c := s.c
- if t.CheckEquals(succeeded, 0) && t.CheckEquals(lexer.error, "") {
+ if t.CheckEquals(zeroMeansSuccess, 0) && t.CheckEquals(lexer.error, "") {
if !t.CheckDeepEquals(lexer.result, expected) {
actualJSON, actualErr := json.MarshalIndent(lexer.result, "", " ")
expectedJSON, expectedErr := json.MarshalIndent(expected, "", " ")
@@ -664,6 +681,35 @@ func (s *ShSuite) Test_ShellLexer_Lex__keywords(c *check.C) {
"if cond ; then : ; fi")
}
+func (s *Suite) Test_ShellLexer_Lex__case_patterns(c *check.C) {
+ t := s.Init(c)
+
+ test := func(shellProgram string, expectedTokens ...int) {
+ tokens, rest := splitIntoShellTokens(nil, shellProgram)
+ lexer := NewShellLexer(tokens, rest)
+
+ var actualTokens []int
+ for {
+ var token shyySymType
+ tokenType := lexer.Lex(&token)
+ if tokenType <= 0 {
+ break
+ }
+ actualTokens = append(actualTokens, tokenType)
+ }
+ t.CheckDeepEquals(actualTokens, expectedTokens)
+ }
+
+ test(
+ "case $$expr in ${PATTERNS:@p@(${p}) action ;; @} esac",
+
+ tkCASE,
+ tkWORD,
+ tkIN,
+ tkWORD,
+ tkESAC)
+}
+
type MkShBuilder struct {
}
@@ -728,7 +774,11 @@ func (b *MkShBuilder) Case(selector *ShToken, items ...*MkShCaseItem) *MkShComma
}
func (b *MkShBuilder) CaseItem(patterns []*ShToken, action *MkShList, separator MkShSeparator) *MkShCaseItem {
- return &MkShCaseItem{patterns, action, separator}
+ return &MkShCaseItem{patterns, action, separator, nil}
+}
+
+func (b *MkShBuilder) CaseItemVar(varUseText string) *MkShCaseItem {
+ return &MkShCaseItem{nil, nil, sepNone, b.Token(varUseText)}
}
func (b *MkShBuilder) While(cond, action *MkShList, redirects ...*MkShRedirection) *MkShCommand {
diff --git a/pkgtools/pkglint/files/mkshtypes.go b/pkgtools/pkglint/files/mkshtypes.go
index ea778fe3986..1f3e5f74f8c 100644
--- a/pkgtools/pkglint/files/mkshtypes.go
+++ b/pkgtools/pkglint/files/mkshtypes.go
@@ -127,6 +127,7 @@ type MkShCaseItem struct {
Patterns []*ShToken
Action *MkShList
Separator MkShSeparator
+ Var *ShToken // ${PATTERNS:@p@ (${p}) action ;; @}
}
// MkShIf is a conditional statement, possibly having
diff --git a/pkgtools/pkglint/files/mkshwalker.go b/pkgtools/pkglint/files/mkshwalker.go
index 012b88dcbd6..eb96104ff70 100644
--- a/pkgtools/pkglint/files/mkshwalker.go
+++ b/pkgtools/pkglint/files/mkshwalker.go
@@ -215,7 +215,9 @@ func (w *MkShWalker) walkCase(caseClause *MkShCase) {
callback(caseItem)
}
w.walkWords(-1, caseItem.Patterns)
- w.walkList(-1, caseItem.Action)
+ if caseItem.Action != nil {
+ w.walkList(-1, caseItem.Action)
+ }
w.pop()
}
diff --git a/pkgtools/pkglint/files/shell.y b/pkgtools/pkglint/files/shell.y
index 3c4f6cc92b5..b10fa5e7a33 100644
--- a/pkgtools/pkglint/files/shell.y
+++ b/pkgtools/pkglint/files/shell.y
@@ -208,20 +208,23 @@ case_selector : pattern tkRPAREN {
}
case_item_ns : case_selector linebreak {
- $$ = &MkShCaseItem{$1, &MkShList{}, sepNone}
+ $$ = &MkShCaseItem{$1, &MkShList{}, sepNone, nil}
}
case_item_ns : case_selector linebreak term linebreak {
- $$ = &MkShCaseItem{$1, $3, sepNone}
+ $$ = &MkShCaseItem{$1, $3, sepNone, nil}
}
case_item_ns : case_selector linebreak term separator_op linebreak {
- $$ = &MkShCaseItem{$1, $3, $4}
+ $$ = &MkShCaseItem{$1, $3, $4, nil}
}
case_item : case_selector linebreak tkSEMISEMI linebreak {
- $$ = &MkShCaseItem{$1, &MkShList{}, sepNone}
+ $$ = &MkShCaseItem{$1, &MkShList{}, sepNone, nil}
}
case_item : case_selector compound_list tkSEMISEMI linebreak {
- $$ = &MkShCaseItem{$1, $2, sepNone}
+ $$ = &MkShCaseItem{$1, $2, sepNone, nil}
+}
+case_item : tkWORD {
+ $$ = &MkShCaseItem{Var: $1}
}
pattern : tkWORD {
diff --git a/pkgtools/pkglint/files/shell_test.go b/pkgtools/pkglint/files/shell_test.go
index 5a4b4fd53ba..081c1fa68fc 100644
--- a/pkgtools/pkglint/files/shell_test.go
+++ b/pkgtools/pkglint/files/shell_test.go
@@ -1099,10 +1099,11 @@ func (s *Suite) Test_ShellLineChecker_CheckShellCommand__case_patterns_from_vari
mklines.Check()
- // FIXME: Support the above variable expansion.
- t.CheckOutputLines(
- "WARN: Makefile:4: Pkglint ShellLine.CheckShellCommand: " +
- "parse error at []string{\"*\", \")\", \"continue\", \";\", \"esac\"}")
+ // TODO: Ensure that the shell word is really only one variable use.
+ // TODO: Ensure that the last modifier is :@@@.
+ // TODO: Ensure that the replacement is a well-formed case-item.
+ // TODO: Ensure that the replacement contains ";;" as the last shell token.
+ t.CheckOutputEmpty()
}
func (s *Suite) Test_ShellLineChecker_checkHiddenAndSuppress(c *check.C) {