...
1
2
3
4
5 package unmarshal
6
7 import (
8 _ "embed"
9 "go/ast"
10 "go/types"
11
12 "golang.org/x/tools/go/analysis"
13 "golang.org/x/tools/go/analysis/passes/inspect"
14 "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
15 "golang.org/x/tools/go/ast/inspector"
16 "golang.org/x/tools/go/types/typeutil"
17 )
18
19
20 var doc string
21
22 var Analyzer = &analysis.Analyzer{
23 Name: "unmarshal",
24 Doc: analysisutil.MustExtractDoc(doc, "unmarshal"),
25 URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unmarshal",
26 Requires: []*analysis.Analyzer{inspect.Analyzer},
27 Run: run,
28 }
29
30 func run(pass *analysis.Pass) (interface{}, error) {
31 switch pass.Pkg.Path() {
32 case "encoding/gob", "encoding/json", "encoding/xml", "encoding/asn1":
33
34
35 return nil, nil
36 }
37
38
39
40
41
42
43
44 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
45
46 nodeFilter := []ast.Node{
47 (*ast.CallExpr)(nil),
48 }
49 inspect.Preorder(nodeFilter, func(n ast.Node) {
50 call := n.(*ast.CallExpr)
51 fn := typeutil.StaticCallee(pass.TypesInfo, call)
52 if fn == nil {
53 return
54 }
55
56
57 argidx := -1
58
59 recv := fn.Type().(*types.Signature).Recv()
60 if fn.Name() == "Unmarshal" && recv == nil {
61
62
63
64 switch fn.Pkg().Path() {
65 case "encoding/json", "encoding/xml", "encoding/asn1":
66 argidx = 1
67 }
68 } else if fn.Name() == "Decode" && recv != nil {
69
70
71
72 t := recv.Type()
73 if ptr, ok := t.(*types.Pointer); ok {
74 t = ptr.Elem()
75 }
76 tname := t.(*types.Named).Obj()
77 if tname.Name() == "Decoder" {
78 switch tname.Pkg().Path() {
79 case "encoding/json", "encoding/xml", "encoding/gob":
80 argidx = 0
81 }
82 }
83 }
84 if argidx < 0 {
85 return
86 }
87
88 if len(call.Args) < argidx+1 {
89 return
90 }
91
92 t := pass.TypesInfo.Types[call.Args[argidx]].Type
93 switch t.Underlying().(type) {
94 case *types.Pointer, *types.Interface, *types.TypeParam:
95 return
96 }
97
98 switch argidx {
99 case 0:
100 pass.Reportf(call.Lparen, "call of %s passes non-pointer", fn.Name())
101 case 1:
102 pass.Reportf(call.Lparen, "call of %s passes non-pointer as second argument", fn.Name())
103 }
104 })
105 return nil, nil
106 }
107
View as plain text