Commit 8969b051 authored by Daniel Martí's avatar Daniel Martí

cmd/vendor: go get -u golang.org/x/tools && go mod vendor

To pick up the structtag vet fix for 1.13.

Fixes #30846.

Change-Id: I5e011a7db1ffb9435793d533097d768f209c18e0
Reviewed-on: https://go-review.googlesource.com/c/go/+/179999
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBryan C. Mills <bcmills@google.com>
parent 82521659
...@@ -8,5 +8,5 @@ require ( ...@@ -8,5 +8,5 @@ require (
golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045 golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 // indirect golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 // indirect
golang.org/x/tools v0.0.0-20190514135123-4789ca9922f0 golang.org/x/tools v0.0.0-20190602112858-2de7f9bf822c
) )
...@@ -13,5 +13,5 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h ...@@ -13,5 +13,5 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dzugGxJ/SQHoNufZJq1w= golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dzugGxJ/SQHoNufZJq1w=
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190514135123-4789ca9922f0 h1:0Bz67IMuNMofIoO/F+rX8oPltlfrAC5HU68DEyynMQg= golang.org/x/tools v0.0.0-20190602112858-2de7f9bf822c h1:8QARbM77BTyoVvSaGaoQPCYgZlVROYX1uKApKK98b+8=
golang.org/x/tools v0.0.0-20190514135123-4789ca9922f0/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190602112858-2de7f9bf822c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
...@@ -161,6 +161,15 @@ func (pass *Pass) Reportf(pos token.Pos, format string, args ...interface{}) { ...@@ -161,6 +161,15 @@ func (pass *Pass) Reportf(pos token.Pos, format string, args ...interface{}) {
pass.Report(Diagnostic{Pos: pos, Message: msg}) pass.Report(Diagnostic{Pos: pos, Message: msg})
} }
// reportNodef is a helper function that reports a Diagnostic using the
// range denoted by the AST node.
//
// WARNING: This is an experimental API and may change in the future.
func (pass *Pass) reportNodef(node ast.Node, format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
pass.Report(Diagnostic{Pos: node.Pos(), End: node.End(), Message: msg})
}
func (pass *Pass) String() string { func (pass *Pass) String() string {
return fmt.Sprintf("%s@%s", pass.Analyzer.Name, pass.Pkg.Path()) return fmt.Sprintf("%s@%s", pass.Analyzer.Name, pass.Pkg.Path())
} }
...@@ -203,13 +212,17 @@ type Fact interface { ...@@ -203,13 +212,17 @@ type Fact interface {
AFact() // dummy method to avoid type errors AFact() // dummy method to avoid type errors
} }
// A Diagnostic is a message associated with a source location. // A Diagnostic is a message associated with a source location or range.
// //
// An Analyzer may return a variety of diagnostics; the optional Category, // An Analyzer may return a variety of diagnostics; the optional Category,
// which should be a constant, may be used to classify them. // which should be a constant, may be used to classify them.
// It is primarily intended to make it easy to look up documentation. // It is primarily intended to make it easy to look up documentation.
//
// If End is provided, the diagnostic is specified to apply to the range between
// Pos and End.
type Diagnostic struct { type Diagnostic struct {
Pos token.Pos Pos token.Pos
Category string // optional End token.Pos // optional
Category string // optional
Message string Message string
} }
...@@ -323,9 +323,14 @@ func PrintPlain(fset *token.FileSet, diag analysis.Diagnostic) { ...@@ -323,9 +323,14 @@ func PrintPlain(fset *token.FileSet, diag analysis.Diagnostic) {
// -c=N: show offending line plus N lines of context. // -c=N: show offending line plus N lines of context.
if Context >= 0 { if Context >= 0 {
posn := fset.Position(diag.Pos)
end := fset.Position(diag.End)
if !end.IsValid() {
end = posn
}
data, _ := ioutil.ReadFile(posn.Filename) data, _ := ioutil.ReadFile(posn.Filename)
lines := strings.Split(string(data), "\n") lines := strings.Split(string(data), "\n")
for i := posn.Line - Context; i <= posn.Line+Context; i++ { for i := posn.Line - Context; i <= end.Line+Context; i++ {
if 1 <= i && i <= len(lines) { if 1 <= i && i <= len(lines) {
fmt.Fprintf(os.Stderr, "%d\t%s\n", i, lines[i-1]) fmt.Fprintf(os.Stderr, "%d\t%s\n", i, lines[i-1])
} }
...@@ -353,6 +358,8 @@ func (tree JSONTree) Add(fset *token.FileSet, id, name string, diags []analysis. ...@@ -353,6 +358,8 @@ func (tree JSONTree) Add(fset *token.FileSet, id, name string, diags []analysis.
Message string `json:"message"` Message string `json:"message"`
} }
var diagnostics []jsonDiagnostic var diagnostics []jsonDiagnostic
// TODO(matloob): Should the JSON diagnostics contain ranges?
// If so, how should they be formatted?
for _, f := range diags { for _, f := range diags {
diagnostics = append(diagnostics, jsonDiagnostic{ diagnostics = append(diagnostics, jsonDiagnostic{
Category: f.Category, Category: f.Category,
......
...@@ -41,7 +41,7 @@ func run(pass *analysis.Pass) (interface{}, error) { ...@@ -41,7 +41,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
} }
inspect.Preorder(nodeFilter, func(n ast.Node) { inspect.Preorder(nodeFilter, func(n ast.Node) {
styp := pass.TypesInfo.Types[n.(*ast.StructType)].Type.(*types.Struct) styp := pass.TypesInfo.Types[n.(*ast.StructType)].Type.(*types.Struct)
var seen map[[2]string]token.Pos var seen namesSeen
for i := 0; i < styp.NumFields(); i++ { for i := 0; i < styp.NumFields(); i++ {
field := styp.Field(i) field := styp.Field(i)
tag := styp.Tag(i) tag := styp.Tag(i)
...@@ -51,11 +51,38 @@ func run(pass *analysis.Pass) (interface{}, error) { ...@@ -51,11 +51,38 @@ func run(pass *analysis.Pass) (interface{}, error) {
return nil, nil return nil, nil
} }
// namesSeen keeps track of encoding tags by their key, name, and nested level
// from the initial struct. The level is taken into account because equal
// encoding key names only conflict when at the same level; otherwise, the lower
// level shadows the higher level.
type namesSeen map[uniqueName]token.Pos
type uniqueName struct {
key string // "xml" or "json"
name string // the encoding name
level int // anonymous struct nesting level
}
func (s *namesSeen) Get(key, name string, level int) (token.Pos, bool) {
if *s == nil {
*s = make(map[uniqueName]token.Pos)
}
pos, ok := (*s)[uniqueName{key, name, level}]
return pos, ok
}
func (s *namesSeen) Set(key, name string, level int, pos token.Pos) {
if *s == nil {
*s = make(map[uniqueName]token.Pos)
}
(*s)[uniqueName{key, name, level}] = pos
}
var checkTagDups = []string{"json", "xml"} var checkTagDups = []string{"json", "xml"}
var checkTagSpaces = map[string]bool{"json": true, "xml": true, "asn1": true} var checkTagSpaces = map[string]bool{"json": true, "xml": true, "asn1": true}
// checkCanonicalFieldTag checks a single struct field tag. // checkCanonicalFieldTag checks a single struct field tag.
func checkCanonicalFieldTag(pass *analysis.Pass, field *types.Var, tag string, seen *map[[2]string]token.Pos) { func checkCanonicalFieldTag(pass *analysis.Pass, field *types.Var, tag string, seen *namesSeen) {
switch pass.Pkg.Path() { switch pass.Pkg.Path() {
case "encoding/json", "encoding/xml": case "encoding/json", "encoding/xml":
// These packages know how to use their own APIs. // These packages know how to use their own APIs.
...@@ -64,7 +91,7 @@ func checkCanonicalFieldTag(pass *analysis.Pass, field *types.Var, tag string, s ...@@ -64,7 +91,7 @@ func checkCanonicalFieldTag(pass *analysis.Pass, field *types.Var, tag string, s
} }
for _, key := range checkTagDups { for _, key := range checkTagDups {
checkTagDuplicates(pass, tag, key, field, field, seen) checkTagDuplicates(pass, tag, key, field, field, seen, 1)
} }
if err := validateStructTag(tag); err != nil { if err := validateStructTag(tag); err != nil {
...@@ -95,28 +122,29 @@ func checkCanonicalFieldTag(pass *analysis.Pass, field *types.Var, tag string, s ...@@ -95,28 +122,29 @@ func checkCanonicalFieldTag(pass *analysis.Pass, field *types.Var, tag string, s
// checkTagDuplicates checks a single struct field tag to see if any tags are // checkTagDuplicates checks a single struct field tag to see if any tags are
// duplicated. nearest is the field that's closest to the field being checked, // duplicated. nearest is the field that's closest to the field being checked,
// while still being part of the top-level struct type. // while still being part of the top-level struct type.
func checkTagDuplicates(pass *analysis.Pass, tag, key string, nearest, field *types.Var, seen *map[[2]string]token.Pos) { func checkTagDuplicates(pass *analysis.Pass, tag, key string, nearest, field *types.Var, seen *namesSeen, level int) {
val := reflect.StructTag(tag).Get(key) val := reflect.StructTag(tag).Get(key)
if val == "-" { if val == "-" {
// Ignored, even if the field is anonymous. // Ignored, even if the field is anonymous.
return return
} }
if val == "" || val[0] == ',' { if val == "" || val[0] == ',' {
if field.Anonymous() { if !field.Anonymous() {
typ, ok := field.Type().Underlying().(*types.Struct) // Ignored if the field isn't anonymous.
if !ok { return
return }
} typ, ok := field.Type().Underlying().(*types.Struct)
for i := 0; i < typ.NumFields(); i++ { if !ok {
field := typ.Field(i) return
if !field.Exported() { }
continue for i := 0; i < typ.NumFields(); i++ {
} field := typ.Field(i)
tag := typ.Tag(i) if !field.Exported() {
checkTagDuplicates(pass, tag, key, nearest, field, seen) continue
} }
tag := typ.Tag(i)
checkTagDuplicates(pass, tag, key, nearest, field, seen, level+1)
} }
// Ignored if the field isn't anonymous.
return return
} }
if key == "xml" && field.Name() == "XMLName" { if key == "xml" && field.Name() == "XMLName" {
...@@ -139,10 +167,7 @@ func checkTagDuplicates(pass *analysis.Pass, tag, key string, nearest, field *ty ...@@ -139,10 +167,7 @@ func checkTagDuplicates(pass *analysis.Pass, tag, key string, nearest, field *ty
} }
val = val[:i] val = val[:i]
} }
if *seen == nil { if pos, ok := seen.Get(key, val, level); ok {
*seen = map[[2]string]token.Pos{}
}
if pos, ok := (*seen)[[2]string{key, val}]; ok {
alsoPos := pass.Fset.Position(pos) alsoPos := pass.Fset.Position(pos)
alsoPos.Column = 0 alsoPos.Column = 0
...@@ -161,7 +186,7 @@ func checkTagDuplicates(pass *analysis.Pass, tag, key string, nearest, field *ty ...@@ -161,7 +186,7 @@ func checkTagDuplicates(pass *analysis.Pass, tag, key string, nearest, field *ty
pass.Reportf(nearest.Pos(), "struct field %s repeats %s tag %q also at %s", field.Name(), key, val, alsoPos) pass.Reportf(nearest.Pos(), "struct field %s repeats %s tag %q also at %s", field.Name(), key, val, alsoPos)
} else { } else {
(*seen)[[2]string{key, val}] = field.Pos() seen.Set(key, val, level, field.Pos())
} }
} }
......
...@@ -20,7 +20,10 @@ const Doc = `check for common mistaken usages of tests and examples ...@@ -20,7 +20,10 @@ const Doc = `check for common mistaken usages of tests and examples
The tests checker walks Test, Benchmark and Example functions checking The tests checker walks Test, Benchmark and Example functions checking
malformed names, wrong signatures and examples documenting non-existent malformed names, wrong signatures and examples documenting non-existent
identifiers.` identifiers.
Please see the documentation for package testing in golang.org/pkg/testing
for the conventions that are enforced for Tests, Benchmarks, and Examples.`
var Analyzer = &analysis.Analyzer{ var Analyzer = &analysis.Analyzer{
Name: "tests", Name: "tests",
......
...@@ -26,7 +26,7 @@ golang.org/x/crypto/ssh/terminal ...@@ -26,7 +26,7 @@ golang.org/x/crypto/ssh/terminal
# golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 # golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82
golang.org/x/sys/unix golang.org/x/sys/unix
golang.org/x/sys/windows golang.org/x/sys/windows
# golang.org/x/tools v0.0.0-20190514135123-4789ca9922f0 # golang.org/x/tools v0.0.0-20190602112858-2de7f9bf822c
golang.org/x/tools/go/analysis golang.org/x/tools/go/analysis
golang.org/x/tools/go/analysis/internal/analysisflags golang.org/x/tools/go/analysis/internal/analysisflags
golang.org/x/tools/go/analysis/internal/facts golang.org/x/tools/go/analysis/internal/facts
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment