gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/github.com/jgautheron/goconst/visitor.go (about) 1 package goconst 2 3 import ( 4 "go/ast" 5 "go/token" 6 "strings" 7 ) 8 9 // treeVisitor carries the package name and file name 10 // for passing it to the imports map, and the fileSet for 11 // retrieving the token.Position. 12 type treeVisitor struct { 13 p *Parser 14 fileSet *token.FileSet 15 packageName, fileName string 16 } 17 18 // Visit browses the AST tree for strings that could be potentially 19 // replaced by constants. 20 // A map of existing constants is built as well (-match-constant). 21 func (v *treeVisitor) Visit(node ast.Node) ast.Visitor { 22 if node == nil { 23 return v 24 } 25 26 // A single case with "ast.BasicLit" would be much easier 27 // but then we wouldn't be able to tell in which context 28 // the string is defined (could be a constant definition). 29 switch t := node.(type) { 30 // Scan for constants in an attempt to match strings with existing constants 31 case *ast.GenDecl: 32 if !v.p.matchConstant { 33 return v 34 } 35 if t.Tok != token.CONST { 36 return v 37 } 38 39 for _, spec := range t.Specs { 40 val := spec.(*ast.ValueSpec) 41 for i, str := range val.Values { 42 lit, ok := str.(*ast.BasicLit) 43 if !ok || !v.isSupported(lit.Kind) { 44 continue 45 } 46 47 v.addConst(val.Names[i].Name, lit.Value, val.Names[i].Pos()) 48 } 49 } 50 51 // foo := "moo" 52 case *ast.AssignStmt: 53 for _, rhs := range t.Rhs { 54 lit, ok := rhs.(*ast.BasicLit) 55 if !ok || !v.isSupported(lit.Kind) { 56 continue 57 } 58 59 v.addString(lit.Value, rhs.(*ast.BasicLit).Pos()) 60 } 61 62 // if foo == "moo" 63 case *ast.BinaryExpr: 64 if t.Op != token.EQL && t.Op != token.NEQ { 65 return v 66 } 67 68 var lit *ast.BasicLit 69 var ok bool 70 71 lit, ok = t.X.(*ast.BasicLit) 72 if ok && v.isSupported(lit.Kind) { 73 v.addString(lit.Value, lit.Pos()) 74 } 75 76 lit, ok = t.Y.(*ast.BasicLit) 77 if ok && v.isSupported(lit.Kind) { 78 v.addString(lit.Value, lit.Pos()) 79 } 80 81 // case "foo": 82 case *ast.CaseClause: 83 for _, item := range t.List { 84 lit, ok := item.(*ast.BasicLit) 85 if ok && v.isSupported(lit.Kind) { 86 v.addString(lit.Value, lit.Pos()) 87 } 88 } 89 90 // return "boo" 91 case *ast.ReturnStmt: 92 for _, item := range t.Results { 93 lit, ok := item.(*ast.BasicLit) 94 if ok && v.isSupported(lit.Kind) { 95 v.addString(lit.Value, lit.Pos()) 96 } 97 } 98 } 99 100 return v 101 } 102 103 // addString adds a string in the map along with its position in the tree. 104 func (v *treeVisitor) addString(str string, pos token.Pos) { 105 str = strings.Replace(str, `"`, "", 2) 106 107 // Ignore empty strings 108 if len(str) == 0 { 109 return 110 } 111 112 if len(str) < v.p.minLength { 113 return 114 } 115 116 _, ok := v.p.strs[str] 117 if !ok { 118 v.p.strs[str] = make([]ExtendedPos, 0) 119 } 120 v.p.strs[str] = append(v.p.strs[str], ExtendedPos{ 121 packageName: v.packageName, 122 Position: v.fileSet.Position(pos), 123 }) 124 } 125 126 // addConst adds a const in the map along with its position in the tree. 127 func (v *treeVisitor) addConst(name string, val string, pos token.Pos) { 128 val = strings.Replace(val, `"`, "", 2) 129 v.p.consts[val] = ConstType{ 130 Name: name, 131 packageName: v.packageName, 132 Position: v.fileSet.Position(pos), 133 } 134 } 135 136 func (v *treeVisitor) isSupported(tk token.Token) bool { 137 for _, s := range v.p.supportedTokens { 138 if tk == s { 139 return true 140 } 141 } 142 return false 143 }