| // 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 ( |
| "cmd/vet/internal/whitelist" |
| "flag" |
| "go/ast" |
| "go/types" |
| "strings" |
| ) |
| |
| 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) { |
| cl := node.(*ast.CompositeLit) |
| |
| typ := f.pkg.types[cl].Type |
| if typ == nil { |
| // cannot determine composite literals' type, skip it |
| return |
| } |
| typeName := typ.String() |
| if *compositeWhiteList && whitelist.UnkeyedLiteral[typeName] { |
| // skip whitelisted types |
| return |
| } |
| under := typ.Underlying() |
| for { |
| ptr, ok := under.(*types.Pointer) |
| if !ok { |
| break |
| } |
| under = ptr.Elem().Underlying() |
| } |
| if _, ok := under.(*types.Struct); !ok { |
| // skip non-struct composite literals |
| return |
| } |
| if isLocalType(f, typeName) { |
| // allow unkeyed locally defined composite literal |
| return |
| } |
| |
| // check if the CompositeLit contains an unkeyed field |
| allKeyValue := true |
| for _, e := range cl.Elts { |
| if _, ok := e.(*ast.KeyValueExpr); !ok { |
| allKeyValue = false |
| break |
| } |
| } |
| if allKeyValue { |
| // all the composite literal fields are keyed |
| return |
| } |
| |
| f.Badf(cl.Pos(), "%s composite literal uses unkeyed fields", typeName) |
| } |
| |
| func isLocalType(f *File, typeName string) bool { |
| if strings.HasPrefix(typeName, "struct{") { |
| // struct literals are local types |
| return true |
| } |
| |
| // make *foo.bar, **foo.bar, etc match with the "foo." prefix |
| // below |
| typeName = strings.TrimLeft(typeName, "*") |
| |
| pkgname := f.pkg.path |
| if strings.HasPrefix(typeName, pkgname+".") { |
| return true |
| } |
| |
| // treat types as local inside test packages with _test name suffix |
| if strings.HasSuffix(pkgname, "_test") { |
| pkgname = pkgname[:len(pkgname)-len("_test")] |
| } |
| return strings.HasPrefix(typeName, pkgname+".") |
| } |