github.com/alash3al/go@v0.0.0-20150827002835-d497eeb00540/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/whitelist" 11 "flag" 12 "go/ast" 13 "strings" 14 ) 15 16 var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite white list; for testing only") 17 18 func init() { 19 register("composites", 20 "check that composite literals used field-keyed elements", 21 checkUnkeyedLiteral, 22 compositeLit) 23 } 24 25 // checkUnkeyedLiteral checks if a composite literal is a struct literal with 26 // unkeyed fields. 27 func checkUnkeyedLiteral(f *File, node ast.Node) { 28 c := node.(*ast.CompositeLit) 29 typ := c.Type 30 for { 31 if typ1, ok := c.Type.(*ast.ParenExpr); ok { 32 typ = typ1 33 continue 34 } 35 break 36 } 37 38 switch typ.(type) { 39 case *ast.ArrayType: 40 return 41 case *ast.MapType: 42 return 43 case *ast.StructType: 44 return // a literal struct type does not need to use keys 45 case *ast.Ident: 46 // A simple type name like t or T does not need keys either, 47 // since it is almost certainly declared in the current package. 48 // (The exception is names being used via import . "pkg", but 49 // those are already breaking the Go 1 compatibility promise, 50 // so not reporting potential additional breakage seems okay.) 51 return 52 } 53 54 // Otherwise the type is a selector like pkg.Name. 55 // We only care if pkg.Name is a struct, not if it's a map, array, or slice. 56 isStruct, typeString := f.pkg.isStruct(c) 57 if !isStruct { 58 return 59 } 60 61 if typeString == "" { // isStruct doesn't know 62 typeString = f.gofmt(typ) 63 } 64 65 // It's a struct, or we can't tell it's not a struct because we don't have types. 66 67 // Check if the CompositeLit contains an unkeyed field. 68 allKeyValue := true 69 for _, e := range c.Elts { 70 if _, ok := e.(*ast.KeyValueExpr); !ok { 71 allKeyValue = false 72 break 73 } 74 } 75 if allKeyValue { 76 return 77 } 78 79 // Check that the CompositeLit's type has the form pkg.Typ. 80 s, ok := c.Type.(*ast.SelectorExpr) 81 if !ok { 82 return 83 } 84 pkg, ok := s.X.(*ast.Ident) 85 if !ok { 86 return 87 } 88 89 // Convert the package name to an import path, and compare to a whitelist. 90 path := pkgPath(f, pkg.Name) 91 if path == "" { 92 f.Badf(c.Pos(), "unresolvable package for %s.%s literal", pkg.Name, s.Sel.Name) 93 return 94 } 95 typeName := path + "." + s.Sel.Name 96 if *compositeWhiteList && whitelist.UnkeyedLiteral[typeName] { 97 return 98 } 99 100 f.Bad(c.Pos(), typeString+" composite literal uses unkeyed fields") 101 } 102 103 // pkgPath returns the import path "image/png" for the package name "png". 104 // 105 // This is based purely on syntax and convention, and not on the imported 106 // package's contents. It will be incorrect if a package name differs from the 107 // leaf element of the import path, or if the package was a dot import. 108 func pkgPath(f *File, pkgName string) (path string) { 109 for _, x := range f.file.Imports { 110 s := strings.Trim(x.Path.Value, `"`) 111 if x.Name != nil { 112 // Catch `import pkgName "foo/bar"`. 113 if x.Name.Name == pkgName { 114 return s 115 } 116 } else { 117 // Catch `import "pkgName"` or `import "foo/bar/pkgName"`. 118 if s == pkgName || strings.HasSuffix(s, "/"+pkgName) { 119 return s 120 } 121 } 122 } 123 return "" 124 }