Source file src/cmd/compile/internal/types2/errorcalls_test.go

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package types2_test
     6  
     7  import (
     8  	"cmd/compile/internal/syntax"
     9  	"strconv"
    10  	"testing"
    11  )
    12  
    13  const (
    14  	errorfMinArgCount = 4
    15  	errorfFormatIndex = 2
    16  )
    17  
    18  // TestErrorCalls makes sure that check.errorf calls have at least
    19  // errorfMinArgCount arguments (otherwise we should use check.error)
    20  // and use balanced parentheses/brackets.
    21  func TestErrorCalls(t *testing.T) {
    22  	files, err := pkgFiles(".")
    23  	if err != nil {
    24  		t.Fatal(err)
    25  	}
    26  
    27  	for _, file := range files {
    28  		syntax.Inspect(file, func(n syntax.Node) bool {
    29  			call, _ := n.(*syntax.CallExpr)
    30  			if call == nil {
    31  				return true
    32  			}
    33  			selx, _ := call.Fun.(*syntax.SelectorExpr)
    34  			if selx == nil {
    35  				return true
    36  			}
    37  			if !(isName(selx.X, "check") && isName(selx.Sel, "errorf")) {
    38  				return true
    39  			}
    40  			// check.errorf calls should have at least errorfMinArgCount arguments:
    41  			// position, code, format string, and arguments to format
    42  			if n := len(call.ArgList); n < errorfMinArgCount {
    43  				t.Errorf("%s: got %d arguments, want at least %d", call.Pos(), n, errorfMinArgCount)
    44  				return false
    45  			}
    46  			format := call.ArgList[errorfFormatIndex]
    47  			syntax.Inspect(format, func(n syntax.Node) bool {
    48  				if lit, _ := n.(*syntax.BasicLit); lit != nil && lit.Kind == syntax.StringLit {
    49  					if s, err := strconv.Unquote(lit.Value); err == nil {
    50  						if !balancedParentheses(s) {
    51  							t.Errorf("%s: unbalanced parentheses/brackets", lit.Pos())
    52  						}
    53  					}
    54  					return false
    55  				}
    56  				return true
    57  			})
    58  			return false
    59  		})
    60  	}
    61  }
    62  
    63  func isName(n syntax.Node, name string) bool {
    64  	if n, ok := n.(*syntax.Name); ok {
    65  		return n.Value == name
    66  	}
    67  	return false
    68  }
    69  
    70  func balancedParentheses(s string) bool {
    71  	var stack []byte
    72  	for _, ch := range s {
    73  		var open byte
    74  		switch ch {
    75  		case '(', '[', '{':
    76  			stack = append(stack, byte(ch))
    77  			continue
    78  		case ')':
    79  			open = '('
    80  		case ']':
    81  			open = '['
    82  		case '}':
    83  			open = '{'
    84  		default:
    85  			continue
    86  		}
    87  		// closing parenthesis/bracket must have matching opening
    88  		top := len(stack) - 1
    89  		if top < 0 || stack[top] != open {
    90  			return false
    91  		}
    92  		stack = stack[:top]
    93  	}
    94  	return len(stack) == 0
    95  }
    96  

View as plain text