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