github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/analysis/passes/unmarshal/unmarshal.go (about) 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. 7 package unmarshal 8 9 import ( 10 "go/ast" 11 "go/types" 12 13 "github.com/powerman/golang-tools/go/analysis" 14 "github.com/powerman/golang-tools/go/analysis/passes/inspect" 15 "github.com/powerman/golang-tools/go/ast/inspector" 16 "github.com/powerman/golang-tools/go/types/typeutil" 17 "github.com/powerman/golang-tools/internal/typeparams" 18 ) 19 20 const Doc = `report passing non-pointer or non-interface values to unmarshal 21 22 The unmarshal analysis reports calls to functions such as json.Unmarshal 23 in which the argument type is not a pointer or an interface.` 24 25 var Analyzer = &analysis.Analyzer{ 26 Name: "unmarshal", 27 Doc: Doc, 28 Requires: []*analysis.Analyzer{inspect.Analyzer}, 29 Run: run, 30 } 31 32 func 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 nil, nil 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.TypesInfo, call) 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 ptr, ok := 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 nil, nil 101 }