From: Robert Griesemer Date: Thu, 5 Jan 2023 22:51:31 +0000 (-0800) Subject: go/types, types2: test that error format strings have matching parentheses/brackets X-Git-Tag: go1.21rc1~1928 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=d4639ecdfc9051a7adcfb8945d93a45da56576ae;p=gostls13.git go/types, types2: test that error format strings have matching parentheses/brackets Also, for go/types, switch to using syntax.Inspect instead of (deprecated) syntax.Crawl. Change-Id: I8333079040e9676e0a61c23d09d41ca790526eeb Reviewed-on: https://go-review.googlesource.com/c/go/+/460759 TryBot-Result: Gopher Robot Reviewed-by: Robert Findley Reviewed-by: Robert Griesemer Run-TryBot: Robert Griesemer Auto-Submit: Robert Griesemer --- diff --git a/src/cmd/compile/internal/types2/errorcalls_test.go b/src/cmd/compile/internal/types2/errorcalls_test.go index edf2a5195d..6153b42a34 100644 --- a/src/cmd/compile/internal/types2/errorcalls_test.go +++ b/src/cmd/compile/internal/types2/errorcalls_test.go @@ -6,13 +6,18 @@ package types2_test import ( "cmd/compile/internal/syntax" + "strconv" "testing" ) -const errorfMinArgCount = 4 +const ( + errorfMinArgCount = 4 + errorfFormatIndex = 2 +) // TestErrorCalls makes sure that check.errorf calls have at least -// errorfMinArgCount arguments (otherwise we should use check.error). +// errorfMinArgCount arguments (otherwise we should use check.error) +// and use balanced parentheses/brackets. func TestErrorCalls(t *testing.T) { files, err := pkgFiles(".") if err != nil { @@ -20,17 +25,17 @@ func TestErrorCalls(t *testing.T) { } for _, file := range files { - syntax.Crawl(file, func(n syntax.Node) bool { + syntax.Inspect(file, func(n syntax.Node) bool { call, _ := n.(*syntax.CallExpr) if call == nil { - return false + return true } selx, _ := call.Fun.(*syntax.SelectorExpr) if selx == nil { - return false + return true } if !(isName(selx.X, "check") && isName(selx.Sel, "errorf")) { - return false + return true } // check.errorf calls should have at least errorfMinArgCount arguments: // position, code, format string, and arguments to format @@ -38,6 +43,18 @@ func TestErrorCalls(t *testing.T) { t.Errorf("%s: got %d arguments, want at least %d", call.Pos(), n, errorfMinArgCount) return false } + format := call.ArgList[errorfFormatIndex] + syntax.Inspect(format, func(n syntax.Node) bool { + if lit, _ := n.(*syntax.BasicLit); lit != nil && lit.Kind == syntax.StringLit { + if s, err := strconv.Unquote(lit.Value); err == nil { + if !balancedParentheses(s) { + t.Errorf("%s: unbalanced parentheses/brackets", lit.Pos()) + } + } + return false + } + return true + }) return false }) } @@ -49,3 +66,30 @@ func isName(n syntax.Node, name string) bool { } return false } + +func balancedParentheses(s string) bool { + var stack []byte + for _, ch := range s { + var open byte + switch ch { + case '(', '[', '{': + stack = append(stack, byte(ch)) + continue + case ')': + open = '(' + case ']': + open = '[' + case '}': + open = '{' + default: + continue + } + // closing parenthesis/bracket must have matching opening + top := len(stack) - 1 + if top < 0 || stack[top] != open { + return false + } + stack = stack[:top] + } + return len(stack) == 0 +} diff --git a/src/go/types/errorcalls_test.go b/src/go/types/errorcalls_test.go index 6d6bd6011f..ea9a122063 100644 --- a/src/go/types/errorcalls_test.go +++ b/src/go/types/errorcalls_test.go @@ -7,13 +7,18 @@ package types_test import ( "go/ast" "go/token" + "strconv" "testing" ) -const errorfMinArgCount = 4 +const ( + errorfMinArgCount = 4 + errorfFormatIndex = 2 +) // TestErrorCalls makes sure that check.errorf calls have at least -// errorfMinArgCount arguments (otherwise we should use check.error). +// errorfMinArgCount arguments (otherwise we should use check.error) +// and use balanced parentheses/brackets. func TestErrorCalls(t *testing.T) { fset := token.NewFileSet() files, err := pkgFiles(fset, ".") @@ -40,7 +45,19 @@ func TestErrorCalls(t *testing.T) { t.Errorf("%s: got %d arguments, want at least %d", fset.Position(call.Pos()), n, errorfMinArgCount) return false } - return true + format := call.Args[errorfFormatIndex] + ast.Inspect(format, func(n ast.Node) bool { + if lit, _ := n.(*ast.BasicLit); lit != nil && lit.Kind == token.STRING { + if s, err := strconv.Unquote(lit.Value); err == nil { + if !balancedParentheses(s) { + t.Errorf("%s: unbalanced parentheses/brackets", fset.Position(lit.ValuePos)) + } + } + return false + } + return true + }) + return false }) } } @@ -51,3 +68,30 @@ func isName(n ast.Node, name string) bool { } return false } + +func balancedParentheses(s string) bool { + var stack []byte + for _, ch := range s { + var open byte + switch ch { + case '(', '[', '{': + stack = append(stack, byte(ch)) + continue + case ')': + open = '(' + case ']': + open = '[' + case '}': + open = '{' + default: + continue + } + // closing parenthesis/bracket must have matching opening + top := len(stack) - 1 + if top < 0 || stack[top] != open { + return false + } + stack = stack[:top] + } + return len(stack) == 0 +} \ No newline at end of file