golang.org/x/tools@v0.21.0/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 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 //go:embed doc.go 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 // These packages know how to use their own APIs. 35 // Sometimes they are testing what happens to incorrect programs. 36 return nil, nil 37 } 38 39 // Note: (*"encoding/json".Decoder).Decode, (* "encoding/gob".Decoder).Decode 40 // and (* "encoding/xml".Decoder).Decode are methods and can be a typeutil.Callee 41 // without directly importing their packages. So we cannot just skip this package 42 // when !analysisutil.Imports(pass.Pkg, "encoding/..."). 43 // TODO(taking): Consider using a prepass to collect typeutil.Callees. 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 // not a static call 55 } 56 57 // Classify the callee (without allocating memory). 58 argidx := -1 59 60 recv := fn.Type().(*types.Signature).Recv() 61 if fn.Name() == "Unmarshal" && recv == nil { 62 // "encoding/json".Unmarshal 63 // "encoding/xml".Unmarshal 64 // "encoding/asn1".Unmarshal 65 switch fn.Pkg().Path() { 66 case "encoding/json", "encoding/xml", "encoding/asn1": 67 argidx = 1 // func([]byte, interface{}) 68 } 69 } else if fn.Name() == "Decode" && recv != nil { 70 // (*"encoding/json".Decoder).Decode 71 // (* "encoding/gob".Decoder).Decode 72 // (* "encoding/xml".Decoder).Decode 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 // func(interface{}) 78 } 79 } 80 } 81 if argidx < 0 { 82 return // not a function we are interested in 83 } 84 85 if len(call.Args) < argidx+1 { 86 return // not enough arguments, e.g. called with return values of another function 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 }