Flag names of type time.Duration that have unit suffixes.
This catches people writing code such as
var timeoutSecs = 5 * time.Second
diff --git a/lint.go b/lint.go
index 57e469d..35c0db1 100644
--- a/lint.go
+++ b/lint.go
@@ -182,6 +182,7 @@
f.lintMake()
f.lintErrorReturn()
f.lintUnexportedReturn()
+ f.lintTimeNames()
}
type link string
@@ -262,6 +263,15 @@
return p.typesInfo.TypeOf(expr)
}
+func (p *pkg) isNamedType(typ types.Type, importPath, name string) bool {
+ n, ok := typ.(*types.Named)
+ if !ok {
+ return false
+ }
+ tn := n.Obj()
+ return tn != nil && tn.Pkg() != nil && tn.Pkg().Path() == importPath && tn.Name() == name
+}
+
// scopeOf returns the tightest scope encompassing id.
func (p *pkg) scopeOf(id *ast.Ident) *types.Scope {
var scope *types.Scope
@@ -1260,6 +1270,49 @@
return true
}
+// timeSuffixes is a list of name suffixes that imply a time unit.
+// This is not an exhaustive list.
+var timeSuffixes = []string{
+ "Sec", "Secs", "Seconds",
+ "Msec", "Msecs",
+ "Milli", "Millis", "Milliseconds",
+ "Usec", "Usecs", "Microseconds",
+ "MS", "Ms",
+}
+
+func (f *file) lintTimeNames() {
+ f.walk(func(node ast.Node) bool {
+ v, ok := node.(*ast.ValueSpec)
+ if !ok {
+ return true
+ }
+ for _, name := range v.Names {
+ origTyp := f.pkg.typeOf(name)
+ // Look for time.Duration or *time.Duration;
+ // the latter is common when using flag.Duration.
+ typ := origTyp
+ if pt, ok := typ.(*types.Pointer); ok {
+ typ = pt.Elem()
+ }
+ if !f.pkg.isNamedType(typ, "time", "Duration") {
+ continue
+ }
+ suffix := ""
+ for _, suf := range timeSuffixes {
+ if strings.HasSuffix(name.Name, suf) {
+ suffix = suf
+ break
+ }
+ }
+ if suffix == "" {
+ continue
+ }
+ f.errorf(v, 0.9, category("time"), "var %s is of type %v; don't use unit-specific suffix %q", name.Name, origTyp, suffix)
+ }
+ return true
+ })
+}
+
func receiverType(fn *ast.FuncDecl) string {
switch e := fn.Recv.List[0].Type.(type) {
case *ast.Ident:
diff --git a/testdata/time.go b/testdata/time.go
new file mode 100644
index 0000000..9271acb
--- /dev/null
+++ b/testdata/time.go
@@ -0,0 +1,13 @@
+// Test of time suffixes.
+
+// Package foo ...
+package foo
+
+import (
+ "flag"
+ "time"
+)
+
+var rpcTimeoutMsec = flag.Duration("rpc_timeout", 100*time.Millisecond, "some flag") // MATCH /Msec.*\*time.Duration/
+
+var timeoutSecs = 5 * time.Second // MATCH /Secs.*time.Duration/