github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/cmd/vet/taglit.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 untagged struct literals.
     6  
     7  package main
     8  
     9  import (
    10  	"flag"
    11  	"go/ast"
    12  	"strings"
    13  )
    14  
    15  var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite white list; for testing only")
    16  
    17  // checkUntaggedLiteral checks if a composite literal is a struct literal with
    18  // untagged fields.
    19  func (f *File) checkUntaggedLiteral(c *ast.CompositeLit) {
    20  	if !vet("composites") {
    21  		return
    22  	}
    23  
    24  	typ := c.Type
    25  	for {
    26  		if typ1, ok := c.Type.(*ast.ParenExpr); ok {
    27  			typ = typ1
    28  			continue
    29  		}
    30  		break
    31  	}
    32  
    33  	switch typ.(type) {
    34  	case *ast.ArrayType:
    35  		return
    36  	case *ast.MapType:
    37  		return
    38  	case *ast.StructType:
    39  		return // a literal struct type does not need to use tags
    40  	case *ast.Ident:
    41  		// A simple type name like t or T does not need tags either,
    42  		// since it is almost certainly declared in the current package.
    43  		// (The exception is names being used via import . "pkg", but
    44  		// those are already breaking the Go 1 compatibility promise,
    45  		// so not reporting potential additional breakage seems okay.)
    46  		return
    47  	}
    48  
    49  	// Otherwise the type is a selector like pkg.Name.
    50  	// We only care if pkg.Name is a struct, not if it's a map, array, or slice.
    51  	isStruct, typeString := f.pkg.isStruct(c)
    52  	if !isStruct {
    53  		return
    54  	}
    55  
    56  	if typeString == "" { // isStruct doesn't know
    57  		typeString = f.gofmt(typ)
    58  	}
    59  
    60  	// It's a struct, or we can't tell it's not a struct because we don't have types.
    61  
    62  	// Check if the CompositeLit contains an untagged field.
    63  	allKeyValue := true
    64  	for _, e := range c.Elts {
    65  		if _, ok := e.(*ast.KeyValueExpr); !ok {
    66  			allKeyValue = false
    67  			break
    68  		}
    69  	}
    70  	if allKeyValue {
    71  		return
    72  	}
    73  
    74  	// Check that the CompositeLit's type has the form pkg.Typ.
    75  	s, ok := c.Type.(*ast.SelectorExpr)
    76  	if !ok {
    77  		return
    78  	}
    79  	pkg, ok := s.X.(*ast.Ident)
    80  	if !ok {
    81  		return
    82  	}
    83  
    84  	// Convert the package name to an import path, and compare to a whitelist.
    85  	path := pkgPath(f, pkg.Name)
    86  	if path == "" {
    87  		f.Badf(c.Pos(), "unresolvable package for %s.%s literal", pkg.Name, s.Sel.Name)
    88  		return
    89  	}
    90  	typeName := path + "." + s.Sel.Name
    91  	if *compositeWhiteList && untaggedLiteralWhitelist[typeName] {
    92  		return
    93  	}
    94  
    95  	f.Warn(c.Pos(), typeString+" composite literal uses untagged fields")
    96  }
    97  
    98  // pkgPath returns the import path "image/png" for the package name "png".
    99  //
   100  // This is based purely on syntax and convention, and not on the imported
   101  // package's contents. It will be incorrect if a package name differs from the
   102  // leaf element of the import path, or if the package was a dot import.
   103  func pkgPath(f *File, pkgName string) (path string) {
   104  	for _, x := range f.file.Imports {
   105  		s := strings.Trim(x.Path.Value, `"`)
   106  		if x.Name != nil {
   107  			// Catch `import pkgName "foo/bar"`.
   108  			if x.Name.Name == pkgName {
   109  				return s
   110  			}
   111  		} else {
   112  			// Catch `import "pkgName"` or `import "foo/bar/pkgName"`.
   113  			if s == pkgName || strings.HasSuffix(s, "/"+pkgName) {
   114  				return s
   115  			}
   116  		}
   117  	}
   118  	return ""
   119  }
   120  
   121  var untaggedLiteralWhitelist = map[string]bool{
   122  	/*
   123  		These types are actually slices. Syntactically, we cannot tell
   124  		whether the Typ in pkg.Typ{1, 2, 3} is a slice or a struct, so we
   125  		whitelist all the standard package library's exported slice types.
   126  
   127  		find $GOROOT/src/pkg -type f | grep -v _test.go | xargs grep '^type.*\[\]' | \
   128  			grep -v ' map\[' | sed 's,/[^/]*go.type,,' | sed 's,.*src/pkg/,,' | \
   129  			sed 's, ,.,' |  sed 's, .*,,' | grep -v '\.[a-z]' | \
   130  			sort | awk '{ print "\"" $0 "\": true," }'
   131  	*/
   132  	"crypto/x509/pkix.RDNSequence":                  true,
   133  	"crypto/x509/pkix.RelativeDistinguishedNameSET": true,
   134  	"database/sql.RawBytes":                         true,
   135  	"debug/macho.LoadBytes":                         true,
   136  	"encoding/asn1.ObjectIdentifier":                true,
   137  	"encoding/asn1.RawContent":                      true,
   138  	"encoding/json.RawMessage":                      true,
   139  	"encoding/xml.CharData":                         true,
   140  	"encoding/xml.Comment":                          true,
   141  	"encoding/xml.Directive":                        true,
   142  	"go/scanner.ErrorList":                          true,
   143  	"image/color.Palette":                           true,
   144  	"net.HardwareAddr":                              true,
   145  	"net.IP":                                        true,
   146  	"net.IPMask":                                    true,
   147  	"sort.Float64Slice":                             true,
   148  	"sort.IntSlice":                                 true,
   149  	"sort.StringSlice":                              true,
   150  	"unicode.SpecialCase":                           true,
   151  
   152  	// These image and image/color struct types are frozen. We will never add fields to them.
   153  	"image/color.Alpha16": true,
   154  	"image/color.Alpha":   true,
   155  	"image/color.Gray16":  true,
   156  	"image/color.Gray":    true,
   157  	"image/color.NRGBA64": true,
   158  	"image/color.NRGBA":   true,
   159  	"image/color.RGBA64":  true,
   160  	"image/color.RGBA":    true,
   161  	"image/color.YCbCr":   true,
   162  	"image.Point":         true,
   163  	"image.Rectangle":     true,
   164  }