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