144 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			144 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package goconst
 | 
						|
 | 
						|
import (
 | 
						|
	"go/ast"
 | 
						|
	"go/token"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
// treeVisitor carries the package name and file name
 | 
						|
// for passing it to the imports map, and the fileSet for
 | 
						|
// retrieving the token.Position.
 | 
						|
type treeVisitor struct {
 | 
						|
	p                     *Parser
 | 
						|
	fileSet               *token.FileSet
 | 
						|
	packageName, fileName string
 | 
						|
}
 | 
						|
 | 
						|
// Visit browses the AST tree for strings that could be potentially
 | 
						|
// replaced by constants.
 | 
						|
// A map of existing constants is built as well (-match-constant).
 | 
						|
func (v *treeVisitor) Visit(node ast.Node) ast.Visitor {
 | 
						|
	if node == nil {
 | 
						|
		return v
 | 
						|
	}
 | 
						|
 | 
						|
	// A single case with "ast.BasicLit" would be much easier
 | 
						|
	// but then we wouldn't be able to tell in which context
 | 
						|
	// the string is defined (could be a constant definition).
 | 
						|
	switch t := node.(type) {
 | 
						|
	// Scan for constants in an attempt to match strings with existing constants
 | 
						|
	case *ast.GenDecl:
 | 
						|
		if !v.p.matchConstant {
 | 
						|
			return v
 | 
						|
		}
 | 
						|
		if t.Tok != token.CONST {
 | 
						|
			return v
 | 
						|
		}
 | 
						|
 | 
						|
		for _, spec := range t.Specs {
 | 
						|
			val := spec.(*ast.ValueSpec)
 | 
						|
			for i, str := range val.Values {
 | 
						|
				lit, ok := str.(*ast.BasicLit)
 | 
						|
				if !ok || !v.isSupported(lit.Kind) {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
 | 
						|
				v.addConst(val.Names[i].Name, lit.Value, val.Names[i].Pos())
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
	// foo := "moo"
 | 
						|
	case *ast.AssignStmt:
 | 
						|
		for _, rhs := range t.Rhs {
 | 
						|
			lit, ok := rhs.(*ast.BasicLit)
 | 
						|
			if !ok || !v.isSupported(lit.Kind) {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
 | 
						|
			v.addString(lit.Value, rhs.(*ast.BasicLit).Pos())
 | 
						|
		}
 | 
						|
 | 
						|
	// if foo == "moo"
 | 
						|
	case *ast.BinaryExpr:
 | 
						|
		if t.Op != token.EQL && t.Op != token.NEQ {
 | 
						|
			return v
 | 
						|
		}
 | 
						|
 | 
						|
		var lit *ast.BasicLit
 | 
						|
		var ok bool
 | 
						|
 | 
						|
		lit, ok = t.X.(*ast.BasicLit)
 | 
						|
		if ok && v.isSupported(lit.Kind) {
 | 
						|
			v.addString(lit.Value, lit.Pos())
 | 
						|
		}
 | 
						|
 | 
						|
		lit, ok = t.Y.(*ast.BasicLit)
 | 
						|
		if ok && v.isSupported(lit.Kind) {
 | 
						|
			v.addString(lit.Value, lit.Pos())
 | 
						|
		}
 | 
						|
 | 
						|
	// case "foo":
 | 
						|
	case *ast.CaseClause:
 | 
						|
		for _, item := range t.List {
 | 
						|
			lit, ok := item.(*ast.BasicLit)
 | 
						|
			if ok && v.isSupported(lit.Kind) {
 | 
						|
				v.addString(lit.Value, lit.Pos())
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
	// return "boo"
 | 
						|
	case *ast.ReturnStmt:
 | 
						|
		for _, item := range t.Results {
 | 
						|
			lit, ok := item.(*ast.BasicLit)
 | 
						|
			if ok && v.isSupported(lit.Kind) {
 | 
						|
				v.addString(lit.Value, lit.Pos())
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return v
 | 
						|
}
 | 
						|
 | 
						|
// addString adds a string in the map along with its position in the tree.
 | 
						|
func (v *treeVisitor) addString(str string, pos token.Pos) {
 | 
						|
	str = strings.Replace(str, `"`, "", 2)
 | 
						|
 | 
						|
	// Ignore empty strings
 | 
						|
	if len(str) == 0 {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	if len(str) < v.p.minLength {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	_, ok := v.p.strs[str]
 | 
						|
	if !ok {
 | 
						|
		v.p.strs[str] = make([]ExtendedPos, 0)
 | 
						|
	}
 | 
						|
	v.p.strs[str] = append(v.p.strs[str], ExtendedPos{
 | 
						|
		packageName: v.packageName,
 | 
						|
		Position:    v.fileSet.Position(pos),
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
// addConst adds a const in the map along with its position in the tree.
 | 
						|
func (v *treeVisitor) addConst(name string, val string, pos token.Pos) {
 | 
						|
	val = strings.Replace(val, `"`, "", 2)
 | 
						|
	v.p.consts[val] = ConstType{
 | 
						|
		Name:        name,
 | 
						|
		packageName: v.packageName,
 | 
						|
		Position:    v.fileSet.Position(pos),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (v *treeVisitor) isSupported(tk token.Token) bool {
 | 
						|
	for _, s := range v.p.supportedTokens {
 | 
						|
		if tk == s {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 |