diff --git a/.golangci.reference.yml b/.golangci.reference.yml index 1864e5b11388..b79979451a26 100644 --- a/.golangci.reference.yml +++ b/.golangci.reference.yml @@ -137,6 +137,14 @@ linters-settings: first-strong-isolate: false pop-directional-isolate: false + checkdeprecated: + # report malformed deprecated comments. The proper format is `Deprecated: `. + # Default: false + check-comment: true + # Custom Deprecated comment header. + patterns: + - "MyDeprecated" + cyclop: # The maximal code complexity to report. # Default: 10 @@ -2062,6 +2070,7 @@ linters: - asciicheck - bidichk - bodyclose + - checkdeprecated - containedctx - contextcheck - cyclop @@ -2175,6 +2184,7 @@ linters: - asciicheck - bidichk - bodyclose + - checkdeprecated - containedctx - contextcheck - cyclop diff --git a/go.mod b/go.mod index a9a44d009b96..cae0622e5812 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/ashanbrown/forbidigo v1.5.1 github.com/ashanbrown/makezero v1.1.1 github.com/bkielbasa/cyclop v1.2.0 + github.com/black-06/check-deprecated v0.1.1 github.com/blizzy78/varnamelen v0.8.0 github.com/bombsimon/wsl/v3 v3.4.0 github.com/breml/bidichk v0.2.4 diff --git a/go.sum b/go.sum index fbf7a3e3d398..e6b3ed962474 100644 --- a/go.sum +++ b/go.sum @@ -84,6 +84,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bkielbasa/cyclop v1.2.0 h1:7Jmnh0yL2DjKfw28p86YTd/B4lRGcNuu12sKE35sM7A= github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= +github.com/black-06/check-deprecated v0.1.1 h1:cVUGQkwD6wORqWsGjJMtuDoCI8NFtBoeA7VuoQJ842Y= +github.com/black-06/check-deprecated v0.1.1/go.mod h1:rQW28tz+ctj4N3IdH9j6Ts59fI4SqosPav6eyGp9cCo= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= github.com/bombsimon/wsl/v3 v3.4.0 h1:RkSxjT3tmlptwfgEgTgU+KYKLI35p/tviNXNXiL2aNU= diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go index 9386b4631e19..1ca183a7f28d 100644 --- a/pkg/config/linters_settings.go +++ b/pkg/config/linters_settings.go @@ -150,6 +150,7 @@ var defaultLintersSettings = LintersSettings{ type LintersSettings struct { Asasalint AsasalintSettings BiDiChk BiDiChkSettings + CheckDeprecated CheckDeprecated Cyclop Cyclop Decorder DecorderSettings Depguard DepGuardSettings @@ -246,6 +247,11 @@ type BiDiChkSettings struct { PopDirectionalIsolate bool `mapstructure:"pop-directional-isolate"` } +type CheckDeprecated struct { + CheckComment bool `mapstructure:"check-comment"` + Patterns []string `mapstructure:"patterns"` +} + type Cyclop struct { MaxComplexity int `mapstructure:"max-complexity"` PackageAverage float64 `mapstructure:"package-average"` diff --git a/pkg/golinters/checkdeprecated.go b/pkg/golinters/checkdeprecated.go new file mode 100644 index 000000000000..297b7799e9e0 --- /dev/null +++ b/pkg/golinters/checkdeprecated.go @@ -0,0 +1,28 @@ +package golinters + +import ( + checkdeprecated "github.com/black-06/check-deprecated" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/pkg/config" + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" +) + +func NewCheckDeprecated(settings *config.CheckDeprecated) *goanalysis.Linter { + if settings == nil { + settings = &config.CheckDeprecated{} + } + analyzers := []*analysis.Analyzer{ + checkdeprecated.NewCheckDeprecatedAnalyzer(settings.Patterns...), + } + if settings.CheckComment { + analyzers = append(analyzers, checkdeprecated.NewCheckDeprecatedCommentAnalyzer(settings.Patterns...)) + } + + return goanalysis.NewLinter( + "checkdeprecated", + "check for using a deprecated function, variable, constant or field", + analyzers, + nil, + ).WithLoadMode(goanalysis.LoadModeTypesInfo) +} diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index ffe10721cf43..b2853eb2c3f0 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -103,6 +103,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { var ( asasalintCfg *config.AsasalintSettings bidichkCfg *config.BiDiChkSettings + checkDeprecatedCfg *config.CheckDeprecated cyclopCfg *config.Cyclop decorderCfg *config.DecorderSettings depGuardCfg *config.DepGuardSettings @@ -183,6 +184,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { if m.cfg != nil { asasalintCfg = &m.cfg.LintersSettings.Asasalint bidichkCfg = &m.cfg.LintersSettings.BiDiChk + checkDeprecatedCfg = &m.cfg.LintersSettings.CheckDeprecated cyclopCfg = &m.cfg.LintersSettings.Cyclop decorderCfg = &m.cfg.LintersSettings.Decorder depGuardCfg = &m.cfg.LintersSettings.Depguard @@ -320,6 +322,12 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithLoadForGoAnalysis(). WithURL("https://github.com/kkHAIKE/contextcheck"), + linter.NewConfig(golinters.NewCheckDeprecated(checkDeprecatedCfg)). + WithSince("v1.53.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetComment, linter.PresetStyle). + WithURL("https://github.com/black-06/check-deprecated"), + linter.NewConfig(golinters.NewCyclop(cyclopCfg)). WithSince("v1.37.0"). WithLoadForGoAnalysis(). diff --git a/test/testdata/checkdeprecated.go b/test/testdata/checkdeprecated.go new file mode 100644 index 000000000000..bc32153c6986 --- /dev/null +++ b/test/testdata/checkdeprecated.go @@ -0,0 +1,122 @@ +//golangcitest:args -Echeckdeprecated +package testdata + +import ( + "fmt" + "io/ioutil" +) + +func Caller() { + ioutil.ReadAll(nil) // want "using deprecated: As of Go 1.16, this function simply calls io.ReadAll." + fmt.Println(VarDeprecated) // want "using deprecated: VarDeprecated by GenDecl ValueSpec" + var ( + _ = VarDeprecated // want "using deprecated: VarDeprecated by GenDecl ValueSpec" + _ = VarDeprecated + "..." // want "using deprecated: VarDeprecated by GenDecl ValueSpec" + _ = vars1 // want "using deprecated: vars1 by ValueSpec" + _ = vars2 // want "using deprecated: vars1/2/3 by GenDecl ValueSpec" + _ = vars3 // want "using deprecated: vars1/2/3 by GenDecl ValueSpec" + _ = ConstDeprecated // want "using deprecated: ConstDeprecated by GenDecl ValueSpec" + _ = consts1 // want "using deprecated: consts1 by ValueSpec" + _ = consts2 // want "using deprecated: consts 1/2/3 by GenDecl ValueSpec" + _ = consts3 // want "using deprecated: consts 1/2/3 by GenDecl ValueSpec" + ) + FuncDeprecated() // want "using deprecated: don't use FuncDeprecated by FuncDecl" + var ( + _ = StructDeprecated{} // want "using deprecated: use s3 instead of StructDeprecated, by GenDecl TypeSpec" + _ = StructDeprecated2{} // want "using deprecated: \\(no comment\\)" + s = Struct{} + _ = struct1{} // want "using deprecated: struct1 by TypeSpec" + s2 = struct2{} // want "using deprecated: struct 1/2/3 by GenDecl TypeSpec" + _ = s2.F2 // want "using deprecated: F2 by Field" + s3 = struct3{} // want "using deprecated: struct 1/2/3 by GenDecl TypeSpec" + ) + s.StructFun() // want "using deprecated: don't use it" + s3.fun1() + s3.fun2() // want "using deprecated: fun2 by FuncDecl" + + var ( + _ InterfaceDeprecated // want "using deprecated: InterfaceDeprecated by GenDecl TypeSpec" + _ interface1 // want "using deprecated: interface1 by TypeSpec" + _ interface2 // want "using deprecated: interface 1/2/3 by GenDecl TypeSpec" + i3 interface3 // want "using deprecated: interface 1/2/3 by GenDecl TypeSpec" + ) + i3.fun2() // want "using deprecated: interface3 fun2 by Field" +} + +// Deprecated: VarDeprecated by GenDecl ValueSpec +var VarDeprecated = "" + +// DEPRECATED: vars1/2/3 by GenDecl ValueSpec +var ( + // deprecated. vars1 by ValueSpec + vars1 = "" + vars2 = "" + vars3 = "" +) + +// ConstDeprecated +// it's deprecated. ConstDeprecated by GenDecl ValueSpec +const ConstDeprecated = "" + +// NOTE: deprecated. consts 1/2/3 by GenDecl ValueSpec +const ( + // deprecated, consts1 by ValueSpec + consts1 = iota + consts2 + consts3 +) + +// FuncDeprecated +// +// Deprecated: don't use FuncDeprecated by FuncDecl +func FuncDeprecated() { +} + +type Struct struct{} + +// Deprecated, don't use it +func (p Struct) StructFun() {} + +// Deprecated: use s3 instead of StructDeprecated, by GenDecl TypeSpec +type StructDeprecated struct{} + +func (p StructDeprecated) Fun() {} // want "using deprecated: use s3 instead of StructDeprecated, by GenDecl TypeSpec" + +// Deprecated. +type StructDeprecated2 struct{} + +// InterfaceDeprecated +// +// Deprecated, InterfaceDeprecated by GenDecl TypeSpec +type InterfaceDeprecated interface{} + +// Deprecated struct 1/2/3 by GenDecl TypeSpec +type ( + // Deprecated struct1 by TypeSpec + struct1 struct{} + struct2 struct { + F1 string + // Deprecated F2 by Field + F2 string + } + struct3 struct{} +) + +func (s struct3) fun1() {} // want "using deprecated: struct 1/2/3 by GenDecl TypeSpec" + +// Deprecated fun2 by FuncDecl +func (s struct3) fun2() {} // want "using deprecated: struct 1/2/3 by GenDecl TypeSpec" + +// Deprecated interface 1/2/3 +// by GenDecl TypeSpec +type ( + // Deprecated interface1 by TypeSpec + interface1 interface{} + interface2 interface { + } + interface3 interface { + fun1() + // deprecated. interface3 fun2 by Field + fun2() + } +) diff --git a/test/testdata/checkdeprecated_comment.go b/test/testdata/checkdeprecated_comment.go new file mode 100644 index 000000000000..dfac4da3c06b --- /dev/null +++ b/test/testdata/checkdeprecated_comment.go @@ -0,0 +1,81 @@ +//golangcitest:args -Echeckdeprecated +//golangcitest:config_path testdata/configs/checkdeprecated.yml +package testdata + +// Deprecated: VarDeprecatedComment by GenDecl ValueSpec +var VarDeprecateComment = "" + +// DEPRECATED: vars1/2/3 by GenDecl ValueSpec +var ( + // deprecated. vars1 by ValueSpec + vars1Comment = "" // want "malformed deprecated header: the proper format is `Deprecated: `" + vars2Comment = "" // want "malformed deprecated header: use `Deprecated: ` \\(note the casing\\) instead of `DEPRECATED: `" + vars3Comment = "" // want "malformed deprecated header: use `Deprecated: ` \\(note the casing\\) instead of `DEPRECATED: `" +) + +// ConstDeprecated +// it's deprecated. ConstDeprecated by GenDecl ValueSpec +const ConstDeprecatedComment = "" // want "malformed deprecated header: the proper format is `Deprecated: `" + +// NOTE: deprecated. consts 1/2/3 by GenDecl ValueSpec +const ( + // deprecated, consts1 by ValueSpec + consts1Comment = iota // want "malformed deprecated header: the proper format is `Deprecated: `" + consts2Comment // want "malformed deprecated header: the proper format is `Deprecated: `" + consts3Comment // want "malformed deprecated header: the proper format is `Deprecated: `" +) + +// FuncDeprecated +// +// Deprecated: don't use FuncDeprecated by FuncDecl +func FuncDeprecatedComment() { +} + +type StructComment struct{} + +// Deprecated, don't use it +func (p StructComment) StructFun() {} // want "malformed deprecated header: the proper format is `Deprecated: `" + +// Deprecated: use s3 instead of StructDeprecated, by GenDecl TypeSpec +type StructDeprecatedComment struct{} + +func (p StructDeprecatedComment) Fun() {} // want "using deprecated: use s3 instead of StructDeprecated, by GenDecl TypeSpec" + +// Deprecated. +type StructDeprecated2Comment struct{} // want "malformed deprecated header: the proper format is `Deprecated: `" + +// InterfaceDeprecated +// +// Deprecated, InterfaceDeprecated by GenDecl TypeSpec +type InterfaceDeprecatedComment interface{} // want "malformed deprecated header: the proper format is `Deprecated: `" + +// Deprecated struct 1/2/3 by GenDecl TypeSpec +type ( + // Deprecated struct1 by TypeSpec + struct1Comment struct{} // want "malformed deprecated header: the proper format is `Deprecated: `" + struct2Comment struct { // want "malformed deprecated header: the proper format is `Deprecated: `" + F1 string + // Deprecated F2 by Field + F2 string // want "malformed deprecated header: the proper format is `Deprecated: `" + } + struct3Comment struct{} // want "malformed deprecated header: the proper format is `Deprecated: `" +) + +func (s struct3Comment) fun1() {} // want "using deprecated: struct 1/2/3 by GenDecl TypeSpec" + +// Deprecated fun2 by FuncDecl +func (s struct3Comment) fun2() {} // want "using deprecated: struct 1/2/3 by GenDecl TypeSpec" + +// Deprecated interface 1/2/3 +// by GenDecl TypeSpec +type ( + // Deprecated interface1 by TypeSpec + interface1Comment interface{} // want "malformed deprecated header: the proper format is `Deprecated: `" + interface2Comment interface { // want "malformed deprecated header: the proper format is `Deprecated: `" + } + interface3Comment interface { // want "malformed deprecated header: the proper format is `Deprecated: `" + fun1() + // deprecated. interface3 fun2 by Field + fun2() // want "malformed deprecated header: the proper format is `Deprecated: `" + } +) diff --git a/test/testdata/configs/checkdeprecated.yml b/test/testdata/configs/checkdeprecated.yml new file mode 100644 index 000000000000..5a72c5e8609d --- /dev/null +++ b/test/testdata/configs/checkdeprecated.yml @@ -0,0 +1,5 @@ +linters-settings: + checkdeprecated: + check-comment: true + patterns: + - "MyDeprecated"