Source file
src/go/types/errorcalls_test.go
1
2
3
4
5 package types_test
6
7 import (
8 "go/ast"
9 "go/token"
10 "strconv"
11 "testing"
12 )
13
14 const (
15 errorfMinArgCount = 4
16 errorfFormatIndex = 2
17 )
18
19
20
21
22 func TestErrorCalls(t *testing.T) {
23 fset := token.NewFileSet()
24 files, err := pkgFiles(fset, ".")
25 if err != nil {
26 t.Fatal(err)
27 }
28
29 for _, file := range files {
30 ast.Inspect(file, func(n ast.Node) bool {
31 call, _ := n.(*ast.CallExpr)
32 if call == nil {
33 return true
34 }
35 selx, _ := call.Fun.(*ast.SelectorExpr)
36 if selx == nil {
37 return true
38 }
39 if !(isName(selx.X, "check") && isName(selx.Sel, "errorf")) {
40 return true
41 }
42
43
44 if n := len(call.Args); n < errorfMinArgCount {
45 t.Errorf("%s: got %d arguments, want at least %d", fset.Position(call.Pos()), n, errorfMinArgCount)
46 return false
47 }
48 format := call.Args[errorfFormatIndex]
49 ast.Inspect(format, func(n ast.Node) bool {
50 if lit, _ := n.(*ast.BasicLit); lit != nil && lit.Kind == token.STRING {
51 if s, err := strconv.Unquote(lit.Value); err == nil {
52 if !balancedParentheses(s) {
53 t.Errorf("%s: unbalanced parentheses/brackets", fset.Position(lit.ValuePos))
54 }
55 }
56 return false
57 }
58 return true
59 })
60 return false
61 })
62 }
63 }
64
65 func isName(n ast.Node, name string) bool {
66 if n, ok := n.(*ast.Ident); ok {
67 return n.Name == name
68 }
69 return false
70 }
71
72 func balancedParentheses(s string) bool {
73 var stack []byte
74 for _, ch := range s {
75 var open byte
76 switch ch {
77 case '(', '[', '{':
78 stack = append(stack, byte(ch))
79 continue
80 case ')':
81 open = '('
82 case ']':
83 open = '['
84 case '}':
85 open = '{'
86 default:
87 continue
88 }
89
90 top := len(stack) - 1
91 if top < 0 || stack[top] != open {
92 return false
93 }
94 stack = stack[:top]
95 }
96 return len(stack) == 0
97 }
98
View as plain text