diff --git a/.golangci.reference.yml b/.golangci.reference.yml index 1105594e0595..865d90849bda 100644 --- a/.golangci.reference.yml +++ b/.golangci.reference.yml @@ -2090,6 +2090,7 @@ linters: - unparam - unused - usestdlibvars + - validjson - varcheck - varnamelen - wastedassign @@ -2200,6 +2201,7 @@ linters: - unparam - unused - usestdlibvars + - validjson - varcheck - varnamelen - wastedassign diff --git a/go.mod b/go.mod index ecd772a245a6..f0a0c59f361d 100644 --- a/go.mod +++ b/go.mod @@ -65,6 +65,7 @@ require ( github.com/maratori/testableexamples v1.0.0 github.com/maratori/testpackage v1.1.0 github.com/matoous/godox v0.0.0-20210227103229-6504466cf951 // v1.0 + github.com/matthewloring/validjson v0.2.0 github.com/mattn/go-colorable v0.1.13 github.com/mbilski/exhaustivestruct v1.2.0 github.com/mgechev/revive v1.2.5 diff --git a/go.sum b/go.sum index 10aa93835407..4594ca3d0c83 100644 --- a/go.sum +++ b/go.sum @@ -357,6 +357,8 @@ github.com/matoous/godox v0.0.0-20210227103229-6504466cf951 h1:pWxk9e//NbPwfxat7 github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +github.com/matthewloring/validjson v0.2.0 h1:vmqsgBrKoKSA3CcRtGcrnJyittDXbGwg2PLwkeoBDHs= +github.com/matthewloring/validjson v0.2.0/go.mod h1:gDZkywm1HGWITqIwAxCs5eL5zdjAYZl0qcprWcRqf4s= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= diff --git a/pkg/golinters/validjson.go b/pkg/golinters/validjson.go new file mode 100644 index 000000000000..fe2be07ba03d --- /dev/null +++ b/pkg/golinters/validjson.go @@ -0,0 +1,19 @@ +package golinters + +import ( + "github.com/matthewloring/validjson" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" +) + +func NewValidJSON() *goanalysis.Linter { + a := validjson.Analyzer + + return goanalysis.NewLinter( + a.Name, + a.Doc, + []*analysis.Analyzer{a}, + nil, + ).WithLoadMode(goanalysis.LoadModeTypesInfo) +} diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 3bfbbdc26943..7ce7c2b34c07 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -835,6 +835,11 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithPresets(linter.PresetStyle). WithURL("https://github.com/sashamelentyev/usestdlibvars"), + linter.NewConfig(golinters.NewValidJSON()). + WithSince("v1.51.0"). + WithPresets(linter.PresetBugs). + WithURL("https://github.com/matthewloring/validjson"), + linter.NewConfig(golinters.NewVarcheck(varcheckCfg)). WithSince("v1.0.0"). WithLoadForGoAnalysis(). diff --git a/test/testdata/validjson.go b/test/testdata/validjson.go new file mode 100644 index 000000000000..073a73900800 --- /dev/null +++ b/test/testdata/validjson.go @@ -0,0 +1,42 @@ +//golangcitest:args -Evalidjson +package testdata + +type Key struct { + A, B string +} + +type StrAlias string + +type ChanAlias chan<- int + +type Textable [2]byte + +func (Textable) MarshalText() ([]byte, error) { + return nil, nil +} + +func (*Textable) UnmarshalText(_ []byte) error { + return nil +} + +type ValidJsonTest struct { + A int `json:"A"` + B int `json:"B,omitempty"` + C int `json:",omitempty"` + D int `json:"-"` + E chan<- int `json:"E"` // want "struct field has json tag but non-serializable type chan<- int" + F chan<- int `json:"-"` + G chan<- int `json:"-,"` // want "struct field has json tag but non-serializable type chan<- int" + H ChanAlias `json:"H"` // want "struct field has json tag but non-serializable type command-line-arguments.ChanAlias" + I map[int]int `json:"I"` + J map[int64]int `json:"J"` + K map[Key]int `json:"K"` // want "struct field has json tag but non-serializable type" + L map[struct{ A, B string }]int `json:"L"` // want "struct field has json tag but non-serializable type" + M map[string]int `json:"M"` + N map[StrAlias]int `json:"N"` + O func() `json:"O"` // want "struct field has json tag but non-serializable type func()" + P complex64 `json:"P"` // want "struct field has json tag but non-serializable type complex64" + Q complex128 `json:"Q"` // want "struct field has json tag but non-serializable type complex128" + R StrAlias `json:"R"` + S map[Textable]int `json:"S"` +}