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