src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

sa1003.go (2698B)


      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 := range n {
     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 }