src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

st1011.go (2269B)


      1 package st1011
      2 
      3 import (
      4 	"fmt"
      5 	"go/ast"
      6 	"go/token"
      7 	"strings"
      8 
      9 	"honnef.co/go/tools/analysis/code"
     10 	"honnef.co/go/tools/analysis/lint"
     11 	"honnef.co/go/tools/analysis/report"
     12 	"honnef.co/go/tools/go/types/typeutil"
     13 
     14 	"golang.org/x/tools/go/analysis"
     15 	"golang.org/x/tools/go/analysis/passes/inspect"
     16 )
     17 
     18 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
     19 	Analyzer: &analysis.Analyzer{
     20 		Name:     "ST1011",
     21 		Run:      run,
     22 		Requires: []*analysis.Analyzer{inspect.Analyzer},
     23 	},
     24 	Doc: &lint.RawDocumentation{
     25 		Title: `Poorly chosen name for variable of type \'time.Duration\'`,
     26 		Text: `\'time.Duration\' values represent an amount of time, which is represented
     27 as a count of nanoseconds. An expression like \'5 * time.Microsecond\'
     28 yields the value \'5000\'. It is therefore not appropriate to suffix a
     29 variable of type \'time.Duration\' with any time unit, such as \'Msec\' or
     30 \'Milli\'.`,
     31 		Since:   `2019.1`,
     32 		MergeIf: lint.MergeIfAny,
     33 	},
     34 })
     35 
     36 var Analyzer = SCAnalyzer.Analyzer
     37 
     38 func run(pass *analysis.Pass) (any, error) {
     39 	suffixes := []string{
     40 		"Sec", "Secs", "Seconds",
     41 		"Msec", "Msecs",
     42 		"Milli", "Millis", "Milliseconds",
     43 		"Usec", "Usecs", "Microseconds",
     44 		"MS", "Ms",
     45 	}
     46 	fn := func(names []*ast.Ident) {
     47 		for _, name := range names {
     48 			if _, ok := pass.TypesInfo.Defs[name]; !ok {
     49 				continue
     50 			}
     51 			T := pass.TypesInfo.TypeOf(name)
     52 			if !typeutil.IsTypeWithName(T, "time.Duration") && !typeutil.IsPointerToTypeWithName(T, "time.Duration") {
     53 				continue
     54 			}
     55 			for _, suffix := range suffixes {
     56 				if strings.HasSuffix(name.Name, suffix) {
     57 					report.Report(pass, name, fmt.Sprintf("var %s is of type %v; don't use unit-specific suffix %q", name.Name, T, suffix))
     58 					break
     59 				}
     60 			}
     61 		}
     62 	}
     63 
     64 	fn2 := func(node ast.Node) {
     65 		switch node := node.(type) {
     66 		case *ast.ValueSpec:
     67 			fn(node.Names)
     68 		case *ast.FieldList:
     69 			for _, field := range node.List {
     70 				fn(field.Names)
     71 			}
     72 		case *ast.AssignStmt:
     73 			if node.Tok != token.DEFINE {
     74 				break
     75 			}
     76 			var names []*ast.Ident
     77 			for _, lhs := range node.Lhs {
     78 				if lhs, ok := lhs.(*ast.Ident); ok {
     79 					names = append(names, lhs)
     80 				}
     81 			}
     82 			fn(names)
     83 		}
     84 	}
     85 
     86 	code.Preorder(pass, fn2, (*ast.ValueSpec)(nil), (*ast.FieldList)(nil), (*ast.AssignStmt)(nil))
     87 	return nil, nil
     88 }