github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/src/cmd/vet/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  // This file defines the check for passing non-pointer or non-interface
     6  // types to unmarshal and decode functions.
     7  
     8  package main
     9  
    10  import (
    11  	"go/ast"
    12  	"go/types"
    13  	"strings"
    14  )
    15  
    16  func init() {
    17  	register("unmarshal",
    18  		"check for passing non-pointer or non-interface types to unmarshal and decode functions",
    19  		checkUnmarshalArg,
    20  		callExpr)
    21  }
    22  
    23  var pointerArgFuncs = map[string]int{
    24  	"encoding/json.Unmarshal":         1,
    25  	"(*encoding/json.Decoder).Decode": 0,
    26  	"(*encoding/gob.Decoder).Decode":  0,
    27  	"encoding/xml.Unmarshal":          1,
    28  	"(*encoding/xml.Decoder).Decode":  0,
    29  }
    30  
    31  func checkUnmarshalArg(f *File, n ast.Node) {
    32  	call, ok := n.(*ast.CallExpr)
    33  	if !ok {
    34  		return // not a call statement
    35  	}
    36  	fun := unparen(call.Fun)
    37  
    38  	if f.pkg.types[fun].IsType() {
    39  		return // a conversion, not a call
    40  	}
    41  
    42  	info := &types.Info{Uses: f.pkg.uses, Selections: f.pkg.selectors}
    43  	name := callName(info, call)
    44  
    45  	arg, ok := pointerArgFuncs[name]
    46  	if !ok {
    47  		return // not a function we are interested in
    48  	}
    49  
    50  	if len(call.Args) < arg+1 {
    51  		return // not enough arguments, e.g. called with return values of another function
    52  	}
    53  
    54  	typ := f.pkg.types[call.Args[arg]]
    55  
    56  	if typ.Type == nil {
    57  		return // type error prevents further analysis
    58  	}
    59  
    60  	switch typ.Type.Underlying().(type) {
    61  	case *types.Pointer, *types.Interface:
    62  		return
    63  	}
    64  
    65  	shortname := name[strings.LastIndexByte(name, '.')+1:]
    66  	switch arg {
    67  	case 0:
    68  		f.Badf(call.Lparen, "call of %s passes non-pointer", shortname)
    69  	case 1:
    70  		f.Badf(call.Lparen, "call of %s passes non-pointer as second argument", shortname)
    71  	}
    72  }