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 }