@@ -10,6 +10,7 @@ import (
10
10
"cmd/vet/internal/whitelist"
11
11
"flag"
12
12
"go/ast"
13
+ "go/types"
13
14
"strings"
14
15
)
15
16
@@ -25,102 +26,57 @@ func init() {
25
26
// checkUnkeyedLiteral checks if a composite literal is a struct literal with
26
27
// unkeyed fields.
27
28
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
- }
29
+ cl := node .(* ast.CompositeLit )
37
30
38
- switch typ .(type ) {
39
- case * ast.ArrayType :
40
- return
41
- case * ast.MapType :
31
+ typ := f .pkg .types [cl ].Type
32
+ if typ == nil {
33
+ // cannot determine composite literals' type, skip it
42
34
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.)
35
+ }
36
+ typeName := typ .String ()
37
+ if * compositeWhiteList && whitelist .UnkeyedLiteral [typeName ] {
38
+ // skip whitelisted types
51
39
return
52
40
}
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 {
41
+ if _ , ok := typ .Underlying ().(* types.Struct ); ! ok {
42
+ // skip non-struct composite literals
58
43
return
59
44
}
60
-
61
- if typeString == "" { // isStruct doesn't know
62
- typeString = f . gofmt ( typ )
45
+ if isLocalType ( f , typeName ) {
46
+ // allow unkeyed locally defined composite literal
47
+ return
63
48
}
64
49
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.
50
+ // check if the CompositeLit contains an unkeyed field
68
51
allKeyValue := true
69
- for _ , e := range c .Elts {
52
+ for _ , e := range cl .Elts {
70
53
if _ , ok := e .(* ast.KeyValueExpr ); ! ok {
71
- if cl , ok := e .(* ast.CompositeLit ); ! ok || cl .Type != nil {
72
- allKeyValue = false
73
- break
74
- }
54
+ allKeyValue = false
55
+ break
75
56
}
76
57
}
77
58
if allKeyValue {
59
+ // all the composite literal fields are keyed
78
60
return
79
61
}
80
62
81
- // Check that the CompositeLit's type has the form pkg.Typ.
82
- s , ok := c .Type .(* ast.SelectorExpr )
83
- if ! ok {
84
- return
85
- }
86
- pkg , ok := s .X .(* ast.Ident )
87
- if ! ok {
88
- return
89
- }
63
+ f .Badf (cl .Pos (), "%s composite literal uses unkeyed fields" , typeName )
64
+ }
90
65
91
- // Convert the package name to an import path, and compare to a whitelist.
92
- path := pkgPath (f , pkg .Name )
93
- if path == "" {
94
- f .Badf (c .Pos (), "unresolvable package for %s.%s literal" , pkg .Name , s .Sel .Name )
95
- return
96
- }
97
- typeName := path + "." + s .Sel .Name
98
- if * compositeWhiteList && whitelist .UnkeyedLiteral [typeName ] {
99
- return
66
+ func isLocalType (f * File , typeName string ) bool {
67
+ if strings .HasPrefix (typeName , "struct{" ) {
68
+ // struct literals are local types
69
+ return true
100
70
}
101
71
102
- f .Bad (c .Pos (), typeString + " composite literal uses unkeyed fields" )
103
- }
72
+ pkgname := f .pkg .path
73
+ if strings .HasPrefix (typeName , pkgname + "." ) {
74
+ return true
75
+ }
104
76
105
- // pkgPath returns the import path "image/png" for the package name "png".
106
- //
107
- // This is based purely on syntax and convention, and not on the imported
108
- // package's contents. It will be incorrect if a package name differs from the
109
- // leaf element of the import path, or if the package was a dot import.
110
- func pkgPath (f * File , pkgName string ) (path string ) {
111
- for _ , x := range f .file .Imports {
112
- s := strings .Trim (x .Path .Value , `"` )
113
- if x .Name != nil {
114
- // Catch `import pkgName "foo/bar"`.
115
- if x .Name .Name == pkgName {
116
- return s
117
- }
118
- } else {
119
- // Catch `import "pkgName"` or `import "foo/bar/pkgName"`.
120
- if s == pkgName || strings .HasSuffix (s , "/" + pkgName ) {
121
- return s
122
- }
123
- }
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" )]
124
80
}
125
- return ""
81
+ return strings . HasPrefix ( typeName , pkgname + "." )
126
82
}
0 commit comments