github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/cmd/vet/taglit.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 untagged struct literals. 6 7 package main 8 9 import ( 10 "flag" 11 "go/ast" 12 "strings" 13 ) 14 15 var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite white list; for testing only") 16 17 // checkUntaggedLiteral checks if a composite literal is a struct literal with 18 // untagged fields. 19 func (f *File) checkUntaggedLiteral(c *ast.CompositeLit) { 20 if !vet("composites") { 21 return 22 } 23 24 typ := c.Type 25 for { 26 if typ1, ok := c.Type.(*ast.ParenExpr); ok { 27 typ = typ1 28 continue 29 } 30 break 31 } 32 33 switch typ.(type) { 34 case *ast.ArrayType: 35 return 36 case *ast.MapType: 37 return 38 case *ast.StructType: 39 return // a literal struct type does not need to use tags 40 case *ast.Ident: 41 // A simple type name like t or T does not need tags either, 42 // since it is almost certainly declared in the current package. 43 // (The exception is names being used via import . "pkg", but 44 // those are already breaking the Go 1 compatibility promise, 45 // so not reporting potential additional breakage seems okay.) 46 return 47 } 48 49 // Otherwise the type is a selector like pkg.Name. 50 // We only care if pkg.Name is a struct, not if it's a map, array, or slice. 51 isStruct, typeString := f.pkg.isStruct(c) 52 if !isStruct { 53 return 54 } 55 56 if typeString == "" { // isStruct doesn't know 57 typeString = f.gofmt(typ) 58 } 59 60 // It's a struct, or we can't tell it's not a struct because we don't have types. 61 62 // Check if the CompositeLit contains an untagged field. 63 allKeyValue := true 64 for _, e := range c.Elts { 65 if _, ok := e.(*ast.KeyValueExpr); !ok { 66 allKeyValue = false 67 break 68 } 69 } 70 if allKeyValue { 71 return 72 } 73 74 // Check that the CompositeLit's type has the form pkg.Typ. 75 s, ok := c.Type.(*ast.SelectorExpr) 76 if !ok { 77 return 78 } 79 pkg, ok := s.X.(*ast.Ident) 80 if !ok { 81 return 82 } 83 84 // Convert the package name to an import path, and compare to a whitelist. 85 path := pkgPath(f, pkg.Name) 86 if path == "" { 87 f.Badf(c.Pos(), "unresolvable package for %s.%s literal", pkg.Name, s.Sel.Name) 88 return 89 } 90 typeName := path + "." + s.Sel.Name 91 if *compositeWhiteList && untaggedLiteralWhitelist[typeName] { 92 return 93 } 94 95 f.Warn(c.Pos(), typeString+" composite literal uses untagged fields") 96 } 97 98 // pkgPath returns the import path "image/png" for the package name "png". 99 // 100 // This is based purely on syntax and convention, and not on the imported 101 // package's contents. It will be incorrect if a package name differs from the 102 // leaf element of the import path, or if the package was a dot import. 103 func pkgPath(f *File, pkgName string) (path string) { 104 for _, x := range f.file.Imports { 105 s := strings.Trim(x.Path.Value, `"`) 106 if x.Name != nil { 107 // Catch `import pkgName "foo/bar"`. 108 if x.Name.Name == pkgName { 109 return s 110 } 111 } else { 112 // Catch `import "pkgName"` or `import "foo/bar/pkgName"`. 113 if s == pkgName || strings.HasSuffix(s, "/"+pkgName) { 114 return s 115 } 116 } 117 } 118 return "" 119 } 120 121 var untaggedLiteralWhitelist = map[string]bool{ 122 /* 123 These types are actually slices. Syntactically, we cannot tell 124 whether the Typ in pkg.Typ{1, 2, 3} is a slice or a struct, so we 125 whitelist all the standard package library's exported slice types. 126 127 find $GOROOT/src/pkg -type f | grep -v _test.go | xargs grep '^type.*\[\]' | \ 128 grep -v ' map\[' | sed 's,/[^/]*go.type,,' | sed 's,.*src/pkg/,,' | \ 129 sed 's, ,.,' | sed 's, .*,,' | grep -v '\.[a-z]' | \ 130 sort | awk '{ print "\"" $0 "\": true," }' 131 */ 132 "crypto/x509/pkix.RDNSequence": true, 133 "crypto/x509/pkix.RelativeDistinguishedNameSET": true, 134 "database/sql.RawBytes": true, 135 "debug/macho.LoadBytes": true, 136 "encoding/asn1.ObjectIdentifier": true, 137 "encoding/asn1.RawContent": true, 138 "encoding/json.RawMessage": true, 139 "encoding/xml.CharData": true, 140 "encoding/xml.Comment": true, 141 "encoding/xml.Directive": true, 142 "go/scanner.ErrorList": true, 143 "image/color.Palette": true, 144 "net.HardwareAddr": true, 145 "net.IP": true, 146 "net.IPMask": true, 147 "sort.Float64Slice": true, 148 "sort.IntSlice": true, 149 "sort.StringSlice": true, 150 "unicode.SpecialCase": true, 151 152 // These image and image/color struct types are frozen. We will never add fields to them. 153 "image/color.Alpha16": true, 154 "image/color.Alpha": true, 155 "image/color.Gray16": true, 156 "image/color.Gray": true, 157 "image/color.NRGBA64": true, 158 "image/color.NRGBA": true, 159 "image/color.RGBA64": true, 160 "image/color.RGBA": true, 161 "image/color.YCbCr": true, 162 "image.Point": true, 163 "image.Rectangle": true, 164 }