github.com/karrick/go@v0.0.0-20170817181416-d5b0ec858b37/src/cmd/vet/composite.go (about) 1 // Copyright 2012 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // This file contains the test for unkeyed struct literals. 6 7 package main 8 9 import ( 10 "cmd/vet/internal/whitelist" 11 "flag" 12 "go/ast" 13 "go/types" 14 "strings" 15 ) 16 17 var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite white list; for testing only") 18 19 func init() { 20 register("composites", 21 "check that composite literals used field-keyed elements", 22 checkUnkeyedLiteral, 23 compositeLit) 24 } 25 26 // checkUnkeyedLiteral checks if a composite literal is a struct literal with 27 // unkeyed fields. 28 func checkUnkeyedLiteral(f *File, node ast.Node) { 29 cl := node.(*ast.CompositeLit) 30 31 typ := f.pkg.types[cl].Type 32 if typ == nil { 33 // cannot determine composite literals' type, skip it 34 return 35 } 36 typeName := typ.String() 37 if *compositeWhiteList && whitelist.UnkeyedLiteral[typeName] { 38 // skip whitelisted types 39 return 40 } 41 if _, ok := typ.Underlying().(*types.Struct); !ok { 42 // skip non-struct composite literals 43 return 44 } 45 if isLocalType(f, typeName) { 46 // allow unkeyed locally defined composite literal 47 return 48 } 49 50 // check if the CompositeLit contains an unkeyed field 51 allKeyValue := true 52 for _, e := range cl.Elts { 53 if _, ok := e.(*ast.KeyValueExpr); !ok { 54 allKeyValue = false 55 break 56 } 57 } 58 if allKeyValue { 59 // all the composite literal fields are keyed 60 return 61 } 62 63 f.Badf(cl.Pos(), "%s composite literal uses unkeyed fields", typeName) 64 } 65 66 func isLocalType(f *File, typeName string) bool { 67 if strings.HasPrefix(typeName, "struct{") { 68 // struct literals are local types 69 return true 70 } 71 72 pkgname := f.pkg.path 73 if strings.HasPrefix(typeName, pkgname+".") { 74 return true 75 } 76 77 // treat types as local inside test packages with _test name suffix 78 if strings.HasSuffix(pkgname, "_test") { 79 pkgname = pkgname[:len(pkgname)-len("_test")] 80 } 81 return strings.HasPrefix(typeName, pkgname+".") 82 }