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.
5 // Package composite defines an Analyzer that checks for unkeyed
14 "golang.org/x/tools/go/analysis"
15 "golang.org/x/tools/go/analysis/passes/inspect"
16 "golang.org/x/tools/go/ast/inspector"
19 const Doc = `check for unkeyed composite literals
21 This analyzer reports a diagnostic for composite literals of struct
22 types imported from another package that do not use the field-keyed
23 syntax. Such literals are fragile because the addition of a new field
24 (even if unexported) to the struct will cause compilation to fail.
28 err = &net.DNSConfigError{err}
30 should be replaced by:
32 err = &net.DNSConfigError{Err: err}
35 var Analyzer = &analysis.Analyzer{
38 Requires: []*analysis.Analyzer{inspect.Analyzer},
39 RunDespiteErrors: true,
46 Analyzer.Flags.BoolVar(&whitelist, "whitelist", whitelist, "use composite white list; for testing only")
49 // runUnkeyedLiteral checks if a composite literal is a struct literal with
51 func run(pass *analysis.Pass) (interface{}, error) {
52 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
54 nodeFilter := []ast.Node{
55 (*ast.CompositeLit)(nil),
57 inspect.Preorder(nodeFilter, func(n ast.Node) {
58 cl := n.(*ast.CompositeLit)
60 typ := pass.TypesInfo.Types[cl].Type
62 // cannot determine composite literals' type, skip it
65 typeName := typ.String()
66 if whitelist && unkeyedLiteral[typeName] {
67 // skip whitelisted types
70 under := typ.Underlying()
72 ptr, ok := under.(*types.Pointer)
76 under = ptr.Elem().Underlying()
78 if _, ok := under.(*types.Struct); !ok {
79 // skip non-struct composite literals
82 if isLocalType(pass, typ) {
83 // allow unkeyed locally defined composite literal
87 // check if the CompositeLit contains an unkeyed field
89 for _, e := range cl.Elts {
90 if _, ok := e.(*ast.KeyValueExpr); !ok {
96 // all the composite literal fields are keyed
100 pass.ReportRangef(cl, "%s composite literal uses unkeyed fields", typeName)
105 func isLocalType(pass *analysis.Pass, typ types.Type) bool {
106 switch x := typ.(type) {
108 // struct literals are local types
111 return isLocalType(pass, x.Elem())
113 // names in package foo are local to foo_test too
114 return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test")