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 }