From 3b13842d278f4b5c849c6690a65b90b402c38784 Mon Sep 17 00:00:00 2001 From: Lucas Bremgartner Date: Sat, 6 Nov 2021 11:25:10 +0100 Subject: [PATCH 1/7] Add errchkjson linter --- .golangci.example.yml | 5 + go.mod | 1 + go.sum | 2 + pkg/config/linters_settings.go | 5 + pkg/golinters/errchkjson.go | 31 + pkg/lint/lintersdb/manager.go | 7 + test/testdata/configs/errchkjson.yml | 2 + .../testdata/configs/errchkjson_omit_safe.yml | 5 + test/testdata/errchkjson.go | 614 ++++++++++++++++++ test/testdata/errchkjson_omit_safe.go | 614 ++++++++++++++++++ 10 files changed, 1286 insertions(+) create mode 100644 pkg/golinters/errchkjson.go create mode 100644 test/testdata/configs/errchkjson.yml create mode 100644 test/testdata/configs/errchkjson_omit_safe.yml create mode 100644 test/testdata/errchkjson.go create mode 100644 test/testdata/errchkjson_omit_safe.go diff --git a/.golangci.example.yml b/.golangci.example.yml index 5ac4cd13c893..4207aa1c3a9e 100644 --- a/.golangci.example.yml +++ b/.golangci.example.yml @@ -103,6 +103,11 @@ linters-settings: # should ignore tests (default false) skip-tests: false + errchkjson: + # with omit-safe set to true, errchkjson does not warn about errors from json encoding functions that are safe + # to be ignored, because they are not possible to happen (default false) + omit-safe: false + dogsled: # checks assignments with too many blank identifiers; default is 2 max-blank-identifiers: 2 diff --git a/go.mod b/go.mod index 63d37f086a33..e87685189e22 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/blizzy78/varnamelen v0.4.0 github.com/bombsimon/wsl/v3 v3.3.0 github.com/breml/bidichk v0.2.1 + github.com/breml/errchkjson v0.1.0 github.com/butuzov/ireturn v0.1.1 github.com/charithe/durationcheck v0.0.9 github.com/daixiang0/gci v0.2.9 diff --git a/go.sum b/go.sum index 736652990519..3e7f138f368f 100644 --- a/go.sum +++ b/go.sum @@ -108,6 +108,8 @@ github.com/bombsimon/wsl/v3 v3.3.0 h1:Mka/+kRLoQJq7g2rggtgQsjuI/K5Efd87WX96EWFxj github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/breml/bidichk v0.2.1 h1:SRNtZuLdfkxtocj+xyHXKC1Uv3jVi6EPYx+NHSTNQvE= github.com/breml/bidichk v0.2.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= +github.com/breml/errchkjson v0.1.0 h1:TXe36u08hr8KPsEpi4uinmQgZP13Jh9638aZCv0BcJI= +github.com/breml/errchkjson v0.1.0/go.mod h1:jZEATw/jF69cL1iy7//Yih8yp/mXp2CBoBr9GJwCAsY= github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY= github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go index 192e6d8e9226..ae46495417d4 100644 --- a/pkg/config/linters_settings.go +++ b/pkg/config/linters_settings.go @@ -89,6 +89,7 @@ type LintersSettings struct { Dogsled DogsledSettings Dupl DuplSettings Errcheck ErrcheckSettings + ErrChkJSON ErrChkJSONSettings ErrorLint ErrorLintSettings Exhaustive ExhaustiveSettings ExhaustiveStruct ExhaustiveStructSettings @@ -165,6 +166,10 @@ type Cyclop struct { SkipTests bool `mapstructure:"skip-tests"` } +type ErrChkJSONSettings struct { + OmitSafe bool `mapstructure:"omit-safe"` +} + type DepGuardSettings struct { ListType string `mapstructure:"list-type"` Packages []string diff --git a/pkg/golinters/errchkjson.go b/pkg/golinters/errchkjson.go new file mode 100644 index 000000000000..b7cf734c37ad --- /dev/null +++ b/pkg/golinters/errchkjson.go @@ -0,0 +1,31 @@ +package golinters + +import ( + "golang.org/x/tools/go/analysis" + + "github.com/breml/errchkjson" + + "github.com/golangci/golangci-lint/pkg/config" + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" +) + +func NewErrChkJSONFuncName(cfg *config.ErrChkJSONSettings) *goanalysis.Linter { + a := errchkjson.NewAnalyzer() + + cfgMap := map[string]map[string]interface{}{} + if cfg != nil { + if cfg.OmitSafe { + cfgMap[a.Name] = map[string]interface{}{ + "omit-safe": "true", + } + } + } + + return goanalysis.NewLinter( + "errchkjson", + "Checks types passed to the json encoding functions. "+ + "Reports unsupported types and reports occations, where the check for the returned error can be omitted.", + []*analysis.Analyzer{a}, + cfgMap, + ).WithLoadMode(goanalysis.LoadModeTypesInfo) +} diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 7d3e2130ae2d..b380d3786176 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -102,6 +102,7 @@ func enableLinterConfigs(lcs []*linter.Config, isEnabled func(lc *linter.Config) func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { var bidichkCfg *config.BiDiChkSettings var cyclopCfg *config.Cyclop + var errchkjsonCfg *config.ErrChkJSONSettings var errorlintCfg *config.ErrorLintSettings var exhaustiveCfg *config.ExhaustiveSettings var exhaustiveStructCfg *config.ExhaustiveStructSettings @@ -129,6 +130,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { if m.cfg != nil { bidichkCfg = &m.cfg.LintersSettings.BiDiChk cyclopCfg = &m.cfg.LintersSettings.Cyclop + errchkjsonCfg = &m.cfg.LintersSettings.ErrChkJSON errorlintCfg = &m.cfg.LintersSettings.ErrorLint exhaustiveCfg = &m.cfg.LintersSettings.Exhaustive exhaustiveStructCfg = &m.cfg.LintersSettings.ExhaustiveStruct @@ -548,6 +550,11 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithSince("1.43.0"). WithPresets(linter.PresetBugs). WithURL("https://github.com/breml/bidichk"), + linter.NewConfig(golinters.NewErrChkJSONFuncName(errchkjsonCfg)). + WithSince("1.44.0"). + WithPresets(linter.PresetBugs, linter.PresetUnused). + WithLoadForGoAnalysis(). + WithURL("https://github.com/breml/errchkjson"), // nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives linter.NewConfig(golinters.NewNoLintLint()). diff --git a/test/testdata/configs/errchkjson.yml b/test/testdata/configs/errchkjson.yml new file mode 100644 index 000000000000..d7d534574241 --- /dev/null +++ b/test/testdata/configs/errchkjson.yml @@ -0,0 +1,2 @@ +issues: + max-issues-per-linter: 100 diff --git a/test/testdata/configs/errchkjson_omit_safe.yml b/test/testdata/configs/errchkjson_omit_safe.yml new file mode 100644 index 000000000000..9efbe58c5972 --- /dev/null +++ b/test/testdata/configs/errchkjson_omit_safe.yml @@ -0,0 +1,5 @@ +issues: + max-issues-per-linter: 100 +linters-settings: + errchkjson: + omit-safe: true diff --git a/test/testdata/errchkjson.go b/test/testdata/errchkjson.go new file mode 100644 index 000000000000..9129a5365676 --- /dev/null +++ b/test/testdata/errchkjson.go @@ -0,0 +1,614 @@ +// args: -Eerrchkjson +// config_path: testdata/configs/errchkjson.yml +package testdata + +import ( + "encoding" + "encoding/json" + "fmt" + "io/ioutil" + "unsafe" +) + +type marshalText struct{} + +func (mt marshalText) MarshalText() ([]byte, error) { + return []byte(`mt`), nil +} + +var _ encoding.TextMarshaler = marshalText(struct{}{}) + +// JSONMarshalSaveTypes contains a multitude of test cases to marshal different combinations of types to JSON, +// that are save, that is, they will never return an error, if these types are marshaled to JSON. +func JSONMarshalSaveTypes() { + var err error + + _, _ = json.Marshal(nil) // nil is safe + json.Marshal(nil) // nil is safe + _, err = json.Marshal(nil) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + _, _ = json.MarshalIndent(nil, "", " ") // nil is safe + json.MarshalIndent(nil, "", " ") // nil is safe + _, err = json.MarshalIndent(nil, "", " ") // ERROR "Error return value of `encoding/json.MarshalIndent` is checked but passed argument is safe" + _ = err + + enc := json.NewEncoder(ioutil.Discard) + _ = enc.Encode(nil) // nil is safe + enc.Encode(nil) // nil is safe + err = enc.Encode(nil) // ERROR "Error return value of `\\([*]encoding/json.Encoder\\).Encode` is checked but passed argument is safe" + _ = err + + var b bool + _, _ = json.Marshal(b) // bool is safe + _, err = json.Marshal(b) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var i int + _, _ = json.Marshal(i) // int is safe + _, err = json.Marshal(i) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var i8 int8 + _, _ = json.Marshal(i8) // int8 is safe + _, err = json.Marshal(i8) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var i16 int16 + _, _ = json.Marshal(i16) // int16 is safe + _, err = json.Marshal(i16) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var i32 int32 + _, _ = json.Marshal(i32) // int32 / rune is safe + _, err = json.Marshal(i32) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var i64 int64 + _, _ = json.Marshal(i64) // int64 is safe + _, err = json.Marshal(i64) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var ui uint + _, _ = json.Marshal(ui) // uint is safe + _, err = json.Marshal(ui) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var ui8 uint8 + _, _ = json.Marshal(ui8) // uint8 / byte is safe + _, err = json.Marshal(ui8) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var ui16 uint16 + _, _ = json.Marshal(ui16) // uint16 is safe + _, err = json.Marshal(ui16) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var ui32 uint32 + _, _ = json.Marshal(ui32) // uint32 / rune is safe + _, err = json.Marshal(ui32) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var ui64 uint64 + _, _ = json.Marshal(ui64) // uint64 is safe + _, err = json.Marshal(ui64) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var uiptr uintptr + _, _ = json.Marshal(uiptr) // uintptr is safe + _, err = json.Marshal(uiptr) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var str string + _, _ = json.Marshal(str) // string is safe + _, err = json.Marshal(str) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var strSlice []string + _, _ = json.Marshal(strSlice) // []string is safe + _, err = json.Marshal(strSlice) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var intSlice []int + _, _ = json.Marshal(intSlice) // []int is safe + _, err = json.Marshal(intSlice) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var boolSlice []bool + _, _ = json.Marshal(boolSlice) // []bool is safe + _, err = json.Marshal(boolSlice) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var strArray [10]string + _, _ = json.Marshal(strArray) // [10]string is safe + _, err = json.Marshal(strArray) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var intArray [10]int + _, _ = json.Marshal(intArray) // [10]int is safe + _, err = json.Marshal(intArray) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var boolArray [10]bool + _, _ = json.Marshal(boolArray) // [10]bool is safe + _, err = json.Marshal(boolArray) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var basicStruct struct { + Bool bool + Int int + Int8 int8 + Int16 int16 + Int32 int32 // also rune + Int64 int64 + Uint uint + Uint8 uint8 // also byte + Uint16 uint16 + Uint32 uint32 + Uint64 uint64 + Uintptr uintptr + String string + } + _, _ = json.Marshal(basicStruct) // struct containing only safe basic types is safe + _, err = json.Marshal(basicStruct) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var ptrStruct struct { + Bool *bool + Int *int + Int8 *int8 + Int16 *int16 + Int32 *int32 + Int64 *int64 + Uint *uint + Uint8 *uint8 + Uint16 *uint16 + Uint32 *uint32 + Uint64 *uint64 + Uintptr *uintptr + String *string + } + _, _ = json.Marshal(ptrStruct) // struct containing pointer to only safe basic types is safe + _, err = json.Marshal(ptrStruct) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var mapStrStr map[string]string + _, _ = json.Marshal(mapStrStr) // map[string]string is safe + _, err = json.Marshal(mapStrStr) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var mapStrInt map[string]int + _, _ = json.Marshal(mapStrInt) // map[string]int is safe + _, err = json.Marshal(mapStrInt) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var mapStrBool map[string]bool + _, _ = json.Marshal(mapStrBool) // map[string]bool is safe + _, err = json.Marshal(mapStrBool) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var mapIntStr map[int]string + _, _ = json.Marshal(mapIntStr) // map[int]string is safe + _, err = json.Marshal(mapIntStr) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var mapIntInt map[int]int + _, _ = json.Marshal(mapIntInt) // map[int]int is safe + _, err = json.Marshal(mapIntInt) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + var mapIntBool map[int]bool + _, _ = json.Marshal(mapIntBool) // map[int]bool is safe + _, err = json.Marshal(mapIntBool) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err + + type innerStruct struct { + Bool bool + Int int + String string + + StrSlice []string + IntSlice []int + BoolSlice []bool + + StrArray [10]string + IntArray [10]int + BoolArray [10]bool + + MapStrStr map[string]string + MapStrInt map[string]int + MapStrBool map[string]bool + + MapIntStr map[int]string + MapIntInt map[int]int + MapIntBool map[int]bool + } + var outerStruct struct { + Bool bool + Int int + String string + + StrSlice []string + IntSlice []int + BoolSlice []bool + + StrArray [10]string + IntArray [10]int + BoolArray [10]bool + + MapStrStr map[string]string + MapStrInt map[string]int + MapStrBool map[string]bool + + MapIntStr map[int]string + MapIntInt map[int]int + MapIntBool map[int]bool + + InnerStruct innerStruct + } + _, _ = json.Marshal(outerStruct) // struct with only safe types + _, err = json.Marshal(outerStruct) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err +} + +type ( + structKey struct{ id int } + ExportedUnsafeAndInvalidStruct struct { // unsafe unexported but ommited + F64 float64 + F64Ptr *float64 + F64Slice []float64 + F64Array [10]float64 + MapStrF64 map[string]float64 + MapEIStr map[interface{}]string + Number json.Number + NumberPtr *json.Number + NumberSlice []json.Number + MapNumberStr map[json.Number]string + Ei interface{} + Stringer fmt.Stringer + Mt marshalText + MapMarshalTextString map[marshalText]string + + C128 complex128 + C128Ptr *complex128 + C128Slice []complex128 + C128Array [10]complex128 + MapBoolStr map[bool]string + MapF64Str map[float64]string + F func() + Ch chan struct{} + UnsafePtr unsafe.Pointer + MapStructStr map[structKey]string + } +) + +// JSONMarshalSaveStructWithUnexportedFields contains a struct with unexported, unsafe fields. +func JSONMarshalSaveStructWithUnexportedFields() { + var err error + + var unexportedInStruct struct { + Bool bool // safe exported + + f64 float64 // unsafe unexported + f64Ptr *float64 // unsafe unexported + f64Slice []float64 // unsafe unexported + f64Array [10]float64 // unsafe unexported + mapStrF64 map[string]float64 // unsafe unexported + mapEIStr map[interface{}]string // unsafe unexported + number json.Number // unsafe unexported + numberPtr *json.Number // unsafe unexported + numberSlice []json.Number // unsafe unexported + mapNumberStr map[json.Number]string // unsafe unexported + ei interface{} // unsafe unexported + stringer fmt.Stringer // unsafe unexported + mt marshalText // unsafe unexported + mapMarshalTextString map[marshalText]string // unsafe unexported + unexportedStruct ExportedUnsafeAndInvalidStruct // unsafe unexported + unexportedStructPtr *ExportedUnsafeAndInvalidStruct // unsafe unexported + + c128 complex128 // invalid unexported + c128Slice []complex128 // invalid unexported + c128Array [10]complex128 // invalid unexported + mapBoolStr map[bool]string // invalid unexported + mapF64Str map[float64]string // invalid unexported + f func() // invalid unexported + ch chan struct{} // invalid unexported + unsafePtr unsafe.Pointer // invalid unexported + mapStructStr map[structKey]string // invalid unexported + } + _ = unexportedInStruct.f64 + _ = unexportedInStruct.f64Ptr + _ = unexportedInStruct.f64Slice + _ = unexportedInStruct.f64Array + _ = unexportedInStruct.mapStrF64 + _ = unexportedInStruct.mapEIStr + _ = unexportedInStruct.number + _ = unexportedInStruct.numberPtr + _ = unexportedInStruct.numberSlice + _ = unexportedInStruct.mapNumberStr + _ = unexportedInStruct.ei + _ = unexportedInStruct.stringer + _ = unexportedInStruct.mt + _ = unexportedInStruct.mapMarshalTextString + _ = unexportedInStruct.unexportedStruct + _ = unexportedInStruct.unexportedStructPtr + + _ = unexportedInStruct.c128 + _ = unexportedInStruct.c128Slice + _ = unexportedInStruct.c128Array + _ = unexportedInStruct.mapBoolStr + _ = unexportedInStruct.mapF64Str + _ = unexportedInStruct.f + _ = unexportedInStruct.ch + _ = unexportedInStruct.unsafePtr + _ = unexportedInStruct.mapStructStr[structKey{1}] + _, _ = json.Marshal(unexportedInStruct) // struct containing unsafe but unexported fields is safe + _, err = json.Marshal(unexportedInStruct) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err +} + +// JSONMarshalSaveStructWithOmittedFields contains a struct with omitted, unsafe fields. +func JSONMarshalSaveStructWithOmittedFields() { + var err error + + var ommitInStruct struct { + Bool bool // safe exported + + F64 float64 `json:"-"` // unsafe exported but ommited + F64Ptr *float64 `json:"-"` // unsafe exported but ommited + F64Slice []float64 `json:"-"` // unsafe exported but ommited + F64Array [10]float64 `json:"-"` // unsafe exported but ommited + MapStrF64 map[string]float64 `json:"-"` // unsafe exported but ommited + MapEIStr map[interface{}]string `json:"-"` // unsafe exported but ommited + Number json.Number `json:"-"` // unsafe exported but ommited + NumberPtr *json.Number `json:"-"` // unsafe exported but ommited + NumberSlice []json.Number `json:"-"` // unsafe exported but ommited + MapNumberStr map[json.Number]string `json:"-"` // unsafe exported but ommited + Ei interface{} `json:"-"` // unsafe exported but ommited + Stringer fmt.Stringer `json:"-"` // unsafe exported but ommited + Mt marshalText `json:"-"` // unsafe exported but ommited + MapMarshalTextString map[marshalText]string `json:"-"` // unsafe exported but ommited + ExportedStruct ExportedUnsafeAndInvalidStruct `json:"-"` // unsafe exported but ommited + ExportedStructPtr *ExportedUnsafeAndInvalidStruct `json:"-"` // unsafe exported but ommited + + C128 complex128 `json:"-"` // invalid exported but ommited + C128Slice []complex128 `json:"-"` // invalid exported but ommited + C128Array [10]complex128 `json:"-"` // invalid exported but ommited + MapBoolStr map[bool]string `json:"-"` // invalid exported but ommited + MapF64Str map[float64]string `json:"-"` // invalid exported but ommited + F func() `json:"-"` // invalid exported but ommited + Ch chan struct{} `json:"-"` // invalid exported but ommited + UnsafePtr unsafe.Pointer `json:"-"` // invalid exported but ommited + MapStructStr map[structKey]string `json:"-"` // invalid exported but ommited + } + _ = ommitInStruct.MapStructStr[structKey{1}] + _, _ = json.Marshal(ommitInStruct) // struct containing unsafe but omitted, exported fields is safe + _, err = json.Marshal(ommitInStruct) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = err +} + +// JSONMarshalUnsaveTypes contains a multitude of test cases to marshal different combinations of types to JSON, +// that can potentially lead to json.Marshal returning an error. +func JSONMarshalUnsaveTypes() { + var err error + + var f32 float32 + _, _ = json.Marshal(f32) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float32` found" + _, err = json.Marshal(f32) // err is checked + _ = err + + var f64 float64 + _, _ = json.Marshal(f64) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float64` found" + _, err = json.Marshal(f64) // err is checked + _ = err + + var f32Slice []float32 + _, _ = json.Marshal(f32Slice) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float32` found" + _, err = json.Marshal(f32Slice) // err is checked + _ = err + + var f64Slice []float64 + _, _ = json.Marshal(f64Slice) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float64` found" + _, err = json.Marshal(f64Slice) // err is checked + _ = err + + var f32Array [10]float32 + _, _ = json.Marshal(f32Array) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float32` found" + _, err = json.Marshal(f32Array) // err is checked + _ = err + + var f64Array [10]float64 + _, _ = json.Marshal(f64Array) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float64` found" + _, err = json.Marshal(f64Array) // err is checked + _ = err + + var structPtrF32 struct { + F32 *float32 + } + _, _ = json.Marshal(structPtrF32) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float32` found" + _, err = json.Marshal(structPtrF32) // err is checked + _ = err + + var structPtrF64 struct { + F64 *float64 + } + _, _ = json.Marshal(structPtrF64) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float64` found" + _, err = json.Marshal(structPtrF64) // err is checked + _ = err + + var mapStrF32 map[string]float32 + _, _ = json.Marshal(mapStrF32) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float32` found" + _, err = json.Marshal(mapStrF32) // err is checked + _ = err + + var mapStrF64 map[string]float64 + _, _ = json.Marshal(mapStrF64) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float64` found" + _, err = json.Marshal(mapStrF64) // err is checked + _ = err + + var mapEIStr map[interface{}]string + _, _ = json.Marshal(mapEIStr) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `interface{}` as map key found" + _, err = json.Marshal(mapEIStr) // err is checked + _ = err + + var mapStringerStr map[fmt.Stringer]string + _, _ = json.Marshal(mapStringerStr) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `fmt.Stringer` as map key found" + _, err = json.Marshal(mapStringerStr) // err is checked + _ = err + + var number json.Number + _, _ = json.Marshal(number) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `encoding/json.Number` found" + _, err = json.Marshal(number) // err is checked + _ = err + + var numberSlice []json.Number + _, _ = json.Marshal(numberSlice) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `encoding/json.Number` found" + _, err = json.Marshal(numberSlice) // err is checked + _ = err + + var mapNumberStr map[json.Number]string + _, _ = json.Marshal(mapNumberStr) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `encoding/json.Number` as map key found" + _, err = json.Marshal(mapNumberStr) // err is checked + _ = err + + var mapStrNumber map[string]json.Number + _, _ = json.Marshal(mapStrNumber) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `encoding/json.Number` found" + _, err = json.Marshal(mapStrNumber) // err is checked + _ = err + + var ei interface{} + _, _ = json.Marshal(ei) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `interface{}` found" + _, err = json.Marshal(ei) // err is checked + _ = err + + var eiptr *interface{} + _, _ = json.Marshal(eiptr) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `*interface{}` found" + _, err = json.Marshal(eiptr) // err is checked + _ = err + + var stringer fmt.Stringer + _, _ = json.Marshal(stringer) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `fmt.Stringer` found" + _, err = json.Marshal(stringer) // err is checked + _ = err + + var structWithEmptyInterface struct { + EmptyInterface interface{} + } + _, _ = json.Marshal(structWithEmptyInterface) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `interface{}` found" + _, err = json.Marshal(structWithEmptyInterface) // err is checked + _ = err + + var mt marshalText + _, _ = json.Marshal(mt) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `[a-z-]+.marshalText` found" + _, err = json.Marshal(mt) // err is checked + _ = err + + var mapMarshalTextString map[marshalText]string + _, _ = json.Marshal(mapMarshalTextString) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `[a-z-]+.marshalText` as map key found" + _, err = json.Marshal(mapMarshalTextString) // err is checked + _ = err +} + +// JSONMarshalInvalidTypes contains a multitude of test cases to marshal different combinations of types to JSON, +// that are invalid and not supported by json.Marshal, that is they will always return an error, if these types used +// with json.Marshal. +func JSONMarshalInvalidTypes() { + var err error + + var c64 complex64 + _, _ = json.Marshal(c64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" + _, err = json.Marshal(c64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" + _ = err + + var c128 complex128 + _, _ = json.Marshal(c128) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` found" + _, err = json.Marshal(c128) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` found" + _ = err + + var sliceC64 []complex64 + _, _ = json.Marshal(sliceC64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" + _, err = json.Marshal(sliceC64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" + _ = err + + var sliceC128 []complex128 + _, _ = json.Marshal(sliceC128) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` found" + _, err = json.Marshal(sliceC128) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` found" + _ = err + + var arrayC64 []complex64 + _, _ = json.Marshal(arrayC64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" + _, err = json.Marshal(arrayC64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" + _ = err + + var arrayC128 []complex128 + _, _ = json.Marshal(arrayC128) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` found" + _, err = json.Marshal(arrayC128) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` found" + _ = err + + var structPtrC64 struct { + C64 *complex64 + } + _, _ = json.Marshal(structPtrC64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" + _, err = json.Marshal(structPtrC64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" + _ = err + + var structPtrC128 struct { + C128 *complex128 + } + _, _ = json.Marshal(structPtrC128) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` found" + _, err = json.Marshal(structPtrC128) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` found" + _ = err + + var mapBoolStr map[bool]string + _, _ = json.Marshal(mapBoolStr) // ERROR "`encoding/json.Marshal` for unsupported type `bool` as map key found" + _, err = json.Marshal(mapBoolStr) // ERROR "`encoding/json.Marshal` for unsupported type `bool` as map key found" + _ = err + + var mapF32Str map[float32]string + _, _ = json.Marshal(mapF32Str) // ERROR "`encoding/json.Marshal` for unsupported type `float32` as map key found" + _, err = json.Marshal(mapF32Str) // ERROR "`encoding/json.Marshal` for unsupported type `float32` as map key found" + _ = err + + var mapF64Str map[float64]string + _, _ = json.Marshal(mapF64Str) // ERROR "`encoding/json.Marshal` for unsupported type `float64` as map key found" + _, err = json.Marshal(mapF64Str) // ERROR "`encoding/json.Marshal` for unsupported type `float64` as map key found" + _ = err + + var mapC64Str map[complex64]string + _, _ = json.Marshal(mapC64Str) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` as map key found" + _, err = json.Marshal(mapC64Str) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` as map key found" + _ = err + + var mapC128Str map[complex128]string + _, _ = json.Marshal(mapC128Str) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` as map key found" + _, err = json.Marshal(mapC128Str) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` as map key found" + _ = err + + mapStructStr := map[structKey]string{structKey{1}: "str"} + _, _ = json.Marshal(mapStructStr) // ERROR "`encoding/json.Marshal` for unsupported type `[a-z-]+.structKey` as map key found" + _, err = json.Marshal(mapStructStr) // ERROR "`encoding/json.Marshal` for unsupported type `[a-z-]+.structKey` as map key found" + _ = err + + f := func() {} + _, _ = json.Marshal(f) // ERROR "`encoding/json.Marshal` for unsupported type `func\\(\\)` found" + _, err = json.Marshal(f) // ERROR "`encoding/json.Marshal` for unsupported type `func\\(\\)` found" + _ = err + + var ch chan struct{} = make(chan struct{}) + _, _ = json.Marshal(ch) // ERROR "`encoding/json.Marshal` for unsupported type `chan struct{}` found" + _, err = json.Marshal(ch) // ERROR "`encoding/json.Marshal` for unsupported type `chan struct{}` found" + _ = err + + var unsafePtr unsafe.Pointer + _, _ = json.Marshal(unsafePtr) // ERROR "`encoding/json.Marshal` for unsupported type `unsafe.Pointer` found" + _, err = json.Marshal(unsafePtr) // ERROR "`encoding/json.Marshal` for unsupported type `unsafe.Pointer` found" + _ = err +} + +// NotJSONMarshal contains other go ast node types, that are not considered by errchkjson +func NotJSONMarshal() { + s := fmt.Sprintln("I am not considered by errchkjson") + _ = s + f := func() bool { return false } + _ = f() +} diff --git a/test/testdata/errchkjson_omit_safe.go b/test/testdata/errchkjson_omit_safe.go new file mode 100644 index 000000000000..bc218c67cc37 --- /dev/null +++ b/test/testdata/errchkjson_omit_safe.go @@ -0,0 +1,614 @@ +// args: -Eerrchkjson +// config_path: testdata/configs/errchkjson_omit_safe.yml +package testdata + +import ( + "encoding" + "encoding/json" + "fmt" + "io/ioutil" + "unsafe" +) + +type marshalText struct{} + +func (mt marshalText) MarshalText() ([]byte, error) { + return []byte(`mt`), nil +} + +var _ encoding.TextMarshaler = marshalText(struct{}{}) + +// JSONMarshalSaveTypesWithNoSafe contains a multitude of test cases to marshal different combinations of types to JSON, +// that are save, that is, they will never return an error, if these types are marshaled to JSON. +func JSONMarshalSaveTypesWithNoSafe() { + var err error + + _, _ = json.Marshal(nil) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + json.Marshal(nil) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(nil) // nil is safe, but omit-safe is set + _ = err + + _, _ = json.MarshalIndent(nil, "", " ") // ERROR "Error return value of `encoding/json.MarshalIndent` is not checked" + json.MarshalIndent(nil, "", " ") // ERROR "Error return value of `encoding/json.MarshalIndent` is not checked" + _, err = json.MarshalIndent(nil, "", " ") // nil is safe, but omit-safe is set + _ = err + + enc := json.NewEncoder(ioutil.Discard) + _ = enc.Encode(nil) // ERROR "Error return value of `\\([*]encoding/json.Encoder\\).Encode` is not checked" + enc.Encode(nil) // ERROR "Error return value of `\\([*]encoding/json.Encoder\\).Encode` is not checked" + err = enc.Encode(nil) // nil is safe, but omit-safe is set + _ = err + + var b bool + _, _ = json.Marshal(b) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(b) // bool is safe, but omit-safe is set + _ = err + + var i int + _, _ = json.Marshal(i) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(i) // int is safe, but omit-safe is set + _ = err + + var i8 int8 + _, _ = json.Marshal(i8) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(i8) // int8 is safe, but omit-safe is set + _ = err + + var i16 int16 + _, _ = json.Marshal(i16) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(i16) // int16 is safe, but omit-safe is set + _ = err + + var i32 int32 + _, _ = json.Marshal(i32) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(i32) // int32 / rune is safe, but omit-safe is set + _ = err + + var i64 int64 + _, _ = json.Marshal(i64) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(i64) // int64 is safe, but omit-safe is set + _ = err + + var ui uint + _, _ = json.Marshal(ui) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(ui) // uint is safe, but omit-safe is set + _ = err + + var ui8 uint8 + _, _ = json.Marshal(ui8) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(ui8) // uint8 is safe, but omit-safe is set + _ = err + + var ui16 uint16 + _, _ = json.Marshal(ui16) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(ui16) // uint16 is safe, but omit-safe is set + _ = err + + var ui32 uint32 + _, _ = json.Marshal(ui32) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(ui32) // uint32 is safe, but omit-safe is set + _ = err + + var ui64 uint64 + _, _ = json.Marshal(ui64) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(ui64) // uint64 is safe, but omit-safe is set + _ = err + + var uiptr uintptr + _, _ = json.Marshal(uiptr) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(uiptr) // uintptr is safe, but omit-safe is set + _ = err + + var str string + _, _ = json.Marshal(str) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(str) // string is safe, but omit-safe is set + _ = err + + var strSlice []string + _, _ = json.Marshal(strSlice) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(strSlice) // []string is safe, but omit-safe is set + _ = err + + var intSlice []int + _, _ = json.Marshal(intSlice) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(intSlice) // []int is safe, but omit-safe is set + _ = err + + var boolSlice []bool + _, _ = json.Marshal(boolSlice) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(boolSlice) // []bool is safe, but omit-safe is set + _ = err + + var strArray [10]string + _, _ = json.Marshal(strArray) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(strArray) // [10]string is safe, but omit-safe is set + _ = err + + var intArray [10]int + _, _ = json.Marshal(intArray) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(intArray) // [10]int is safe, but omit-safe is set + _ = err + + var boolArray [10]bool + _, _ = json.Marshal(boolArray) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(boolArray) // [10]bool is safe, but omit-safe is set + _ = err + + var basicStruct struct { + Bool bool + Int int + Int8 int8 + Int16 int16 + Int32 int32 // also rune + Int64 int64 + Uint uint + Uint8 uint8 // also byte + Uint16 uint16 + Uint32 uint32 + Uint64 uint64 + Uintptr uintptr + String string + } + _, _ = json.Marshal(basicStruct) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(basicStruct) // struct containing only safe basic types is safe, but omit-safe is set + _ = err + + var ptrStruct struct { + Bool *bool + Int *int + Int8 *int8 + Int16 *int16 + Int32 *int32 + Int64 *int64 + Uint *uint + Uint8 *uint8 + Uint16 *uint16 + Uint32 *uint32 + Uint64 *uint64 + Uintptr *uintptr + String *string + } + _, _ = json.Marshal(ptrStruct) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(ptrStruct) // struct containing pointer to only safe basic types is safe, but omit-safe is set + _ = err + + var mapStrStr map[string]string + _, _ = json.Marshal(mapStrStr) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(mapStrStr) // map[string]string is safe, but omit-safe is set + _ = err + + var mapStrInt map[string]int + _, _ = json.Marshal(mapStrInt) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(mapStrInt) // map[string]int is safe, but omit-safe is set + _ = err + + var mapStrBool map[string]bool + _, _ = json.Marshal(mapStrBool) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(mapStrBool) // map[string]bool is safe, but omit-safe is set + _ = err + + var mapIntStr map[int]string + _, _ = json.Marshal(mapIntStr) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(mapIntStr) // map[int]string is safe, but omit-safe is set + _ = err + + var mapIntInt map[int]int + _, _ = json.Marshal(mapIntInt) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(mapIntInt) // map[int]int is safe, but omit-safe is set + _ = err + + var mapIntBool map[int]bool + _, _ = json.Marshal(mapIntBool) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(mapIntBool) // map[int]bool is safe, but omit-safe is set + _ = err + + type innerStruct struct { + Bool bool + Int int + String string + + StrSlice []string + IntSlice []int + BoolSlice []bool + + StrArray [10]string + IntArray [10]int + BoolArray [10]bool + + MapStrStr map[string]string + MapStrInt map[string]int + MapStrBool map[string]bool + + MapIntStr map[int]string + MapIntInt map[int]int + MapIntBool map[int]bool + } + var outerStruct struct { + Bool bool + Int int + String string + + StrSlice []string + IntSlice []int + BoolSlice []bool + + StrArray [10]string + IntArray [10]int + BoolArray [10]bool + + MapStrStr map[string]string + MapStrInt map[string]int + MapStrBool map[string]bool + + MapIntStr map[int]string + MapIntInt map[int]int + MapIntBool map[int]bool + + InnerStruct innerStruct + } + _, _ = json.Marshal(outerStruct) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(outerStruct) // struct with only safe types is safe, but omit-safe is set + _ = err +} + +type ( + structKey struct{ id int } + ExportedUnsafeAndInvalidStruct struct { // unsafe unexported but ommited + F64 float64 + F64Ptr *float64 + F64Slice []float64 + F64Array [10]float64 + MapStrF64 map[string]float64 + MapEIStr map[interface{}]string + Number json.Number + NumberPtr *json.Number + NumberSlice []json.Number + MapNumberStr map[json.Number]string + Ei interface{} + Stringer fmt.Stringer + Mt marshalText + MapMarshalTextString map[marshalText]string + + C128 complex128 + C128Ptr *complex128 + C128Slice []complex128 + C128Array [10]complex128 + MapBoolStr map[bool]string + MapF64Str map[float64]string + F func() + Ch chan struct{} + UnsafePtr unsafe.Pointer + MapStructStr map[structKey]string + } +) + +// JSONMarshalSaveStructWithUnexportedFields contains a struct with unexported, unsafe fields. +func JSONMarshalSaveStructWithUnexportedFieldsWithNoSafe() { + var err error + + var unexportedInStruct struct { + Bool bool // safe exported + + f64 float64 // unsafe unexported + f64Ptr *float64 // unsafe unexported + f64Slice []float64 // unsafe unexported + f64Array [10]float64 // unsafe unexported + mapStrF64 map[string]float64 // unsafe unexported + mapEIStr map[interface{}]string // unsafe unexported + number json.Number // unsafe unexported + numberPtr *json.Number // unsafe unexported + numberSlice []json.Number // unsafe unexported + mapNumberStr map[json.Number]string // unsafe unexported + ei interface{} // unsafe unexported + stringer fmt.Stringer // unsafe unexported + mt marshalText // unsafe unexported + mapMarshalTextString map[marshalText]string // unsafe unexported + unexportedStruct ExportedUnsafeAndInvalidStruct // unsafe unexported + unexportedStructPtr *ExportedUnsafeAndInvalidStruct // unsafe unexported + + c128 complex128 // invalid unexported + c128Slice []complex128 // invalid unexported + c128Array [10]complex128 // invalid unexported + mapBoolStr map[bool]string // invalid unexported + mapF64Str map[float64]string // invalid unexported + f func() // invalid unexported + ch chan struct{} // invalid unexported + unsafePtr unsafe.Pointer // invalid unexported + mapStructStr map[structKey]string // invalid unexported + } + _ = unexportedInStruct.f64 + _ = unexportedInStruct.f64Ptr + _ = unexportedInStruct.f64Slice + _ = unexportedInStruct.f64Array + _ = unexportedInStruct.mapStrF64 + _ = unexportedInStruct.mapEIStr + _ = unexportedInStruct.number + _ = unexportedInStruct.numberPtr + _ = unexportedInStruct.numberSlice + _ = unexportedInStruct.mapNumberStr + _ = unexportedInStruct.ei + _ = unexportedInStruct.stringer + _ = unexportedInStruct.mt + _ = unexportedInStruct.mapMarshalTextString + _ = unexportedInStruct.unexportedStruct + _ = unexportedInStruct.unexportedStructPtr + + _ = unexportedInStruct.c128 + _ = unexportedInStruct.c128Slice + _ = unexportedInStruct.c128Array + _ = unexportedInStruct.mapBoolStr + _ = unexportedInStruct.mapF64Str + _ = unexportedInStruct.f + _ = unexportedInStruct.ch + _ = unexportedInStruct.unsafePtr + _ = unexportedInStruct.mapStructStr[structKey{1}] + _, _ = json.Marshal(unexportedInStruct) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(unexportedInStruct) // struct containing unsafe but unexported fields is safe + _ = err +} + +// JSONMarshalSaveStructWithOmittedFields contains a struct with omitted, unsafe fields. +func JSONMarshalSaveStructWithOmittedFieldsWithNoSafe() { + var err error + + var ommitInStruct struct { + Bool bool // safe exported + + F64 float64 `json:"-"` // unsafe exported but ommited + F64Ptr *float64 `json:"-"` // unsafe exported but ommited + F64Slice []float64 `json:"-"` // unsafe exported but ommited + F64Array [10]float64 `json:"-"` // unsafe exported but ommited + MapStrF64 map[string]float64 `json:"-"` // unsafe exported but ommited + MapEIStr map[interface{}]string `json:"-"` // unsafe exported but ommited + Number json.Number `json:"-"` // unsafe exported but ommited + NumberPtr *json.Number `json:"-"` // unsafe exported but ommited + NumberSlice []json.Number `json:"-"` // unsafe exported but ommited + MapNumberStr map[json.Number]string `json:"-"` // unsafe exported but ommited + Ei interface{} `json:"-"` // unsafe exported but ommited + Stringer fmt.Stringer `json:"-"` // unsafe exported but ommited + Mt marshalText `json:"-"` // unsafe exported but ommited + MapMarshalTextString map[marshalText]string `json:"-"` // unsafe exported but ommited + ExportedStruct ExportedUnsafeAndInvalidStruct `json:"-"` // unsafe exported but ommited + ExportedStructPtr *ExportedUnsafeAndInvalidStruct `json:"-"` // unsafe exported but ommited + + C128 complex128 `json:"-"` // invalid exported but ommited + C128Slice []complex128 `json:"-"` // invalid exported but ommited + C128Array [10]complex128 `json:"-"` // invalid exported but ommited + MapBoolStr map[bool]string `json:"-"` // invalid exported but ommited + MapF64Str map[float64]string `json:"-"` // invalid exported but ommited + F func() `json:"-"` // invalid exported but ommited + Ch chan struct{} `json:"-"` // invalid exported but ommited + UnsafePtr unsafe.Pointer `json:"-"` // invalid exported but ommited + MapStructStr map[structKey]string `json:"-"` // invalid exported but ommited + } + _ = ommitInStruct.MapStructStr[structKey{1}] + _, _ = json.Marshal(ommitInStruct) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(ommitInStruct) // struct containing unsafe but omitted, exported fields is safe, but omit-safe is set + _ = err +} + +// JSONMarshalUnsaveTypes contains a multitude of test cases to marshal different combinations of types to JSON, +// that can potentially lead to json.Marshal returning an error. +func JSONMarshalUnsaveTypes() { + var err error + + var f32 float32 + _, _ = json.Marshal(f32) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float32` found" + _, err = json.Marshal(f32) // err is checked + _ = err + + var f64 float64 + _, _ = json.Marshal(f64) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float64` found" + _, err = json.Marshal(f64) // err is checked + _ = err + + var f32Slice []float32 + _, _ = json.Marshal(f32Slice) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float32` found" + _, err = json.Marshal(f32Slice) // err is checked + _ = err + + var f64Slice []float64 + _, _ = json.Marshal(f64Slice) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float64` found" + _, err = json.Marshal(f64Slice) // err is checked + _ = err + + var f32Array [10]float32 + _, _ = json.Marshal(f32Array) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float32` found" + _, err = json.Marshal(f32Array) // err is checked + _ = err + + var f64Array [10]float64 + _, _ = json.Marshal(f64Array) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float64` found" + _, err = json.Marshal(f64Array) // err is checked + _ = err + + var structPtrF32 struct { + F32 *float32 + } + _, _ = json.Marshal(structPtrF32) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float32` found" + _, err = json.Marshal(structPtrF32) // err is checked + _ = err + + var structPtrF64 struct { + F64 *float64 + } + _, _ = json.Marshal(structPtrF64) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float64` found" + _, err = json.Marshal(structPtrF64) // err is checked + _ = err + + var mapStrF32 map[string]float32 + _, _ = json.Marshal(mapStrF32) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float32` found" + _, err = json.Marshal(mapStrF32) // err is checked + _ = err + + var mapStrF64 map[string]float64 + _, _ = json.Marshal(mapStrF64) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float64` found" + _, err = json.Marshal(mapStrF64) // err is checked + _ = err + + var mapEIStr map[interface{}]string + _, _ = json.Marshal(mapEIStr) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `interface{}` as map key found" + _, err = json.Marshal(mapEIStr) // err is checked + _ = err + + var mapStringerStr map[fmt.Stringer]string + _, _ = json.Marshal(mapStringerStr) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `fmt.Stringer` as map key found" + _, err = json.Marshal(mapStringerStr) // err is checked + _ = err + + var number json.Number + _, _ = json.Marshal(number) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `encoding/json.Number` found" + _, err = json.Marshal(number) // err is checked + _ = err + + var numberSlice []json.Number + _, _ = json.Marshal(numberSlice) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `encoding/json.Number` found" + _, err = json.Marshal(numberSlice) // err is checked + _ = err + + var mapNumberStr map[json.Number]string + _, _ = json.Marshal(mapNumberStr) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `encoding/json.Number` as map key found" + _, err = json.Marshal(mapNumberStr) // err is checked + _ = err + + var mapStrNumber map[string]json.Number + _, _ = json.Marshal(mapStrNumber) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `encoding/json.Number` found" + _, err = json.Marshal(mapStrNumber) // err is checked + _ = err + + var ei interface{} + _, _ = json.Marshal(ei) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `interface{}` found" + _, err = json.Marshal(ei) // err is checked + _ = err + + var eiptr *interface{} + _, _ = json.Marshal(eiptr) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `*interface{}` found" + _, err = json.Marshal(eiptr) // err is checked + _ = err + + var stringer fmt.Stringer + _, _ = json.Marshal(stringer) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `fmt.Stringer` found" + _, err = json.Marshal(stringer) // err is checked + _ = err + + var structWithEmptyInterface struct { + EmptyInterface interface{} + } + _, _ = json.Marshal(structWithEmptyInterface) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `interface{}` found" + _, err = json.Marshal(structWithEmptyInterface) // err is checked + _ = err + + var mt marshalText + _, _ = json.Marshal(mt) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `[a-z-]+.marshalText` found" + _, err = json.Marshal(mt) // err is checked + _ = err + + var mapMarshalTextString map[marshalText]string + _, _ = json.Marshal(mapMarshalTextString) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `[a-z-]+.marshalText` as map key found" + _, err = json.Marshal(mapMarshalTextString) // err is checked + _ = err +} + +// JSONMarshalInvalidTypes contains a multitude of test cases to marshal different combinations of types to JSON, +// that are invalid and not supported by json.Marshal, that is they will always return an error, if these types used +// with json.Marshal. +func JSONMarshalInvalidTypes() { + var err error + + var c64 complex64 + _, _ = json.Marshal(c64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" + _, err = json.Marshal(c64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" + _ = err + + var c128 complex128 + _, _ = json.Marshal(c128) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` found" + _, err = json.Marshal(c128) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` found" + _ = err + + var sliceC64 []complex64 + _, _ = json.Marshal(sliceC64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" + _, err = json.Marshal(sliceC64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" + _ = err + + var sliceC128 []complex128 + _, _ = json.Marshal(sliceC128) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` found" + _, err = json.Marshal(sliceC128) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` found" + _ = err + + var arrayC64 []complex64 + _, _ = json.Marshal(arrayC64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" + _, err = json.Marshal(arrayC64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" + _ = err + + var arrayC128 []complex128 + _, _ = json.Marshal(arrayC128) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` found" + _, err = json.Marshal(arrayC128) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` found" + _ = err + + var structPtrC64 struct { + C64 *complex64 + } + _, _ = json.Marshal(structPtrC64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" + _, err = json.Marshal(structPtrC64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" + _ = err + + var structPtrC128 struct { + C128 *complex128 + } + _, _ = json.Marshal(structPtrC128) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` found" + _, err = json.Marshal(structPtrC128) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` found" + _ = err + + var mapBoolStr map[bool]string + _, _ = json.Marshal(mapBoolStr) // ERROR "`encoding/json.Marshal` for unsupported type `bool` as map key found" + _, err = json.Marshal(mapBoolStr) // ERROR "`encoding/json.Marshal` for unsupported type `bool` as map key found" + _ = err + + var mapF32Str map[float32]string + _, _ = json.Marshal(mapF32Str) // ERROR "`encoding/json.Marshal` for unsupported type `float32` as map key found" + _, err = json.Marshal(mapF32Str) // ERROR "`encoding/json.Marshal` for unsupported type `float32` as map key found" + _ = err + + var mapF64Str map[float64]string + _, _ = json.Marshal(mapF64Str) // ERROR "`encoding/json.Marshal` for unsupported type `float64` as map key found" + _, err = json.Marshal(mapF64Str) // ERROR "`encoding/json.Marshal` for unsupported type `float64` as map key found" + _ = err + + var mapC64Str map[complex64]string + _, _ = json.Marshal(mapC64Str) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` as map key found" + _, err = json.Marshal(mapC64Str) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` as map key found" + _ = err + + var mapC128Str map[complex128]string + _, _ = json.Marshal(mapC128Str) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` as map key found" + _, err = json.Marshal(mapC128Str) // ERROR "`encoding/json.Marshal` for unsupported type `complex128` as map key found" + _ = err + + mapStructStr := map[structKey]string{structKey{1}: "str"} + _, _ = json.Marshal(mapStructStr) // ERROR "`encoding/json.Marshal` for unsupported type `[a-z-]+.structKey` as map key found" + _, err = json.Marshal(mapStructStr) // ERROR "`encoding/json.Marshal` for unsupported type `[a-z-]+.structKey` as map key found" + _ = err + + f := func() {} + _, _ = json.Marshal(f) // ERROR "`encoding/json.Marshal` for unsupported type `func\\(\\)` found" + _, err = json.Marshal(f) // ERROR "`encoding/json.Marshal` for unsupported type `func\\(\\)` found" + _ = err + + var ch chan struct{} = make(chan struct{}) + _, _ = json.Marshal(ch) // ERROR "`encoding/json.Marshal` for unsupported type `chan struct{}` found" + _, err = json.Marshal(ch) // ERROR "`encoding/json.Marshal` for unsupported type `chan struct{}` found" + _ = err + + var unsafePtr unsafe.Pointer + _, _ = json.Marshal(unsafePtr) // ERROR "`encoding/json.Marshal` for unsupported type `unsafe.Pointer` found" + _, err = json.Marshal(unsafePtr) // ERROR "`encoding/json.Marshal` for unsupported type `unsafe.Pointer` found" + _ = err +} + +// NotJSONMarshal contains other go ast node types, that are not considered by errchkjson +func NotJSONMarshal() { + s := fmt.Sprintln("I am not considered by errchkjson") + _ = s + f := func() bool { return false } + _ = f() +} From 78e0e0e03931f9481627e350fd6d20b306471e5c Mon Sep 17 00:00:00 2001 From: Lucas Bremgartner Date: Mon, 8 Nov 2021 23:05:09 +0100 Subject: [PATCH 2/7] Make report-no-exported configurable Prevent redundant checking with staticcheck. See: * https://github.com/golangci/golangci-lint/pull/2349#issuecomment-962695413 * https://staticcheck.io/docs/checks/#SA9005 --- .golangci.example.yml | 2 ++ go.mod | 2 +- go.sum | 2 ++ pkg/config/linters_settings.go | 3 ++- pkg/golinters/errchkjson.go | 9 ++++--- .../configs/errchkjson_no_exported.yml | 3 +++ test/testdata/errchkjson_no_exported.go | 26 +++++++++++++++++++ 7 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 test/testdata/configs/errchkjson_no_exported.yml create mode 100644 test/testdata/errchkjson_no_exported.go diff --git a/.golangci.example.yml b/.golangci.example.yml index 4207aa1c3a9e..cf4f79d99be4 100644 --- a/.golangci.example.yml +++ b/.golangci.example.yml @@ -107,6 +107,8 @@ linters-settings: # with omit-safe set to true, errchkjson does not warn about errors from json encoding functions that are safe # to be ignored, because they are not possible to happen (default false) omit-safe: false + # if report-no-exported is true, encoding a struct without exported fields is reported as issue (default false) + report-no-exported: false dogsled: # checks assignments with too many blank identifiers; default is 2 diff --git a/go.mod b/go.mod index e87685189e22..ae4bfbcc427b 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/blizzy78/varnamelen v0.4.0 github.com/bombsimon/wsl/v3 v3.3.0 github.com/breml/bidichk v0.2.1 - github.com/breml/errchkjson v0.1.0 + github.com/breml/errchkjson v0.2.0 github.com/butuzov/ireturn v0.1.1 github.com/charithe/durationcheck v0.0.9 github.com/daixiang0/gci v0.2.9 diff --git a/go.sum b/go.sum index 3e7f138f368f..e2cee8e6bd5f 100644 --- a/go.sum +++ b/go.sum @@ -110,6 +110,8 @@ github.com/breml/bidichk v0.2.1 h1:SRNtZuLdfkxtocj+xyHXKC1Uv3jVi6EPYx+NHSTNQvE= github.com/breml/bidichk v0.2.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= github.com/breml/errchkjson v0.1.0 h1:TXe36u08hr8KPsEpi4uinmQgZP13Jh9638aZCv0BcJI= github.com/breml/errchkjson v0.1.0/go.mod h1:jZEATw/jF69cL1iy7//Yih8yp/mXp2CBoBr9GJwCAsY= +github.com/breml/errchkjson v0.2.0 h1:5XK9tXXqahYiPHuJ5Asx9a5ucpASxLMxq3EvQyLb26c= +github.com/breml/errchkjson v0.2.0/go.mod h1:jZEATw/jF69cL1iy7//Yih8yp/mXp2CBoBr9GJwCAsY= github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY= github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go index ae46495417d4..dc62c7e5610d 100644 --- a/pkg/config/linters_settings.go +++ b/pkg/config/linters_settings.go @@ -167,7 +167,8 @@ type Cyclop struct { } type ErrChkJSONSettings struct { - OmitSafe bool `mapstructure:"omit-safe"` + OmitSafe bool `mapstructure:"omit-safe"` + ReportNoExported bool `mapstructure:"report-no-exported"` } type DepGuardSettings struct { diff --git a/pkg/golinters/errchkjson.go b/pkg/golinters/errchkjson.go index b7cf734c37ad..b981d79293fc 100644 --- a/pkg/golinters/errchkjson.go +++ b/pkg/golinters/errchkjson.go @@ -14,10 +14,13 @@ func NewErrChkJSONFuncName(cfg *config.ErrChkJSONSettings) *goanalysis.Linter { cfgMap := map[string]map[string]interface{}{} if cfg != nil { + cfgMap[a.Name] = map[string]interface{}{} + if cfg.OmitSafe { - cfgMap[a.Name] = map[string]interface{}{ - "omit-safe": "true", - } + cfgMap[a.Name]["omit-safe"] = true + } + if cfg.ReportNoExported { + cfgMap[a.Name]["report-no-exported"] = true } } diff --git a/test/testdata/configs/errchkjson_no_exported.yml b/test/testdata/configs/errchkjson_no_exported.yml new file mode 100644 index 000000000000..b62e297c6160 --- /dev/null +++ b/test/testdata/configs/errchkjson_no_exported.yml @@ -0,0 +1,3 @@ +linters-settings: + errchkjson: + report-no-exported: true diff --git a/test/testdata/errchkjson_no_exported.go b/test/testdata/errchkjson_no_exported.go new file mode 100644 index 000000000000..4c982b894935 --- /dev/null +++ b/test/testdata/errchkjson_no_exported.go @@ -0,0 +1,26 @@ +// args: -Eerrchkjson +// config_path: testdata/configs/errchkjson_no_exported.yml +package testdata + +import ( + "encoding/json" +) + +// JSONMarshalStructWithoutExportedFields contains a struct without exported fields. +func JSONMarshalStructWithoutExportedFields() { + var withoutExportedFields struct { + privateField bool + ExportedButOmittedField bool `json:"-"` + } + _, _ = json.Marshal(withoutExportedFields) // ERROR "Error argument passed to `encoding/json.Marshal` does not contain any exported field" +} + +// JSONMarshalStructWithNestedStructWithoutExportedFields contains a struct without exported fields. +func JSONMarshalStructWithNestedStructWithoutExportedFields() { + var withNestedStructWithoutExportedFields struct { + ExportedStruct struct { + privatField bool + } + } + _, _ = json.Marshal(withNestedStructWithoutExportedFields) +} From a6b6db95baca9117e5586c8dcb045c5946e4e979 Mon Sep 17 00:00:00 2001 From: Lucas Bremgartner Date: Mon, 8 Nov 2021 23:13:28 +0100 Subject: [PATCH 3/7] Exclude json encoding functions from errcheck if errchkjson is enabled and omit-safe is not activated. In this case, the check for the errors in errchkjson is superior to the "generic" check in errcheck. Therefore we instruct errcheck to ignore these cases and let errchkjson handle them. --- go.sum | 4 +-- pkg/golinters/errchkjson.go | 63 ++++++++++++++++++++++++++++++----- pkg/lint/lintersdb/manager.go | 15 ++++++--- test/testdata/errchkjson.go | 3 +- 4 files changed, 69 insertions(+), 16 deletions(-) diff --git a/go.sum b/go.sum index e2cee8e6bd5f..89d2e078d869 100644 --- a/go.sum +++ b/go.sum @@ -108,8 +108,8 @@ github.com/bombsimon/wsl/v3 v3.3.0 h1:Mka/+kRLoQJq7g2rggtgQsjuI/K5Efd87WX96EWFxj github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/breml/bidichk v0.2.1 h1:SRNtZuLdfkxtocj+xyHXKC1Uv3jVi6EPYx+NHSTNQvE= github.com/breml/bidichk v0.2.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= -github.com/breml/errchkjson v0.1.0 h1:TXe36u08hr8KPsEpi4uinmQgZP13Jh9638aZCv0BcJI= -github.com/breml/errchkjson v0.1.0/go.mod h1:jZEATw/jF69cL1iy7//Yih8yp/mXp2CBoBr9GJwCAsY= +github.com/breml/bidichk v0.2.0 h1:kBrsPFWq0GTrExB4G55zd1fCiw+0XN4o505lMx9XnFM= +github.com/breml/bidichk v0.2.0/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= github.com/breml/errchkjson v0.2.0 h1:5XK9tXXqahYiPHuJ5Asx9a5ucpASxLMxq3EvQyLb26c= github.com/breml/errchkjson v0.2.0/go.mod h1:jZEATw/jF69cL1iy7//Yih8yp/mXp2CBoBr9GJwCAsY= github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY= diff --git a/pkg/golinters/errchkjson.go b/pkg/golinters/errchkjson.go index b981d79293fc..0737ee0e8997 100644 --- a/pkg/golinters/errchkjson.go +++ b/pkg/golinters/errchkjson.go @@ -7,21 +7,38 @@ import ( "github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" + "github.com/golangci/golangci-lint/pkg/lint/linter" ) -func NewErrChkJSONFuncName(cfg *config.ErrChkJSONSettings) *goanalysis.Linter { +func NewErrChkJSONFuncName(linters *config.Linters, + cfg *config.ErrChkJSONSettings, + errcheckCfg *config.ErrcheckSettings, +) *goanalysis.Linter { a := errchkjson.NewAnalyzer() - cfgMap := map[string]map[string]interface{}{} + var omitSafe bool + var reportNoExported bool if cfg != nil { - cfgMap[a.Name] = map[string]interface{}{} + omitSafe = cfg.OmitSafe + reportNoExported = cfg.ReportNoExported + } - if cfg.OmitSafe { - cfgMap[a.Name]["omit-safe"] = true - } - if cfg.ReportNoExported { - cfgMap[a.Name]["report-no-exported"] = true + // Modify errcheck config if this linter is enabled and OmitSafe is false + if isEnabled(linters, a.Name) && !omitSafe { + if errcheckCfg == nil { + errcheckCfg = &config.ErrcheckSettings{} } + errcheckCfg.ExcludeFunctions = append(errcheckCfg.ExcludeFunctions, + "encoding/json.Marshal", + "encoding/json.MarshalIndent", + "(*encoding/json.Encoder).Encode", + ) + } + + cfgMap := map[string]map[string]interface{}{} + cfgMap[a.Name] = map[string]interface{}{ + "omit-safe": omitSafe, + "report-no-exported": reportNoExported, } return goanalysis.NewLinter( @@ -32,3 +49,33 @@ func NewErrChkJSONFuncName(cfg *config.ErrChkJSONSettings) *goanalysis.Linter { cfgMap, ).WithLoadMode(goanalysis.LoadModeTypesInfo) } + +func isEnabled(linters *config.Linters, linterName string) bool { + if linters != nil { + var enabled bool + for _, linter := range linters.Enable { + if linter == linterName { + enabled = true + break + } + } + var disabled bool + for _, linter := range linters.Disable { + if linter == linterName { + disabled = true + break + } + } + var presetEnabled bool + for _, preset := range linters.Presets { + if preset == linter.PresetBugs || preset == linter.PresetUnused { + presetEnabled = true + break + } + } + return enabled || + linters.EnableAll && !disabled || + presetEnabled && !disabled + } + return false +} diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index b380d3786176..5b00d5bca192 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -100,8 +100,11 @@ func enableLinterConfigs(lcs []*linter.Config, isEnabled func(lc *linter.Config) //nolint:funlen func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { + var linters *config.Linters + var bidichkCfg *config.BiDiChkSettings var cyclopCfg *config.Cyclop + var errcheckCfg *config.ErrcheckSettings var errchkjsonCfg *config.ErrChkJSONSettings var errorlintCfg *config.ErrorLintSettings var exhaustiveCfg *config.ExhaustiveSettings @@ -128,8 +131,10 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { var nlreturnCfg *config.NlreturnSettings if m.cfg != nil { + linters = &m.cfg.Linters bidichkCfg = &m.cfg.LintersSettings.BiDiChk cyclopCfg = &m.cfg.LintersSettings.Cyclop + errcheckCfg = &m.cfg.LintersSettings.Errcheck errchkjsonCfg = &m.cfg.LintersSettings.ErrChkJSON errorlintCfg = &m.cfg.LintersSettings.ErrorLint exhaustiveCfg = &m.cfg.LintersSettings.Exhaustive @@ -175,6 +180,11 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithLoadForGoAnalysis(). WithPresets(linter.PresetPerformance, linter.PresetBugs). WithURL("https://github.com/sonatard/noctx"), + linter.NewConfig(golinters.NewErrChkJSONFuncName(linters, errchkjsonCfg, errcheckCfg)). + WithSince("1.44.0"). + WithPresets(linter.PresetBugs, linter.PresetUnused). + WithLoadForGoAnalysis(). + WithURL("https://github.com/breml/errchkjson"), linter.NewConfig(golinters.NewErrcheck()). WithSince("v1.0.0"). WithLoadForGoAnalysis(). @@ -550,11 +560,6 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithSince("1.43.0"). WithPresets(linter.PresetBugs). WithURL("https://github.com/breml/bidichk"), - linter.NewConfig(golinters.NewErrChkJSONFuncName(errchkjsonCfg)). - WithSince("1.44.0"). - WithPresets(linter.PresetBugs, linter.PresetUnused). - WithLoadForGoAnalysis(). - WithURL("https://github.com/breml/errchkjson"), // nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives linter.NewConfig(golinters.NewNoLintLint()). diff --git a/test/testdata/errchkjson.go b/test/testdata/errchkjson.go index 9129a5365676..a07f64ef496f 100644 --- a/test/testdata/errchkjson.go +++ b/test/testdata/errchkjson.go @@ -1,5 +1,6 @@ -// args: -Eerrchkjson +// args: -Eerrchkjson -Eerrcheck // config_path: testdata/configs/errchkjson.yml +// expected_linter: errchkjson package testdata import ( From 869c2fffb799635c99a2679a662b1c3956f08edd Mon Sep 17 00:00:00 2001 From: Lucas Bremgartner Date: Sat, 6 Nov 2021 11:25:10 +0100 Subject: [PATCH 4/7] Add errchkjson linter --- pkg/golinters/errchkjson.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/golinters/errchkjson.go b/pkg/golinters/errchkjson.go index 0737ee0e8997..631c80c9856c 100644 --- a/pkg/golinters/errchkjson.go +++ b/pkg/golinters/errchkjson.go @@ -1,9 +1,8 @@ package golinters import ( - "golang.org/x/tools/go/analysis" - "github.com/breml/errchkjson" + "golang.org/x/tools/go/analysis" "github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" From 0607414639e244be485c650e1f78fb86c8f11fc1 Mon Sep 17 00:00:00 2001 From: Lucas Bremgartner Date: Sat, 6 Nov 2021 15:48:29 +0100 Subject: [PATCH 5/7] Fix typo, order imports Update pkg/golinters/errchkjson.go Update test/testdata/errchkjson.go Update test/testdata/errchkjson_omit_safe.go Co-authored-by: Ludovic Fernandez --- test/testdata/errchkjson.go | 72 +++++++++++++-------------- test/testdata/errchkjson_omit_safe.go | 68 ++++++++++++------------- 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/test/testdata/errchkjson.go b/test/testdata/errchkjson.go index a07f64ef496f..9c306758b5a0 100644 --- a/test/testdata/errchkjson.go +++ b/test/testdata/errchkjson.go @@ -19,9 +19,9 @@ func (mt marshalText) MarshalText() ([]byte, error) { var _ encoding.TextMarshaler = marshalText(struct{}{}) -// JSONMarshalSaveTypes contains a multitude of test cases to marshal different combinations of types to JSON, -// that are save, that is, they will never return an error, if these types are marshaled to JSON. -func JSONMarshalSaveTypes() { +// JSONMarshalSafeTypes contains a multitude of test cases to marshal different combinations of types to JSON, +// that are safe, that is, they will never return an error, if these types are marshaled to JSON. +func JSONMarshalSafeTypes() { var err error _, _ = json.Marshal(nil) // nil is safe @@ -254,7 +254,7 @@ func JSONMarshalSaveTypes() { type ( structKey struct{ id int } - ExportedUnsafeAndInvalidStruct struct { // unsafe unexported but ommited + ExportedUnsafeAndInvalidStruct struct { // unsafe unexported but omitted F64 float64 F64Ptr *float64 F64Slice []float64 @@ -352,45 +352,45 @@ func JSONMarshalSaveStructWithUnexportedFields() { func JSONMarshalSaveStructWithOmittedFields() { var err error - var ommitInStruct struct { + var omitInStruct struct { Bool bool // safe exported - F64 float64 `json:"-"` // unsafe exported but ommited - F64Ptr *float64 `json:"-"` // unsafe exported but ommited - F64Slice []float64 `json:"-"` // unsafe exported but ommited - F64Array [10]float64 `json:"-"` // unsafe exported but ommited - MapStrF64 map[string]float64 `json:"-"` // unsafe exported but ommited - MapEIStr map[interface{}]string `json:"-"` // unsafe exported but ommited - Number json.Number `json:"-"` // unsafe exported but ommited - NumberPtr *json.Number `json:"-"` // unsafe exported but ommited - NumberSlice []json.Number `json:"-"` // unsafe exported but ommited - MapNumberStr map[json.Number]string `json:"-"` // unsafe exported but ommited - Ei interface{} `json:"-"` // unsafe exported but ommited - Stringer fmt.Stringer `json:"-"` // unsafe exported but ommited - Mt marshalText `json:"-"` // unsafe exported but ommited - MapMarshalTextString map[marshalText]string `json:"-"` // unsafe exported but ommited - ExportedStruct ExportedUnsafeAndInvalidStruct `json:"-"` // unsafe exported but ommited - ExportedStructPtr *ExportedUnsafeAndInvalidStruct `json:"-"` // unsafe exported but ommited - - C128 complex128 `json:"-"` // invalid exported but ommited - C128Slice []complex128 `json:"-"` // invalid exported but ommited - C128Array [10]complex128 `json:"-"` // invalid exported but ommited - MapBoolStr map[bool]string `json:"-"` // invalid exported but ommited - MapF64Str map[float64]string `json:"-"` // invalid exported but ommited - F func() `json:"-"` // invalid exported but ommited - Ch chan struct{} `json:"-"` // invalid exported but ommited - UnsafePtr unsafe.Pointer `json:"-"` // invalid exported but ommited - MapStructStr map[structKey]string `json:"-"` // invalid exported but ommited + F64 float64 `json:"-"` // unsafe exported but omitted + F64Ptr *float64 `json:"-"` // unsafe exported but omitted + F64Slice []float64 `json:"-"` // unsafe exported but omitted + F64Array [10]float64 `json:"-"` // unsafe exported but omitted + MapStrF64 map[string]float64 `json:"-"` // unsafe exported but omitted + MapEIStr map[interface{}]string `json:"-"` // unsafe exported but omitted + Number json.Number `json:"-"` // unsafe exported but omitted + NumberPtr *json.Number `json:"-"` // unsafe exported but omitted + NumberSlice []json.Number `json:"-"` // unsafe exported but omitted + MapNumberStr map[json.Number]string `json:"-"` // unsafe exported but omitted + Ei interface{} `json:"-"` // unsafe exported but omitted + Stringer fmt.Stringer `json:"-"` // unsafe exported but omitted + Mt marshalText `json:"-"` // unsafe exported but omitted + MapMarshalTextString map[marshalText]string `json:"-"` // unsafe exported but omitted + ExportedStruct ExportedUnsafeAndInvalidStruct `json:"-"` // unsafe exported but omitted + ExportedStructPtr *ExportedUnsafeAndInvalidStruct `json:"-"` // unsafe exported but omitted + + C128 complex128 `json:"-"` // invalid exported but omitted + C128Slice []complex128 `json:"-"` // invalid exported but omitted + C128Array [10]complex128 `json:"-"` // invalid exported but omitted + MapBoolStr map[bool]string `json:"-"` // invalid exported but omitted + MapF64Str map[float64]string `json:"-"` // invalid exported but omitted + F func() `json:"-"` // invalid exported but omitted + Ch chan struct{} `json:"-"` // invalid exported but omitted + UnsafePtr unsafe.Pointer `json:"-"` // invalid exported but omitted + MapStructStr map[structKey]string `json:"-"` // invalid exported but omitted } - _ = ommitInStruct.MapStructStr[structKey{1}] - _, _ = json.Marshal(ommitInStruct) // struct containing unsafe but omitted, exported fields is safe - _, err = json.Marshal(ommitInStruct) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" + _ = omitInStruct.MapStructStr[structKey{1}] + _, _ = json.Marshal(omitInStruct) // struct containing unsafe but omitted, exported fields is safe + _, err = json.Marshal(omitInStruct) // ERROR "Error return value of `encoding/json.Marshal` is checked but passed argument is safe" _ = err } -// JSONMarshalUnsaveTypes contains a multitude of test cases to marshal different combinations of types to JSON, +// JSONMarshalUnsafeTypes contains a multitude of test cases to marshal different combinations of types to JSON, // that can potentially lead to json.Marshal returning an error. -func JSONMarshalUnsaveTypes() { +func JSONMarshalUnsafeTypes() { var err error var f32 float32 diff --git a/test/testdata/errchkjson_omit_safe.go b/test/testdata/errchkjson_omit_safe.go index bc218c67cc37..2021d685605d 100644 --- a/test/testdata/errchkjson_omit_safe.go +++ b/test/testdata/errchkjson_omit_safe.go @@ -18,9 +18,9 @@ func (mt marshalText) MarshalText() ([]byte, error) { var _ encoding.TextMarshaler = marshalText(struct{}{}) -// JSONMarshalSaveTypesWithNoSafe contains a multitude of test cases to marshal different combinations of types to JSON, -// that are save, that is, they will never return an error, if these types are marshaled to JSON. -func JSONMarshalSaveTypesWithNoSafe() { +// JSONMarshalSafeTypesWithNoSafe contains a multitude of test cases to marshal different combinations of types to JSON, +// that are safe, that is, they will never return an error, if these types are marshaled to JSON. +func JSONMarshalSafeTypesWithNoSafe() { var err error _, _ = json.Marshal(nil) // ERROR "Error return value of `encoding/json.Marshal` is not checked" @@ -253,7 +253,7 @@ func JSONMarshalSaveTypesWithNoSafe() { type ( structKey struct{ id int } - ExportedUnsafeAndInvalidStruct struct { // unsafe unexported but ommited + ExportedUnsafeAndInvalidStruct struct { // unsafe unexported but omitted F64 float64 F64Ptr *float64 F64Slice []float64 @@ -282,7 +282,7 @@ type ( } ) -// JSONMarshalSaveStructWithUnexportedFields contains a struct with unexported, unsafe fields. +// JSONMarshalSafeStructWithUnexportedFieldsWithNoSafe contains a struct with unexported, unsafe fields. func JSONMarshalSaveStructWithUnexportedFieldsWithNoSafe() { var err error @@ -347,39 +347,39 @@ func JSONMarshalSaveStructWithUnexportedFieldsWithNoSafe() { _ = err } -// JSONMarshalSaveStructWithOmittedFields contains a struct with omitted, unsafe fields. +// JSONMarshalSafeStructWithOmittedFieldsWithNoSafe contains a struct with omitted, unsafe fields. func JSONMarshalSaveStructWithOmittedFieldsWithNoSafe() { var err error var ommitInStruct struct { Bool bool // safe exported - F64 float64 `json:"-"` // unsafe exported but ommited - F64Ptr *float64 `json:"-"` // unsafe exported but ommited - F64Slice []float64 `json:"-"` // unsafe exported but ommited - F64Array [10]float64 `json:"-"` // unsafe exported but ommited - MapStrF64 map[string]float64 `json:"-"` // unsafe exported but ommited - MapEIStr map[interface{}]string `json:"-"` // unsafe exported but ommited - Number json.Number `json:"-"` // unsafe exported but ommited - NumberPtr *json.Number `json:"-"` // unsafe exported but ommited - NumberSlice []json.Number `json:"-"` // unsafe exported but ommited - MapNumberStr map[json.Number]string `json:"-"` // unsafe exported but ommited - Ei interface{} `json:"-"` // unsafe exported but ommited - Stringer fmt.Stringer `json:"-"` // unsafe exported but ommited - Mt marshalText `json:"-"` // unsafe exported but ommited - MapMarshalTextString map[marshalText]string `json:"-"` // unsafe exported but ommited - ExportedStruct ExportedUnsafeAndInvalidStruct `json:"-"` // unsafe exported but ommited - ExportedStructPtr *ExportedUnsafeAndInvalidStruct `json:"-"` // unsafe exported but ommited - - C128 complex128 `json:"-"` // invalid exported but ommited - C128Slice []complex128 `json:"-"` // invalid exported but ommited - C128Array [10]complex128 `json:"-"` // invalid exported but ommited - MapBoolStr map[bool]string `json:"-"` // invalid exported but ommited - MapF64Str map[float64]string `json:"-"` // invalid exported but ommited - F func() `json:"-"` // invalid exported but ommited - Ch chan struct{} `json:"-"` // invalid exported but ommited - UnsafePtr unsafe.Pointer `json:"-"` // invalid exported but ommited - MapStructStr map[structKey]string `json:"-"` // invalid exported but ommited + F64 float64 `json:"-"` // unsafe exported but omitted + F64Ptr *float64 `json:"-"` // unsafe exported but omitted + F64Slice []float64 `json:"-"` // unsafe exported but omitted + F64Array [10]float64 `json:"-"` // unsafe exported but omitted + MapStrF64 map[string]float64 `json:"-"` // unsafe exported but omitted + MapEIStr map[interface{}]string `json:"-"` // unsafe exported but omitted + Number json.Number `json:"-"` // unsafe exported but omitted + NumberPtr *json.Number `json:"-"` // unsafe exported but omitted + NumberSlice []json.Number `json:"-"` // unsafe exported but omitted + MapNumberStr map[json.Number]string `json:"-"` // unsafe exported but omitted + Ei interface{} `json:"-"` // unsafe exported but omitted + Stringer fmt.Stringer `json:"-"` // unsafe exported but omitted + Mt marshalText `json:"-"` // unsafe exported but omitted + MapMarshalTextString map[marshalText]string `json:"-"` // unsafe exported but omitted + ExportedStruct ExportedUnsafeAndInvalidStruct `json:"-"` // unsafe exported but omitted + ExportedStructPtr *ExportedUnsafeAndInvalidStruct `json:"-"` // unsafe exported but omitted + + C128 complex128 `json:"-"` // invalid exported but omitted + C128Slice []complex128 `json:"-"` // invalid exported but omitted + C128Array [10]complex128 `json:"-"` // invalid exported but omitted + MapBoolStr map[bool]string `json:"-"` // invalid exported but omitted + MapF64Str map[float64]string `json:"-"` // invalid exported but omitted + F func() `json:"-"` // invalid exported but omitted + Ch chan struct{} `json:"-"` // invalid exported but omitted + UnsafePtr unsafe.Pointer `json:"-"` // invalid exported but omitted + MapStructStr map[structKey]string `json:"-"` // invalid exported but omitted } _ = ommitInStruct.MapStructStr[structKey{1}] _, _ = json.Marshal(ommitInStruct) // ERROR "Error return value of `encoding/json.Marshal` is not checked" @@ -387,9 +387,9 @@ func JSONMarshalSaveStructWithOmittedFieldsWithNoSafe() { _ = err } -// JSONMarshalUnsaveTypes contains a multitude of test cases to marshal different combinations of types to JSON, +// JSONMarshalUnsafeTypes contains a multitude of test cases to marshal different combinations of types to JSON, // that can potentially lead to json.Marshal returning an error. -func JSONMarshalUnsaveTypes() { +func JSONMarshalUnsafeTypes() { var err error var f32 float32 From 228fb75a761b6e421a782354eab6ffe742ec83b8 Mon Sep 17 00:00:00 2001 From: Lucas Bremgartner Date: Sun, 7 Nov 2021 21:37:09 +0100 Subject: [PATCH 6/7] Update errchkjson to v0.2.0 --- go.sum | 2 -- test/testdata/errchkjson.go | 2 ++ test/testdata/errchkjson_omit_safe.go | 27 +++++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/go.sum b/go.sum index 89d2e078d869..d7b33736b174 100644 --- a/go.sum +++ b/go.sum @@ -108,8 +108,6 @@ github.com/bombsimon/wsl/v3 v3.3.0 h1:Mka/+kRLoQJq7g2rggtgQsjuI/K5Efd87WX96EWFxj github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/breml/bidichk v0.2.1 h1:SRNtZuLdfkxtocj+xyHXKC1Uv3jVi6EPYx+NHSTNQvE= github.com/breml/bidichk v0.2.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= -github.com/breml/bidichk v0.2.0 h1:kBrsPFWq0GTrExB4G55zd1fCiw+0XN4o505lMx9XnFM= -github.com/breml/bidichk v0.2.0/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= github.com/breml/errchkjson v0.2.0 h1:5XK9tXXqahYiPHuJ5Asx9a5ucpASxLMxq3EvQyLb26c= github.com/breml/errchkjson v0.2.0/go.mod h1:jZEATw/jF69cL1iy7//Yih8yp/mXp2CBoBr9GJwCAsY= github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY= diff --git a/test/testdata/errchkjson.go b/test/testdata/errchkjson.go index 9c306758b5a0..c7aac1a87c31 100644 --- a/test/testdata/errchkjson.go +++ b/test/testdata/errchkjson.go @@ -394,6 +394,7 @@ func JSONMarshalUnsafeTypes() { var err error var f32 float32 + json.Marshal(f32) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float32` found" _, _ = json.Marshal(f32) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float32` found" _, err = json.Marshal(f32) // err is checked _ = err @@ -517,6 +518,7 @@ func JSONMarshalInvalidTypes() { var err error var c64 complex64 + json.Marshal(c64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" _, _ = json.Marshal(c64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" _, err = json.Marshal(c64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" _ = err diff --git a/test/testdata/errchkjson_omit_safe.go b/test/testdata/errchkjson_omit_safe.go index 2021d685605d..59d923381c3f 100644 --- a/test/testdata/errchkjson_omit_safe.go +++ b/test/testdata/errchkjson_omit_safe.go @@ -393,6 +393,7 @@ func JSONMarshalUnsafeTypes() { var err error var f32 float32 + json.Marshal(f32) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float32` found" _, _ = json.Marshal(f32) // ERROR "Error return value of `encoding/json.Marshal` is not checked: unsafe type `float32` found" _, err = json.Marshal(f32) // err is checked _ = err @@ -516,6 +517,7 @@ func JSONMarshalInvalidTypes() { var err error var c64 complex64 + json.Marshal(c64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" _, _ = json.Marshal(c64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" _, err = json.Marshal(c64) // ERROR "`encoding/json.Marshal` for unsupported type `complex64` found" _ = err @@ -612,3 +614,28 @@ func NotJSONMarshal() { f := func() bool { return false } _ = f() } + +// JSONMarshalStructWithoutExportedFields contains a struct without exported fields. +func JSONMarshalStructWithoutExportedFields() { + var err error + + var withoutExportedFields struct { + privateField bool + ExportedButOmittedField bool `json:"-"` + } + _, err = json.Marshal(withoutExportedFields) // want "Error argument passed to `encoding/json.Marshal` does not contain any exported field" + _ = err +} + +// JSONMarshalStructWithoutExportedFields contains a struct without exported fields. +func JSONMarshalStructWithNestedStructWithoutExportedFields() { + var err error + + var withNestedStructWithoutExportedFields struct { + ExportedStruct struct { + privatField bool + } + } + _, err = json.Marshal(withNestedStructWithoutExportedFields) + _ = err +} From b4d995bef72c242260598449da196b53b7c9d093 Mon Sep 17 00:00:00 2001 From: Lucas Bremgartner Date: Tue, 9 Nov 2021 00:01:09 +0100 Subject: [PATCH 7/7] Fix typo --- test/testdata/errchkjson_omit_safe.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/testdata/errchkjson_omit_safe.go b/test/testdata/errchkjson_omit_safe.go index 59d923381c3f..c2c601fb9875 100644 --- a/test/testdata/errchkjson_omit_safe.go +++ b/test/testdata/errchkjson_omit_safe.go @@ -351,7 +351,7 @@ func JSONMarshalSaveStructWithUnexportedFieldsWithNoSafe() { func JSONMarshalSaveStructWithOmittedFieldsWithNoSafe() { var err error - var ommitInStruct struct { + var omitInStruct struct { Bool bool // safe exported F64 float64 `json:"-"` // unsafe exported but omitted @@ -381,9 +381,9 @@ func JSONMarshalSaveStructWithOmittedFieldsWithNoSafe() { UnsafePtr unsafe.Pointer `json:"-"` // invalid exported but omitted MapStructStr map[structKey]string `json:"-"` // invalid exported but omitted } - _ = ommitInStruct.MapStructStr[structKey{1}] - _, _ = json.Marshal(ommitInStruct) // ERROR "Error return value of `encoding/json.Marshal` is not checked" - _, err = json.Marshal(ommitInStruct) // struct containing unsafe but omitted, exported fields is safe, but omit-safe is set + _ = omitInStruct.MapStructStr[structKey{1}] + _, _ = json.Marshal(omitInStruct) // ERROR "Error return value of `encoding/json.Marshal` is not checked" + _, err = json.Marshal(omitInStruct) // struct containing unsafe but omitted, exported fields is safe, but omit-safe is set _ = err }