github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa1026/sa1026.go (about)

     1  package sa1026
     2  
     3  import (
     4  	"fmt"
     5  	"go/types"
     6  
     7  	"github.com/amarpal/go-tools/analysis/callcheck"
     8  	"github.com/amarpal/go-tools/analysis/lint"
     9  	"github.com/amarpal/go-tools/internal/passes/buildir"
    10  	"github.com/amarpal/go-tools/staticcheck/fakejson"
    11  	"github.com/amarpal/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.Documentation{
    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  }