sa1003.go raw

   1  package sa1003
   2  
   3  import (
   4  	"fmt"
   5  	"go/types"
   6  	"go/version"
   7  
   8  	"honnef.co/go/tools/analysis/callcheck"
   9  	"honnef.co/go/tools/analysis/code"
  10  	"honnef.co/go/tools/analysis/lint"
  11  	"honnef.co/go/tools/internal/passes/buildir"
  12  	"honnef.co/go/tools/knowledge"
  13  
  14  	"golang.org/x/tools/go/analysis"
  15  )
  16  
  17  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  18  	Analyzer: &analysis.Analyzer{
  19  		Name:     "SA1003",
  20  		Requires: []*analysis.Analyzer{buildir.Analyzer},
  21  		Run:      callcheck.Analyzer(checkEncodingBinaryRules),
  22  	},
  23  	Doc: &lint.RawDocumentation{
  24  		Title: `Unsupported argument to functions in \'encoding/binary\'`,
  25  		Text: `The \'encoding/binary\' package can only serialize types with known sizes.
  26  This precludes the use of the \'int\' and \'uint\' types, as their sizes
  27  differ on different architectures. Furthermore, it doesn't support
  28  serializing maps, channels, strings, or functions.
  29  
  30  Before Go 1.8, \'bool\' wasn't supported, either.`,
  31  		Since:    "2017.1",
  32  		Severity: lint.SeverityError,
  33  		MergeIf:  lint.MergeIfAny,
  34  	},
  35  })
  36  
  37  var Analyzer = SCAnalyzer.Analyzer
  38  
  39  var checkEncodingBinaryRules = map[string]callcheck.Check{
  40  	"encoding/binary.Write": func(call *callcheck.Call) {
  41  		arg := call.Args[knowledge.Arg("encoding/binary.Write.data")]
  42  		if !CanBinaryMarshal(call.Pass, call.Parent, arg.Value) {
  43  			arg.Invalid(fmt.Sprintf("value of type %s cannot be used with binary.Write", arg.Value.Value.Type()))
  44  		}
  45  	},
  46  }
  47  
  48  func CanBinaryMarshal(pass *analysis.Pass, node code.Positioner, v callcheck.Value) bool {
  49  	typ := v.Value.Type().Underlying()
  50  	if ttyp, ok := typ.(*types.Pointer); ok {
  51  		typ = ttyp.Elem().Underlying()
  52  	}
  53  	if ttyp, ok := types.Unalias(typ).(interface {
  54  		Elem() types.Type
  55  	}); ok {
  56  		if _, ok := ttyp.(*types.Pointer); !ok {
  57  			typ = ttyp.Elem()
  58  		}
  59  	}
  60  
  61  	return validEncodingBinaryType(pass, node, typ)
  62  }
  63  
  64  func validEncodingBinaryType(pass *analysis.Pass, node code.Positioner, typ types.Type) bool {
  65  	typ = typ.Underlying()
  66  	switch typ := typ.(type) {
  67  	case *types.Basic:
  68  		switch typ.Kind() {
  69  		case types.Uint8, types.Uint16, types.Uint32, types.Uint64,
  70  			types.Int8, types.Int16, types.Int32, types.Int64,
  71  			types.Float32, types.Float64, types.Complex64, types.Complex128, types.Invalid:
  72  			return true
  73  		case types.Bool:
  74  			return version.Compare(code.StdlibVersion(pass, node), "go1.8") >= 0
  75  		}
  76  		return false
  77  	case *types.Struct:
  78  		n := typ.NumFields()
  79  		for i := 0; i < n; i++ {
  80  			if !validEncodingBinaryType(pass, node, typ.Field(i).Type()) {
  81  				return false
  82  			}
  83  		}
  84  		return true
  85  	case *types.Array:
  86  		return validEncodingBinaryType(pass, node, typ.Elem())
  87  	case *types.Interface:
  88  		// we can't determine if it's a valid type or not
  89  		return true
  90  	}
  91  	return false
  92  }
  93