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

     1  package rule
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/types"
     7  	"strings"
     8  
     9  	"github.com/mgechev/revive/lint"
    10  )
    11  
    12  // TimeNamingRule lints given else constructs.
    13  type TimeNamingRule struct{}
    14  
    15  // Apply applies the rule to given file.
    16  func (r *TimeNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
    17  	var failures []lint.Failure
    18  
    19  	onFailure := func(failure lint.Failure) {
    20  		failures = append(failures, failure)
    21  	}
    22  
    23  	w := &lintTimeNames{file, onFailure}
    24  
    25  	file.Pkg.TypeCheck()
    26  	ast.Walk(w, file.AST)
    27  	return failures
    28  }
    29  
    30  // Name returns the rule name.
    31  func (r *TimeNamingRule) Name() string {
    32  	return "time-naming"
    33  }
    34  
    35  type lintTimeNames struct {
    36  	file      *lint.File
    37  	onFailure func(lint.Failure)
    38  }
    39  
    40  func (w *lintTimeNames) Visit(node ast.Node) ast.Visitor {
    41  	v, ok := node.(*ast.ValueSpec)
    42  	if !ok {
    43  		return w
    44  	}
    45  	for _, name := range v.Names {
    46  		origTyp := w.file.Pkg.TypeOf(name)
    47  		// Look for time.Duration or *time.Duration;
    48  		// the latter is common when using flag.Duration.
    49  		typ := origTyp
    50  		if pt, ok := typ.(*types.Pointer); ok {
    51  			typ = pt.Elem()
    52  		}
    53  		if !isNamedType(typ, "time", "Duration") {
    54  			continue
    55  		}
    56  		suffix := ""
    57  		for _, suf := range timeSuffixes {
    58  			if strings.HasSuffix(name.Name, suf) {
    59  				suffix = suf
    60  				break
    61  			}
    62  		}
    63  		if suffix == "" {
    64  			continue
    65  		}
    66  		w.onFailure(lint.Failure{
    67  			Category:   "time",
    68  			Confidence: 0.9,
    69  			Node:       v,
    70  			Failure:    fmt.Sprintf("var %s is of type %v; don't use unit-specific suffix %q", name.Name, origTyp, suffix),
    71  		})
    72  	}
    73  	return w
    74  }
    75  
    76  // timeSuffixes is a list of name suffixes that imply a time unit.
    77  // This is not an exhaustive list.
    78  var timeSuffixes = []string{
    79  	"Sec", "Secs", "Seconds",
    80  	"Msec", "Msecs",
    81  	"Milli", "Millis", "Milliseconds",
    82  	"Usec", "Usecs", "Microseconds",
    83  	"MS", "Ms",
    84  }
    85  
    86  func isNamedType(typ types.Type, importPath, name string) bool {
    87  	n, ok := typ.(*types.Named)
    88  	if !ok {
    89  		return false
    90  	}
    91  	tn := n.Obj()
    92  	return tn != nil && tn.Pkg() != nil && tn.Pkg().Path() == importPath && tn.Name() == name
    93  }