diff --git a/pkg/lint/linter/config.go b/pkg/lint/linter/config.go index 8e57d6bdf6d2..57c51fa75e4c 100644 --- a/pkg/lint/linter/config.go +++ b/pkg/lint/linter/config.go @@ -27,10 +27,19 @@ const ( // LastLinter nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives. const LastLinter = "nolintlint" +type DeprecationLevel int + +const ( + DeprecationNone DeprecationLevel = iota + DeprecationWarning + DeprecationError +) + type Deprecation struct { Since string Message string Replacement string + Level DeprecationLevel } type Config struct { @@ -113,15 +122,24 @@ func (lc *Config) WithSince(version string) *Config { return lc } -func (lc *Config) Deprecated(message, version, replacement string) *Config { +func (lc *Config) Deprecated(message, version, replacement string, level DeprecationLevel) *Config { lc.Deprecation = &Deprecation{ Since: version, Message: message, Replacement: replacement, + Level: level, } return lc } +func (lc *Config) DeprecatedWarning(message, version, replacement string) *Config { + return lc.Deprecated(message, version, replacement, DeprecationWarning) +} + +func (lc *Config) DeprecatedError(message, version, replacement string) *Config { + return lc.Deprecated(message, version, replacement, DeprecationError) +} + func (lc *Config) IsDeprecated() bool { return lc.Deprecation != nil } diff --git a/pkg/lint/linter/linter.go b/pkg/lint/linter/linter.go index 1d4e7b04ccce..088aa3d78b3f 100644 --- a/pkg/lint/linter/linter.go +++ b/pkg/lint/linter/linter.go @@ -17,6 +17,7 @@ type Noop struct { name string desc string reason string + level DeprecationLevel } func NewNoop(l Linter, reason string) Noop { @@ -27,11 +28,12 @@ func NewNoop(l Linter, reason string) Noop { } } -func NewNoopDeprecated(name string, cfg *config.Config) Noop { +func NewNoopDeprecated(name string, cfg *config.Config, level DeprecationLevel) Noop { noop := Noop{ name: name, desc: "Deprecated", reason: "This linter is fully inactivated: it will not produce any reports.", + level: level, } if cfg.InternalCmdTest { @@ -42,9 +44,17 @@ func NewNoopDeprecated(name string, cfg *config.Config) Noop { } func (n Noop) Run(_ context.Context, lintCtx *Context) ([]result.Issue, error) { - if n.reason != "" { + if n.reason == "" { + return nil, nil + } + + switch n.level { + case DeprecationError: + lintCtx.Log.Errorf("%s: %s", n.name, n.reason) + default: lintCtx.Log.Warnf("%s: %s", n.name, n.reason) } + return nil, nil } diff --git a/pkg/lint/lintersdb/builder_linter.go b/pkg/lint/lintersdb/builder_linter.go index d9b1af8e3a95..7b9d2a559bab 100644 --- a/pkg/lint/lintersdb/builder_linter.go +++ b/pkg/lint/lintersdb/builder_linter.go @@ -190,12 +190,12 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithPresets(linter.PresetFormatting, linter.PresetStyle). WithURL("https://gitlab.com/bosi/decorder"), - linter.NewConfig(linter.NewNoopDeprecated("deadcode", cfg)). + linter.NewConfig(linter.NewNoopDeprecated("deadcode", cfg, linter.DeprecationError)). WithSince("v1.0.0"). WithLoadForGoAnalysis(). WithPresets(linter.PresetUnused). WithURL("https://github.com/remyoudompheng/go-misc/tree/master/deadcode"). - Deprecated("The owner seems to have abandoned the linter.", "v1.49.0", "unused"), + DeprecatedError("The owner seems to have abandoned the linter.", "v1.49.0", "unused"), linter.NewConfig(depguard.New(&cfg.LintersSettings.Depguard)). WithSince("v1.4.0"). @@ -253,7 +253,7 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithPresets(linter.PresetSQL). WithLoadForGoAnalysis(). WithURL("https://github.com/1uf3/execinquery"). - Deprecated("The repository of the linter has been archived by the owner.", "v1.58.0", ""), + DeprecatedWarning("The repository of the linter has been archived by the owner.", "v1.58.0", ""), linter.NewConfig(exhaustive.New(&cfg.LintersSettings.Exhaustive)). WithSince(" v1.28.0"). @@ -261,12 +261,12 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithLoadForGoAnalysis(). WithURL("https://github.com/nishanths/exhaustive"), - linter.NewConfig(linter.NewNoopDeprecated("exhaustivestruct", cfg)). + linter.NewConfig(linter.NewNoopDeprecated("exhaustivestruct", cfg, linter.DeprecationError)). WithSince("v1.32.0"). WithPresets(linter.PresetStyle, linter.PresetTest). WithLoadForGoAnalysis(). WithURL("https://github.com/mbilski/exhaustivestruct"). - Deprecated("The repository of the linter has been deprecated by the owner.", "v1.46.0", "exhaustruct"), + DeprecatedError("The repository of the linter has been deprecated by the owner.", "v1.46.0", "exhaustruct"), linter.NewConfig(exhaustruct.New(&cfg.LintersSettings.Exhaustruct)). WithSince("v1.46.0"). @@ -403,12 +403,12 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithAutoFix(). WithURL("https://pkg.go.dev/golang.org/x/tools/cmd/goimports"), - linter.NewConfig(linter.NewNoopDeprecated("golint", cfg)). + linter.NewConfig(linter.NewNoopDeprecated("golint", cfg, linter.DeprecationError)). WithSince("v1.0.0"). WithLoadForGoAnalysis(). WithPresets(linter.PresetStyle). WithURL("https://github.com/golang/lint"). - Deprecated("The repository of the linter has been archived by the owner.", "v1.41.0", "revive"), + DeprecatedError("The repository of the linter has been archived by the owner.", "v1.41.0", "revive"), linter.NewConfig(mnd.New(&cfg.LintersSettings.Mnd)). WithSince("v1.22.0"). @@ -418,8 +418,8 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { linter.NewConfig(mnd.NewGoMND(&cfg.LintersSettings.Gomnd)). WithSince("v1.22.0"). WithPresets(linter.PresetStyle). - Deprecated("The linter has been renamed.", "v1.58.0", "mnd"). - WithURL("https://github.com/tommy-muehle/go-mnd"), + WithURL("https://github.com/tommy-muehle/go-mnd"). + DeprecatedWarning("The linter has been renamed.", "v1.58.0", "mnd"), linter.NewConfig(gomoddirectives.New(&cfg.LintersSettings.GoModDirectives)). WithSince("v1.39.0"). @@ -470,11 +470,11 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithPresets(linter.PresetStyle). WithURL("https://github.com/leonklingele/grouper"), - linter.NewConfig(linter.NewNoopDeprecated("ifshort", cfg)). + linter.NewConfig(linter.NewNoopDeprecated("ifshort", cfg, linter.DeprecationError)). WithSince("v1.36.0"). WithPresets(linter.PresetStyle). WithURL("https://github.com/esimonov/ifshort"). - Deprecated("The repository of the linter has been deprecated by the owner.", "v1.48.0", ""), + DeprecatedError("The repository of the linter has been deprecated by the owner.", "v1.48.0", ""), linter.NewConfig(importas.New(&cfg.LintersSettings.ImportAs)). WithSince("v1.38.0"). @@ -498,12 +498,12 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithPresets(linter.PresetStyle). WithURL("https://github.com/sashamelentyev/interfacebloat"), - linter.NewConfig(linter.NewNoopDeprecated("interfacer", cfg)). + linter.NewConfig(linter.NewNoopDeprecated("interfacer", cfg, linter.DeprecationError)). WithSince("v1.0.0"). WithLoadForGoAnalysis(). WithPresets(linter.PresetStyle). WithURL("https://github.com/mvdan/interfacer"). - Deprecated("The repository of the linter has been archived by the owner.", "v1.38.0", ""), + DeprecatedError("The repository of the linter has been archived by the owner.", "v1.38.0", ""), linter.NewConfig(intrange.New()). WithSince("v1.57.0"). @@ -538,12 +538,12 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithLoadForGoAnalysis(). WithURL("https://github.com/ashanbrown/makezero"), - linter.NewConfig(linter.NewNoopDeprecated("maligned", cfg)). + linter.NewConfig(linter.NewNoopDeprecated("maligned", cfg, linter.DeprecationError)). WithSince("v1.0.0"). WithLoadForGoAnalysis(). WithPresets(linter.PresetPerformance). WithURL("https://github.com/mdempsky/maligned"). - Deprecated("The repository of the linter has been archived by the owner.", "v1.38.0", "govet 'fieldalignment'"), + DeprecatedError("The repository of the linter has been archived by the owner.", "v1.38.0", "govet 'fieldalignment'"), linter.NewConfig(mirror.New()). WithSince("v1.53.0"). @@ -603,11 +603,11 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithPresets(linter.PresetStyle). WithURL("https://github.com/firefart/nonamedreturns"), - linter.NewConfig(linter.NewNoopDeprecated("nosnakecase", cfg)). + linter.NewConfig(linter.NewNoopDeprecated("nosnakecase", cfg, linter.DeprecationError)). WithSince("v1.47.0"). WithPresets(linter.PresetStyle). WithURL("https://github.com/sivchari/nosnakecase"). - Deprecated("The repository of the linter has been deprecated by the owner.", "v1.48.1", "revive 'var-naming'"), + DeprecatedError("The repository of the linter has been deprecated by the owner.", "v1.48.1", "revive 'var-naming'"), linter.NewConfig(nosprintfhostport.New()). WithSince("v1.46.0"). @@ -672,11 +672,11 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithPresets(linter.PresetStyle, linter.PresetFormatting). WithURL("https://github.com/go-simpler/sloglint"), - linter.NewConfig(linter.NewNoopDeprecated("scopelint", cfg)). + linter.NewConfig(linter.NewNoopDeprecated("scopelint", cfg, linter.DeprecationError)). WithSince("v1.12.0"). WithPresets(linter.PresetBugs). WithURL("https://github.com/kyoh86/scopelint"). - Deprecated("The repository of the linter has been deprecated by the owner.", "v1.39.0", "exportloopref"), + DeprecatedError("The repository of the linter has been deprecated by the owner.", "v1.39.0", "exportloopref"), linter.NewConfig(sqlclosecheck.New()). WithSince("v1.28.0"). @@ -698,12 +698,12 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithAlternativeNames(megacheckName). WithURL("https://staticcheck.io/"), - linter.NewConfig(linter.NewNoopDeprecated("structcheck", cfg)). + linter.NewConfig(linter.NewNoopDeprecated("structcheck", cfg, linter.DeprecationError)). WithSince("v1.0.0"). WithLoadForGoAnalysis(). WithPresets(linter.PresetUnused). WithURL("https://github.com/opennota/check"). - Deprecated("The owner seems to have abandoned the linter.", "v1.49.0", "unused"), + DeprecatedError("The owner seems to have abandoned the linter.", "v1.49.0", "unused"), linter.NewConfig(stylecheck.New(&cfg.LintersSettings.Stylecheck)). WithSince("v1.20.0"). @@ -788,12 +788,12 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithPresets(linter.PresetStyle). WithURL("https://github.com/sashamelentyev/usestdlibvars"), - linter.NewConfig(linter.NewNoopDeprecated("varcheck", cfg)). + linter.NewConfig(linter.NewNoopDeprecated("varcheck", cfg, linter.DeprecationError)). WithSince("v1.0.0"). WithLoadForGoAnalysis(). WithPresets(linter.PresetUnused). WithURL("https://github.com/opennota/check"). - Deprecated("The owner seems to have abandoned the linter.", "v1.49.0", "unused"), + DeprecatedError("The owner seems to have abandoned the linter.", "v1.49.0", "unused"), linter.NewConfig(varnamelen.New(&cfg.LintersSettings.Varnamelen)). WithSince("v1.43.0"). diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 19995005547f..0a487be92e73 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -303,6 +303,10 @@ func AllPresets() []string { func linterConfigsToMap(lcs []*linter.Config) map[string]*linter.Config { ret := map[string]*linter.Config{} for _, lc := range lcs { + if lc.IsDeprecated() && lc.Deprecation.Level > linter.DeprecationWarning { + continue + } + ret[lc.Name()] = lc } diff --git a/scripts/website/dump_info/main.go b/scripts/website/dump_info/main.go index 599981ea50ae..db7e00b36c93 100644 --- a/scripts/website/dump_info/main.go +++ b/scripts/website/dump_info/main.go @@ -10,6 +10,7 @@ import ( "path/filepath" "github.com/golangci/golangci-lint/pkg/config" + "github.com/golangci/golangci-lint/pkg/lint/linter" "github.com/golangci/golangci-lint/pkg/lint/lintersdb" "github.com/golangci/golangci-lint/scripts/website/types" ) @@ -36,6 +37,10 @@ func saveLinters() error { var wraps []types.LinterWrapper for _, l := range linters { + if l.IsDeprecated() && l.Deprecation.Level > linter.DeprecationWarning { + continue + } + wrapper := types.LinterWrapper{ Name: l.Linter.Name(), Desc: l.Linter.Desc(), diff --git a/scripts/website/expand_templates/thanks.go b/scripts/website/expand_templates/thanks.go index d4a548f629e0..b4943a0e7f9a 100644 --- a/scripts/website/expand_templates/thanks.go +++ b/scripts/website/expand_templates/thanks.go @@ -8,6 +8,7 @@ import ( "golang.org/x/exp/maps" "github.com/golangci/golangci-lint/pkg/config" + "github.com/golangci/golangci-lint/pkg/lint/linter" "github.com/golangci/golangci-lint/pkg/lint/lintersdb" ) @@ -31,6 +32,10 @@ func getThanksList() string { continue } + if lc.IsDeprecated() && lc.Deprecation.Level > linter.DeprecationWarning { + continue + } + linterURL := lc.OriginalURL if lc.Name() == "staticcheck" { linterURL = "https://github.com/dominikh/go-tools" diff --git a/test/enabled_linters_test.go b/test/enabled_linters_test.go index 6044e9c63a83..0de10a5a6037 100644 --- a/test/enabled_linters_test.go +++ b/test/enabled_linters_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/golangci/golangci-lint/pkg/lint/linter" "github.com/golangci/golangci-lint/pkg/lint/lintersdb" "github.com/golangci/golangci-lint/pkg/logutils" "github.com/golangci/golangci-lint/test/testshared" @@ -163,15 +164,18 @@ func getEnabledByDefaultFastLintersExcept(t *testing.T, except ...string) []stri func getAllFastLintersWith(t *testing.T, with ...string) []string { t.Helper() + ret := append([]string{}, with...) + dbManager, err := lintersdb.NewManager(nil, nil, lintersdb.NewLinterBuilder()) require.NoError(t, err) linters := dbManager.GetAllSupportedLinterConfigs() - ret := append([]string{}, with...) + for _, lc := range linters { - if lc.IsSlowLinter() || lc.Internal { + if lc.IsSlowLinter() || lc.Internal || (lc.IsDeprecated() && lc.Deprecation.Level > linter.DeprecationWarning) { continue } + ret = append(ret, lc.Name()) }