...

Source file src/pkg/cmd/vendor/golang.org/x/tools/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.
     7	package unmarshal
     8	
     9	import (
    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	)
    18	
    19	const doc = `report passing non-pointer or non-interface values to unmarshal
    20	
    21	The unmarshal analysis reports calls to functions such as json.Unmarshal
    22	in which the argument type is not a pointer or an interface.`
    23	
    24	var Analyzer = &analysis.Analyzer{
    25		Name:     "unmarshal",
    26		Doc:      doc,
    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":
    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		inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    40	
    41		nodeFilter := []ast.Node{
    42			(*ast.CallExpr)(nil),
    43		}
    44		inspect.Preorder(nodeFilter, func(n ast.Node) {
    45			call := n.(*ast.CallExpr)
    46			fn := typeutil.StaticCallee(pass.TypesInfo, call)
    47			if fn == nil {
    48				return // not a static call
    49			}
    50	
    51			// Classify the callee (without allocating memory).
    52			argidx := -1
    53			recv := fn.Type().(*types.Signature).Recv()
    54			if fn.Name() == "Unmarshal" && recv == nil {
    55				// "encoding/json".Unmarshal
    56				//  "encoding/xml".Unmarshal
    57				switch fn.Pkg().Path() {
    58				case "encoding/json", "encoding/xml":
    59					argidx = 1 // func([]byte, interface{})
    60				}
    61			} else if fn.Name() == "Decode" && recv != nil {
    62				// (*"encoding/json".Decoder).Decode
    63				// (* "encoding/gob".Decoder).Decode
    64				// (* "encoding/xml".Decoder).Decode
    65				t := recv.Type()
    66				if ptr, ok := t.(*types.Pointer); ok {
    67					t = ptr.Elem()
    68				}
    69				tname := t.(*types.Named).Obj()
    70				if tname.Name() == "Decoder" {
    71					switch tname.Pkg().Path() {
    72					case "encoding/json", "encoding/xml", "encoding/gob":
    73						argidx = 0 // func(interface{})
    74					}
    75				}
    76			}
    77			if argidx < 0 {
    78				return // not a function we are interested in
    79			}
    80	
    81			if len(call.Args) < argidx+1 {
    82				return // not enough arguments, e.g. called with return values of another function
    83			}
    84	
    85			t := pass.TypesInfo.Types[call.Args[argidx]].Type
    86			switch t.Underlying().(type) {
    87			case *types.Pointer, *types.Interface:
    88				return
    89			}
    90	
    91			switch argidx {
    92			case 0:
    93				pass.Reportf(call.Lparen, "call of %s passes non-pointer", fn.Name())
    94			case 1:
    95				pass.Reportf(call.Lparen, "call of %s passes non-pointer as second argument", fn.Name())
    96			}
    97		})
    98		return nil, nil
    99	}
   100	

View as plain text