Home | History | Annotate | Download | only in vet
      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 	"cmd/vet/internal/whitelist"
     11 	"flag"
     12 	"go/ast"
     13 	"go/types"
     14 	"strings"
     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 	cl := node.(*ast.CompositeLit)
     30 
     31 	typ := f.pkg.types[cl].Type
     32 	if typ == nil {
     33 		// cannot determine composite literals' type, skip it
     34 		return
     35 	}
     36 	typeName := typ.String()
     37 	if *compositeWhiteList && whitelist.UnkeyedLiteral[typeName] {
     38 		// skip whitelisted types
     39 		return
     40 	}
     41 	if _, ok := typ.Underlying().(*types.Struct); !ok {
     42 		// skip non-struct composite literals
     43 		return
     44 	}
     45 	if isLocalType(f, typeName) {
     46 		// allow unkeyed locally defined composite literal
     47 		return
     48 	}
     49 
     50 	// check if the CompositeLit contains an unkeyed field
     51 	allKeyValue := true
     52 	for _, e := range cl.Elts {
     53 		if _, ok := e.(*ast.KeyValueExpr); !ok {
     54 			allKeyValue = false
     55 			break
     56 		}
     57 	}
     58 	if allKeyValue {
     59 		// all the composite literal fields are keyed
     60 		return
     61 	}
     62 
     63 	f.Badf(cl.Pos(), "%s composite literal uses unkeyed fields", typeName)
     64 }
     65 
     66 func isLocalType(f *File, typeName string) bool {
     67 	if strings.HasPrefix(typeName, "struct{") {
     68 		// struct literals are local types
     69 		return true
     70 	}
     71 
     72 	pkgname := f.pkg.path
     73 	if strings.HasPrefix(typeName, pkgname+".") {
     74 		return true
     75 	}
     76 
     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")]
     80 	}
     81 	return strings.HasPrefix(typeName, pkgname+".")
     82 }
     83