]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: test that error format strings have matching parentheses/brackets
authorRobert Griesemer <gri@golang.org>
Thu, 5 Jan 2023 22:51:31 +0000 (14:51 -0800)
committerGopher Robot <gobot@golang.org>
Tue, 17 Jan 2023 19:55:02 +0000 (19:55 +0000)
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>

src/cmd/compile/internal/types2/errorcalls_test.go
src/go/types/errorcalls_test.go

index edf2a5195d3960fed44e5eb9367a81717720c2d0..6153b42a347468726a4d96f61a6e973cec92439e 100644 (file)
@@ -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
+}
index 6d6bd6011fc172593c4ae484d28937be7280b927..ea9a122063cf8b0d24b6b7474a07f87d838258e9 100644 (file)
@@ -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