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 <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
diff --git a/src/cmd/compile/internal/types2/errorcalls_test.go b/src/cmd/compile/internal/types2/errorcalls_test.go
index edf2a51..6153b42 100644
--- a/src/cmd/compile/internal/types2/errorcalls_test.go
+++ b/src/cmd/compile/internal/types2/errorcalls_test.go
@@ -6,13 +6,18 @@
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 @@
}
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 @@
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 @@
}
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 6d6bd60..ea9a122 100644
--- a/src/go/types/errorcalls_test.go
+++ b/src/go/types/errorcalls_test.go
@@ -7,13 +7,18 @@
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 @@
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 @@
}
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