github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/rule/utils.go (about)

     1  package rule
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"go/ast"
     7  	"go/printer"
     8  	"go/token"
     9  	"go/types"
    10  	"regexp"
    11  	"strings"
    12  
    13  	"github.com/mgechev/revive/lint"
    14  )
    15  
    16  const styleGuideBase = "https://golang.org/wiki/CodeReviewComments"
    17  
    18  // isBlank returns whether id is the blank identifier "_".
    19  // If id == nil, the answer is false.
    20  func isBlank(id *ast.Ident) bool { return id != nil && id.Name == "_" }
    21  
    22  func isTest(f *lint.File) bool {
    23  	return strings.HasSuffix(f.Name, "_test.go")
    24  }
    25  
    26  var commonMethods = map[string]bool{
    27  	"Error":     true,
    28  	"Read":      true,
    29  	"ServeHTTP": true,
    30  	"String":    true,
    31  	"Write":     true,
    32  }
    33  
    34  func receiverType(fn *ast.FuncDecl) string {
    35  	switch e := fn.Recv.List[0].Type.(type) {
    36  	case *ast.Ident:
    37  		return e.Name
    38  	case *ast.StarExpr:
    39  		if id, ok := e.X.(*ast.Ident); ok {
    40  			return id.Name
    41  		}
    42  	}
    43  	// The parser accepts much more than just the legal forms.
    44  	return "invalid-type"
    45  }
    46  
    47  var knownNameExceptions = map[string]bool{
    48  	"LastInsertId": true, // must match database/sql
    49  	"kWh":          true,
    50  }
    51  
    52  func isCgoExported(f *ast.FuncDecl) bool {
    53  	if f.Recv != nil || f.Doc == nil {
    54  		return false
    55  	}
    56  
    57  	cgoExport := regexp.MustCompile(fmt.Sprintf("(?m)^//export %s$", regexp.QuoteMeta(f.Name.Name)))
    58  	for _, c := range f.Doc.List {
    59  		if cgoExport.MatchString(c.Text) {
    60  			return true
    61  		}
    62  	}
    63  	return false
    64  }
    65  
    66  var allCapsRE = regexp.MustCompile(`^[A-Z0-9_]+$`)
    67  
    68  func isIdent(expr ast.Expr, ident string) bool {
    69  	id, ok := expr.(*ast.Ident)
    70  	return ok && id.Name == ident
    71  }
    72  
    73  var zeroLiteral = map[string]bool{
    74  	"false": true, // bool
    75  	// runes
    76  	`'\x00'`: true,
    77  	`'\000'`: true,
    78  	// strings
    79  	`""`: true,
    80  	"``": true,
    81  	// numerics
    82  	"0":   true,
    83  	"0.":  true,
    84  	"0.0": true,
    85  	"0i":  true,
    86  }
    87  
    88  func validType(T types.Type) bool {
    89  	return T != nil &&
    90  		T != types.Typ[types.Invalid] &&
    91  		!strings.Contains(T.String(), "invalid type") // good but not foolproof
    92  }
    93  
    94  func isPkgDot(expr ast.Expr, pkg, name string) bool {
    95  	sel, ok := expr.(*ast.SelectorExpr)
    96  	return ok && isIdent(sel.X, pkg) && isIdent(sel.Sel, name)
    97  }
    98  
    99  func srcLine(src []byte, p token.Position) string {
   100  	// Run to end of line in both directions if not at line start/end.
   101  	lo, hi := p.Offset, p.Offset+1
   102  	for lo > 0 && src[lo-1] != '\n' {
   103  		lo--
   104  	}
   105  	for hi < len(src) && src[hi-1] != '\n' {
   106  		hi++
   107  	}
   108  	return string(src[lo:hi])
   109  }
   110  
   111  // pick yields a list of nodes by picking them from a sub-ast with root node n.
   112  // Nodes are selected by applying the fselect function
   113  // f function is applied to each selected node before inseting it in the final result.
   114  // If f==nil then it defaults to the identity function (ie it returns the node itself)
   115  func pick(n ast.Node, fselect func(n ast.Node) bool, f func(n ast.Node) []ast.Node) []ast.Node {
   116  	var result []ast.Node
   117  
   118  	if n == nil {
   119  		return result
   120  	}
   121  
   122  	if f == nil {
   123  		f = func(n ast.Node) []ast.Node { return []ast.Node{n} }
   124  	}
   125  
   126  	onSelect := func(n ast.Node) {
   127  		result = append(result, f(n)...)
   128  	}
   129  	p := picker{fselect: fselect, onSelect: onSelect}
   130  	ast.Walk(p, n)
   131  	return result
   132  }
   133  
   134  func pickFromExpList(l []ast.Expr, fselect func(n ast.Node) bool, f func(n ast.Node) []ast.Node) []ast.Node {
   135  	result := make([]ast.Node, 0)
   136  	for _, e := range l {
   137  		result = append(result, pick(e, fselect, f)...)
   138  	}
   139  	return result
   140  }
   141  
   142  type picker struct {
   143  	fselect  func(n ast.Node) bool
   144  	onSelect func(n ast.Node)
   145  }
   146  
   147  func (p picker) Visit(node ast.Node) ast.Visitor {
   148  	if p.fselect == nil {
   149  		return nil
   150  	}
   151  
   152  	if p.fselect(node) {
   153  		p.onSelect(node)
   154  	}
   155  
   156  	return p
   157  }
   158  
   159  // isBoolOp returns true if the given token corresponds to
   160  // a bool operator
   161  func isBoolOp(t token.Token) bool {
   162  	switch t {
   163  	case token.LAND, token.LOR, token.EQL, token.NEQ:
   164  		return true
   165  	}
   166  
   167  	return false
   168  }
   169  
   170  const (
   171  	trueName  = "true"
   172  	falseName = "false"
   173  )
   174  
   175  func isExprABooleanLit(n ast.Node) (lexeme string, ok bool) {
   176  	oper, ok := n.(*ast.Ident)
   177  
   178  	if !ok {
   179  		return "", false
   180  	}
   181  
   182  	return oper.Name, (oper.Name == trueName || oper.Name == falseName)
   183  }
   184  
   185  // gofmt returns a string representation of an AST subtree.
   186  func gofmt(x interface{}) string {
   187  	buf := bytes.Buffer{}
   188  	fs := token.NewFileSet()
   189  	printer.Fprint(&buf, fs, x)
   190  	return buf.String()
   191  }