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 }