mirror of
https://github.com/golang/go
synced 2024-11-05 17:36:15 -07:00
f895b43688
This removes much of the AST logic out of main.go, and makes it easier to build custom vet binaries The trade-off in this change is for flexibility. There's very little change in the per-check files, a lot less code in main.go (specifically the AST walking logic has shrunk), and it makes it much easier to build custom vet binaries simply by dropping new source files in the directory. LGTM=josharian, r R=r, josharian, kamil.kisiel CC=golang-codereviews https://golang.org/cl/83400043
126 lines
3.2 KiB
Go
126 lines
3.2 KiB
Go
// Copyright 2012 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.
|
|
|
|
// This file contains the test for unkeyed struct literals.
|
|
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"go/ast"
|
|
"strings"
|
|
|
|
"code.google.com/p/go.tools/cmd/vet/whitelist"
|
|
)
|
|
|
|
var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite white list; for testing only")
|
|
|
|
func init() {
|
|
register("composites",
|
|
"check that composite literals used field-keyed elements",
|
|
checkUnkeyedLiteral,
|
|
compositeLit)
|
|
}
|
|
|
|
// checkUnkeyedLiteral checks if a composite literal is a struct literal with
|
|
// unkeyed fields.
|
|
func checkUnkeyedLiteral(f *File, node ast.Node) {
|
|
c := node.(*ast.CompositeLit)
|
|
typ := c.Type
|
|
for {
|
|
if typ1, ok := c.Type.(*ast.ParenExpr); ok {
|
|
typ = typ1
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
|
|
switch typ.(type) {
|
|
case *ast.ArrayType:
|
|
return
|
|
case *ast.MapType:
|
|
return
|
|
case *ast.StructType:
|
|
return // a literal struct type does not need to use keys
|
|
case *ast.Ident:
|
|
// A simple type name like t or T does not need keys either,
|
|
// since it is almost certainly declared in the current package.
|
|
// (The exception is names being used via import . "pkg", but
|
|
// those are already breaking the Go 1 compatibility promise,
|
|
// so not reporting potential additional breakage seems okay.)
|
|
return
|
|
}
|
|
|
|
// Otherwise the type is a selector like pkg.Name.
|
|
// We only care if pkg.Name is a struct, not if it's a map, array, or slice.
|
|
isStruct, typeString := f.pkg.isStruct(c)
|
|
if !isStruct {
|
|
return
|
|
}
|
|
|
|
if typeString == "" { // isStruct doesn't know
|
|
typeString = f.gofmt(typ)
|
|
}
|
|
|
|
// It's a struct, or we can't tell it's not a struct because we don't have types.
|
|
|
|
// Check if the CompositeLit contains an unkeyed field.
|
|
allKeyValue := true
|
|
for _, e := range c.Elts {
|
|
if _, ok := e.(*ast.KeyValueExpr); !ok {
|
|
allKeyValue = false
|
|
break
|
|
}
|
|
}
|
|
if allKeyValue {
|
|
return
|
|
}
|
|
|
|
// Check that the CompositeLit's type has the form pkg.Typ.
|
|
s, ok := c.Type.(*ast.SelectorExpr)
|
|
if !ok {
|
|
return
|
|
}
|
|
pkg, ok := s.X.(*ast.Ident)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
// Convert the package name to an import path, and compare to a whitelist.
|
|
path := pkgPath(f, pkg.Name)
|
|
if path == "" {
|
|
f.Badf(c.Pos(), "unresolvable package for %s.%s literal", pkg.Name, s.Sel.Name)
|
|
return
|
|
}
|
|
typeName := path + "." + s.Sel.Name
|
|
if *compositeWhiteList && whitelist.UnkeyedLiteral[typeName] {
|
|
return
|
|
}
|
|
|
|
f.Bad(c.Pos(), typeString+" composite literal uses unkeyed fields")
|
|
}
|
|
|
|
// pkgPath returns the import path "image/png" for the package name "png".
|
|
//
|
|
// This is based purely on syntax and convention, and not on the imported
|
|
// package's contents. It will be incorrect if a package name differs from the
|
|
// leaf element of the import path, or if the package was a dot import.
|
|
func pkgPath(f *File, pkgName string) (path string) {
|
|
for _, x := range f.file.Imports {
|
|
s := strings.Trim(x.Path.Value, `"`)
|
|
if x.Name != nil {
|
|
// Catch `import pkgName "foo/bar"`.
|
|
if x.Name.Name == pkgName {
|
|
return s
|
|
}
|
|
} else {
|
|
// Catch `import "pkgName"` or `import "foo/bar/pkgName"`.
|
|
if s == pkgName || strings.HasSuffix(s, "/"+pkgName) {
|
|
return s
|
|
}
|
|
}
|
|
}
|
|
return ""
|
|
}
|