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 {
}
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
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
})
}
}
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
+}
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, ".")
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
})
}
}
}
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