sa1014.go raw
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 Since: "2017.1",
23 Severity: lint.SeverityError,
24 MergeIf: lint.MergeIfAny,
25 },
26 })
27
28 var Analyzer = SCAnalyzer.Analyzer
29
30 var checkUnmarshalPointerRules = map[string]callcheck.Check{
31 "encoding/xml.Unmarshal": unmarshalPointer("xml.Unmarshal", 1),
32 "(*encoding/xml.Decoder).Decode": unmarshalPointer("Decode", 0),
33 "(*encoding/xml.Decoder).DecodeElement": unmarshalPointer("DecodeElement", 0),
34 "encoding/json.Unmarshal": unmarshalPointer("json.Unmarshal", 1),
35 "(*encoding/json.Decoder).Decode": unmarshalPointer("Decode", 0),
36 }
37
38 func unmarshalPointer(name string, arg int) callcheck.Check {
39 return func(call *callcheck.Call) {
40 if !Pointer(call.Args[arg].Value) {
41 call.Args[arg].Invalid(fmt.Sprintf("%s expects to unmarshal into a pointer, but the provided value is not a pointer", name))
42 }
43 }
44 }
45
46 func Pointer(v callcheck.Value) bool {
47 switch v.Value.Type().Underlying().(type) {
48 case *types.Pointer, *types.Interface:
49 return true
50 }
51 return false
52 }
53