From e3d0247ee24aed2fc231b21d3eaa04f0fd7eca47 Mon Sep 17 00:00:00 2001 From: Hiroyuki Yagihashi Date: Tue, 18 Jan 2022 21:50:52 +0900 Subject: [PATCH] Add maintidx linter (#2435) --- .golangci.example.yml | 6 + go.mod | 1 + go.sum | 2 + pkg/config/linters_settings.go | 8 + pkg/golinters/maintidx.go | 32 +++ pkg/lint/lintersdb/manager.go | 7 + test/testdata/configs/maintidx_under_100.yml | 3 + test/testdata/maintidx.go | 196 ++++++++++++++++++ test/testdata/maintidx_under_100.go | 197 +++++++++++++++++++ 9 files changed, 452 insertions(+) create mode 100644 pkg/golinters/maintidx.go create mode 100644 test/testdata/configs/maintidx_under_100.yml create mode 100644 test/testdata/maintidx.go create mode 100644 test/testdata/maintidx_under_100.go diff --git a/.golangci.example.yml b/.golangci.example.yml index cdceb330e3b6..df2af8e2525e 100644 --- a/.golangci.example.yml +++ b/.golangci.example.yml @@ -590,6 +590,12 @@ linters-settings: # tab width in spaces. Default to 1. tab-width: 1 + maintidx: + # Show functions with maintainability index lower than N. + # A high index indicates better maintainability (it's kind of the opposite of complexity). + # default: 20 + under: 100 + makezero: # Allow only slices initialized with a length of zero. Default is false. always: false diff --git a/go.mod b/go.mod index 5c4fbe630dcf..79835dae4bf2 100644 --- a/go.mod +++ b/go.mod @@ -91,6 +91,7 @@ require ( github.com/ultraware/whitespace v0.0.4 github.com/uudashr/gocognit v1.0.5 github.com/valyala/quicktemplate v1.7.0 + github.com/yagipy/maintidx v1.0.0 github.com/yeya24/promlinter v0.1.0 gitlab.com/bosi/decorder v0.2.1 golang.org/x/tools v0.1.9-0.20211228192929-ee1ca4ffc4da diff --git a/go.sum b/go.sum index 6d66b9159e90..4c60d553b8cb 100644 --- a/go.sum +++ b/go.sum @@ -799,6 +799,8 @@ github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= +github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= github.com/yeya24/promlinter v0.1.0 h1:goWULN0jH5Yajmu/K+v1xCqIREeB+48OiJ2uu2ssc7U= github.com/yeya24/promlinter v0.1.0/go.mod h1:rs5vtZzeBHqqMwXqFScncpCF6u06lezhZepno9AB1Oc= github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go index acace3075036..e7f07db50aa4 100644 --- a/pkg/config/linters_settings.go +++ b/pkg/config/linters_settings.go @@ -51,6 +51,9 @@ var defaultLintersSettings = LintersSettings{ LineLength: 120, TabWidth: 1, }, + MaintIdx: MaintIdxSettings{ + Under: 20, + }, Nakedret: NakedretSettings{ MaxFuncLines: 30, }, @@ -128,6 +131,7 @@ type LintersSettings struct { ImportAs ImportAsSettings Ireturn IreturnSettings Lll LllSettings + MaintIdx MaintIdxSettings Makezero MakezeroSettings Maligned MalignedSettings Misspell MisspellSettings @@ -389,6 +393,10 @@ type LllSettings struct { TabWidth int `mapstructure:"tab-width"` } +type MaintIdxSettings struct { + Under int `mapstructure:"under"` +} + type MakezeroSettings struct { Always bool } diff --git a/pkg/golinters/maintidx.go b/pkg/golinters/maintidx.go new file mode 100644 index 000000000000..2b02b948f47e --- /dev/null +++ b/pkg/golinters/maintidx.go @@ -0,0 +1,32 @@ +package golinters + +import ( + "github.com/yagipy/maintidx" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/pkg/config" + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" +) + +func NewMaintIdx(cfg *config.MaintIdxSettings) *goanalysis.Linter { + analyzer := maintidx.Analyzer + + cfgMap := map[string]map[string]interface{}{ + analyzer.Name: {"under": 20}, + } + + if cfg != nil { + cfgMap[analyzer.Name] = map[string]interface{}{ + "under": cfg.Under, + } + } + + return goanalysis.NewLinter( + analyzer.Name, + analyzer.Doc, + []*analysis.Analyzer{ + analyzer, + }, + cfgMap, + ).WithLoadMode(goanalysis.LoadModeSyntax) +} diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index db386a17d58d..28bf08056a9d 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -114,6 +114,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { var ifshortCfg *config.IfshortSettings var importAsCfg *config.ImportAsSettings var ireturnCfg *config.IreturnSettings + var maintIdxCfg *config.MaintIdxSettings var nilNilCfg *config.NilNilSettings var nlreturnCfg *config.NlreturnSettings var predeclaredCfg *config.PredeclaredSettings @@ -143,6 +144,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { ifshortCfg = &m.cfg.LintersSettings.Ifshort importAsCfg = &m.cfg.LintersSettings.ImportAs ireturnCfg = &m.cfg.LintersSettings.Ireturn + maintIdxCfg = &m.cfg.LintersSettings.MaintIdx nilNilCfg = &m.cfg.LintersSettings.NilNil nlreturnCfg = &m.cfg.LintersSettings.Nlreturn predeclaredCfg = &m.cfg.LintersSettings.Predeclared @@ -439,6 +441,11 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithSince("v1.8.0"). WithPresets(linter.PresetStyle), + linter.NewConfig(golinters.NewMaintIdx(maintIdxCfg)). + WithSince("v1.44.0"). + WithPresets(linter.PresetComplexity). + WithURL("https://github.com/yagipy/maintidx"), + linter.NewConfig(golinters.NewMakezero()). WithSince("v1.34.0"). WithPresets(linter.PresetStyle, linter.PresetBugs). diff --git a/test/testdata/configs/maintidx_under_100.yml b/test/testdata/configs/maintidx_under_100.yml new file mode 100644 index 000000000000..8d54963412e7 --- /dev/null +++ b/test/testdata/configs/maintidx_under_100.yml @@ -0,0 +1,3 @@ +linters-settings: + maintidx: + under: 100 diff --git a/test/testdata/maintidx.go b/test/testdata/maintidx.go new file mode 100644 index 000000000000..51f95235040c --- /dev/null +++ b/test/testdata/maintidx.go @@ -0,0 +1,196 @@ +//args: -Emaintidx +package testdata + +func over20() { +} + +func under20() { // ERROR "Function name: under20, Cyclomatic Complexity: 76, Halstead Volume: 1636.00, Maintainability Index: 17" + for true { + if false { + if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else { + n := 0 + switch n { + case 0: + case 1: + default: + } + } + } else if false { + if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else { + n := 0 + switch n { + case 0: + case 1: + default: + } + } + } else if false { + if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else { + n := 0 + switch n { + case 0: + case 1: + default: + } + } + } else if false { + if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else { + n := 0 + switch n { + case 0: + case 1: + default: + } + } + } else { + if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else { + n := 0 + switch n { + case 0: + case 1: + default: + } + } + } + } +} diff --git a/test/testdata/maintidx_under_100.go b/test/testdata/maintidx_under_100.go new file mode 100644 index 000000000000..ce80be08d667 --- /dev/null +++ b/test/testdata/maintidx_under_100.go @@ -0,0 +1,197 @@ +// args: -Emaintidx +// config_path: testdata/configs/maintidx_under_100.yml +package testdata + +func over20() { // ERROR "Function name: over20, Cyclomatic Complexity: 1, Halstead Volume: 8.00, Maintainability Index: 86" +} + +func under20() { // ERROR "Function name: under20, Cyclomatic Complexity: 76, Halstead Volume: 1636.00, Maintainability Index: 17" + for true { + if false { + if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else { + n := 0 + switch n { + case 0: + case 1: + default: + } + } + } else if false { + if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else { + n := 0 + switch n { + case 0: + case 1: + default: + } + } + } else if false { + if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else { + n := 0 + switch n { + case 0: + case 1: + default: + } + } + } else if false { + if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else { + n := 0 + switch n { + case 0: + case 1: + default: + } + } + } else { + if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else if false { + n := 0 + switch n { + case 0: + case 1: + default: + } + } else { + n := 0 + switch n { + case 0: + case 1: + default: + } + } + } + } +}