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  }