mirror of
https://github.com/golang/go
synced 2024-09-29 14:14:29 -06:00
cmd/vet: enable timeformat analyzer
Fixes #48801 Change-Id: I6230b62f77252a9a694b79caacb38ef15af36e39 Reviewed-on: https://go-review.googlesource.com/c/go/+/450495 TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com> Reviewed-by: Ian Lance Taylor <iant@google.com> Run-TryBot: Tim King <taking@google.com>
This commit is contained in:
parent
6a9c76524f
commit
f977ffe82d
129
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/timeformat/timeformat.go
generated
vendored
Normal file
129
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/timeformat/timeformat.go
generated
vendored
Normal file
@ -0,0 +1,129 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package timeformat defines an Analyzer that checks for the use
|
||||
// of time.Format or time.Parse calls with a bad format.
|
||||
package timeformat
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
const badFormat = "2006-02-01"
|
||||
const goodFormat = "2006-01-02"
|
||||
|
||||
const Doc = `check for calls of (time.Time).Format or time.Parse with 2006-02-01
|
||||
|
||||
The timeformat checker looks for time formats with the 2006-02-01 (yyyy-dd-mm)
|
||||
format. Internationally, "yyyy-dd-mm" does not occur in common calendar date
|
||||
standards, and so it is more likely that 2006-01-02 (yyyy-mm-dd) was intended.
|
||||
`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "timeformat",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.CallExpr)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
call := n.(*ast.CallExpr)
|
||||
fn, ok := typeutil.Callee(pass.TypesInfo, call).(*types.Func)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if !isTimeDotFormat(fn) && !isTimeDotParse(fn) {
|
||||
return
|
||||
}
|
||||
if len(call.Args) > 0 {
|
||||
arg := call.Args[0]
|
||||
badAt := badFormatAt(pass.TypesInfo, arg)
|
||||
|
||||
if badAt > -1 {
|
||||
// Check if it's a literal string, otherwise we can't suggest a fix.
|
||||
if _, ok := arg.(*ast.BasicLit); ok {
|
||||
pos := int(arg.Pos()) + badAt + 1 // +1 to skip the " or `
|
||||
end := pos + len(badFormat)
|
||||
|
||||
pass.Report(analysis.Diagnostic{
|
||||
Pos: token.Pos(pos),
|
||||
End: token.Pos(end),
|
||||
Message: badFormat + " should be " + goodFormat,
|
||||
SuggestedFixes: []analysis.SuggestedFix{{
|
||||
Message: "Replace " + badFormat + " with " + goodFormat,
|
||||
TextEdits: []analysis.TextEdit{{
|
||||
Pos: token.Pos(pos),
|
||||
End: token.Pos(end),
|
||||
NewText: []byte(goodFormat),
|
||||
}},
|
||||
}},
|
||||
})
|
||||
} else {
|
||||
pass.Reportf(arg.Pos(), badFormat+" should be "+goodFormat)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func isTimeDotFormat(f *types.Func) bool {
|
||||
if f.Name() != "Format" || f.Pkg().Path() != "time" {
|
||||
return false
|
||||
}
|
||||
sig, ok := f.Type().(*types.Signature)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
// Verify that the receiver is time.Time.
|
||||
recv := sig.Recv()
|
||||
if recv == nil {
|
||||
return false
|
||||
}
|
||||
named, ok := recv.Type().(*types.Named)
|
||||
return ok && named.Obj().Name() == "Time"
|
||||
}
|
||||
|
||||
func isTimeDotParse(f *types.Func) bool {
|
||||
if f.Name() != "Parse" || f.Pkg().Path() != "time" {
|
||||
return false
|
||||
}
|
||||
// Verify that there is no receiver.
|
||||
sig, ok := f.Type().(*types.Signature)
|
||||
return ok && sig.Recv() == nil
|
||||
}
|
||||
|
||||
// badFormatAt return the start of a bad format in e or -1 if no bad format is found.
|
||||
func badFormatAt(info *types.Info, e ast.Expr) int {
|
||||
tv, ok := info.Types[e]
|
||||
if !ok { // no type info, assume good
|
||||
return -1
|
||||
}
|
||||
|
||||
t, ok := tv.Type.(*types.Basic)
|
||||
if !ok || t.Info()&types.IsString == 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
if tv.Value == nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
return strings.Index(constant.StringVal(tv.Value), badFormat)
|
||||
}
|
1
src/cmd/vendor/modules.txt
vendored
1
src/cmd/vendor/modules.txt
vendored
@ -77,6 +77,7 @@ golang.org/x/tools/go/analysis/passes/stringintconv
|
||||
golang.org/x/tools/go/analysis/passes/structtag
|
||||
golang.org/x/tools/go/analysis/passes/testinggoroutine
|
||||
golang.org/x/tools/go/analysis/passes/tests
|
||||
golang.org/x/tools/go/analysis/passes/timeformat
|
||||
golang.org/x/tools/go/analysis/passes/unmarshal
|
||||
golang.org/x/tools/go/analysis/passes/unreachable
|
||||
golang.org/x/tools/go/analysis/passes/unsafeptr
|
||||
|
@ -32,6 +32,7 @@ import (
|
||||
"golang.org/x/tools/go/analysis/passes/structtag"
|
||||
"golang.org/x/tools/go/analysis/passes/testinggoroutine"
|
||||
"golang.org/x/tools/go/analysis/passes/tests"
|
||||
"golang.org/x/tools/go/analysis/passes/timeformat"
|
||||
"golang.org/x/tools/go/analysis/passes/unmarshal"
|
||||
"golang.org/x/tools/go/analysis/passes/unreachable"
|
||||
"golang.org/x/tools/go/analysis/passes/unsafeptr"
|
||||
@ -65,6 +66,7 @@ func main() {
|
||||
structtag.Analyzer,
|
||||
tests.Analyzer,
|
||||
testinggoroutine.Analyzer,
|
||||
timeformat.Analyzer,
|
||||
unmarshal.Analyzer,
|
||||
unreachable.Analyzer,
|
||||
unsafeptr.Analyzer,
|
||||
|
Loading…
Reference in New Issue
Block a user