github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/rule/add-constant.go (about)

     1  package rule
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/mgechev/revive/lint"
    10  )
    11  
    12  const (
    13  	defaultStrLitLimit = 2
    14  	kindFLOAT          = "FLOAT"
    15  	kindINT            = "INT"
    16  	kindSTRING         = "STRING"
    17  )
    18  
    19  type whiteList map[string]map[string]bool
    20  
    21  func newWhiteList() whiteList {
    22  	return map[string]map[string]bool{kindINT: {}, kindFLOAT: {}, kindSTRING: {}}
    23  }
    24  
    25  func (wl whiteList) add(kind string, list string) {
    26  	elems := strings.Split(list, ",")
    27  	for _, e := range elems {
    28  		wl[kind][e] = true
    29  	}
    30  }
    31  
    32  // AddConstantRule lints unused params in functions.
    33  type AddConstantRule struct{}
    34  
    35  // Apply applies the rule to given file.
    36  func (r *AddConstantRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
    37  	strLitLimit := defaultStrLitLimit
    38  	var whiteList = newWhiteList()
    39  	if len(arguments) > 0 {
    40  		args, ok := arguments[0].(map[string]interface{})
    41  		if !ok {
    42  			panic(fmt.Sprintf("Invalid argument to the add-constant rule. Expecting a k,v map, got %T", arguments[0]))
    43  		}
    44  		for k, v := range args {
    45  			kind := ""
    46  			switch k {
    47  			case "allowFloats":
    48  				kind = kindFLOAT
    49  				fallthrough
    50  			case "allowInts":
    51  				if kind == "" {
    52  					kind = kindINT
    53  				}
    54  				fallthrough
    55  			case "allowStrs":
    56  				if kind == "" {
    57  					kind = kindSTRING
    58  				}
    59  				list, ok := v.(string)
    60  				if !ok {
    61  					panic(fmt.Sprintf("Invalid argument to the add-constant rule, string expected. Got '%v' (%T)", v, v))
    62  				}
    63  				whiteList.add(kind, list)
    64  			case "maxLitCount":
    65  				sl, ok := v.(string)
    66  				if !ok {
    67  					panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v' (%T)", v, v))
    68  				}
    69  
    70  				limit, err := strconv.Atoi(sl)
    71  				if err != nil {
    72  					panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v'", v))
    73  				}
    74  				strLitLimit = limit
    75  			}
    76  		}
    77  	}
    78  
    79  	var failures []lint.Failure
    80  
    81  	onFailure := func(failure lint.Failure) {
    82  		failures = append(failures, failure)
    83  	}
    84  
    85  	w := lintAddConstantRule{onFailure: onFailure, strLits: make(map[string]int, 0), strLitLimit: strLitLimit, whiteLst: whiteList}
    86  
    87  	ast.Walk(w, file.AST)
    88  
    89  	return failures
    90  }
    91  
    92  // Name returns the rule name.
    93  func (r *AddConstantRule) Name() string {
    94  	return "add-constant"
    95  }
    96  
    97  type lintAddConstantRule struct {
    98  	onFailure   func(lint.Failure)
    99  	strLits     map[string]int
   100  	strLitLimit int
   101  	whiteLst    whiteList
   102  }
   103  
   104  func (w lintAddConstantRule) Visit(node ast.Node) ast.Visitor {
   105  	switch n := node.(type) {
   106  	case *ast.GenDecl:
   107  		return nil // skip declarations
   108  	case *ast.BasicLit:
   109  		switch kind := n.Kind.String(); kind {
   110  		case kindFLOAT, kindINT:
   111  			w.checkNumLit(kind, n)
   112  		case kindSTRING:
   113  			w.checkStrLit(n)
   114  		}
   115  	}
   116  
   117  	return w
   118  
   119  }
   120  
   121  func (w lintAddConstantRule) checkStrLit(n *ast.BasicLit) {
   122  	if w.whiteLst[kindSTRING][n.Value] {
   123  		return
   124  	}
   125  
   126  	count := w.strLits[n.Value]
   127  	if count >= 0 {
   128  		w.strLits[n.Value] = count + 1
   129  		if w.strLits[n.Value] > w.strLitLimit {
   130  			w.onFailure(lint.Failure{
   131  				Confidence: 1,
   132  				Node:       n,
   133  				Category:   "style",
   134  				Failure:    fmt.Sprintf("string literal %s appears, at least, %d times, create a named constant for it", n.Value, w.strLits[n.Value]),
   135  			})
   136  			w.strLits[n.Value] = -1 // mark it to avoid failing again on the same literal
   137  		}
   138  	}
   139  }
   140  
   141  func (w lintAddConstantRule) checkNumLit(kind string, n *ast.BasicLit) {
   142  	if w.whiteLst[kind][n.Value] {
   143  		return
   144  	}
   145  
   146  	w.onFailure(lint.Failure{
   147  		Confidence: 1,
   148  		Node:       n,
   149  		Category:   "style",
   150  		Failure:    fmt.Sprintf("avoid magic numbers like '%s', create a named constant for it", n.Value),
   151  	})
   152  }