123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- package rule
-
- import (
- "bytes"
- "fmt"
- "go/ast"
- "go/printer"
- "go/token"
- "go/types"
- "regexp"
- "strings"
-
- "github.com/mgechev/revive/lint"
- )
-
- const styleGuideBase = "https://golang.org/wiki/CodeReviewComments"
-
- // isBlank returns whether id is the blank identifier "_".
- // If id == nil, the answer is false.
- func isBlank(id *ast.Ident) bool { return id != nil && id.Name == "_" }
-
- func isTest(f *lint.File) bool {
- return strings.HasSuffix(f.Name, "_test.go")
- }
-
- var commonMethods = map[string]bool{
- "Error": true,
- "Read": true,
- "ServeHTTP": true,
- "String": true,
- "Write": true,
- }
-
- func receiverType(fn *ast.FuncDecl) string {
- switch e := fn.Recv.List[0].Type.(type) {
- case *ast.Ident:
- return e.Name
- case *ast.StarExpr:
- if id, ok := e.X.(*ast.Ident); ok {
- return id.Name
- }
- }
- // The parser accepts much more than just the legal forms.
- return "invalid-type"
- }
-
- var knownNameExceptions = map[string]bool{
- "LastInsertId": true, // must match database/sql
- "kWh": true,
- }
-
- func isCgoExported(f *ast.FuncDecl) bool {
- if f.Recv != nil || f.Doc == nil {
- return false
- }
-
- cgoExport := regexp.MustCompile(fmt.Sprintf("(?m)^//export %s$", regexp.QuoteMeta(f.Name.Name)))
- for _, c := range f.Doc.List {
- if cgoExport.MatchString(c.Text) {
- return true
- }
- }
- return false
- }
-
- var allCapsRE = regexp.MustCompile(`^[A-Z0-9_]+$`)
-
- func isIdent(expr ast.Expr, ident string) bool {
- id, ok := expr.(*ast.Ident)
- return ok && id.Name == ident
- }
-
- var zeroLiteral = map[string]bool{
- "false": true, // bool
- // runes
- `'\x00'`: true,
- `'\000'`: true,
- // strings
- `""`: true,
- "``": true,
- // numerics
- "0": true,
- "0.": true,
- "0.0": true,
- "0i": true,
- }
-
- func validType(T types.Type) bool {
- return T != nil &&
- T != types.Typ[types.Invalid] &&
- !strings.Contains(T.String(), "invalid type") // good but not foolproof
- }
-
- func isPkgDot(expr ast.Expr, pkg, name string) bool {
- sel, ok := expr.(*ast.SelectorExpr)
- return ok && isIdent(sel.X, pkg) && isIdent(sel.Sel, name)
- }
-
- func srcLine(src []byte, p token.Position) string {
- // Run to end of line in both directions if not at line start/end.
- lo, hi := p.Offset, p.Offset+1
- for lo > 0 && src[lo-1] != '\n' {
- lo--
- }
- for hi < len(src) && src[hi-1] != '\n' {
- hi++
- }
- return string(src[lo:hi])
- }
-
- // pick yields a list of nodes by picking them from a sub-ast with root node n.
- // Nodes are selected by applying the fselect function
- // f function is applied to each selected node before inseting it in the final result.
- // If f==nil then it defaults to the identity function (ie it returns the node itself)
- func pick(n ast.Node, fselect func(n ast.Node) bool, f func(n ast.Node) []ast.Node) []ast.Node {
- var result []ast.Node
-
- if n == nil {
- return result
- }
-
- if f == nil {
- f = func(n ast.Node) []ast.Node { return []ast.Node{n} }
- }
-
- onSelect := func(n ast.Node) {
- result = append(result, f(n)...)
- }
- p := picker{fselect: fselect, onSelect: onSelect}
- ast.Walk(p, n)
- return result
- }
-
- func pickFromExpList(l []ast.Expr, fselect func(n ast.Node) bool, f func(n ast.Node) []ast.Node) []ast.Node {
- result := make([]ast.Node, 0)
- for _, e := range l {
- result = append(result, pick(e, fselect, f)...)
- }
- return result
- }
-
- type picker struct {
- fselect func(n ast.Node) bool
- onSelect func(n ast.Node)
- }
-
- func (p picker) Visit(node ast.Node) ast.Visitor {
- if p.fselect == nil {
- return nil
- }
-
- if p.fselect(node) {
- p.onSelect(node)
- }
-
- return p
- }
-
- // isBoolOp returns true if the given token corresponds to
- // a bool operator
- func isBoolOp(t token.Token) bool {
- switch t {
- case token.LAND, token.LOR, token.EQL, token.NEQ:
- return true
- }
-
- return false
- }
-
- const (
- trueName = "true"
- falseName = "false"
- )
-
- func isExprABooleanLit(n ast.Node) (lexeme string, ok bool) {
- oper, ok := n.(*ast.Ident)
-
- if !ok {
- return "", false
- }
-
- return oper.Name, (oper.Name == trueName || oper.Name == falseName)
- }
-
- // gofmt returns a string representation of an AST subtree.
- func gofmt(x interface{}) string {
- buf := bytes.Buffer{}
- fs := token.NewFileSet()
- printer.Fprint(&buf, fs, x)
- return buf.String()
- }
|