diff --git a/go.mod b/go.mod index 26aed1b14155..78a778dbbe3c 100644 --- a/go.mod +++ b/go.mod @@ -60,6 +60,7 @@ require ( github.com/ldez/tagliatelle v0.3.1 github.com/leonklingele/grouper v1.1.0 github.com/lufeee/execinquery v1.2.1 + github.com/maratori/pairedbrackets v1.0.0 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 @@ -179,7 +180,7 @@ require ( golang.org/x/exp/typeparams v0.0.0-20220827204233-334a2380cb91 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect + golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 // indirect golang.org/x/text v0.3.7 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index c924e9c2d742..d4407f16be41 100644 --- a/go.sum +++ b/go.sum @@ -345,6 +345,8 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/maratori/pairedbrackets v1.0.0 h1:kTwEZfEJbJ/0aZ+YHHs5OkviIg2SlQqqQ+Q7DvQA+1Q= +github.com/maratori/pairedbrackets v1.0.0/go.mod h1:OQnKQBTMTq+E2PCrQfhOkhMmbovXjOGd5xzUpawsbog= github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI= github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE= github.com/maratori/testpackage v1.1.0 h1:GJY4wlzQhuBusMF1oahQCBtUV/AQ/k69IZ68vxaac2Q= @@ -758,8 +760,8 @@ golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 h1:wM1k/lXfpc5HdkJJyW9GELpd8ERGdnh8sMGL6Gzq3Ho= +golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go index 12ffde213401..c7ca02e3ed52 100644 --- a/pkg/config/linters_settings.go +++ b/pkg/config/linters_settings.go @@ -95,6 +95,12 @@ var defaultLintersSettings = LintersSettings{ RequireSpecific: false, AllowUnused: false, }, + PairedBrackets: PairedBracketsSettings{ + IgnoreFuncCalls: []string{ + "github.com/stretchr/testify/assert", + "github.com/stretchr/testify/require", + }, + }, Prealloc: PreallocSettings{ Simple: true, RangeLoops: true, @@ -184,6 +190,7 @@ type LintersSettings struct { Nlreturn NlreturnSettings NoLintLint NoLintLintSettings NoNamedReturns NoNamedReturnsSettings + PairedBrackets PairedBracketsSettings ParallelTest ParallelTestSettings Prealloc PreallocSettings Predeclared PredeclaredSettings @@ -556,6 +563,11 @@ type NoLintLintSettings struct { type NoNamedReturnsSettings struct { ReportErrorInDefer bool `mapstructure:"report-error-in-defer"` } + +type PairedBracketsSettings struct { + IgnoreFuncCalls []string `mapstructure:"ignore-func-calls"` +} + type ParallelTestSettings struct { IgnoreMissing bool `mapstructure:"ignore-missing"` } diff --git a/pkg/golinters/pairedbrackets.go b/pkg/golinters/pairedbrackets.go new file mode 100644 index 000000000000..d736088da1e7 --- /dev/null +++ b/pkg/golinters/pairedbrackets.go @@ -0,0 +1,25 @@ +package golinters + +import ( + "github.com/maratori/pairedbrackets/pkg/pairedbrackets" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/pkg/config" + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" +) + +func NewPairedbrackets(cfg *config.PairedBracketsSettings) *goanalysis.Linter { + var a = pairedbrackets.NewAnalyzer() + + var settings map[string]map[string]interface{} + if cfg != nil { + settings = map[string]map[string]interface{}{ + a.Name: { + pairedbrackets.IgnoreFuncCallsFlagName: cfg.IgnoreFuncCalls, + }, + } + } + + return goanalysis.NewLinter(a.Name, a.Doc, []*analysis.Analyzer{a}, settings). + WithLoadMode(goanalysis.LoadModeTypesInfo) +} diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 16a6961d5dc6..4033ca94568d 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -152,6 +152,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { nlreturnCfg *config.NlreturnSettings noLintLintCfg *config.NoLintLintSettings noNamedReturnsCfg *config.NoNamedReturnsSettings + pairedBracketsCfg *config.PairedBracketsSettings parallelTestCfg *config.ParallelTestSettings preallocCfg *config.PreallocSettings predeclaredCfg *config.PredeclaredSettings @@ -229,6 +230,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { noLintLintCfg = &m.cfg.LintersSettings.NoLintLint noNamedReturnsCfg = &m.cfg.LintersSettings.NoNamedReturns preallocCfg = &m.cfg.LintersSettings.Prealloc + pairedBracketsCfg = &m.cfg.LintersSettings.PairedBrackets parallelTestCfg = &m.cfg.LintersSettings.ParallelTest predeclaredCfg = &m.cfg.LintersSettings.Predeclared promlinterCfg = &m.cfg.LintersSettings.Promlinter @@ -674,6 +676,12 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithPresets(linter.PresetStyle). WithURL("https://github.com/stbenjam/no-sprintf-host-port"), + linter.NewConfig(golinters.NewPairedbrackets(pairedBracketsCfg)). + WithSince("v1.50.0"). + WithPresets(linter.PresetFormatting). + WithLoadForGoAnalysis(). + WithURL("https://github.com/maratori/pairedbrackets"), + linter.NewConfig(golinters.NewParallelTest(parallelTestCfg)). WithSince("v1.33.0"). WithLoadForGoAnalysis(). diff --git a/test/testdata/pairedbrackets.go b/test/testdata/pairedbrackets.go new file mode 100644 index 000000000000..43d15b964216 --- /dev/null +++ b/test/testdata/pairedbrackets.go @@ -0,0 +1,279 @@ +//golangcitest:args -Epairedbrackets +package testdata + +import ( + "fmt" + "net/http" + + "github.com/stretchr/testify/assert" + alias "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func _() { + + // good - empty + fmt.Println() + + // good - one line, one element + fmt.Println("xxx") + + // good - one line, several elements + fmt.Printf("%s %d", "xxx", 10) + + // good - multiline + fmt.Printf( + "%s %d", + "xxx", + 10, + ) + + // good - multiline, arguments are not validated (it should be different linter) + fmt.Printf( + "%s %d", + "xxx", 10, + ) + + // good - last item exception + http.HandleFunc("/api/v1", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + // bad left - good right + fmt.Printf("%s %d", // want `^left parenthesis should either be the last character on a line or be on the same line with the last argument$` + "xxx", 10, + ) + + // bad left - right is ignored + fmt.Printf("%s %d", // want `^left parenthesis should either be the last character on a line or be on the same line with the last argument$` + "xxx", 10) + + // bad right - next + fmt.Printf( + "%s %d", + "xxx", + 10) // want `^right parenthesis should be on the next line$` + + // bad right - previous, multiline + http.HandleFunc("/api/v1", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }, + ) // want `^right parenthesis should be on the previous line$` + +} + +// default ignore-func-calls +func _() { + + assert.Equal(nil, []int{ + 1, + }, nil) + + alias.Equal(nil, []int{ + 1, + }, nil) + + require.Equalf(nil, []int{ + 1, + }, nil, "") + + assert.New(nil).JSONEq("", "", []int{ + 1, + }, "") + + require.New(nil).Eventually(func() bool { + return false + }, 0, 0) + +} + +func _() { + + // good - empty + _ = []int{} + + // good - one line, one element + _ = []int{1} + + // good - one line, several elements + _ = []int{1, 2, 3} + + // good - multiline + _ = []int{ + 1, + 2, + 3, + } + + // good - multiline, elements are not validated (it should be different linter) + _ = []int{ + 1, 2, + 3, + } + + // good - last item exception + _ = []any{1, 2, 3, `x + y`} + + // bad left - good right + _ = []int{1, 2, // want `^left brace should either be the last character on a line or be on the same line with the last composite element$` + 3, + } + + // bad left - right is ignored + _ = []int{1, 2, // want `^left brace should either be the last character on a line or be on the same line with the last composite element$` + 3} + + // bad right - next + _ = []int{ + 1, 2, 3} // want `^right brace should be on the next line$` + + // bad right - previous, one line + // ../../testdata/no_go_fmt/ast_composite_lit.go + + // bad right - previous, multiline + _ = []any{1, 2, 3, `x + y`, + } // want `^right brace should be on the previous line$` +} + +func _() { + + type goodEmpty func() + + type goodOneLineOneParam func(int) + + type goodOneLineTwoParams func(int, string) + + type goodMultiline func( + int, + string, + ) + + type goodMultilineParamsNotValidated func( + int, bool, + string, + ) + + type goodLastItemException func(int, struct { + }) + + type badLeftGoodRight func(int, // want `^left parenthesis should either be the last character on a line or be on the same line with the last parameter$` + string, + ) + + type badLeftRightIsIgnored func(int, // want `^left parenthesis should either be the last character on a line or be on the same line with the last parameter$` + string) + + type badRightNext func( + int, + string) // want `^right parenthesis should be on the next line$` + + type badRightPreviousOneLine func(int, string, + ) // want `^right parenthesis should be on the previous line$` + + type badRightPreviousMultiline func(int, struct { + }, + ) // want `^right parenthesis should be on the previous line$` + +} + +func _() { + + type x[T, V, R any] struct{} + + // good - one line + type _ x[int, string, bool] + + // good - multiline + type _ x[ + int, + string, + bool, + ] + + // good - multiline, types are not validated (it should be different linter) + type _ x[ + int, string, + bool, + ] + + // good - last item exception + type _ x[int, string, struct { + }] + + // bad left - good right + type _ x[int, string, // want `^left bracket should either be the last character on a line or be on the same line with the last element$` + bool, + ] + + // bad left - right is ignored + type _ x[int, string, // want `^left bracket should either be the last character on a line or be on the same line with the last element$` + bool] + + // bad right - next + type _ x[ + int, string, + bool] // want `^right bracket should be on the next line$` + + // bad right - previous, one line + // ../../testdata/no_go_fmt/ast_index_list_expr.go + + // bad right - previous, multiline + type _ x[int, string, struct { + }, + ] // want `^right bracket should be on the previous line$` + +} + +func _() { + + // good - [ast.TypeSpec].TypeParams == nil + type _ int + + // good - one line, one type parameter + type _[T int] int + + // good - one line, several elements + type _[T int, V string] int + + // good - multiline + type _[ + T int, + V string, + ] int + + // good - multiline, type parameters are not validated (it should be different linter) + type _[ + T int, V string, + R bool, + ] int + + // good - last item exception + type _[T int, V struct { + }] int + + // bad left - good right + type _[T int, V string, // want `^left bracket should either be the last character on a line or be on the same line with the last type parameter$` + R bool, + ] int + + // bad left - right is ignored + type _[T int, V string, // want `^left bracket should either be the last character on a line or be on the same line with the last type parameter$` + R bool] int + + // bad right - next + type _[ + T int, + V string] int // want `^right bracket should be on the next line$` + + // bad right - previous, one line + type _[T int, V string, + ] int // want `^right bracket should be on the previous line$` + + // bad right - previous, multiline + type _[T int, V struct { + }, + ] int // want `^right bracket should be on the previous line$` + +}