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  }