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  }