src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

sa1014.go (1815B)


      1 package sa1014
      2 
      3 import (
      4 	"fmt"
      5 	"go/types"
      6 
      7 	"honnef.co/go/tools/analysis/callcheck"
      8 	"honnef.co/go/tools/analysis/lint"
      9 	"honnef.co/go/tools/internal/passes/buildir"
     10 
     11 	"golang.org/x/tools/go/analysis"
     12 )
     13 
     14 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
     15 	Analyzer: &analysis.Analyzer{
     16 		Name:     "SA1014",
     17 		Requires: []*analysis.Analyzer{buildir.Analyzer},
     18 		Run:      callcheck.Analyzer(checkUnmarshalPointerRules),
     19 	},
     20 	Doc: &lint.RawDocumentation{
     21 		Title: `Non-pointer value passed to \'Unmarshal\' or \'Decode\'`,
     22 		Text: `Functions such as \'encoding/json.Unmarshal\' and
     23 \'(*encoding/json.Decoder).Decode\' require a pointer to the value that should
     24 be populated. Passing a non-pointer value results in the function returning an
     25 error at runtime, as it cannot modify the target value.`,
     26 		Since:    "2017.1",
     27 		Severity: lint.SeverityError,
     28 		MergeIf:  lint.MergeIfAny,
     29 	},
     30 })
     31 
     32 var Analyzer = SCAnalyzer.Analyzer
     33 
     34 var checkUnmarshalPointerRules = map[string]callcheck.Check{
     35 	"encoding/xml.Unmarshal":                unmarshalPointer("xml.Unmarshal", 1),
     36 	"(*encoding/xml.Decoder).Decode":        unmarshalPointer("Decode", 0),
     37 	"(*encoding/xml.Decoder).DecodeElement": unmarshalPointer("DecodeElement", 0),
     38 	"encoding/json.Unmarshal":               unmarshalPointer("json.Unmarshal", 1),
     39 	"(*encoding/json.Decoder).Decode":       unmarshalPointer("Decode", 0),
     40 }
     41 
     42 func unmarshalPointer(name string, arg int) callcheck.Check {
     43 	return func(call *callcheck.Call) {
     44 		if !Pointer(call.Args[arg].Value) {
     45 			call.Args[arg].Invalid(fmt.Sprintf("%s expects to unmarshal into a pointer, but the provided value is not a pointer", name))
     46 		}
     47 	}
     48 }
     49 
     50 func Pointer(v callcheck.Value) bool {
     51 	switch v.Value.Type().Underlying().(type) {
     52 	case *types.Pointer, *types.Interface:
     53 		return true
     54 	}
     55 	return false
     56 }