GoPLS Viewer

Home|gopls/go/analysis/passes/unmarshal/unmarshal.go
1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// The unmarshal package defines an Analyzer that checks for passing
6// non-pointer or non-interface types to unmarshal and decode functions.
7package unmarshal
8
9import (
10    "go/ast"
11    "go/types"
12
13    "golang.org/x/tools/go/analysis"
14    "golang.org/x/tools/go/analysis/passes/inspect"
15    "golang.org/x/tools/go/ast/inspector"
16    "golang.org/x/tools/go/types/typeutil"
17    "golang.org/x/tools/internal/typeparams"
18)
19
20const Doc = `report passing non-pointer or non-interface values to unmarshal
21
22The unmarshal analysis reports calls to functions such as json.Unmarshal
23in which the argument type is not a pointer or an interface.`
24
25var Analyzer = &analysis.Analyzer{
26    Name:     "unmarshal",
27    Doc:      Doc,
28    Requires: []*analysis.Analyzer{inspect.Analyzer},
29    Run:      run,
30}
31
32func run(pass *analysis.Pass) (interface{}, error) {
33    switch pass.Pkg.Path() {
34    case "encoding/gob""encoding/json""encoding/xml""encoding/asn1":
35        // These packages know how to use their own APIs.
36        // Sometimes they are testing what happens to incorrect programs.
37        return nilnil
38    }
39
40    inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
41
42    nodeFilter := []ast.Node{
43        (*ast.CallExpr)(nil),
44    }
45    inspect.Preorder(nodeFilter, func(n ast.Node) {
46        call := n.(*ast.CallExpr)
47        fn := typeutil.StaticCallee(pass.TypesInfocall)
48        if fn == nil {
49            return // not a static call
50        }
51
52        // Classify the callee (without allocating memory).
53        argidx := -1
54        recv := fn.Type().(*types.Signature).Recv()
55        if fn.Name() == "Unmarshal" && recv == nil {
56            // "encoding/json".Unmarshal
57            // "encoding/xml".Unmarshal
58            // "encoding/asn1".Unmarshal
59            switch fn.Pkg().Path() {
60            case "encoding/json""encoding/xml""encoding/asn1":
61                argidx = 1 // func([]byte, interface{})
62            }
63        } else if fn.Name() == "Decode" && recv != nil {
64            // (*"encoding/json".Decoder).Decode
65            // (* "encoding/gob".Decoder).Decode
66            // (* "encoding/xml".Decoder).Decode
67            t := recv.Type()
68            if ptrok := t.(*types.Pointer); ok {
69                t = ptr.Elem()
70            }
71            tname := t.(*types.Named).Obj()
72            if tname.Name() == "Decoder" {
73                switch tname.Pkg().Path() {
74                case "encoding/json""encoding/xml""encoding/gob":
75                    argidx = 0 // func(interface{})
76                }
77            }
78        }
79        if argidx < 0 {
80            return // not a function we are interested in
81        }
82
83        if len(call.Args) < argidx+1 {
84            return // not enough arguments, e.g. called with return values of another function
85        }
86
87        t := pass.TypesInfo.Types[call.Args[argidx]].Type
88        switch t.Underlying().(type) {
89        case *types.Pointer, *types.Interface, *typeparams.TypeParam:
90            return
91        }
92
93        switch argidx {
94        case 0:
95            pass.Reportf(call.Lparen"call of %s passes non-pointer"fn.Name())
96        case 1:
97            pass.Reportf(call.Lparen"call of %s passes non-pointer as second argument"fn.Name())
98        }
99    })
100    return nilnil
101}
102
MembersX
run
run.BlockStmt.recv
run.BlockStmt.BlockStmt.t
types
typeparams
Doc
run.pass
run.nodeFilter
ast
inspector
run.BlockStmt.fn
run.BlockStmt.BlockStmt.tname
analysis
inspect
typeutil
run.BlockStmt.t
Members
X