aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/breml/errchkjson/errchkjson.go
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2025-01-22 16:07:17 +0100
committerTaras Madan <tarasmadan@google.com>2025-01-23 10:42:36 +0000
commit7b4377ad9d8a7205416df8d6217ef2b010f89481 (patch)
treee6fec4fd12ff807a16d847923f501075bf71d16c /vendor/github.com/breml/errchkjson/errchkjson.go
parent475a4c203afb8b7d3af51c4fd32bb170ff32a45e (diff)
vendor: delete
Diffstat (limited to 'vendor/github.com/breml/errchkjson/errchkjson.go')
-rw-r--r--vendor/github.com/breml/errchkjson/errchkjson.go348
1 files changed, 0 insertions, 348 deletions
diff --git a/vendor/github.com/breml/errchkjson/errchkjson.go b/vendor/github.com/breml/errchkjson/errchkjson.go
deleted file mode 100644
index 7c8cd82e9..000000000
--- a/vendor/github.com/breml/errchkjson/errchkjson.go
+++ /dev/null
@@ -1,348 +0,0 @@
-// Package errchkjson defines an Analyzer that finds places, where it is
-// safe to omit checking the error returned from json.Marshal.
-package errchkjson
-
-import (
- "flag"
- "fmt"
- "go/ast"
- "go/token"
- "go/types"
- "reflect"
-
- "golang.org/x/tools/go/analysis"
- "golang.org/x/tools/go/types/typeutil"
-)
-
-type errchkjson struct {
- omitSafe bool // -omit-safe flag
- reportNoExported bool // -report-no-exported flag
-}
-
-// NewAnalyzer returns a new errchkjson analyzer.
-func NewAnalyzer() *analysis.Analyzer {
- errchkjson := &errchkjson{}
-
- a := &analysis.Analyzer{
- Name: "errchkjson",
- Doc: "Checks types passed to the json encoding functions. Reports unsupported types and reports occurrences where the check for the returned error can be omitted.",
- Run: errchkjson.run,
- }
-
- a.Flags.Init("errchkjson", flag.ExitOnError)
- a.Flags.BoolVar(&errchkjson.omitSafe, "omit-safe", false, "if omit-safe is true, checking of safe returns is omitted")
- a.Flags.BoolVar(&errchkjson.reportNoExported, "report-no-exported", false, "if report-no-exported is true, encoding a struct without exported fields is reported as issue")
- a.Flags.Var(versionFlag{}, "V", "print version and exit")
-
- return a
-}
-
-func (e *errchkjson) run(pass *analysis.Pass) (interface{}, error) {
- for _, file := range pass.Files {
- ast.Inspect(file, func(n ast.Node) bool {
- if n == nil {
- return true
- }
-
- // if the error is returned, it is the caller's responsibility to check
- // the return value.
- if _, ok := n.(*ast.ReturnStmt); ok {
- return false
- }
-
- ce, ok := n.(*ast.CallExpr)
- if ok {
- fn, _ := typeutil.Callee(pass.TypesInfo, ce).(*types.Func)
- if fn == nil {
- return true
- }
-
- switch fn.FullName() {
- case "encoding/json.Marshal", "encoding/json.MarshalIndent":
- e.handleJSONMarshal(pass, ce, fn.FullName(), blankIdentifier, e.omitSafe)
- case "(*encoding/json.Encoder).Encode":
- e.handleJSONMarshal(pass, ce, fn.FullName(), blankIdentifier, true)
- default:
- e.inspectArgs(pass, ce.Args)
- }
- return false
- }
-
- as, ok := n.(*ast.AssignStmt)
- if !ok {
- return true
- }
-
- ce, ok = as.Rhs[0].(*ast.CallExpr)
- if !ok {
- return true
- }
-
- fn, _ := typeutil.Callee(pass.TypesInfo, ce).(*types.Func)
- if fn == nil {
- return true
- }
-
- switch fn.FullName() {
- case "encoding/json.Marshal", "encoding/json.MarshalIndent":
- e.handleJSONMarshal(pass, ce, fn.FullName(), evaluateMarshalErrorTarget(as.Lhs[1]), e.omitSafe)
- case "(*encoding/json.Encoder).Encode":
- e.handleJSONMarshal(pass, ce, fn.FullName(), evaluateMarshalErrorTarget(as.Lhs[0]), true)
- default:
- return true
- }
- return false
- })
- }
-
- return nil, nil
-}
-
-func evaluateMarshalErrorTarget(n ast.Expr) marshalErrorTarget {
- if errIdent, ok := n.(*ast.Ident); ok {
- if errIdent.Name == "_" {
- return blankIdentifier
- }
- }
- return variableAssignment
-}
-
-type marshalErrorTarget int
-
-const (
- blankIdentifier = iota // the returned error from the JSON marshal function is assigned to the blank identifier "_".
- variableAssignment // the returned error from the JSON marshal function is assigned to a variable.
- functionArgument // the returned error from the JSON marshal function is passed to an other function as argument.
-)
-
-func (e *errchkjson) handleJSONMarshal(pass *analysis.Pass, ce *ast.CallExpr, fnName string, errorTarget marshalErrorTarget, omitSafe bool) {
- t := pass.TypesInfo.TypeOf(ce.Args[0])
- if t == nil {
- // Not sure, if this is at all possible
- if errorTarget == blankIdentifier {
- pass.Reportf(ce.Pos(), "Type of argument to `%s` could not be evaluated and error return value is not checked", fnName)
- }
- return
- }
-
- if _, ok := t.(*types.Pointer); ok {
- t = t.(*types.Pointer).Elem()
- }
-
- err := e.jsonSafe(t, 0, map[types.Type]struct{}{})
- if err != nil {
- if _, ok := err.(unsupported); ok {
- pass.Reportf(ce.Pos(), "`%s` for %v", fnName, err)
- return
- }
- if _, ok := err.(noexported); ok {
- pass.Reportf(ce.Pos(), "Error argument passed to `%s` does not contain any exported field", fnName)
- }
- // Only care about unsafe types if they are assigned to the blank identifier.
- if errorTarget == blankIdentifier {
- pass.Reportf(ce.Pos(), "Error return value of `%s` is not checked: %v", fnName, err)
- }
- }
- if err == nil && errorTarget == variableAssignment && !omitSafe {
- pass.Reportf(ce.Pos(), "Error return value of `%s` is checked but passed argument is safe", fnName)
- }
- // Report an error, if err for json.Marshal is not checked and safe types are omitted
- if err == nil && errorTarget == blankIdentifier && omitSafe {
- pass.Reportf(ce.Pos(), "Error return value of `%s` is not checked", fnName)
- }
-}
-
-const (
- allowedBasicTypes = types.IsBoolean | types.IsInteger | types.IsString
- allowedMapKeyBasicTypes = types.IsInteger | types.IsString
- unsupportedBasicTypes = types.IsComplex
-)
-
-func (e *errchkjson) jsonSafe(t types.Type, level int, seenTypes map[types.Type]struct{}) error {
- if _, ok := seenTypes[t]; ok {
- return nil
- }
-
- if types.Implements(t, textMarshalerInterface()) || types.Implements(t, jsonMarshalerInterface()) {
- return fmt.Errorf("unsafe type `%s` found", t.String())
- }
-
- switch ut := t.Underlying().(type) {
- case *types.Basic:
- if ut.Info()&allowedBasicTypes > 0 { // bool, int-family, string
- if ut.Info()&types.IsString > 0 && t.String() == "encoding/json.Number" {
- return fmt.Errorf("unsafe type `%s` found", t.String())
- }
- return nil
- }
- if ut.Info()&unsupportedBasicTypes > 0 { // complex64, complex128
- return newUnsupportedError(fmt.Errorf("unsupported type `%s` found", ut.String()))
- }
- switch ut.Kind() {
- case types.UntypedNil:
- return nil
- case types.UnsafePointer:
- return newUnsupportedError(fmt.Errorf("unsupported type `%s` found", ut.String()))
- default:
- // E.g. float32, float64
- return fmt.Errorf("unsafe type `%s` found", ut.String())
- }
-
- case *types.Array:
- err := e.jsonSafe(ut.Elem(), level+1, seenTypes)
- if err != nil {
- return err
- }
- return nil
-
- case *types.Slice:
- err := e.jsonSafe(ut.Elem(), level+1, seenTypes)
- if err != nil {
- return err
- }
- return nil
-
- case *types.Struct:
- seenTypes[t] = struct{}{}
- exported := 0
- for i := 0; i < ut.NumFields(); i++ {
- if !ut.Field(i).Exported() {
- // Unexported fields can be ignored
- continue
- }
- if tag, ok := reflect.StructTag(ut.Tag(i)).Lookup("json"); ok {
- if tag == "-" {
- // Fields omitted in json can be ignored
- continue
- }
- }
- err := e.jsonSafe(ut.Field(i).Type(), level+1, seenTypes)
- if err != nil {
- return err
- }
- exported++
- }
- if e.reportNoExported && level == 0 && exported == 0 {
- return newNoexportedError(fmt.Errorf("struct does not export any field"))
- }
- return nil
-
- case *types.Pointer:
- err := e.jsonSafe(ut.Elem(), level+1, seenTypes)
- if err != nil {
- return err
- }
- return nil
-
- case *types.Map:
- err := jsonSafeMapKey(ut.Key())
- if err != nil {
- return err
- }
- err = e.jsonSafe(ut.Elem(), level+1, seenTypes)
- if err != nil {
- return err
- }
- return nil
-
- case *types.Chan, *types.Signature:
- // Types that are not supported for encoding to json:
- return newUnsupportedError(fmt.Errorf("unsupported type `%s` found", ut.String()))
-
- default:
- // Types that are not supported for encoding to json or are not completely safe, like: interfaces
- return fmt.Errorf("unsafe type `%s` found", t.String())
- }
-}
-
-func jsonSafeMapKey(t types.Type) error {
- if types.Implements(t, textMarshalerInterface()) || types.Implements(t, jsonMarshalerInterface()) {
- return fmt.Errorf("unsafe type `%s` as map key found", t.String())
- }
- switch ut := t.Underlying().(type) {
- case *types.Basic:
- if ut.Info()&types.IsString > 0 && t.String() == "encoding/json.Number" {
- return fmt.Errorf("unsafe type `%s` as map key found", t.String())
- }
- if ut.Info()&allowedMapKeyBasicTypes > 0 { // bool, int-family, string
- return nil
- }
- // E.g. bool, float32, float64, complex64, complex128
- return newUnsupportedError(fmt.Errorf("unsupported type `%s` as map key found", t.String()))
- case *types.Interface:
- return fmt.Errorf("unsafe type `%s` as map key found", t.String())
- default:
- // E.g. struct composed solely of basic types, that are comparable
- return newUnsupportedError(fmt.Errorf("unsupported type `%s` as map key found", t.String()))
- }
-}
-
-func (e *errchkjson) inspectArgs(pass *analysis.Pass, args []ast.Expr) {
- for _, a := range args {
- ast.Inspect(a, func(n ast.Node) bool {
- if n == nil {
- return true
- }
-
- ce, ok := n.(*ast.CallExpr)
- if !ok {
- return false
- }
-
- fn, _ := typeutil.Callee(pass.TypesInfo, ce).(*types.Func)
- if fn == nil {
- return true
- }
-
- switch fn.FullName() {
- case "encoding/json.Marshal", "encoding/json.MarshalIndent":
- e.handleJSONMarshal(pass, ce, fn.FullName(), functionArgument, e.omitSafe)
- case "(*encoding/json.Encoder).Encode":
- e.handleJSONMarshal(pass, ce, fn.FullName(), functionArgument, true)
- default:
- e.inspectArgs(pass, ce.Args)
- }
- return false
- })
- }
-}
-
-// Construct *types.Interface for interface encoding.TextMarshaler
-//
-// type TextMarshaler interface {
-// MarshalText() (text []byte, err error)
-// }
-func textMarshalerInterface() *types.Interface {
- textMarshalerInterface := types.NewInterfaceType([]*types.Func{
- types.NewFunc(token.NoPos, nil, "MarshalText", types.NewSignatureType(
- nil, nil, nil, nil, types.NewTuple(
- types.NewVar(token.NoPos, nil, "text",
- types.NewSlice(
- types.Universe.Lookup("byte").Type())),
- types.NewVar(token.NoPos, nil, "err", types.Universe.Lookup("error").Type())),
- false)),
- }, nil)
- textMarshalerInterface.Complete()
-
- return textMarshalerInterface
-}
-
-// Construct *types.Interface for interface json.Marshaler
-//
-// type Marshaler interface {
-// MarshalJSON() ([]byte, error)
-// }
-func jsonMarshalerInterface() *types.Interface {
- textMarshalerInterface := types.NewInterfaceType([]*types.Func{
- types.NewFunc(token.NoPos, nil, "MarshalJSON", types.NewSignatureType(
- nil, nil, nil, nil, types.NewTuple(
- types.NewVar(token.NoPos, nil, "",
- types.NewSlice(
- types.Universe.Lookup("byte").Type())),
- types.NewVar(token.NoPos, nil, "", types.Universe.Lookup("error").Type())),
- false)),
- }, nil)
- textMarshalerInterface.Complete()
-
- return textMarshalerInterface
-}