...

Source file src/pkg/cmd/vendor/golang.org/x/tools/go/analysis/passes/composite/composite.go

     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	// Package composite defines an Analyzer that checks for unkeyed
     6	// composite literals.
     7	package composite
     8	
     9	import (
    10		"go/ast"
    11		"go/types"
    12		"strings"
    13	
    14		"golang.org/x/tools/go/analysis"
    15		"golang.org/x/tools/go/analysis/passes/inspect"
    16		"golang.org/x/tools/go/ast/inspector"
    17	)
    18	
    19	const Doc = `check for unkeyed composite literals
    20	
    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.
    25	
    26	As an example,
    27	
    28		err = &net.DNSConfigError{err}
    29	
    30	should be replaced by:
    31	
    32		err = &net.DNSConfigError{Err: err}
    33	`
    34	
    35	var Analyzer = &analysis.Analyzer{
    36		Name:             "composites",
    37		Doc:              Doc,
    38		Requires:         []*analysis.Analyzer{inspect.Analyzer},
    39		RunDespiteErrors: true,
    40		Run:              run,
    41	}
    42	
    43	var whitelist = true
    44	
    45	func init() {
    46		Analyzer.Flags.BoolVar(&whitelist, "whitelist", whitelist, "use composite white list; for testing only")
    47	}
    48	
    49	// runUnkeyedLiteral checks if a composite literal is a struct literal with
    50	// unkeyed fields.
    51	func run(pass *analysis.Pass) (interface{}, error) {
    52		inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    53	
    54		nodeFilter := []ast.Node{
    55			(*ast.CompositeLit)(nil),
    56		}
    57		inspect.Preorder(nodeFilter, func(n ast.Node) {
    58			cl := n.(*ast.CompositeLit)
    59	
    60			typ := pass.TypesInfo.Types[cl].Type
    61			if typ == nil {
    62				// cannot determine composite literals' type, skip it
    63				return
    64			}
    65			typeName := typ.String()
    66			if whitelist && unkeyedLiteral[typeName] {
    67				// skip whitelisted types
    68				return
    69			}
    70			under := typ.Underlying()
    71			for {
    72				ptr, ok := under.(*types.Pointer)
    73				if !ok {
    74					break
    75				}
    76				under = ptr.Elem().Underlying()
    77			}
    78			if _, ok := under.(*types.Struct); !ok {
    79				// skip non-struct composite literals
    80				return
    81			}
    82			if isLocalType(pass, typ) {
    83				// allow unkeyed locally defined composite literal
    84				return
    85			}
    86	
    87			// check if the CompositeLit contains an unkeyed field
    88			allKeyValue := true
    89			for _, e := range cl.Elts {
    90				if _, ok := e.(*ast.KeyValueExpr); !ok {
    91					allKeyValue = false
    92					break
    93				}
    94			}
    95			if allKeyValue {
    96				// all the composite literal fields are keyed
    97				return
    98			}
    99	
   100			pass.Reportf(cl.Pos(), "%s composite literal uses unkeyed fields", typeName)
   101		})
   102		return nil, nil
   103	}
   104	
   105	func isLocalType(pass *analysis.Pass, typ types.Type) bool {
   106		switch x := typ.(type) {
   107		case *types.Struct:
   108			// struct literals are local types
   109			return true
   110		case *types.Pointer:
   111			return isLocalType(pass, x.Elem())
   112		case *types.Named:
   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")
   115		}
   116		return false
   117	}
   118	

View as plain text