sa1026.go raw

   1  package sa1026
   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  	"honnef.co/go/tools/staticcheck/fakejson"
  11  	"honnef.co/go/tools/staticcheck/fakexml"
  12  
  13  	"golang.org/x/tools/go/analysis"
  14  )
  15  
  16  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  17  	Analyzer: &analysis.Analyzer{
  18  		Name:     "SA1026",
  19  		Requires: []*analysis.Analyzer{buildir.Analyzer},
  20  		Run:      callcheck.Analyzer(rules),
  21  	},
  22  	Doc: &lint.RawDocumentation{
  23  		Title:    `Cannot marshal channels or functions`,
  24  		Since:    "2019.2",
  25  		Severity: lint.SeverityError,
  26  		MergeIf:  lint.MergeIfAny,
  27  	},
  28  })
  29  
  30  var Analyzer = SCAnalyzer.Analyzer
  31  
  32  var rules = map[string]callcheck.Check{
  33  	"encoding/json.Marshal":           checkJSON,
  34  	"encoding/xml.Marshal":            checkXML,
  35  	"(*encoding/json.Encoder).Encode": checkJSON,
  36  	"(*encoding/xml.Encoder).Encode":  checkXML,
  37  }
  38  
  39  func checkJSON(call *callcheck.Call) {
  40  	arg := call.Args[0]
  41  	T := arg.Value.Value.Type()
  42  	if err := fakejson.Marshal(T); err != nil {
  43  		typ := types.TypeString(err.Type, types.RelativeTo(arg.Value.Value.Parent().Pkg.Pkg))
  44  		if err.Path == "x" {
  45  			arg.Invalid(fmt.Sprintf("trying to marshal unsupported type %s", typ))
  46  		} else {
  47  			arg.Invalid(fmt.Sprintf("trying to marshal unsupported type %s, via %s", typ, err.Path))
  48  		}
  49  	}
  50  }
  51  
  52  func checkXML(call *callcheck.Call) {
  53  	arg := call.Args[0]
  54  	T := arg.Value.Value.Type()
  55  	if err := fakexml.Marshal(T); err != nil {
  56  		switch err := err.(type) {
  57  		case *fakexml.UnsupportedTypeError:
  58  			typ := types.TypeString(err.Type, types.RelativeTo(arg.Value.Value.Parent().Pkg.Pkg))
  59  			if err.Path == "x" {
  60  				arg.Invalid(fmt.Sprintf("trying to marshal unsupported type %s", typ))
  61  			} else {
  62  				arg.Invalid(fmt.Sprintf("trying to marshal unsupported type %s, via %s", typ, err.Path))
  63  			}
  64  		case *fakexml.CyclicTypeError:
  65  			typ := types.TypeString(err.Type, types.RelativeTo(arg.Value.Value.Parent().Pkg.Pkg))
  66  			if err.Path == "x" {
  67  				arg.Invalid(fmt.Sprintf("trying to marshal cyclic type %s", typ))
  68  			} else {
  69  				arg.Invalid(fmt.Sprintf("trying to marshal cyclic type %s, via %s", typ, err.Path))
  70  			}
  71  		case *fakexml.TagPathError:
  72  			// Vet does a better job at reporting this error, because it can flag the actual struct tags, not just the call to Marshal
  73  		default:
  74  			// These errors get reported by SA5008 instead, which can flag the actual fields, independently of calls to xml.Marshal
  75  		}
  76  	}
  77  }
  78