// Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package types_test import ( "go/ast" "go/token" "strconv" "testing" ) const ( errorfMinArgCount = 4 errorfFormatIndex = 2 ) // TestErrorCalls makes sure that check.errorf calls have at least // 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, ".") if err != nil { t.Fatal(err) } for _, file := range files { ast.Inspect(file, func(n ast.Node) bool { call, _ := n.(*ast.CallExpr) if call == nil { return true } selx, _ := call.Fun.(*ast.SelectorExpr) if selx == nil { return true } if !(isName(selx.X, "check") && isName(selx.Sel, "errorf")) { return true } // check.errorf calls should have at least errorfMinArgCount arguments: // position, code, format string, and arguments to format if n := len(call.Args); n < errorfMinArgCount { t.Errorf("%s: got %d arguments, want at least %d", fset.Position(call.Pos()), n, errorfMinArgCount) return false } 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 }) } } func isName(n ast.Node, name string) bool { if n, ok := n.(*ast.Ident); ok { return n.Name == name } 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 }