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