sa1014.go (1815B)
1 package sa1014 2 3 import ( 4 "fmt" 5 "go/types" 6 7 "honnef.co/go/tools/analysis/callcheck" 8 "honnef.co/go/tools/analysis/lint" 9 "honnef.co/go/tools/internal/passes/buildir" 10 11 "golang.org/x/tools/go/analysis" 12 ) 13 14 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 15 Analyzer: &analysis.Analyzer{ 16 Name: "SA1014", 17 Requires: []*analysis.Analyzer{buildir.Analyzer}, 18 Run: callcheck.Analyzer(checkUnmarshalPointerRules), 19 }, 20 Doc: &lint.RawDocumentation{ 21 Title: `Non-pointer value passed to \'Unmarshal\' or \'Decode\'`, 22 Text: `Functions such as \'encoding/json.Unmarshal\' and 23 \'(*encoding/json.Decoder).Decode\' require a pointer to the value that should 24 be populated. Passing a non-pointer value results in the function returning an 25 error at runtime, as it cannot modify the target value.`, 26 Since: "2017.1", 27 Severity: lint.SeverityError, 28 MergeIf: lint.MergeIfAny, 29 }, 30 }) 31 32 var Analyzer = SCAnalyzer.Analyzer 33 34 var checkUnmarshalPointerRules = map[string]callcheck.Check{ 35 "encoding/xml.Unmarshal": unmarshalPointer("xml.Unmarshal", 1), 36 "(*encoding/xml.Decoder).Decode": unmarshalPointer("Decode", 0), 37 "(*encoding/xml.Decoder).DecodeElement": unmarshalPointer("DecodeElement", 0), 38 "encoding/json.Unmarshal": unmarshalPointer("json.Unmarshal", 1), 39 "(*encoding/json.Decoder).Decode": unmarshalPointer("Decode", 0), 40 } 41 42 func unmarshalPointer(name string, arg int) callcheck.Check { 43 return func(call *callcheck.Call) { 44 if !Pointer(call.Args[arg].Value) { 45 call.Args[arg].Invalid(fmt.Sprintf("%s expects to unmarshal into a pointer, but the provided value is not a pointer", name)) 46 } 47 } 48 } 49 50 func Pointer(v callcheck.Value) bool { 51 switch v.Value.Type().Underlying().(type) { 52 case *types.Pointer, *types.Interface: 53 return true 54 } 55 return false 56 }