From c7f6fc5b21cf154fabe12125d2cd1b274c57cb53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Aug 2023 01:40:38 +0000 Subject: mod: do: bump github.com/golangci/golangci-lint from 1.53.3 to 1.54.0 Bumps [github.com/golangci/golangci-lint](https://github.com/golangci/golangci-lint) from 1.53.3 to 1.54.0. - [Release notes](https://github.com/golangci/golangci-lint/releases) - [Changelog](https://github.com/golangci/golangci-lint/blob/master/CHANGELOG.md) - [Commits](https://github.com/golangci/golangci-lint/compare/v1.53.3...v1.54.0) --- updated-dependencies: - dependency-name: github.com/golangci/golangci-lint dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- vendor/github.com/4meepo/tagalign/.golangci.yml | 3 +- vendor/github.com/4meepo/tagalign/README.md | 69 +++-- vendor/github.com/4meepo/tagalign/options.go | 9 + vendor/github.com/4meepo/tagalign/tagalign.go | 123 +++++++-- .../GaijinEntertainment/go-exhaustruct/v2/LICENSE | 21 -- .../go-exhaustruct/v2/pkg/analyzer/analyzer.go | 289 --------------------- .../v2/pkg/analyzer/patterns-list.go | 68 ----- .../v2/pkg/analyzer/struct-fields.go | 61 ----- .../GaijinEntertainment/go-exhaustruct/v3/LICENSE | 21 ++ .../go-exhaustruct/v3/analyzer/analyzer.go | 268 +++++++++++++++++++ .../go-exhaustruct/v3/internal/fields/struct.go | 124 +++++++++ .../go-exhaustruct/v3/internal/pattern/list.go | 82 ++++++ .../ashanbrown/forbidigo/forbidigo/forbidigo.go | 35 ++- .../github.com/daixiang0/gci/pkg/config/config.go | 1 + vendor/github.com/daixiang0/gci/pkg/gci/gci.go | 14 +- vendor/github.com/daixiang0/gci/pkg/io/file.go | 9 +- vendor/github.com/daixiang0/gci/pkg/io/search.go | 38 ++- .../daixiang0/gci/pkg/section/standard_list.go | 8 +- .../go-critic/go-critic/linter/linter.go | 21 +- .../golangci-lint/pkg/commands/executor.go | 2 +- .../golangci/golangci-lint/pkg/commands/help.go | 10 +- .../golangci/golangci-lint/pkg/commands/linters.go | 14 +- .../golangci/golangci-lint/pkg/config/config.go | 4 +- .../golangci-lint/pkg/config/linters_settings.go | 35 ++- .../golangci-lint/pkg/golinters/decorder.go | 8 + .../golangci-lint/pkg/golinters/exhaustruct.go | 2 +- .../golangci/golangci-lint/pkg/golinters/funlen.go | 2 +- .../golangci/golangci-lint/pkg/golinters/gci.go | 2 +- .../golangci-lint/pkg/golinters/ginkgolinter.go | 1 + .../golangci-lint/pkg/golinters/gocritic.go | 14 + .../golangci-lint/pkg/golinters/gofmt_common.go | 10 + .../golangci/golangci-lint/pkg/golinters/govet.go | 2 + .../golangci-lint/pkg/golinters/paralleltest.go | 5 +- .../golangci-lint/pkg/golinters/tagalign.go | 5 + .../golangci-lint/pkg/lint/linter/config.go | 8 +- .../pkg/lint/lintersdb/custom_linters.go | 127 +++++++++ .../pkg/lint/lintersdb/enabled_set.go | 20 +- .../golangci-lint/pkg/lint/lintersdb/manager.go | 134 +++------- vendor/github.com/golangci/misspell/.golangci.yml | 11 +- vendor/github.com/golangci/misspell/Dockerfile | 5 +- vendor/github.com/golangci/misspell/Makefile | 15 +- vendor/github.com/golangci/misspell/case.go | 6 +- vendor/github.com/golangci/misspell/goreleaser.yml | 3 +- .../golangci/misspell/install-misspell.sh | 11 +- vendor/github.com/golangci/misspell/replace.go | 3 +- .../paralleltest/pkg/paralleltest/paralleltest.go | 47 ++-- .../github.com/nunnatsa/ginkgolinter/.golangci.yml | 3 + vendor/github.com/nunnatsa/ginkgolinter/README.md | 207 ++++++++------- .../nunnatsa/ginkgolinter/ginkgo_linter.go | 95 ++++++- .../nunnatsa/ginkgolinter/ginkgohandler/handler.go | 66 +++++ .../nunnatsa/ginkgolinter/gomegahandler/handler.go | 9 +- .../nunnatsa/ginkgolinter/types/config.go | 24 +- .../polyfloyd/go-errorlint/errorlint/allowed.go | 7 + vendor/github.com/ultraware/funlen/README.md | 42 ++- vendor/github.com/ultraware/funlen/main.go | 28 +- vendor/github.com/uudashr/gocognit/gocognit.go | 25 +- vendor/github.com/ykadowak/zerologlint/README.md | 12 + .../github.com/ykadowak/zerologlint/zerologlint.go | 102 ++++++-- 58 files changed, 1576 insertions(+), 814 deletions(-) delete mode 100644 vendor/github.com/GaijinEntertainment/go-exhaustruct/v2/LICENSE delete mode 100644 vendor/github.com/GaijinEntertainment/go-exhaustruct/v2/pkg/analyzer/analyzer.go delete mode 100644 vendor/github.com/GaijinEntertainment/go-exhaustruct/v2/pkg/analyzer/patterns-list.go delete mode 100644 vendor/github.com/GaijinEntertainment/go-exhaustruct/v2/pkg/analyzer/struct-fields.go create mode 100644 vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/LICENSE create mode 100644 vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer/analyzer.go create mode 100644 vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/internal/fields/struct.go create mode 100644 vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/internal/pattern/list.go create mode 100644 vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/custom_linters.go create mode 100644 vendor/github.com/nunnatsa/ginkgolinter/.golangci.yml create mode 100644 vendor/github.com/nunnatsa/ginkgolinter/ginkgohandler/handler.go (limited to 'vendor/github.com') diff --git a/vendor/github.com/4meepo/tagalign/.golangci.yml b/vendor/github.com/4meepo/tagalign/.golangci.yml index 99baa8c21..e65f604fe 100644 --- a/vendor/github.com/4meepo/tagalign/.golangci.yml +++ b/vendor/github.com/4meepo/tagalign/.golangci.yml @@ -42,7 +42,6 @@ linters-settings: - name: receiver-naming - name: redefines-builtin-id - name: string-of-int - - name: struct-tag - name: superfluous-else - name: time-naming - name: unconditional-recursion @@ -81,7 +80,7 @@ linters: - goprintffuncname - gosec - gosimple - - govet + # - govet - importas - ineffassign - makezero diff --git a/vendor/github.com/4meepo/tagalign/README.md b/vendor/github.com/4meepo/tagalign/README.md index 9181e7a19..262a2e429 100644 --- a/vendor/github.com/4meepo/tagalign/README.md +++ b/vendor/github.com/4meepo/tagalign/README.md @@ -41,6 +41,40 @@ type FooBar struct { } ``` +## Usage + +By default tagalign will only align tags, but not sort them. But alignment and [sort feature](https://github.com/4meepo/tagalign#sort-tag) can work together or separately. + +* As a Golangci Linter (Recommended) + + Tagalign is a built-in linter in [Golangci Lint](https://golangci-lint.run/usage/linters/#tagalign) since `v1.53`. + > Note: In order to have the best experience, add the `--fix` flag to `golangci-lint` to enabled the aufofix feature. + +* Standalone Mode + + Install it using `GO` or download it [here](https://github.com/4meepo/tagalign/releases). + + ```bash + go install github.com/4meepo/tagalign/cmd/tagalign@latest + ``` + + Run it in your terminal. + + ```bash + # Only align tags. + tagalign -fix {package path} + # Only sort tags with fixed order. + tagalign -fix -noalign -sort -order "json,xml" {package path} + # Align and sort together. + tagalign -fix -sort -order "json,xml" {package path} + # Align and sort together in strict style. + tagalign -fix -sort -order "json,xml" -strict {package path} + ``` + +## Advanced Features + +### Sort Tag + In addition to alignment, it can also sort tags with fixed order. If we enable sort with fixed order `json,xml`, the following code ```go @@ -63,32 +97,29 @@ type SortExample struct { The fixed order is `json,xml`, so the tags `json` and `xml` will be sorted and aligned first, and the rest tags will be sorted and aligned in the dictionary order. -## Install - -```bash -go install github.com/4meepo/tagalign/cmd/tagalign -``` - -## Usage +### Strict Style -By default tagalign will only align tags, but not sort them. But alignment and sort can work together or separately. +Sometimes, you may want to align your tags in strict style. In this style, the tags will be sorted and aligned in the dictionary order, and the tags with the same name will be aligned together. For example, the following code -If you don't want to align tags, you can use `-noalign` to disable alignment. +```go +type StrictStyleExample struct { + Foo int ` xml:"baz" yaml:"bar" zip:"foo" binding:"required" gorm:"column:foo" validate:"required"` + Bar int `validate:"required" gorm:"column:bar" yaml:"foo" xml:"bar" binding:"required" json:"bar,omitempty" ` +} +``` -You can use `-sort` to enable sort and `-order` to set the fixed order of tags. +will be aligned to -```bash -# Only align tags. -tagalign -fix {package path} -# Only sort tags with fixed order. -tagalign -fix -noalign -sort -order "json,xml" {package path} -# Align and sort together. -tagalign -fix -sort -order "json,xml" {package path} +```go +type StrictStyleExample struct { + Foo int `binding:"required" gorm:"column:foo" validate:"required" xml:"baz" yaml:"bar" zip:"foo"` + Bar int `binding:"required" gorm:"column:bar" json:"bar,omitempty" validate:"required" xml:"bar" yaml:"foo"` +} ``` -TODO: integrate with golangci-lint +> Note: The strict style can't run without the align or sort feature enabled. -## Reference +## References [Golang AST Visualizer](http://goast.yuroyoro.net/) diff --git a/vendor/github.com/4meepo/tagalign/options.go b/vendor/github.com/4meepo/tagalign/options.go index 4deaf8cbc..ddec98da7 100644 --- a/vendor/github.com/4meepo/tagalign/options.go +++ b/vendor/github.com/4meepo/tagalign/options.go @@ -26,3 +26,12 @@ func WithAlign(enabled bool) Option { h.align = enabled } } + +// WithStrictStyle configure whether enable strict style. +// StrictStyle is disabled by default. +// Note: StrictStyle must be used with WithAlign(true) and WithSort(...) together, or it will be ignored. +func WithStrictStyle() Option { + return func(h *Helper) { + h.style = StrictStyle + } +} diff --git a/vendor/github.com/4meepo/tagalign/tagalign.go b/vendor/github.com/4meepo/tagalign/tagalign.go index 3dae96bc3..c99851036 100644 --- a/vendor/github.com/4meepo/tagalign/tagalign.go +++ b/vendor/github.com/4meepo/tagalign/tagalign.go @@ -22,6 +22,13 @@ const ( GolangciLintMode ) +type Style int + +const ( + DefaultStyle Style = iota + StrictStyle +) + func NewAnalyzer(options ...Option) *analysis.Analyzer { return &analysis.Analyzer{ Name: "tagalign", @@ -38,12 +45,18 @@ func Run(pass *analysis.Pass, options ...Option) []Issue { for _, f := range pass.Files { h := &Helper{ mode: StandaloneMode, + style: DefaultStyle, align: true, } for _, opt := range options { opt(h) } + // StrictStyle must be used with WithAlign(true) and WithSort(...) together, or it will be ignored. + if h.style == StrictStyle && (!h.align || !h.sort) { + h.style = DefaultStyle + } + if !h.align && !h.sort { // do nothing return nil @@ -62,6 +75,8 @@ func Run(pass *analysis.Pass, options ...Option) []Issue { type Helper struct { mode Mode + style Style + align bool // whether enable tags align. sort bool // whether enable tags sort. fixedTagOrder []string // the order of tags, the other tags will be sorted by name. @@ -182,6 +197,17 @@ func (w *Helper) Process(pass *analysis.Pass) { //nolint:gocognit var maxTagNum int var tagsGroup, notSortedTagsGroup [][]*structtag.Tag + + var uniqueKeys []string + addKey := func(k string) { + for _, key := range uniqueKeys { + if key == k { + return + } + } + uniqueKeys = append(uniqueKeys, k) + } + for i, field := range fields { offsets[i] = pass.Fset.Position(field.Tag.Pos()).Column tag, err := strconv.Unquote(field.Tag.Value) @@ -204,24 +230,45 @@ func (w *Helper) Process(pass *analysis.Pass) { //nolint:gocognit notSortedTagsGroup = append(notSortedTagsGroup, cp) sortBy(w.fixedTagOrder, tags) } - + for _, t := range tags.Tags() { + addKey(t.Key) + } tagsGroup = append(tagsGroup, tags.Tags()) } - // if w.align{ - // record the max length of each column tag - tagMaxLens := make([]int, maxTagNum) + if w.sort && StrictStyle == w.style { + sortAllKeys(w.fixedTagOrder, uniqueKeys) + maxTagNum = len(uniqueKeys) + } + // record the max length of each column tag + type tagLen struct { + Key string // present only when sort enabled + Len int + } + tagMaxLens := make([]tagLen, maxTagNum) for j := 0; j < maxTagNum; j++ { var maxLength int + var key string for i := 0; i < len(tagsGroup); i++ { - if len(tagsGroup[i]) <= j { - // in case of index out of range - continue + if w.style == StrictStyle { + key = uniqueKeys[j] + // search by key + for _, tag := range tagsGroup[i] { + if tag.Key == key { + maxLength = max(maxLength, len(tag.String())) + break + } + } + } else { + if len(tagsGroup[i]) <= j { + // in case of index out of range + continue + } + maxLength = max(maxLength, len(tagsGroup[i][j].String())) } - maxLength = max(maxLength, len(tagsGroup[i][j].String())) } - tagMaxLens[j] = maxLength + tagMaxLens[j] = tagLen{key, maxLength} } for i, field := range fields { @@ -231,9 +278,28 @@ func (w *Helper) Process(pass *analysis.Pass) { //nolint:gocognit if w.align { // if align enabled, align tags. newTagBuilder := strings.Builder{} - for i, tag := range tags { - format := alignFormat(tagMaxLens[i] + 1) // with an extra space - newTagBuilder.WriteString(fmt.Sprintf(format, tag.String())) + for i, n := 0, 0; i < len(tags) && n < len(tagMaxLens); { + tag := tags[i] + var format string + if w.style == StrictStyle { + if tagMaxLens[n].Key == tag.Key { + // match + format = alignFormat(tagMaxLens[n].Len + 1) // with an extra space + newTagBuilder.WriteString(fmt.Sprintf(format, tag.String())) + i++ + n++ + } else { + // tag missing + format = alignFormat(tagMaxLens[n].Len + 1) + newTagBuilder.WriteString(fmt.Sprintf(format, "")) + n++ + } + } else { + format = alignFormat(tagMaxLens[n].Len + 1) // with an extra space + newTagBuilder.WriteString(fmt.Sprintf(format, tag.String())) + i++ + n++ + } } newTagStr = newTagBuilder.String() } else { @@ -249,7 +315,8 @@ func (w *Helper) Process(pass *analysis.Pass) { //nolint:gocognit newTagStr = strings.Join(tagsStr, " ") } - unquoteTag := strings.TrimSpace(newTagStr) + unquoteTag := strings.TrimRight(newTagStr, " ") + // unquoteTag := newTagStr newTagValue := fmt.Sprintf("`%s`", unquoteTag) if field.Tag.Value == newTagValue { // nothing changed @@ -278,14 +345,9 @@ func (w *Helper) Process(pass *analysis.Pass) { //nolint:gocognit sortBy(w.fixedTagOrder, tags) } - if reflect.DeepEqual(originalTags, tags.Tags()) { - // if tags order not changed, do nothing - continue - } - newTagValue := fmt.Sprintf("`%s`", tags.String()) - if field.Tag.Value == newTagValue { - // nothing changed + if reflect.DeepEqual(originalTags, tags.Tags()) && field.Tag.Value == newTagValue { + // if tags order not changed, do nothing continue } @@ -329,6 +391,27 @@ func sortBy(fixedOrder []string, tags *structtag.Tags) { }) } +func sortAllKeys(fixedOrder []string, keys []string) { + sort.Slice(keys, func(i, j int) bool { + oi := findIndex(fixedOrder, keys[i]) + oj := findIndex(fixedOrder, keys[j]) + + if oi == -1 && oj == -1 { + return keys[i] < keys[j] + } + + if oi == -1 { + return false + } + + if oj == -1 { + return true + } + + return oi < oj + }) +} + func findIndex(s []string, e string) int { for i, a := range s { if a == e { diff --git a/vendor/github.com/GaijinEntertainment/go-exhaustruct/v2/LICENSE b/vendor/github.com/GaijinEntertainment/go-exhaustruct/v2/LICENSE deleted file mode 100644 index 6698196c5..000000000 --- a/vendor/github.com/GaijinEntertainment/go-exhaustruct/v2/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Gaijin Entertainment - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/GaijinEntertainment/go-exhaustruct/v2/pkg/analyzer/analyzer.go b/vendor/github.com/GaijinEntertainment/go-exhaustruct/v2/pkg/analyzer/analyzer.go deleted file mode 100644 index 279f2189d..000000000 --- a/vendor/github.com/GaijinEntertainment/go-exhaustruct/v2/pkg/analyzer/analyzer.go +++ /dev/null @@ -1,289 +0,0 @@ -package analyzer - -import ( - "errors" - "flag" - "go/ast" - "go/types" - "strings" - "sync" - - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/passes/inspect" - "golang.org/x/tools/go/ast/inspector" -) - -var ( - ErrEmptyPattern = errors.New("pattern can't be empty") -) - -type analyzer struct { - include PatternsList - exclude PatternsList - - typesProcessCache map[types.Type]bool - typesProcessCacheMu sync.RWMutex - - structFieldsCache map[types.Type]*StructFields - structFieldsCacheMu sync.RWMutex -} - -// NewAnalyzer returns a go/analysis-compatible analyzer. -// -i arguments adds include patterns -// -e arguments adds exclude patterns -func NewAnalyzer(include []string, exclude []string) (*analysis.Analyzer, error) { - a := analyzer{ //nolint:exhaustruct - typesProcessCache: map[types.Type]bool{}, - - structFieldsCache: map[types.Type]*StructFields{}, - } - - var err error - - a.include, err = newPatternsList(include) - if err != nil { - return nil, err - } - - a.exclude, err = newPatternsList(exclude) - if err != nil { - return nil, err - } - - return &analysis.Analyzer{ //nolint:exhaustruct - Name: "exhaustruct", - Doc: "Checks if all structure fields are initialized", - Run: a.run, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - Flags: a.newFlagSet(), - }, nil -} - -func (a *analyzer) newFlagSet() flag.FlagSet { - fs := flag.NewFlagSet("exhaustruct flags", flag.PanicOnError) - - fs.Var( - &reListVar{values: &a.include}, - "i", - "Regular expression to match struct packages and names, can receive multiple flags", - ) - fs.Var( - &reListVar{values: &a.exclude}, - "e", - "Regular expression to exclude struct packages and names, can receive multiple flags", - ) - - return *fs -} - -func (a *analyzer) run(pass *analysis.Pass) (interface{}, error) { - insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) //nolint:forcetypeassert - - nodeTypes := []ast.Node{ - (*ast.CompositeLit)(nil), - (*ast.ReturnStmt)(nil), - } - - insp.Preorder(nodeTypes, a.newVisitor(pass)) - - return nil, nil //nolint:nilnil -} - -//nolint:cyclop -func (a *analyzer) newVisitor(pass *analysis.Pass) func(node ast.Node) { - var ret *ast.ReturnStmt - - return func(node ast.Node) { - if retLit, ok := node.(*ast.ReturnStmt); ok { - // save return statement for future (to detect error-containing returns) - ret = retLit - - return - } - - lit, _ := node.(*ast.CompositeLit) - if lit.Type == nil { - // we're not interested in non-typed literals - return - } - - typ := pass.TypesInfo.TypeOf(lit.Type) - if typ == nil { - return - } - - strct, ok := typ.Underlying().(*types.Struct) - if !ok { - // we also not interested in non-structure literals - return - } - - strctName := exprName(lit.Type) - if strctName == "" { - return - } - - if !a.shouldProcessType(typ) { - return - } - - if len(lit.Elts) == 0 && ret != nil { - if ret.End() < lit.Pos() { - // we're outside last return statement - ret = nil - } else if returnContainsLiteral(ret, lit) && returnContainsError(ret, pass) { - // we're okay with empty literals in return statements with non-nil errors, like - // `return my.Struct{}, fmt.Errorf("non-nil error!")` - return - } - } - - missingFields := a.structMissingFields(lit, strct, strings.HasPrefix(typ.String(), pass.Pkg.Path()+".")) - - if len(missingFields) == 1 { - pass.Reportf(node.Pos(), "%s is missing in %s", missingFields[0], strctName) - } else if len(missingFields) > 1 { - pass.Reportf(node.Pos(), "%s are missing in %s", strings.Join(missingFields, ", "), strctName) - } - } -} - -func (a *analyzer) shouldProcessType(typ types.Type) bool { - if len(a.include) == 0 && len(a.exclude) == 0 { - // skip whole part with cache, since we have no restrictions and have to check everything - return true - } - - a.typesProcessCacheMu.RLock() - v, ok := a.typesProcessCache[typ] - a.typesProcessCacheMu.RUnlock() - - if !ok { - a.typesProcessCacheMu.Lock() - defer a.typesProcessCacheMu.Unlock() - - v = true - typStr := typ.String() - - if len(a.include) > 0 && !a.include.MatchesAny(typStr) { - v = false - } - - if v && a.exclude.MatchesAny(typStr) { - v = false - } - - a.typesProcessCache[typ] = v - } - - return v -} - -func (a *analyzer) structMissingFields(lit *ast.CompositeLit, strct *types.Struct, private bool) []string { - keys, unnamed := literalKeys(lit) - fields := a.structFields(strct) - - if unnamed { - if private { - return fields.All[len(keys):] - } - - return fields.Public[len(keys):] - } - - if private { - return difference(fields.AllRequired, keys) - } - - return difference(fields.PublicRequired, keys) -} - -func (a *analyzer) structFields(strct *types.Struct) *StructFields { - typ := strct.Underlying() - - a.structFieldsCacheMu.RLock() - fields, ok := a.structFieldsCache[typ] - a.structFieldsCacheMu.RUnlock() - - if !ok { - a.structFieldsCacheMu.Lock() - defer a.structFieldsCacheMu.Unlock() - - fields = NewStructFields(strct) - a.structFieldsCache[typ] = fields - } - - return fields -} - -func returnContainsLiteral(ret *ast.ReturnStmt, lit *ast.CompositeLit) bool { - for _, result := range ret.Results { - if l, ok := result.(*ast.CompositeLit); ok { - if lit == l { - return true - } - } - } - - return false -} - -func returnContainsError(ret *ast.ReturnStmt, pass *analysis.Pass) bool { - for _, result := range ret.Results { - if pass.TypesInfo.TypeOf(result).String() == "error" { - return true - } - } - - return false -} - -func literalKeys(lit *ast.CompositeLit) (keys []string, unnamed bool) { - for _, elt := range lit.Elts { - if k, ok := elt.(*ast.KeyValueExpr); ok { - if ident, ok := k.Key.(*ast.Ident); ok { - keys = append(keys, ident.Name) - } - - continue - } - - // in case we deal with unnamed initialization - no need to iterate over all - // elements - simply create slice with proper size - unnamed = true - keys = make([]string, len(lit.Elts)) - - return - } - - return -} - -// difference returns elements that are in `a` and not in `b`. -func difference(a, b []string) (diff []string) { - mb := make(map[string]struct{}, len(b)) - for _, x := range b { - mb[x] = struct{}{} - } - - for _, x := range a { - if _, found := mb[x]; !found { - diff = append(diff, x) - } - } - - return diff -} - -func exprName(expr ast.Expr) string { - if i, ok := expr.(*ast.Ident); ok { - return i.Name - } - - s, ok := expr.(*ast.SelectorExpr) - if !ok { - return "" - } - - return s.Sel.Name -} diff --git a/vendor/github.com/GaijinEntertainment/go-exhaustruct/v2/pkg/analyzer/patterns-list.go b/vendor/github.com/GaijinEntertainment/go-exhaustruct/v2/pkg/analyzer/patterns-list.go deleted file mode 100644 index 2884cab62..000000000 --- a/vendor/github.com/GaijinEntertainment/go-exhaustruct/v2/pkg/analyzer/patterns-list.go +++ /dev/null @@ -1,68 +0,0 @@ -package analyzer - -import ( - "fmt" - "regexp" -) - -type PatternsList []*regexp.Regexp - -// MatchesAny matches provided string against all regexps in a slice. -func (l PatternsList) MatchesAny(str string) bool { - for _, r := range l { - if r.MatchString(str) { - return true - } - } - - return false -} - -// newPatternsList parses slice of strings to a slice of compiled regular -// expressions. -func newPatternsList(in []string) (PatternsList, error) { - list := PatternsList{} - - for _, str := range in { - re, err := strToRegexp(str) - if err != nil { - return nil, err - } - - list = append(list, re) - } - - return list, nil -} - -type reListVar struct { - values *PatternsList -} - -func (v *reListVar) Set(value string) error { - re, err := strToRegexp(value) - if err != nil { - return err - } - - *v.values = append(*v.values, re) - - return nil -} - -func (v *reListVar) String() string { - return "" -} - -func strToRegexp(str string) (*regexp.Regexp, error) { - if str == "" { - return nil, ErrEmptyPattern - } - - re, err := regexp.Compile(str) - if err != nil { - return nil, fmt.Errorf("unable to compile %s as regular expression: %w", str, err) - } - - return re, nil -} diff --git a/vendor/github.com/GaijinEntertainment/go-exhaustruct/v2/pkg/analyzer/struct-fields.go b/vendor/github.com/GaijinEntertainment/go-exhaustruct/v2/pkg/analyzer/struct-fields.go deleted file mode 100644 index 56eda05f7..000000000 --- a/vendor/github.com/GaijinEntertainment/go-exhaustruct/v2/pkg/analyzer/struct-fields.go +++ /dev/null @@ -1,61 +0,0 @@ -package analyzer - -import ( - "go/types" - "reflect" - - "golang.org/x/exp/slices" -) - -type StructFields struct { - All []string - AllRequired []string - - Public []string - PublicRequired []string -} - -func NewStructFields(strct *types.Struct) *StructFields { - sf := StructFields{ - All: make([]string, strct.NumFields()), - AllRequired: make([]string, 0, strct.NumFields()), - Public: make([]string, 0, strct.NumFields()), - PublicRequired: make([]string, 0, strct.NumFields()), - } - - for i := 0; i < strct.NumFields(); i++ { - f := strct.Field(i) - isOptional := isFieldOptional(strct.Tag(i)) - - sf.All[i] = f.Name() - if !isOptional { - sf.AllRequired = append(sf.AllRequired, f.Name()) - } - - if f.Exported() { - sf.Public = append(sf.Public, f.Name()) - - if !isOptional { - sf.PublicRequired = append(sf.PublicRequired, f.Name()) - } - } - } - - sf.All = slices.Clip(sf.All) - sf.AllRequired = slices.Clip(sf.AllRequired) - sf.Public = slices.Clip(sf.Public) - sf.PublicRequired = slices.Clip(sf.PublicRequired) - - return &sf -} - -const ( - TagName = "exhaustruct" - OptionalTagValue = "optional" -) - -// isFieldOptional checks if field tags has an optional tag, and therefore can -// be omitted during structure initialization. -func isFieldOptional(tags string) bool { - return reflect.StructTag(tags).Get(TagName) == OptionalTagValue -} diff --git a/vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/LICENSE b/vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/LICENSE new file mode 100644 index 000000000..6698196c5 --- /dev/null +++ b/vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Gaijin Entertainment + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer/analyzer.go b/vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer/analyzer.go new file mode 100644 index 000000000..d0cd2d5bb --- /dev/null +++ b/vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer/analyzer.go @@ -0,0 +1,268 @@ +package analyzer + +import ( + "flag" + "fmt" + "go/ast" + "go/token" + "go/types" + "sync" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" + + "github.com/GaijinEntertainment/go-exhaustruct/v3/internal/fields" + "github.com/GaijinEntertainment/go-exhaustruct/v3/internal/pattern" +) + +type analyzer struct { + include pattern.List `exhaustruct:"optional"` + exclude pattern.List `exhaustruct:"optional"` + + fieldsCache map[types.Type]fields.StructFields + fieldsCacheMu sync.RWMutex `exhaustruct:"optional"` + + typeProcessingNeed map[string]bool + typeProcessingNeedMu sync.RWMutex `exhaustruct:"optional"` +} + +func NewAnalyzer(include, exclude []string) (*analysis.Analyzer, error) { + a := analyzer{ + fieldsCache: make(map[types.Type]fields.StructFields), + typeProcessingNeed: make(map[string]bool), + } + + var err error + + a.include, err = pattern.NewList(include...) + if err != nil { + return nil, err //nolint:wrapcheck + } + + a.exclude, err = pattern.NewList(exclude...) + if err != nil { + return nil, err //nolint:wrapcheck + } + + return &analysis.Analyzer{ //nolint:exhaustruct + Name: "exhaustruct", + Doc: "Checks if all structure fields are initialized", + Run: a.run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: a.newFlagSet(), + }, nil +} + +func (a *analyzer) newFlagSet() flag.FlagSet { + fs := flag.NewFlagSet("", flag.PanicOnError) + + fs.Var(&a.include, "i", `Regular expression to match type names, can receive multiple flags. +Anonymous structs can be matched by '' alias. +4ex: + github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer\. + github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer\.TypeInfo`) + fs.Var(&a.exclude, "e", `Regular expression to exclude type names, can receive multiple flags. +Anonymous structs can be matched by '' alias. +4ex: + github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer\. + github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer\.TypeInfo`) + + return *fs +} + +func (a *analyzer) run(pass *analysis.Pass) (any, error) { + insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) //nolint:forcetypeassert + + insp.WithStack( + []ast.Node{ + (*ast.CompositeLit)(nil), + }, + a.newVisitor(pass), + ) + + return nil, nil //nolint:nilnil +} + +// newVisitor returns visitor that only expects [ast.CompositeLit] nodes. +func (a *analyzer) newVisitor(pass *analysis.Pass) func(n ast.Node, push bool, stack []ast.Node) bool { + return func(n ast.Node, push bool, stack []ast.Node) bool { + if !push { + return true + } + + lit, ok := n.(*ast.CompositeLit) + if !ok { + // this should never happen, but better be prepared + return true + } + + structTyp, typeInfo, ok := getStructType(pass, lit) + if !ok { + return true + } + + if len(lit.Elts) == 0 { + if ret, ok := stackParentIsReturn(stack); ok { + if returnContainsNonNilError(pass, ret) { + // it is okay to return uninitialized structure in case struct's direct parent is + // a return statement containing non-nil error + // + // we're unable to check if returned error is custom, but at least we're able to + // cover str [error] type. + return true + } + } + } + + pos, msg := a.processStruct(pass, lit, structTyp, typeInfo) + if pos != nil { + pass.Reportf(*pos, msg) + } + + return true + } +} + +func getStructType(pass *analysis.Pass, lit *ast.CompositeLit) (*types.Struct, *TypeInfo, bool) { + switch typ := pass.TypesInfo.TypeOf(lit).(type) { + case *types.Named: // named type + if structTyp, ok := typ.Underlying().(*types.Struct); ok { + pkg := typ.Obj().Pkg() + ti := TypeInfo{ + Name: typ.Obj().Name(), + PackageName: pkg.Name(), + PackagePath: pkg.Path(), + } + + return structTyp, &ti, true + } + + return nil, nil, false + + case *types.Struct: // anonymous struct + ti := TypeInfo{ + Name: "", + PackageName: pass.Pkg.Name(), + PackagePath: pass.Pkg.Path(), + } + + return typ, &ti, true + + default: + return nil, nil, false + } +} + +func stackParentIsReturn(stack []ast.Node) (*ast.ReturnStmt, bool) { + // it is safe to skip boundary check, since stack always has at least one element + // - whole file. + ret, ok := stack[len(stack)-2].(*ast.ReturnStmt) + + return ret, ok +} + +func returnContainsNonNilError(pass *analysis.Pass, ret *ast.ReturnStmt) bool { + // errors are mostly located at the end of return statement, so we're starting + // from the end. + for i := len(ret.Results) - 1; i >= 0; i-- { + if pass.TypesInfo.TypeOf(ret.Results[i]).String() == "error" { + return true + } + } + + return false +} + +func (a *analyzer) processStruct( + pass *analysis.Pass, + lit *ast.CompositeLit, + structTyp *types.Struct, + info *TypeInfo, +) (*token.Pos, string) { + if !a.shouldProcessType(info) { + return nil, "" + } + + // unnamed structures are only defined in same package, along with types that has + // prefix identical to current package name. + isSamePackage := info.PackagePath == pass.Pkg.Path() + + if f := a.litSkippedFields(lit, structTyp, !isSamePackage); len(f) > 0 { + pos := lit.Pos() + + if len(f) == 1 { + return &pos, fmt.Sprintf("%s is missing field %s", info.ShortString(), f.String()) + } + + return &pos, fmt.Sprintf("%s is missing fields %s", info.ShortString(), f.String()) + } + + return nil, "" +} + +// shouldProcessType returns true if type should be processed basing off include +// and exclude patterns, defined though constructor and\or flags. +func (a *analyzer) shouldProcessType(info *TypeInfo) bool { + if len(a.include) == 0 && len(a.exclude) == 0 { + return true + } + + name := info.String() + + a.typeProcessingNeedMu.RLock() + res, ok := a.typeProcessingNeed[name] + a.typeProcessingNeedMu.RUnlock() + + if !ok { + a.typeProcessingNeedMu.Lock() + res = true + + if a.include != nil && !a.include.MatchFullString(name) { + res = false + } + + if res && a.exclude != nil && a.exclude.MatchFullString(name) { + res = false + } + + a.typeProcessingNeed[name] = res + a.typeProcessingNeedMu.Unlock() + } + + return res +} + +//revive:disable-next-line:unused-receiver +func (a *analyzer) litSkippedFields( + lit *ast.CompositeLit, + typ *types.Struct, + onlyExported bool, +) fields.StructFields { + a.fieldsCacheMu.RLock() + f, ok := a.fieldsCache[typ] + a.fieldsCacheMu.RUnlock() + + if !ok { + a.fieldsCacheMu.Lock() + f = fields.NewStructFields(typ) + a.fieldsCache[typ] = f + a.fieldsCacheMu.Unlock() + } + + return f.SkippedFields(lit, onlyExported) +} + +type TypeInfo struct { + Name string + PackageName string + PackagePath string +} + +func (t TypeInfo) String() string { + return t.PackagePath + "." + t.Name +} + +func (t TypeInfo) ShortString() string { + return t.PackageName + "." + t.Name +} diff --git a/vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/internal/fields/struct.go b/vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/internal/fields/struct.go new file mode 100644 index 000000000..af2390e87 --- /dev/null +++ b/vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/internal/fields/struct.go @@ -0,0 +1,124 @@ +package fields + +import ( + "go/ast" + "go/types" + "reflect" +) + +const ( + TagName = "exhaustruct" + OptionalTagValue = "optional" +) + +type StructField struct { + Name string + Exported bool + Optional bool +} + +type StructFields []*StructField + +// NewStructFields creates a new [StructFields] from a given struct type. +// StructFields items are listed in order they appear in the struct. +func NewStructFields(strct *types.Struct) StructFields { + sf := make(StructFields, 0, strct.NumFields()) + + for i := 0; i < strct.NumFields(); i++ { + f := strct.Field(i) + + sf = append(sf, &StructField{ + Name: f.Name(), + Exported: f.Exported(), + Optional: HasOptionalTag(strct.Tag(i)), + }) + } + + return sf +} + +func HasOptionalTag(tags string) bool { + return reflect.StructTag(tags).Get(TagName) == OptionalTagValue +} + +// String returns a comma-separated list of field names. +func (sf StructFields) String() (res string) { + for i := 0; i < len(sf); i++ { + if res != "" { + res += ", " + } + + res += sf[i].Name + } + + return res +} + +// SkippedFields returns a list of fields that are not present in the given +// literal, but expected to. +// +//revive:disable-next-line:cyclomatic +func (sf StructFields) SkippedFields(lit *ast.CompositeLit, onlyExported bool) StructFields { + if len(lit.Elts) != 0 && !isNamedLiteral(lit) { + if len(lit.Elts) == len(sf) { + return nil + } + + return sf[len(lit.Elts):] + } + + em := sf.existenceMap() + res := make(StructFields, 0, len(sf)) + + for i := 0; i < len(lit.Elts); i++ { + kv, ok := lit.Elts[i].(*ast.KeyValueExpr) + if !ok { + continue + } + + k, ok := kv.Key.(*ast.Ident) + if !ok { + continue + } + + em[k.Name] = true + } + + for i := 0; i < len(sf); i++ { + if em[sf[i].Name] || (!sf[i].Exported && onlyExported) || sf[i].Optional { + continue + } + + res = append(res, sf[i]) + } + + if len(res) == 0 { + return nil + } + + return res +} + +func (sf StructFields) existenceMap() map[string]bool { + m := make(map[string]bool, len(sf)) + + for i := 0; i < len(sf); i++ { + m[sf[i].Name] = false + } + + return m +} + +// isNamedLiteral returns true if the given literal is unnamed. +// +// The logic is basing on the principle that literal is named or unnamed, +// therefore is literal's first element is a [ast.KeyValueExpr], it is named. +// +// Method will panic if the given literal is empty. +func isNamedLiteral(lit *ast.CompositeLit) bool { + if _, ok := lit.Elts[0].(*ast.KeyValueExpr); !ok { + return false + } + + return true +} diff --git a/vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/internal/pattern/list.go b/vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/internal/pattern/list.go new file mode 100644 index 000000000..a16e5058d --- /dev/null +++ b/vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/internal/pattern/list.go @@ -0,0 +1,82 @@ +package pattern + +import ( + "fmt" + "regexp" + "strings" +) + +var ( + ErrEmptyPattern = fmt.Errorf("pattern can't be empty") + ErrCompilationFailed = fmt.Errorf("pattern compilation failed") +) + +// List is a list of regular expressions. +type List []*regexp.Regexp + +// NewList parses slice of strings to a slice of compiled regular expressions. +func NewList(strs ...string) (List, error) { + if len(strs) == 0 { + return nil, nil + } + + l := make(List, 0, len(strs)) + + for _, str := range strs { + re, err := strToRe(str) + if err != nil { + return nil, err + } + + l = append(l, re) + } + + return l, nil +} + +// MatchFullString matches provided string against all regexps in a slice and returns +// true if any of them matches whole string. +func (l List) MatchFullString(str string) bool { + for i := 0; i < len(l); i++ { + if m := l[i].FindStringSubmatch(str); len(m) > 0 && m[0] == str { + return true + } + } + + return false +} + +func (l *List) Set(value string) error { + re, err := strToRe(value) + if err != nil { + return err + } + + *l = append(*l, re) + + return nil +} + +func (l *List) String() string { + res := make([]string, 0, len(*l)) + + for _, re := range *l { + res = append(res, `"`+re.String()+`"`) + } + + return strings.Join(res, ", ") +} + +// strToRe parses string to a compiled regular expression that matches full string. +func strToRe(str string) (*regexp.Regexp, error) { + if str == "" { + return nil, ErrEmptyPattern + } + + re, err := regexp.Compile(str) + if err != nil { + return nil, fmt.Errorf("%w: %s: %w", ErrCompilationFailed, str, err) + } + + return re, nil +} diff --git a/vendor/github.com/ashanbrown/forbidigo/forbidigo/forbidigo.go b/vendor/github.com/ashanbrown/forbidigo/forbidigo/forbidigo.go index 943a69975..a7a3ab591 100644 --- a/vendor/github.com/ashanbrown/forbidigo/forbidigo/forbidigo.go +++ b/vendor/github.com/ashanbrown/forbidigo/forbidigo/forbidigo.go @@ -186,7 +186,40 @@ func (v *visitor) Visit(node ast.Node) ast.Visitor { if isGodocExample && v.cfg.ExcludeGodocExamples { return nil } - return v + ast.Walk(v, node.Type) + if node.Body != nil { + ast.Walk(v, node.Body) + } + return nil + // Ignore constant and type names + case *ast.ValueSpec: + // Look at only type and values for const and variable specs, and not names + if node.Type != nil { + ast.Walk(v, node.Type) + } + if node.Values != nil { + for _, x := range node.Values { + ast.Walk(v, x) + } + } + return nil + // Ignore import alias names + case *ast.ImportSpec: + return nil + // Ignore type names + case *ast.TypeSpec: + // Look at only type parameters for type spec + if node.TypeParams != nil { + ast.Walk(v, node.TypeParams) + } + ast.Walk(v, node.Type) + return nil + // Ignore field names + case *ast.Field: + if node.Type != nil { + ast.Walk(v, node.Type) + } + return nil // The following two are handled below. case *ast.SelectorExpr: case *ast.Ident: diff --git a/vendor/github.com/daixiang0/gci/pkg/config/config.go b/vendor/github.com/daixiang0/gci/pkg/config/config.go index b32148b18..120e787e9 100644 --- a/vendor/github.com/daixiang0/gci/pkg/config/config.go +++ b/vendor/github.com/daixiang0/gci/pkg/config/config.go @@ -23,6 +23,7 @@ type BoolConfig struct { NoPrefixComments bool `yaml:"no-prefixComments"` Debug bool `yaml:"-"` SkipGenerated bool `yaml:"skipGenerated"` + SkipVendor bool `yaml:"skipVendor"` CustomOrder bool `yaml:"customOrder"` } diff --git a/vendor/github.com/daixiang0/gci/pkg/gci/gci.go b/vendor/github.com/daixiang0/gci/pkg/gci/gci.go index 0fd7a0ec6..e84a16676 100644 --- a/vendor/github.com/daixiang0/gci/pkg/gci/gci.go +++ b/vendor/github.com/daixiang0/gci/pkg/gci/gci.go @@ -49,6 +49,16 @@ func WriteFormattedFiles(paths []string, cfg config.Config) error { }) } +func ListUnFormattedFiles(paths []string, cfg config.Config) error { + return processGoFilesInPaths(paths, cfg, func(filePath string, unmodifiedFile, formattedFile []byte) error { + if bytes.Equal(unmodifiedFile, formattedFile) { + return nil + } + fmt.Println(filePath) + return nil + }) +} + func DiffFormattedFiles(paths []string, cfg config.Config) error { return processStdInAndGoFilesInPaths(paths, cfg, func(filePath string, unmodifiedFile, formattedFile []byte) error { fileURI := span.URIFromPath(filePath) @@ -76,11 +86,11 @@ func DiffFormattedFilesToArray(paths []string, cfg config.Config, diffs *[]strin type fileFormattingFunc func(filePath string, unmodifiedFile, formattedFile []byte) error func processStdInAndGoFilesInPaths(paths []string, cfg config.Config, fileFunc fileFormattingFunc) error { - return ProcessFiles(io.StdInGenerator.Combine(io.GoFilesInPathsGenerator(paths)), cfg, fileFunc) + return ProcessFiles(io.StdInGenerator.Combine(io.GoFilesInPathsGenerator(paths, cfg.SkipVendor)), cfg, fileFunc) } func processGoFilesInPaths(paths []string, cfg config.Config, fileFunc fileFormattingFunc) error { - return ProcessFiles(io.GoFilesInPathsGenerator(paths), cfg, fileFunc) + return ProcessFiles(io.GoFilesInPathsGenerator(paths, cfg.SkipVendor), cfg, fileFunc) } func ProcessFiles(fileGenerator io.FileGeneratorFunc, cfg config.Config, fileFunc fileFormattingFunc) error { diff --git a/vendor/github.com/daixiang0/gci/pkg/io/file.go b/vendor/github.com/daixiang0/gci/pkg/io/file.go index f92d16e14..79950792c 100644 --- a/vendor/github.com/daixiang0/gci/pkg/io/file.go +++ b/vendor/github.com/daixiang0/gci/pkg/io/file.go @@ -39,8 +39,13 @@ func (a FileGeneratorFunc) Combine(b FileGeneratorFunc) FileGeneratorFunc { } } -func GoFilesInPathsGenerator(paths []string) FileGeneratorFunc { - return FilesInPathsGenerator(paths, isGoFile) +func GoFilesInPathsGenerator(paths []string, skipVendor bool) FileGeneratorFunc { + checkFunc := isGoFile + if skipVendor { + checkFunc = checkChains(isGoFile, isOutsideVendorDir) + } + + return FilesInPathsGenerator(paths, checkFunc) } func FilesInPathsGenerator(paths []string, fileCheckFun fileCheckFunction) FileGeneratorFunc { diff --git a/vendor/github.com/daixiang0/gci/pkg/io/search.go b/vendor/github.com/daixiang0/gci/pkg/io/search.go index 04f005876..cd821582e 100644 --- a/vendor/github.com/daixiang0/gci/pkg/io/search.go +++ b/vendor/github.com/daixiang0/gci/pkg/io/search.go @@ -6,7 +6,7 @@ import ( "path/filepath" ) -type fileCheckFunction func(file os.FileInfo) bool +type fileCheckFunction func(path string, file os.FileInfo) bool func FindFilesForPath(path string, fileCheckFun fileCheckFunction) ([]string, error) { switch entry, err := os.Stat(path); { @@ -14,7 +14,7 @@ func FindFilesForPath(path string, fileCheckFun fileCheckFunction) ([]string, er return nil, err case entry.IsDir(): return findFilesForDirectory(path, fileCheckFun) - case fileCheckFun(entry): + case fileCheckFun(path, entry): return []string{filepath.Clean(path)}, nil default: return []string{}, nil @@ -31,7 +31,7 @@ func findFilesForDirectory(dirPath string, fileCheckFun fileCheckFunction) ([]st if err != nil { return err } - if !entry.IsDir() && fileCheckFun(file) { + if !entry.IsDir() && fileCheckFun(path, file) { filePaths = append(filePaths, filepath.Clean(path)) } return nil @@ -42,6 +42,36 @@ func findFilesForDirectory(dirPath string, fileCheckFun fileCheckFunction) ([]st return filePaths, nil } -func isGoFile(file os.FileInfo) bool { +func isGoFile(_ string, file os.FileInfo) bool { return !file.IsDir() && filepath.Ext(file.Name()) == ".go" } + +func isOutsideVendorDir(path string, _ os.FileInfo) bool { + for { + base := filepath.Base(path) + if base == "vendor" { + return false + } + + prevPath := path + path = filepath.Dir(path) + + if prevPath == path { + break + } + } + + return true +} + +func checkChains(funcs ...fileCheckFunction) fileCheckFunction { + return func(path string, file os.FileInfo) bool { + for _, checkFunc := range funcs { + if !checkFunc(path, file) { + return false + } + } + + return true + } +} diff --git a/vendor/github.com/daixiang0/gci/pkg/section/standard_list.go b/vendor/github.com/daixiang0/gci/pkg/section/standard_list.go index f0e904d4d..05e799939 100644 --- a/vendor/github.com/daixiang0/gci/pkg/section/standard_list.go +++ b/vendor/github.com/daixiang0/gci/pkg/section/standard_list.go @@ -1,12 +1,14 @@ package section -// Code generated based on go1.20.1. DO NOT EDIT. +// Code generated based on go1.21.0 X:arenas. DO NOT EDIT. var standardPackages = map[string]struct{}{ "archive/tar": {}, "archive/zip": {}, + "arena": {}, "bufio": {}, "bytes": {}, + "cmp": {}, "compress/bzip2": {}, "compress/flate": {}, "compress/gzip": {}, @@ -96,7 +98,9 @@ var standardPackages = map[string]struct{}{ "io/fs": {}, "io/ioutil": {}, "log": {}, + "log/slog": {}, "log/syslog": {}, + "maps": {}, "math": {}, "math/big": {}, "math/bits": {}, @@ -139,6 +143,7 @@ var standardPackages = map[string]struct{}{ "runtime/pprof": {}, "runtime/race": {}, "runtime/trace": {}, + "slices": {}, "sort": {}, "strconv": {}, "strings": {}, @@ -149,6 +154,7 @@ var standardPackages = map[string]struct{}{ "testing/fstest": {}, "testing/iotest": {}, "testing/quick": {}, + "testing/slogtest": {}, "text/scanner": {}, "text/tabwriter": {}, "text/template": {}, diff --git a/vendor/github.com/go-critic/go-critic/linter/linter.go b/vendor/github.com/go-critic/go-critic/linter/linter.go index 27e9b659f..d4bc17536 100644 --- a/vendor/github.com/go-critic/go-critic/linter/linter.go +++ b/vendor/github.com/go-critic/go-critic/linter/linter.go @@ -5,6 +5,7 @@ import ( "go/token" "go/types" "strconv" + "strings" "github.com/go-toolsmith/astfmt" ) @@ -350,7 +351,25 @@ func (ctx *CheckerContext) SizeOf(typ types.Type) (int64, bool) { if named, ok := typ.(*types.Named); ok && named.TypeParams() != nil { return 0, false } - return ctx.SizesInfo.Sizeof(typ), true + return ctx.safeSizesInfoSizeof(typ) +} + +// safeSizesInfoSizeof unlike SizesInfo.Sizeof will not panic on struct with generic fields. +// it will catch a panic and recover from it, see https://github.com/go-critic/go-critic/issues/1354 +func (ctx *CheckerContext) safeSizesInfoSizeof(typ types.Type) (size int64, ok bool) { + ok = true + defer func() { + if r := recover(); r != nil { + if strings.Contains(r.(string), "assertion failed") { + size, ok = 0, false + } else { + panic(r) + } + } + }() + + size = ctx.SizesInfo.Sizeof(typ) + return size, ok } func resolvePkgObjects(ctx *Context, f *ast.File) { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/commands/executor.go b/vendor/github.com/golangci/golangci-lint/pkg/commands/executor.go index 109edcb90..61e221cb8 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/commands/executor.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/commands/executor.go @@ -120,7 +120,7 @@ func NewExecutor(buildInfo BuildInfo) *Executor { } // recreate after getting config - e.DBManager = lintersdb.NewManager(e.cfg, e.log).WithCustomLinters() + e.DBManager = lintersdb.NewManager(e.cfg, e.log) // Slice options must be explicitly set for proper merging of config and command-line options. fixSlicesFlags(e.runCmd.Flags()) diff --git a/vendor/github.com/golangci/golangci-lint/pkg/commands/help.go b/vendor/github.com/golangci/golangci-lint/pkg/commands/help.go index 61d362e7d..a06d508f2 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/commands/help.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/commands/help.go @@ -63,6 +63,10 @@ func printLinterConfigs(lcs []*linter.Config) { func (e *Executor) executeLintersHelp(_ *cobra.Command, _ []string) { var enabledLCs, disabledLCs []*linter.Config for _, lc := range e.DBManager.GetAllSupportedLinterConfigs() { + if lc.Internal { + continue + } + if lc.EnabledByDefault { enabledLCs = append(enabledLCs, lc) } else { @@ -78,8 +82,12 @@ func (e *Executor) executeLintersHelp(_ *cobra.Command, _ []string) { color.Green("\nLinters presets:") for _, p := range e.DBManager.AllPresets() { linters := e.DBManager.GetAllLinterConfigsForPreset(p) - linterNames := make([]string, 0, len(linters)) + var linterNames []string for _, lc := range linters { + if lc.Internal { + continue + } + linterNames = append(linterNames, lc.Name()) } sort.Strings(linterNames) diff --git a/vendor/github.com/golangci/golangci-lint/pkg/commands/linters.go b/vendor/github.com/golangci/golangci-lint/pkg/commands/linters.go index 13bb944d8..292713ec9 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/commands/linters.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/commands/linters.go @@ -29,14 +29,22 @@ func (e *Executor) executeLinters(_ *cobra.Command, _ []string) error { } color.Green("Enabled by your configuration linters:\n") - enabledLinters := make([]*linter.Config, 0, len(enabledLintersMap)) - for _, linter := range enabledLintersMap { - enabledLinters = append(enabledLinters, linter) + var enabledLinters []*linter.Config + for _, lc := range enabledLintersMap { + if lc.Internal { + continue + } + + enabledLinters = append(enabledLinters, lc) } printLinterConfigs(enabledLinters) var disabledLCs []*linter.Config for _, lc := range e.DBManager.GetAllSupportedLinterConfigs() { + if lc.Internal { + continue + } + if enabledLintersMap[lc.Name()] == nil { disabledLCs = append(disabledLCs, lc) } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/config/config.go b/vendor/github.com/golangci/golangci-lint/pkg/config/config.go index af40c63bd..7941f428f 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/config/config.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/config/config.go @@ -41,13 +41,13 @@ type Version struct { Debug bool `mapstructure:"debug"` } -func IsGreaterThanOrEqualGo118(v string) bool { +func IsGreaterThanOrEqualGo121(v string) bool { v1, err := hcversion.NewVersion(strings.TrimPrefix(v, "go")) if err != nil { return false } - limit, err := hcversion.NewVersion("1.18") + limit, err := hcversion.NewVersion("1.21") if err != nil { return false } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/config/linters_settings.go b/vendor/github.com/golangci/golangci-lint/pkg/config/linters_settings.go index b520ea4c6..805d0ff47 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/config/linters_settings.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/config/linters_settings.go @@ -114,9 +114,10 @@ var defaultLintersSettings = LintersSettings{ Qualified: false, }, TagAlign: TagAlignSettings{ - Align: true, - Sort: true, - Order: nil, + Align: true, + Sort: true, + Order: nil, + Strict: false, }, Testpackage: TestpackageSettings{ SkipRegexp: `(export|internal)_test\.go`, @@ -272,7 +273,11 @@ type DepGuardDeny struct { type DecorderSettings struct { DecOrder []string `mapstructure:"dec-order"` + IgnoreUnderscoreVars bool `mapstructure:"ignore-underscore-vars"` DisableDecNumCheck bool `mapstructure:"disable-dec-num-check"` + DisableTypeDecNumCheck bool `mapstructure:"disable-type-dec-num-check"` + DisableConstDecNumCheck bool `mapstructure:"disable-const-dec-num-check"` + DisableVarDecNumCheck bool `mapstructure:"disable-var-dec-num-check"` DisableDecOrderCheck bool `mapstructure:"disable-dec-order-check"` DisableInitFuncFirstCheck bool `mapstructure:"disable-init-func-first-check"` } @@ -375,8 +380,9 @@ func (p *ForbidigoPattern) MarshalString() ([]byte, error) { } type FunlenSettings struct { - Lines int - Statements int + Lines int + Statements int + IgnoreComments bool `mapstructure:"ignore-comments"` } type GciSettings struct { @@ -392,6 +398,7 @@ type GinkgoLinterSettings struct { SuppressErrAssertion bool `mapstructure:"suppress-err-assertion"` SuppressCompareAssertion bool `mapstructure:"suppress-compare-assertion"` SuppressAsyncAssertion bool `mapstructure:"suppress-async-assertion"` + ForbidFocusContainer bool `mapstructure:"forbid-focus-container"` AllowHaveLenZero bool `mapstructure:"allow-havelen-zero"` } @@ -609,7 +616,8 @@ type MalignedSettings struct { } type MisspellSettings struct { - Locale string + Locale string + // TODO(ldez): v2 the options must be renamed to `IgnoredRules`. IgnoreWords []string `mapstructure:"ignore-words"` } @@ -648,7 +656,8 @@ type NoNamedReturnsSettings struct { ReportErrorInDefer bool `mapstructure:"report-error-in-defer"` } type ParallelTestSettings struct { - IgnoreMissing bool `mapstructure:"ignore-missing"` + IgnoreMissing bool `mapstructure:"ignore-missing"` + IgnoreMissingSubtests bool `mapstructure:"ignore-missing-subtests"` } type PreallocSettings struct { @@ -714,9 +723,10 @@ type StructCheckSettings struct { } type TagAlignSettings struct { - Align bool `mapstructure:"align"` - Sort bool `mapstructure:"sort"` - Order []string `mapstructure:"order"` + Align bool `mapstructure:"align"` + Sort bool `mapstructure:"sort"` + Order []string `mapstructure:"order"` + Strict bool `mapstructure:"strict"` } type TagliatelleSettings struct { @@ -828,6 +838,9 @@ type CustomLinterSettings struct { Path string // Description describes the purpose of the private linter. Description string - // The URL containing the source code for the private linter. + // OriginalURL The URL containing the source code for the private linter. OriginalURL string `mapstructure:"original-url"` + + // Settings plugin settings only work with linterdb.PluginConstructor symbol. + Settings any } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/decorder.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/decorder.go index 9d492c4e8..5202a03a4 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/decorder.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/decorder.go @@ -15,14 +15,22 @@ func NewDecorder(settings *config.DecorderSettings) *goanalysis.Linter { // disable all rules/checks by default cfg := map[string]any{ + "ignore-underscore-vars": false, "disable-dec-num-check": true, + "disable-type-dec-num-check": false, + "disable-const-dec-num-check": false, + "disable-var-dec-num-check": false, "disable-dec-order-check": true, "disable-init-func-first-check": true, } if settings != nil { cfg["dec-order"] = strings.Join(settings.DecOrder, ",") + cfg["ignore-underscore-vars"] = settings.IgnoreUnderscoreVars cfg["disable-dec-num-check"] = settings.DisableDecNumCheck + cfg["disable-type-dec-num-check"] = settings.DisableTypeDecNumCheck + cfg["disable-const-dec-num-check"] = settings.DisableConstDecNumCheck + cfg["disable-var-dec-num-check"] = settings.DisableVarDecNumCheck cfg["disable-dec-order-check"] = settings.DisableDecOrderCheck cfg["disable-init-func-first-check"] = settings.DisableInitFuncFirstCheck } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/exhaustruct.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/exhaustruct.go index 37ea055d3..5272879e1 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/exhaustruct.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/exhaustruct.go @@ -1,7 +1,7 @@ package golinters import ( - "github.com/GaijinEntertainment/go-exhaustruct/v2/pkg/analyzer" + "github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer" "golang.org/x/tools/go/analysis" "github.com/golangci/golangci-lint/pkg/config" diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/funlen.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/funlen.go index aae1623c7..8def9c1f6 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/funlen.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/funlen.go @@ -52,7 +52,7 @@ func NewFunlen(settings *config.FunlenSettings) *goanalysis.Linter { func runFunlen(pass *analysis.Pass, settings *config.FunlenSettings) []goanalysis.Issue { var lintIssues []funlen.Message for _, file := range pass.Files { - fileIssues := funlen.Run(file, pass.Fset, settings.Lines, settings.Statements) + fileIssues := funlen.Run(file, pass.Fset, settings.Lines, settings.Statements, settings.IgnoreComments) lintIssues = append(lintIssues, fileIssues...) } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gci.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gci.go index 4eb26dbdf..386226769 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gci.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gci.go @@ -118,7 +118,7 @@ func diffFormattedFilesToArray(paths []string, cfg gcicfg.Config, diffs *[]strin log.InitLogger() defer func() { _ = log.L().Sync() }() - return gci.ProcessFiles(io.GoFilesInPathsGenerator(paths), cfg, func(filePath string, unmodifiedFile, formattedFile []byte) error { + return gci.ProcessFiles(io.GoFilesInPathsGenerator(paths, true), cfg, func(filePath string, unmodifiedFile, formattedFile []byte) error { fileURI := span.URIFromPath(filePath) edits := myers.ComputeEdits(fileURI, string(unmodifiedFile), string(formattedFile)) unifiedEdits := gotextdiff.ToUnified(filePath, filePath, string(unmodifiedFile), edits) diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/ginkgolinter.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/ginkgolinter.go index b9e69b265..7b8102b63 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/ginkgolinter.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/ginkgolinter.go @@ -19,6 +19,7 @@ func NewGinkgoLinter(cfg *config.GinkgoLinterSettings) *goanalysis.Linter { "suppress-err-assertion": cfg.SuppressErrAssertion, "suppress-compare-assertion": cfg.SuppressCompareAssertion, "suppress-async-assertion": cfg.SuppressAsyncAssertion, + "forbid-focus-container": cfg.ForbidFocusContainer, "allow-havelen-0": cfg.AllowHaveLenZero, } } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gocritic.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gocritic.go index 1319c72d9..ffac384ed 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gocritic.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gocritic.go @@ -283,6 +283,20 @@ func newGoCriticSettingsWrapper(settings *config.GoCriticSettings, logger loguti allCheckerMap[checkInfo.Name] = checkInfo } + if settings != nil && config.IsGreaterThanOrEqualGo121(settings.Go) { + var enabledChecks []string + for _, check := range settings.EnabledChecks { + if check == "ruleguard" { + logger.Warnf("%s: check %q is disabled for go1.21 https://github.com/golangci/golangci-lint/issues/3933", goCriticName, "ruleguard") + continue + } + + enabledChecks = append(enabledChecks, check) + } + + settings.EnabledChecks = enabledChecks + } + return &goCriticSettingsWrapper{ GoCriticSettings: settings, logger: logger, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gofmt_common.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gofmt_common.go index cbed4e0bc..6b7184d65 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gofmt_common.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gofmt_common.go @@ -80,6 +80,16 @@ func (p *hunkChangesParser) parseDiffLines(h *diffpkg.Hunk) { ret = append(ret, dl) } + // if > 0, then the original file had a 'No newline at end of file' mark + if h.OrigNoNewlineAt > 0 { + dl := diffLine{ + originalNumber: currentOriginalLineNumber + 1, + typ: diffLineAdded, + data: "", + } + ret = append(ret, dl) + } + p.lines = ret } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/govet.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/govet.go index 704dd6c57..6cd4c9b66 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/govet.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/govet.go @@ -14,6 +14,7 @@ import ( "golang.org/x/tools/go/analysis/passes/copylock" _ "golang.org/x/tools/go/analysis/passes/ctrlflow" // unused, internal analyzer "golang.org/x/tools/go/analysis/passes/deepequalerrors" + "golang.org/x/tools/go/analysis/passes/defers" "golang.org/x/tools/go/analysis/passes/errorsas" "golang.org/x/tools/go/analysis/passes/fieldalignment" "golang.org/x/tools/go/analysis/passes/findcall" @@ -60,6 +61,7 @@ var ( composite.Analyzer, copylock.Analyzer, deepequalerrors.Analyzer, + defers.Analyzer, errorsas.Analyzer, fieldalignment.Analyzer, findcall.Analyzer, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/paralleltest.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/paralleltest.go index 92201e4e2..4c03952c1 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/paralleltest.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/paralleltest.go @@ -9,13 +9,14 @@ import ( ) func NewParallelTest(settings *config.ParallelTestSettings) *goanalysis.Linter { - a := paralleltest.Analyzer + a := paralleltest.NewAnalyzer() var cfg map[string]map[string]any if settings != nil { cfg = map[string]map[string]any{ a.Name: { - "i": settings.IgnoreMissing, + "i": settings.IgnoreMissing, + "ignoremissingsubtests": settings.IgnoreMissingSubtests, }, } } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/tagalign.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/tagalign.go index 07b756464..c23838f70 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/tagalign.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/tagalign.go @@ -24,6 +24,11 @@ func NewTagAlign(settings *config.TagAlignSettings) *goanalysis.Linter { if settings.Sort || len(settings.Order) > 0 { options = append(options, tagalign.WithSort(settings.Order...)) } + + // Strict style will be applied only if Align and Sort are enabled together. + if settings.Strict && settings.Align && settings.Sort { + options = append(options, tagalign.WithStrictStyle()) + } } analyzer := tagalign.NewAnalyzer(options...) diff --git a/vendor/github.com/golangci/golangci-lint/pkg/lint/linter/config.go b/vendor/github.com/golangci/golangci-lint/pkg/lint/linter/config.go index 5891ec277..c911b5613 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/lint/linter/config.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/lint/linter/config.go @@ -42,6 +42,7 @@ type Config struct { AlternativeNames []string OriginalURL string // URL of original (not forked) repo, needed for autogenerated README + Internal bool // Internal linters cannot be disabled (ex: typecheck). CanAutoFix bool IsSlow bool DoesChangeTypes bool @@ -55,6 +56,11 @@ func (lc *Config) WithEnabledByDefault() *Config { return lc } +func (lc *Config) WithInternal() *Config { + lc.Internal = true + return lc +} + func (lc *Config) ConsiderSlow() *Config { lc.IsSlow = true return lc @@ -128,7 +134,7 @@ func (lc *Config) Name() string { } func (lc *Config) WithNoopFallback(cfg *config.Config) *Config { - if cfg != nil && config.IsGreaterThanOrEqualGo118(cfg.Run.Go) { + if cfg != nil && config.IsGreaterThanOrEqualGo121(cfg.Run.Go) { lc.Linter = &Noop{ name: lc.Linter.Name(), desc: lc.Linter.Desc(), diff --git a/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/custom_linters.go b/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/custom_linters.go new file mode 100644 index 000000000..bb1c61c05 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/custom_linters.go @@ -0,0 +1,127 @@ +package lintersdb + +import ( + "fmt" + "path/filepath" + "plugin" + + "github.com/hashicorp/go-multierror" + "github.com/spf13/viper" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/pkg/config" + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" + "github.com/golangci/golangci-lint/pkg/lint/linter" +) + +type AnalyzerPlugin interface { + GetAnalyzers() []*analysis.Analyzer +} + +// getCustomLinterConfigs loads private linters that are specified in the golangci config file. +func (m *Manager) getCustomLinterConfigs() []*linter.Config { + if m.cfg == nil || m.log == nil { + return nil + } + + var linters []*linter.Config + + for name, settings := range m.cfg.LintersSettings.Custom { + lc, err := m.loadCustomLinterConfig(name, settings) + if err != nil { + m.log.Errorf("Unable to load custom analyzer %s:%s, %v", name, settings.Path, err) + } else { + linters = append(linters, lc) + } + } + + return linters +} + +// loadCustomLinterConfig loads the configuration of private linters. +// Private linters are dynamically loaded from .so plugin files. +func (m *Manager) loadCustomLinterConfig(name string, settings config.CustomLinterSettings) (*linter.Config, error) { + analyzers, err := m.getAnalyzerPlugin(settings.Path, settings.Settings) + if err != nil { + return nil, err + } + + m.log.Infof("Loaded %s: %s", settings.Path, name) + + customLinter := goanalysis.NewLinter(name, settings.Description, analyzers, nil). + WithLoadMode(goanalysis.LoadModeTypesInfo) + + linterConfig := linter.NewConfig(customLinter). + WithEnabledByDefault(). + WithLoadForGoAnalysis(). + WithURL(settings.OriginalURL) + + return linterConfig, nil +} + +// getAnalyzerPlugin loads a private linter as specified in the config file, +// loads the plugin from a .so file, +// and returns the 'AnalyzerPlugin' interface implemented by the private plugin. +// An error is returned if the private linter cannot be loaded +// or the linter does not implement the AnalyzerPlugin interface. +func (m *Manager) getAnalyzerPlugin(path string, settings any) ([]*analysis.Analyzer, error) { + if !filepath.IsAbs(path) { + // resolve non-absolute paths relative to config file's directory + configFilePath := viper.ConfigFileUsed() + absConfigFilePath, err := filepath.Abs(configFilePath) + if err != nil { + return nil, fmt.Errorf("could not get absolute representation of config file path %q: %v", configFilePath, err) + } + path = filepath.Join(filepath.Dir(absConfigFilePath), path) + } + + plug, err := plugin.Open(path) + if err != nil { + return nil, err + } + + analyzers, err := m.lookupPlugin(plug, settings) + if err != nil { + return nil, fmt.Errorf("lookup plugin %s: %w", path, err) + } + + return analyzers, nil +} + +func (m *Manager) lookupPlugin(plug *plugin.Plugin, settings any) ([]*analysis.Analyzer, error) { + symbol, err := plug.Lookup("New") + if err != nil { + analyzers, errP := m.lookupAnalyzerPlugin(plug) + if errP != nil { + // TODO(ldez): use `errors.Join` when we will upgrade to go1.20. + return nil, multierror.Append(err, errP) + } + + return analyzers, nil + } + + // The type func cannot be used here, must be the explicit signature. + constructor, ok := symbol.(func(any) ([]*analysis.Analyzer, error)) + if !ok { + return nil, fmt.Errorf("plugin does not abide by 'New' function: %T", symbol) + } + + return constructor(settings) +} + +func (m *Manager) lookupAnalyzerPlugin(plug *plugin.Plugin) ([]*analysis.Analyzer, error) { + symbol, err := plug.Lookup("AnalyzerPlugin") + if err != nil { + return nil, err + } + + m.log.Warnf("plugin: 'AnalyzerPlugin' plugins are deprecated, please use the new plugin signature: " + + "https://golangci-lint.run/contributing/new-linters/#create-a-plugin") + + analyzerPlugin, ok := symbol.(AnalyzerPlugin) + if !ok { + return nil, fmt.Errorf("plugin does not abide by 'AnalyzerPlugin' interface: %T", symbol) + } + + return analyzerPlugin.GetAnalyzers(), nil +} diff --git a/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/enabled_set.go b/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/enabled_set.go index 92615b57a..c5c7874e4 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/enabled_set.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/enabled_set.go @@ -31,8 +31,10 @@ func NewEnabledSet(m *Manager, v *Validator, log logutils.Log, cfg *config.Confi } } +//nolint:gocyclo // the complexity cannot be reduced. func (es EnabledSet) build(lcfg *config.Linters, enabledByDefaultLinters []*linter.Config) map[string]*linter.Config { es.debugf("Linters config: %#v", lcfg) + resultLintersSet := map[string]*linter.Config{} switch { case len(lcfg.Presets) != 0: @@ -78,6 +80,14 @@ func (es EnabledSet) build(lcfg *config.Linters, enabledByDefaultLinters []*lint } } + // typecheck is not a real linter and cannot be disabled. + if _, ok := resultLintersSet["typecheck"]; !ok && (es.cfg == nil || !es.cfg.InternalCmdTest) { + for _, lc := range es.m.GetLinterConfigs("typecheck") { + // it's important to use lc.Name() nor name because name can be alias + resultLintersSet[lc.Name()] = lc + } + } + return resultLintersSet } @@ -134,8 +144,8 @@ func (es EnabledSet) GetOptimizedLinters() ([]*linter.Config, error) { func (es EnabledSet) combineGoAnalysisLinters(linters map[string]*linter.Config) { var goanalysisLinters []*goanalysis.Linter goanalysisPresets := map[string]bool{} - for _, linter := range linters { - lnt, ok := linter.Linter.(*goanalysis.Linter) + for _, lc := range linters { + lnt, ok := lc.Linter.(*goanalysis.Linter) if !ok { continue } @@ -144,7 +154,7 @@ func (es EnabledSet) combineGoAnalysisLinters(linters map[string]*linter.Config) continue } goanalysisLinters = append(goanalysisLinters, lnt) - for _, p := range linter.InPresets { + for _, p := range lc.InPresets { goanalysisPresets[p] = true } } @@ -197,6 +207,10 @@ func (es EnabledSet) combineGoAnalysisLinters(linters map[string]*linter.Config) func (es EnabledSet) verbosePrintLintersStatus(lcs map[string]*linter.Config) { var linterNames []string for _, lc := range lcs { + if lc.Internal { + continue + } + linterNames = append(linterNames, lc.Name()) } sort.StringSlice(linterNames).Sort() diff --git a/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/manager.go b/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/manager.go index 5a04fe193..4de3a1116 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/manager.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/manager.go @@ -1,29 +1,25 @@ package lintersdb import ( - "fmt" - "path/filepath" - "plugin" - - "github.com/spf13/viper" - "golang.org/x/tools/go/analysis" + "regexp" "github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/golinters" - "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" "github.com/golangci/golangci-lint/pkg/lint/linter" "github.com/golangci/golangci-lint/pkg/logutils" - "github.com/golangci/golangci-lint/pkg/report" ) type Manager struct { - nameToLCs map[string][]*linter.Config - cfg *config.Config - log logutils.Log + cfg *config.Config + log logutils.Log + + nameToLCs map[string][]*linter.Config + customLinters []*linter.Config } func NewManager(cfg *config.Config, log logutils.Log) *Manager { m := &Manager{cfg: cfg, log: log} + m.customLinters = m.getCustomLinterConfigs() nameToLCs := make(map[string][]*linter.Config) for _, lc := range m.GetAllSupportedLinterConfigs() { @@ -37,28 +33,6 @@ func NewManager(cfg *config.Config, log logutils.Log) *Manager { return m } -// WithCustomLinters loads private linters that are specified in the golangci config file. -func (m *Manager) WithCustomLinters() *Manager { - if m.log == nil { - m.log = report.NewLogWrapper(logutils.NewStderrLog(logutils.DebugKeyEmpty), &report.Data{}) - } - if m.cfg != nil { - for name, settings := range m.cfg.LintersSettings.Custom { - lc, err := m.loadCustomLinterConfig(name, settings) - - if err != nil { - m.log.Errorf("Unable to load custom analyzer %s:%s, %v", - name, - settings.Path, - err) - } else { - m.nameToLCs[name] = append(m.nameToLCs[name], lc) - } - } - } - return m -} - func (Manager) AllPresets() []string { return []string{ linter.PresetBugs, @@ -255,7 +229,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { } if gocriticCfg != nil { - gocriticCfg.Go = m.cfg.Run.Go + gocriticCfg.Go = trimGoVersion(m.cfg.Run.Go) } if gofumptCfg != nil && gofumptCfg.LangVersion == "" { @@ -263,24 +237,27 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { } if staticcheckCfg != nil && staticcheckCfg.GoVersion == "" { - staticcheckCfg.GoVersion = m.cfg.Run.Go + staticcheckCfg.GoVersion = trimGoVersion(m.cfg.Run.Go) } if gosimpleCfg != nil && gosimpleCfg.GoVersion == "" { - gosimpleCfg.GoVersion = m.cfg.Run.Go + gosimpleCfg.GoVersion = trimGoVersion(m.cfg.Run.Go) } if stylecheckCfg != nil && stylecheckCfg.GoVersion != "" { - stylecheckCfg.GoVersion = m.cfg.Run.Go + stylecheckCfg.GoVersion = trimGoVersion(m.cfg.Run.Go) } if unusedCfg != nil && unusedCfg.GoVersion == "" { - unusedCfg.GoVersion = m.cfg.Run.Go + unusedCfg.GoVersion = trimGoVersion(m.cfg.Run.Go) } } const megacheckName = "megacheck" + var linters []*linter.Config + linters = append(linters, m.customLinters...) + // The linters are sorted in the alphabetical order (case-insensitive). // When a new linter is added the version in `WithSince(...)` must be the next minor version of golangci-lint. - return []*linter.Config{ + linters = append(linters, linter.NewConfig(golinters.NewAsasalint(asasalintCfg)). WithSince("1.47.0"). WithPresets(linter.PresetBugs). @@ -829,6 +806,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithURL("https://github.com/moricho/tparallel"), linter.NewConfig(golinters.NewTypecheck()). + WithInternal(). WithEnabledByDefault(). WithSince("v1.3.0"). WithLoadForGoAnalysis(). @@ -898,18 +876,20 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithPresets(linter.PresetStyle). WithURL("https://github.com/bombsimon/wsl"), + linter.NewConfig(golinters.NewZerologLint()). + WithSince("v1.53.0"). + WithPresets(linter.PresetBugs). + WithLoadForGoAnalysis(). + WithURL("https://github.com/ykadowak/zerologlint"), + // nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives linter.NewConfig(golinters.NewNoLintLint(noLintLintCfg)). WithSince("v1.26.0"). WithPresets(linter.PresetStyle). WithURL("https://github.com/golangci/golangci-lint/blob/master/pkg/golinters/nolintlint/README.md"), + ) - linter.NewConfig(golinters.NewZerologLint()). - WithSince("v1.53.0"). - WithPresets(linter.PresetBugs). - WithLoadForGoAnalysis(). - WithURL("https://github.com/ykadowak/zerologlint"), - } + return linters } func (m Manager) GetAllEnabledByDefaultLinters() []*linter.Config { @@ -951,62 +931,20 @@ func (m Manager) GetAllLinterConfigsForPreset(p string) []*linter.Config { return ret } -// loadCustomLinterConfig loads the configuration of private linters. -// Private linters are dynamically loaded from .so plugin files. -func (m Manager) loadCustomLinterConfig(name string, settings config.CustomLinterSettings) (*linter.Config, error) { - analyzer, err := m.getAnalyzerPlugin(settings.Path) - if err != nil { - return nil, err - } - m.log.Infof("Loaded %s: %s", settings.Path, name) - customLinter := goanalysis.NewLinter( - name, - settings.Description, - analyzer.GetAnalyzers(), - nil).WithLoadMode(goanalysis.LoadModeTypesInfo) - - linterConfig := linter.NewConfig(customLinter). - WithEnabledByDefault(). - WithLoadForGoAnalysis(). - WithURL(settings.OriginalURL) - - return linterConfig, nil -} - -type AnalyzerPlugin interface { - GetAnalyzers() []*analysis.Analyzer -} - -// getAnalyzerPlugin loads a private linter as specified in the config file, -// loads the plugin from a .so file, and returns the 'AnalyzerPlugin' interface -// implemented by the private plugin. -// An error is returned if the private linter cannot be loaded or the linter -// does not implement the AnalyzerPlugin interface. -func (m Manager) getAnalyzerPlugin(path string) (AnalyzerPlugin, error) { - if !filepath.IsAbs(path) { - // resolve non-absolute paths relative to config file's directory - configFilePath := viper.ConfigFileUsed() - absConfigFilePath, err := filepath.Abs(configFilePath) - if err != nil { - return nil, fmt.Errorf("could not get absolute representation of config file path %q: %v", configFilePath, err) - } - path = filepath.Join(filepath.Dir(absConfigFilePath), path) - } - - plug, err := plugin.Open(path) - if err != nil { - return nil, err +// Trims the Go version to keep only M.m. +// Since Go 1.21 the version inside the go.mod can be a patched version (ex: 1.21.0). +// https://go.dev/doc/toolchain#versions +// This a problem with staticcheck and gocritic. +func trimGoVersion(v string) string { + if v == "" { + return "" } - symbol, err := plug.Lookup("AnalyzerPlugin") - if err != nil { - return nil, err - } + exp := regexp.MustCompile(`(\d\.\d+)\.\d+`) - analyzerPlugin, ok := symbol.(AnalyzerPlugin) - if !ok { - return nil, fmt.Errorf("plugin %s does not abide by 'AnalyzerPlugin' interface", path) + if exp.MatchString(v) { + return exp.FindStringSubmatch(v)[1] } - return analyzerPlugin, nil + return v } diff --git a/vendor/github.com/golangci/misspell/.golangci.yml b/vendor/github.com/golangci/misspell/.golangci.yml index 1a53216ae..31c566eab 100644 --- a/vendor/github.com/golangci/misspell/.golangci.yml +++ b/vendor/github.com/golangci/misspell/.golangci.yml @@ -21,10 +21,13 @@ linters-settings: gofumpt: extra-rules: true depguard: - list-type: blacklist - include-go-root: false - packages: - - github.com/pkg/errors + rules: + main: + deny: + - pkg: "github.com/instana/testify" + desc: not allowed + - pkg: "github.com/pkg/errors" + desc: Should be replaced by standard lib errors package godox: keywords: - FIXME diff --git a/vendor/github.com/golangci/misspell/Dockerfile b/vendor/github.com/golangci/misspell/Dockerfile index ce55fe61b..788ce3a77 100644 --- a/vendor/github.com/golangci/misspell/Dockerfile +++ b/vendor/github.com/golangci/misspell/Dockerfile @@ -1,7 +1,7 @@ -FROM golang:1.10.0-alpine +FROM golang:1.19-alpine # cache buster -RUN echo 4 +RUN echo 4 # git is needed for "go get" below RUN apk add --no-cache git make @@ -32,3 +32,4 @@ RUN /bin/true \ && wget -O /scowl-wl/words-US-60.txt ${SOURCE_US} \ && wget -O /scowl-wl/words-GB-ise-60.txt ${SOURCE_GB_ISE} +RUN git config --global --add safe.directory "/go/src/github.com/golangci/misspell" diff --git a/vendor/github.com/golangci/misspell/Makefile b/vendor/github.com/golangci/misspell/Makefile index e64a84b55..783f977cb 100644 --- a/vendor/github.com/golangci/misspell/Makefile +++ b/vendor/github.com/golangci/misspell/Makefile @@ -1,11 +1,11 @@ -CONTAINER=nickg/misspell +CONTAINER=golangci/misspell default: lint test build install: ## install misspell into GOPATH/bin go install ./cmd/misspell -build: ## build and lint misspell +build: ## build misspell go build ./cmd/misspell test: ## run all tests @@ -14,10 +14,6 @@ test: ## run all tests lint: ## run linter golangci-lint run -# real publishing is done only by travis -publish: ## test goreleaser - ./scripts/goreleaser-dryrun.sh - # the grep in line 2 is to remove misspellings in the spelling dictionary # that trigger false positives!! falsepositives: /scowl-wl @@ -42,19 +38,16 @@ clean: ## clean up time go clean ./... git gc --aggressive -ci: ## run test like travis-ci does, requires docker +ci: docker-build ## run test like travis-ci does, requires docker docker run --rm \ -v $(PWD):/go/src/github.com/golangci/misspell \ -w /go/src/github.com/golangci/misspell \ ${CONTAINER} \ - make build falsepositives + make install falsepositives docker-build: ## build a docker test image docker build -t ${CONTAINER} . -docker-pull: ## pull latest test image - docker pull ${CONTAINER} - docker-console: ## log into the test image docker run --rm -it \ -v $(PWD):/go/src/github.com/golangci/misspell \ diff --git a/vendor/github.com/golangci/misspell/case.go b/vendor/github.com/golangci/misspell/case.go index 88ad44fa8..0b580bedb 100644 --- a/vendor/github.com/golangci/misspell/case.go +++ b/vendor/github.com/golangci/misspell/case.go @@ -43,9 +43,9 @@ func CaseStyle(word string) WordCase { } // CaseVariations returns: -// If AllUpper or First-Letter-Only is upcased: add the all upper case version. -// If AllLower, add the original, the title and upcase forms. -// If Mixed, return the original, and the all upcase form. +// If AllUpper or First-Letter-Only is upper-cased: add the all upper case version. +// If AllLower, add the original, the title and upper-case forms. +// If Mixed, return the original, and the all upper-case form. func CaseVariations(word string, style WordCase) []string { switch style { case CaseLower: diff --git a/vendor/github.com/golangci/misspell/goreleaser.yml b/vendor/github.com/golangci/misspell/goreleaser.yml index b4c8c099c..97aa83e5a 100644 --- a/vendor/github.com/golangci/misspell/goreleaser.yml +++ b/vendor/github.com/golangci/misspell/goreleaser.yml @@ -1,8 +1,7 @@ project_name: misspell builds: - - - main: cmd/misspell/main.go + - main: cmd/misspell/main.go binary: misspell ldflags: -s -w -X main.version={{.Version}} goos: diff --git a/vendor/github.com/golangci/misspell/install-misspell.sh b/vendor/github.com/golangci/misspell/install-misspell.sh index e24a84a20..51e9b3372 100644 --- a/vendor/github.com/golangci/misspell/install-misspell.sh +++ b/vendor/github.com/golangci/misspell/install-misspell.sh @@ -6,12 +6,12 @@ set -e usage() { this=$1 cat < 1 && len(positionOfTestRunNode) > 0 { for _, n := range positionOfTestRunNode { - pass.Reportf(n.Pos(), "Function %s has missing the call to method parallel in the test run\n", funcDecl.Name.Name) + pass.Reportf(n.Pos(), "Function %s missing the call to method parallel in the test run\n", funcDecl.Name.Name) } } } diff --git a/vendor/github.com/nunnatsa/ginkgolinter/.golangci.yml b/vendor/github.com/nunnatsa/ginkgolinter/.golangci.yml new file mode 100644 index 000000000..71ff0d034 --- /dev/null +++ b/vendor/github.com/nunnatsa/ginkgolinter/.golangci.yml @@ -0,0 +1,3 @@ +linters: + enable: + - revive diff --git a/vendor/github.com/nunnatsa/ginkgolinter/README.md b/vendor/github.com/nunnatsa/ginkgolinter/README.md index e0a4b0739..3edf065c2 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/README.md +++ b/vendor/github.com/nunnatsa/ginkgolinter/README.md @@ -27,7 +27,7 @@ ginkgolinter [-fix] ./... Use the `-fix` flag to apply the fix suggestions to the source code. ### Use ginkgolinter with golangci-lint -The ginkgolinter is now part of the popular [golangci-lint](https://golangci-lint.run/), starting from version `v0.51.1`. +The ginkgolinter is now part of the popular [golangci-lint](https://golangci-lint.run/), starting from version `v1.51.1`. It is not enabled by default, though. There are two ways to run ginkgolinter with golangci-lint: @@ -45,7 +45,8 @@ It is not enabled by default, though. There are two ways to run ginkgolinter wit - ginkgolinter ``` ## Linter Checks -The linter checks the gomega assertions in golang test code. Gomega may be used together with ginkgo tests, For example: +The linter checks the ginkgo and gomega assertions in golang test code. Gomega may be used together with ginkgo tests, +For example: ```go It("should test something", func() { // It is ginkgo test case function Expect("abcd").To(HaveLen(4), "the string should have a length of 4") // Expect is the gomega assertion @@ -71,7 +72,107 @@ The linter checks the `Expect`, `ExpectWithOffset` and the `Ω` "actual" functio It also supports the embedded `Not()` matcher -### Wrong Length Assertion +Some checks find actual bugs, and some are more for style. + +### Using a function call in async assertion [BUG] +This rule finds an actual bug in tests, where asserting a function call in an async function; e.g. `Eventually`. For +example: +```go +func slowInt(int val) int { + time.Sleep(time.Second) + return val +} + +... + +It("should test that slowInt returns 42, eventually", func() { + Eventually(slowInt(42)).WithPolling(time.Millisecond * 100).WithTimeout(time.Second * 2).Equal(42) +}) +``` +The problem with the above code is that it **should** poll - call the function - until it returns 42, but what actually +happens is that first the function is called, and we pass `42` to `Eventually` - not the function. This is not what we +tried to do here. + +The linter will suggest replacing this code by: +```go +It("should test that slowInt returns 42, eventually", func() { + Eventually(slowInt).WithArguments(42).WithPolling(time.Millisecond * 100).WithTimeout(time.Second * 2).Equal(42) +}) +``` + +The linter suggested replacing the function call by the function name. + +If function arguments are used, the linter will add the `WithArguments()` method to pass them. + +Please notice that `WithArguments()` is only supported from gomenga v1.22.0. + +When using an older version of gomega, change the code manually. For example: + +```go +It("should test that slowInt returns 42, eventually", func() { + Eventually(func() int { + slowint(42) + }).WithPolling(time.Millisecond * 100).WithTimeout(time.Second * 2).Equal(42) +}) +``` + +### Comparing a pointer with a value [BUG] +The linter warns when comparing a pointer with a value. +These comparisons are always wrong and will always fail. + +In case of a positive assertion (`To()` or `Should()`), the test will just fail. + +But the main concern is for false positive tests, when using a negative assertion (`NotTo()`, `ToNot()`, `ShouldNot()`, +`Should(Not())` etc.); e.g. +```go +num := 5 +... +pNum := &num +... +Expect(pNum).ShouldNot(Equal(6)) +``` +This assertion will pass, but for the wrong reasons: pNum is not equal 6, not because num == 5, but because pNum is +a pointer, while `6` is an `int`. + +In the case above, the linter will suggest `Expect(pNum).ShouldNot(HaveValue(Equal(6)))` + +This is also right for additional matchers: `BeTrue()` and `BeFalse()`, `BeIdenticalTo()`, `BeEquivalentTo()` +and `BeNumerically`. + +### Missing Assertion Method [BUG] +The linter warns when calling an "actual" method (e.g. `Expect()`, `Eventually()` etc.), without an assertion method (e.g +`Should()`, `NotTo()` etc.) + +For example: +```go +// no assertion for the result +Eventually(doSomething).WithTimeout(time.Seconds * 5).WithPolling(time.Milliseconds * 100) +``` + +The linter will not suggest a fix for this warning. + +This rule cannot be suppressed. + +### Focus Container Found [BUG] +This rule finds ginkgo focus containers in the code. + +ginkgo supports the `FDescribe`, `FContext`, `FWhen` and `FIt` containers to allow the developer to focus +on a specific test or set of tests during test development or debug. + +***This rule is disabled by default***. Use the `--forbid-focus-container=true` command line flag to enable it. + +For example: +```go +var _ = Describe("checking something", func() { + FIt("this test is the only one that will run", func(){ + ... + }) +}) +``` + +These container must not be part of the final source code, and should only be used locally by the developer. + +### Wrong Length Assertion [STYLE] The linter finds assertion of the golang built-in `len` function, with all kind of matchers, while there are already gomega matchers for these usecases; We want to assert the item, rather than its length. There are several wrong patterns: @@ -102,10 +203,10 @@ The output of the linter,when finding issues, looks like this: ./testdata/src/a/a.go:18:5: ginkgo-linter: wrong length assertion; consider using `Expect("").Should(BeEmpty())` instead ./testdata/src/a/a.go:22:5: ginkgo-linter: wrong length assertion; consider using `Expect("").Should(BeEmpty())` instead ``` -#### use the `HaveLen(0)` matcher. +#### use the `HaveLen(0)` matcher. [STYLE] The linter will also warn about the `HaveLen(0)` matcher, and will suggest to replace it with `BeEmpty()` -### Wrong `nil` Assertion +### Wrong `nil` Assertion [STYLE] The linter finds assertion of the comparison to nil, with all kind of matchers, instead of using the existing `BeNil()` matcher; We want to assert the item, rather than a comparison result. There are several wrong patterns: @@ -127,7 +228,7 @@ Or even (double negative): `Ω(x != nil).Should(Not(BeTrue()))` => `Ω(x).Should(BeNil())` -### Wrong boolean Assertion +### Wrong boolean Assertion [STYLE] The linter finds assertion using the `Equal` method, with the values of to `true` or `false`, instead of using the existing `BeTrue()` or `BeFalse()` matcher. @@ -141,7 +242,7 @@ It also supports the embedded `Not()` matcher; e.g. `Ω(x).Should(Not(Equal(True)))` => `Ω(x).ShouldNot(BeTrue())` -### Wrong Error Assertion +### Wrong Error Assertion [STYLE] The linter finds assertion of errors compared with nil, or to be equal nil, or to be nil. The linter suggests to use `Succeed` for functions or `HaveOccurred` for error values.. There are several wrong patterns: @@ -159,7 +260,7 @@ It also supports the embedded `Not()` matcher; e.g. `Ω(err == nil).Should(Not(BeTrue()))` => `Ω(x).Should(HaveOccurred())` -### Wrong Comparison Assertion +### Wrong Comparison Assertion [STYLE] The linter finds assertion of boolean comparisons, which are already supported by existing gomega matchers. The linter assumes that when compared something to literals or constants, these values should be used for the assertion, @@ -198,85 +299,6 @@ Expect(x1 == c1).Should(BeTrue()) // ==> Expect(x1).Should(Equal(c1)) Expect(c1 == x1).Should(BeTrue()) // ==> Expect(x1).Should(Equal(c1)) ``` -### Using a function call in async assertion -This rule finds an actual bug in tests, where asserting a function call in an async function; e.g. `Eventually`. For -example: -```go -func slowInt(int val) int { - time.Sleep(time.Second) - return val -} - -... - -It("should test that slowInt returns 42, eventually", func() { - Eventually(slowInt(42)).WithPolling(time.Millisecond * 100).WithTimeout(time.Second * 2).Equal(42) -}) -``` -The problem with the above code is that it **should** poll - call the function - until it returns 42, but what actually -happens is that first the function is called, and we pass `42` to `Eventually` - not the function. This is not what we -tried to do here. - -The linter will suggest replacing this code by: -```go -It("should test that slowInt returns 42, eventually", func() { - Eventually(slowInt).WithArguments(42).WithPolling(time.Millisecond * 100).WithTimeout(time.Second * 2).Equal(42) -}) -``` - -The linter suggested replacing the function call by the function name. - -If function arguments are used, the linter will add the `WithArguments()` method to pass them. - -Please notice that `WithArguments()` is only supported from gomenga v1.22.0. - -When using an older version of gomega, change the code manually. For example: - -```go -It("should test that slowInt returns 42, eventually", func() { - Eventually(func() int { - slowint(42) - }).WithPolling(time.Millisecond * 100).WithTimeout(time.Second * 2).Equal(42) -}) -``` - -### Comparing a pointer with a value -The linter warns when comparing a pointer with a value. -These comparisons are always wrong and will always fail. - -In case of a positive assertion (`To()` or `Should()`), the test will just fail. - -But the main concern is for false positive tests, when using a negative assertion (`NotTo()`, `ToNot()`, `ShouldNot()`, -`Should(Not())` etc.); e.g. -```go -num := 5 -... -pNum := &num -... -Expect(pNum).ShouldNot(Equal(6)) -``` -This assertion will pass, but for the wrong reasons: pNum is not equal 6, not because num == 5, but because pNum is -a pointer, while `6` is an `int`. - -In the case above, the linter will suggest `Expect(pNum).ShouldNot(HaveValue(Equal(6)))` - -This is also right for additional matchers: `BeTrue()` and `BeFalse()`, `BeIdenticalTo()`, `BeEquivalentTo()` -and `BeNumerically`. - -### Missing Assertion Method -The linter warns when calling an "actual" method (e.g. `Expect()`, `Eventually()` etc.), without an assertion method (e.g -`Should()`, `NotTo()` etc.) - -For example: -```go -// no assertion for the result -Eventually(doSomething).WithTimeout(time.Seconds * 5).WithPolling(time.Milliseconds * 100) -``` - -The linter will not suggest a fix for this warning. - -This rule cannot be suppressed. - ## Suppress the linter ### Suppress warning from command line * Use the `--suppress-len-assertion=true` flag to suppress the wrong length assertion warning @@ -308,6 +330,17 @@ To suppress the wrong async assertion warning, add a comment with (only) `ginkgo-linter:ignore-async-assert-warning`. +To supress the focus container warning, add a comment with (only) + +`ginkgo-linter:ignore-focus-container-warning` + +Notice that this comment will not work for an anonymous variable container like +```go +// ginkgo-linter:ignore-focus-container-warning (not working!!) +var _ = FDescribe(...) +``` +In this case, use the file comment (see bellow). + There are two options to use these comments: 1. If the comment is at the top of the file, supress the warning for the whole file; e.g.: ```go diff --git a/vendor/github.com/nunnatsa/ginkgolinter/ginkgo_linter.go b/vendor/github.com/nunnatsa/ginkgolinter/ginkgo_linter.go index 2ae6b7751..1635ce4b0 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/ginkgo_linter.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/ginkgo_linter.go @@ -4,7 +4,6 @@ import ( "bytes" "flag" "fmt" - "github.com/nunnatsa/ginkgolinter/version" "go/ast" "go/constant" "go/printer" @@ -14,9 +13,11 @@ import ( "github.com/go-toolsmith/astcopy" "golang.org/x/tools/go/analysis" + "github.com/nunnatsa/ginkgolinter/ginkgohandler" "github.com/nunnatsa/ginkgolinter/gomegahandler" "github.com/nunnatsa/ginkgolinter/reverseassertion" "github.com/nunnatsa/ginkgolinter/types" + "github.com/nunnatsa/ginkgolinter/version" ) // The ginkgolinter enforces standards of using ginkgo and gomega. @@ -35,6 +36,7 @@ const ( comparePointerToValue = linterName + ": comparing a pointer to a value will always fail. consider using `%s` instead" missingAssertionMessage = linterName + `: %q: missing assertion method. Expected "Should()", "To()", "ShouldNot()", "ToNot()" or "NotTo()"` missingAsyncAssertionMessage = linterName + `: %q: missing assertion method. Expected "Should()" or "ShouldNot()"` + focusContainerFound = linterName + ": Focus container found. This is used only for local debug and should not be part of the actual source code, consider to replace with %q" ) const ( // gomega matchers beEmpty = "BeEmpty" @@ -78,6 +80,7 @@ func NewAnalyzer() *analysis.Analyzer { SuppressNil: false, SuppressErr: false, SuppressCompare: false, + ForbidFocus: false, AllowHaveLen0: false, }, } @@ -88,6 +91,7 @@ func NewAnalyzer() *analysis.Analyzer { Run: linter.run, } + var ignored bool a.Flags.Init("ginkgolinter", flag.ExitOnError) a.Flags.Var(&linter.config.SuppressLen, "suppress-len-assertion", "Suppress warning for wrong length assertions") a.Flags.Var(&linter.config.SuppressNil, "suppress-nil-assertion", "Suppress warning for wrong nil assertions") @@ -96,6 +100,9 @@ func NewAnalyzer() *analysis.Analyzer { a.Flags.Var(&linter.config.SuppressAsync, "suppress-async-assertion", "Suppress warning for function call in async assertion, like Eventually") a.Flags.Var(&linter.config.AllowHaveLen0, "allow-havelen-0", "Do not warn for HaveLen(0); default = false") + a.Flags.BoolVar(&ignored, "suppress-focus-container", true, "Suppress warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt. Deprecated and ignored: use --forbid-focus-container instead") + a.Flags.Var(&linter.config.ForbidFocus, "forbid-focus-container", "trigger a warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt; default = false.") + return a } @@ -116,6 +123,8 @@ currently, the linter searches for following: * trigger a warning for missing assertion method: [Bug] Eventually(checkSomething) +* trigger a warning when a ginkgo focus container (FDescribe, FContext, FWhen or FIt) is found. [Bug] + * wrong length assertions. We want to assert the item rather than its length. [Style] For example: Expect(len(x)).Should(Equal(1)) @@ -152,12 +161,27 @@ func (l *ginkgoLinter) run(pass *analysis.Pass) (interface{}, error) { fileConfig.UpdateFromFile(cm) - handler := gomegahandler.GetGomegaHandler(file) - if handler == nil { // no gomega import => no use in gomega in this file; nothing to do here + gomegaHndlr := gomegahandler.GetGomegaHandler(file) + ginkgoHndlr := ginkgohandler.GetGinkgoHandler(file) + + if gomegaHndlr == nil && ginkgoHndlr == nil { // no gomega or ginkgo imports => no use in gomega in this file; nothing to do here continue } ast.Inspect(file, func(n ast.Node) bool { + if ginkgoHndlr != nil && fileConfig.ForbidFocus { + spec, ok := n.(*ast.ValueSpec) + if ok { + for _, val := range spec.Values { + if exp, ok := val.(*ast.CallExpr); ok { + if checkFocusContainer(pass, ginkgoHndlr, exp) { + return true + } + } + } + } + } + stmt, ok := n.(*ast.ExprStmt) if !ok { return true @@ -175,28 +199,47 @@ func (l *ginkgoLinter) run(pass *analysis.Pass) (interface{}, error) { return true } + if ginkgoHndlr != nil && bool(config.ForbidFocus) && checkFocusContainer(pass, ginkgoHndlr, assertionExp) { + return true + } + + // no more ginkgo checks. From here it's only gomega. So if there is no gomega handler, exit here. This is + // mostly to prevent nil pointer error. + if gomegaHndlr == nil { + return true + } + assertionFunc, ok := assertionExp.Fun.(*ast.SelectorExpr) if !ok { - checkNoAssertion(pass, assertionExp, handler) + checkNoAssertion(pass, assertionExp, gomegaHndlr) return true } if !isAssertionFunc(assertionFunc.Sel.Name) { - checkNoAssertion(pass, assertionExp, handler) + checkNoAssertion(pass, assertionExp, gomegaHndlr) return true } - actualExpr := handler.GetActualExpr(assertionFunc) + actualExpr := gomegaHndlr.GetActualExpr(assertionFunc) if actualExpr == nil { return true } - return checkExpression(pass, config, assertionExp, actualExpr, handler) + return checkExpression(pass, config, assertionExp, actualExpr, gomegaHndlr) }) } return nil, nil } +func checkFocusContainer(pass *analysis.Pass, ginkgoHndlr ginkgohandler.Handler, exp *ast.CallExpr) bool { + isFocus, id := ginkgoHndlr.GetFocusContainerName(exp) + if isFocus { + reportNewName(pass, id, id.Name[1:], focusContainerFound, id.Name) + return true + } + return false +} + func checkExpression(pass *analysis.Pass, config types.Config, assertionExp *ast.CallExpr, actualExpr *ast.CallExpr, handler gomegahandler.Handler) bool { expr := astcopy.CallExpr(assertionExp) oldExpr := goFmt(pass.Fset, expr) @@ -333,10 +376,7 @@ func checkAsyncAssertion(pass *analysis.Pass, config types.Config, expr *ast.Cal if len(actualExpr.Args) > funcIndex { if fun, funcCall := actualExpr.Args[funcIndex].(*ast.CallExpr); funcCall { t = pass.TypesInfo.TypeOf(fun) - switch t.(type) { - // allow functions that return function or channel. - case *gotypes.Signature, *gotypes.Chan, *gotypes.Pointer: - default: + if !isValidAsyncValueType(t) { actualExpr = handler.GetActualExpr(expr.Fun.(*ast.SelectorExpr)) if len(fun.Args) > 0 { @@ -371,6 +411,18 @@ func checkAsyncAssertion(pass *analysis.Pass, config types.Config, expr *ast.Cal return true } +func isValidAsyncValueType(t gotypes.Type) bool { + switch t.(type) { + // allow functions that return function or channel. + case *gotypes.Signature, *gotypes.Chan, *gotypes.Pointer: + return true + case *gotypes.Named: + return isValidAsyncValueType(t.Underlying()) + } + + return false +} + func startCheckComparison(exp *ast.CallExpr, handler gomegahandler.Handler) (*ast.CallExpr, bool) { matcher, ok := exp.Args[0].(*ast.CallExpr) if !ok { @@ -959,7 +1011,7 @@ func reportNilAssertion(pass *analysis.Pass, expr *ast.CallExpr, handler gomegah report(pass, expr, template, oldExpr) } -func report(pass *analysis.Pass, expr *ast.CallExpr, messageTemplate, oldExpr string) { +func report(pass *analysis.Pass, expr ast.Expr, messageTemplate, oldExpr string) { newExp := goFmt(pass.Fset, expr) pass.Report(analysis.Diagnostic{ Pos: expr.Pos(), @@ -979,6 +1031,25 @@ func report(pass *analysis.Pass, expr *ast.CallExpr, messageTemplate, oldExpr st }) } +func reportNewName(pass *analysis.Pass, id *ast.Ident, newName string, messageTemplate, oldExpr string) { + pass.Report(analysis.Diagnostic{ + Pos: id.Pos(), + Message: fmt.Sprintf(messageTemplate, newName), + SuggestedFixes: []analysis.SuggestedFix{ + { + Message: fmt.Sprintf("should replace %s with %s", oldExpr, newName), + TextEdits: []analysis.TextEdit{ + { + Pos: id.Pos(), + End: id.End(), + NewText: []byte(newName), + }, + }, + }, + }, + }) +} + func reportNoFix(pass *analysis.Pass, pos token.Pos, message string, args ...any) { if len(args) > 0 { message = fmt.Sprintf(message, args...) diff --git a/vendor/github.com/nunnatsa/ginkgolinter/ginkgohandler/handler.go b/vendor/github.com/nunnatsa/ginkgolinter/ginkgohandler/handler.go new file mode 100644 index 000000000..87703a944 --- /dev/null +++ b/vendor/github.com/nunnatsa/ginkgolinter/ginkgohandler/handler.go @@ -0,0 +1,66 @@ +package ginkgohandler + +import ( + "go/ast" +) + +// Handler provide different handling, depend on the way ginkgo was imported, whether +// in imported with "." name, custom name or without any name. +type Handler interface { + GetFocusContainerName(*ast.CallExpr) (bool, *ast.Ident) +} + +// GetGinkgoHandler returns a ginkgor handler according to the way ginkgo was imported in the specific file +func GetGinkgoHandler(file *ast.File) Handler { + for _, imp := range file.Imports { + if imp.Path.Value != `"github.com/onsi/ginkgo"` && imp.Path.Value != `"github.com/onsi/ginkgo/v2"` { + continue + } + + switch name := imp.Name.String(); { + case name == ".": + return dotHandler{} + case name == "": // import with no local name + return nameHandler("ginkgo") + default: + return nameHandler(name) + } + } + + return nil // no ginkgo import; this file does not use ginkgo +} + +// dotHandler is used when importing ginkgo with dot; i.e. +// import . "github.com/onsi/ginkgo" +type dotHandler struct{} + +func (h dotHandler) GetFocusContainerName(exp *ast.CallExpr) (bool, *ast.Ident) { + if fun, ok := exp.Fun.(*ast.Ident); ok { + return isFocusContainer(fun.Name), fun + } + return false, nil +} + +// nameHandler is used when importing ginkgo without name; i.e. +// import "github.com/onsi/ginkgo" +// +// or with a custom name; e.g. +// import customname "github.com/onsi/ginkgo" +type nameHandler string + +func (h nameHandler) GetFocusContainerName(exp *ast.CallExpr) (bool, *ast.Ident) { + if sel, ok := exp.Fun.(*ast.SelectorExpr); ok { + if id, ok := sel.X.(*ast.Ident); ok && id.Name == string(h) { + return isFocusContainer(sel.Sel.Name), sel.Sel + } + } + return false, nil +} + +func isFocusContainer(name string) bool { + switch name { + case "FDescribe", "FContext", "FWhen", "FIt": + return true + } + return false +} diff --git a/vendor/github.com/nunnatsa/ginkgolinter/gomegahandler/handler.go b/vendor/github.com/nunnatsa/ginkgolinter/gomegahandler/handler.go index 1e8765148..0c34cb7c1 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/gomegahandler/handler.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/gomegahandler/handler.go @@ -52,11 +52,12 @@ func (h dotHandler) GetActualFuncName(expr *ast.CallExpr) (string, bool) { case *ast.SelectorExpr: if isGomegaVar(actualFunc.X, h) { return actualFunc.Sel.Name, true - } else { - if x, ok := actualFunc.X.(*ast.CallExpr); ok { - return h.GetActualFuncName(x) - } } + + if x, ok := actualFunc.X.(*ast.CallExpr); ok { + return h.GetActualFuncName(x) + } + case *ast.CallExpr: return h.GetActualFuncName(actualFunc) } diff --git a/vendor/github.com/nunnatsa/ginkgolinter/types/config.go b/vendor/github.com/nunnatsa/ginkgolinter/types/config.go index 43145d847..6d7a09914 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/types/config.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/types/config.go @@ -13,6 +13,7 @@ const ( suppressErrAssertionWarning = suppressPrefix + "ignore-err-assert-warning" suppressCompareAssertionWarning = suppressPrefix + "ignore-compare-assert-warning" suppressAsyncAsertWarning = suppressPrefix + "ignore-async-assert-warning" + suppressFocusContainerWarning = suppressPrefix + "ignore-focus-container-warning" ) type Config struct { @@ -21,11 +22,12 @@ type Config struct { SuppressErr Boolean SuppressCompare Boolean SuppressAsync Boolean + ForbidFocus Boolean AllowHaveLen0 Boolean } func (s *Config) AllTrue() bool { - return bool(s.SuppressLen && s.SuppressNil && s.SuppressErr && s.SuppressCompare && s.SuppressAsync) + return bool(s.SuppressLen && s.SuppressNil && s.SuppressErr && s.SuppressCompare && s.SuppressAsync && !s.ForbidFocus) } func (s *Config) Clone() Config { @@ -35,6 +37,7 @@ func (s *Config) Clone() Config { SuppressErr: s.SuppressErr, SuppressCompare: s.SuppressCompare, SuppressAsync: s.SuppressAsync, + ForbidFocus: s.ForbidFocus, AllowHaveLen0: s.AllowHaveLen0, } } @@ -53,11 +56,20 @@ func (s *Config) UpdateFromComment(commentGroup []*ast.CommentGroup) { comment = strings.TrimSuffix(comment, "*/") comment = strings.TrimSpace(comment) - s.SuppressLen = s.SuppressLen || (comment == suppressLengthAssertionWarning) - s.SuppressNil = s.SuppressNil || (comment == suppressNilAssertionWarning) - s.SuppressErr = s.SuppressErr || (comment == suppressErrAssertionWarning) - s.SuppressCompare = s.SuppressCompare || (comment == suppressCompareAssertionWarning) - s.SuppressAsync = s.SuppressAsync || (comment == suppressAsyncAsertWarning) + switch comment { + case suppressLengthAssertionWarning: + s.SuppressLen = true + case suppressNilAssertionWarning: + s.SuppressNil = true + case suppressErrAssertionWarning: + s.SuppressErr = true + case suppressCompareAssertionWarning: + s.SuppressCompare = true + case suppressAsyncAsertWarning: + s.SuppressAsync = true + case suppressFocusContainerWarning: + s.ForbidFocus = false + } } } } diff --git a/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go b/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go index 366b5c6b0..d4274b8a7 100644 --- a/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go +++ b/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go @@ -35,8 +35,15 @@ var allowedErrors = []struct { {err: "io.EOF", fun: "(*bytes.Reader).ReadString"}, // pkg/database/sql {err: "sql.ErrNoRows", fun: "(*database/sql.Row).Scan"}, + // pkg/debug/elf + {err: "io.EOF", fun: "elf.Open"}, + {err: "io.EOF", fun: "elf.NewFile"}, // pkg/io {err: "io.EOF", fun: "(io.Reader).Read"}, + {err: "io.EOF", fun: "(io.ReaderAt).ReadAt"}, + {err: "io.EOF", fun: "(*io.LimitedReader).Read"}, + {err: "io.EOF", fun: "(*io.SectionReader).Read"}, + {err: "io.EOF", fun: "(*io.SectionReader).ReadAt"}, {err: "io.ErrClosedPipe", fun: "(*io.PipeWriter).Write"}, {err: "io.ErrShortBuffer", fun: "io.ReadAtLeast"}, {err: "io.ErrUnexpectedEOF", fun: "io.ReadAtLeast"}, diff --git a/vendor/github.com/ultraware/funlen/README.md b/vendor/github.com/ultraware/funlen/README.md index aaf348521..af2187694 100644 --- a/vendor/github.com/ultraware/funlen/README.md +++ b/vendor/github.com/ultraware/funlen/README.md @@ -1,9 +1,47 @@ # Funlen linter -Funlen is a linter that checks for long functions. It can checks both on the number of lines and the number of statements. +Funlen is a linter that checks for long functions. It can check both on the number of lines and the number of statements. The default limits are 60 lines and 40 statements. You can configure these. -## Installation guide +## Description + +The intent for the funlen linter is to fit a function within one screen. If you need to scroll through a long function, tracing variables back to their definition or even just finding matching brackets can become difficult. + +Besides checking lines there's also a separate check for the number of statements, which gives a clearer idea of how much is actually being done in a function. + +The default values are used internally, but might to be adjusted for your specific environment. + +## Installation Funlen is included in [golangci-lint](https://github.com/golangci/golangci-lint/). Install it and enable funlen. + +# Exclude for tests + +golangci-lint offers a way to exclude linters in certain cases. More info can be found here: https://golangci-lint.run/usage/configuration/#issues-configuration. + +## Disable funlen for \_test.go files + +You can utilize the issues configuration in `.golangci.yml` to exclude the funlen linter for all test files: + +```yaml +issues: + exclude-rules: + # disable funlen for all _test.go files + - path: _test.go + linters: + - funlen +``` + +## Disable funlen only for Test funcs + +If you want to keep funlen enabled for example in helper functions in test files but disable it specifically for Test funcs, you can use the following configuration: + +```yaml +issues: + exclude-rules: + # disable funlen for test funcs + - source: "^func Test" + linters: + - funlen +``` diff --git a/vendor/github.com/ultraware/funlen/main.go b/vendor/github.com/ultraware/funlen/main.go index 2ba353002..b68ddb926 100644 --- a/vendor/github.com/ultraware/funlen/main.go +++ b/vendor/github.com/ultraware/funlen/main.go @@ -13,7 +13,7 @@ const ( ) // Run runs this linter on the provided code -func Run(file *ast.File, fset *token.FileSet, lineLimit, stmtLimit int) []Message { +func Run(file *ast.File, fset *token.FileSet, lineLimit int, stmtLimit int, ignoreComments bool) []Message { if lineLimit == 0 { lineLimit = defaultLineLimit } @@ -21,6 +21,8 @@ func Run(file *ast.File, fset *token.FileSet, lineLimit, stmtLimit int) []Messag stmtLimit = defaultStmtLimit } + cmap := ast.NewCommentMap(fset, file, file.Comments) + var msgs []Message for _, f := range file.Decls { decl, ok := f.(*ast.FuncDecl) @@ -36,7 +38,7 @@ func Run(file *ast.File, fset *token.FileSet, lineLimit, stmtLimit int) []Messag } if lineLimit > 0 { - if lines := getLines(fset, decl); lines > lineLimit { + if lines := getLines(fset, decl, cmap.Filter(decl), ignoreComments); lines > lineLimit { msgs = append(msgs, makeLineMessage(fset, decl.Name, lines, lineLimit)) } } @@ -65,8 +67,26 @@ func makeStmtMessage(fset *token.FileSet, funcInfo *ast.Ident, stmts, stmtLimit } } -func getLines(fset *token.FileSet, f *ast.FuncDecl) int { // nolint: interfacer - return fset.Position(f.End()).Line - fset.Position(f.Pos()).Line - 1 +func getLines(fset *token.FileSet, f *ast.FuncDecl, cmap ast.CommentMap, ignoreComments bool) int { // nolint: interfacer + var lineCount int + var commentCount int + + lineCount = fset.Position(f.End()).Line - fset.Position(f.Pos()).Line - 1 + + if !ignoreComments { + return lineCount + } + + for _, c := range cmap.Comments() { + // If the CommenGroup's lines are inside the function + // count how many comments are in the CommentGroup + if (fset.Position(c.Pos()).Line > fset.Position(f.Pos()).Line) && + (fset.Position(c.End()).Line < fset.Position(f.End()).Line) { + commentCount += len(c.List) + } + } + + return lineCount - commentCount } func parseStmts(s []ast.Stmt) (total int) { diff --git a/vendor/github.com/uudashr/gocognit/gocognit.go b/vendor/github.com/uudashr/gocognit/gocognit.go index 1d539ee78..2fe22abc4 100644 --- a/vendor/github.com/uudashr/gocognit/gocognit.go +++ b/vendor/github.com/uudashr/gocognit/gocognit.go @@ -272,7 +272,7 @@ func (v *complexityVisitor) visitBranchStmt(n *ast.BranchStmt) ast.Visitor { } func (v *complexityVisitor) visitBinaryExpr(n *ast.BinaryExpr) ast.Visitor { - if (n.Op == token.LAND || n.Op == token.LOR) && !v.isCalculated(n) { + if isBinaryLogicalOp(n.Op) && !v.isCalculated(n) { ops := v.collectBinaryOps(n) var lastOp token.Token @@ -299,15 +299,10 @@ func (v *complexityVisitor) visitCallExpr(n *ast.CallExpr) ast.Visitor { func (v *complexityVisitor) collectBinaryOps(exp ast.Expr) []token.Token { v.markCalculated(exp) - switch exp := exp.(type) { - case *ast.BinaryExpr: + if exp, ok := exp.(*ast.BinaryExpr); ok { return mergeBinaryOps(v.collectBinaryOps(exp.X), exp.Op, v.collectBinaryOps(exp.Y)) - case *ast.ParenExpr: - // interest only on what inside paranthese - return v.collectBinaryOps(exp.X) - default: - return []token.Token{} } + return nil } func (v *complexityVisitor) incIfComplexity(n *ast.IfStmt) { @@ -320,16 +315,18 @@ func (v *complexityVisitor) incIfComplexity(n *ast.IfStmt) { func mergeBinaryOps(x []token.Token, op token.Token, y []token.Token) []token.Token { var out []token.Token - if len(x) != 0 { - out = append(out, x...) - } - out = append(out, op) - if len(y) != 0 { - out = append(out, y...) + out = append(out, x...) + if isBinaryLogicalOp(op) { + out = append(out, op) } + out = append(out, y...) return out } +func isBinaryLogicalOp(op token.Token) bool { + return op == token.LAND || op == token.LOR +} + const Doc = `Find complex function using cognitive complexity calculation. The gocognit analysis reports functions or methods which the complexity is over diff --git a/vendor/github.com/ykadowak/zerologlint/README.md b/vendor/github.com/ykadowak/zerologlint/README.md index 14443e2e3..b0a5fc0f9 100644 --- a/vendor/github.com/ykadowak/zerologlint/README.md +++ b/vendor/github.com/ykadowak/zerologlint/README.md @@ -47,5 +47,17 @@ func main() { // 4. Deferred case defer log.Info() // "must be dispatched by Msg or Send method" + + // 5. zerolog.Logger case + logger2 := zerolog.New(os.Stdout) + logger2.Info().Send() + + // 6. Dispatch in other function case + event := log.Info() + dispatcher(event) +} + +func dispatcher(e *zerolog.Event) { + e.Send() } ``` diff --git a/vendor/github.com/ykadowak/zerologlint/zerologlint.go b/vendor/github.com/ykadowak/zerologlint/zerologlint.go index bd588f996..bec50e52b 100644 --- a/vendor/github.com/ykadowak/zerologlint/zerologlint.go +++ b/vendor/github.com/ykadowak/zerologlint/zerologlint.go @@ -2,6 +2,7 @@ package zerologlint import ( "go/token" + "go/types" "strings" "golang.org/x/tools/go/analysis" @@ -65,14 +66,11 @@ func inspect(cd callDefer, set *map[posser]struct{}) { // check if it's in github.com/rs/zerolog/log since there's some // functions in github.com/rs/zerolog that returns zerolog.Event - // which should not be included - if isInLogPkg(*c) { + // which should not be included. However, zerolog.Logger receiver is an exception. + if isInLogPkg(*c) || isLoggerRecv(*c) { if isZerologEvent(c.Value) { - // check if this is a new instance of zerolog.Event like logger := log.Error() - // which should be dispatched afterwards at some point - if len(c.Args) == 0 { - (*set)[cd] = struct{}{} - } + // this ssa block should be dispatched afterwards at some point + (*set)[cd] = struct{}{} return } } @@ -81,23 +79,63 @@ func inspect(cd callDefer, set *map[posser]struct{}) { // check if the base is zerolog.Event. // if so, check if the StaticCallee is Send() or Msg(). // if so, remove the arg[0] from the set. + f := c.StaticCallee() + if f == nil { + return + } + if !isDispatchMethod(f) { + shouldReturn := true + for _, p := range f.Params { + if isZerologEvent(p) { + // check if this zerolog.Event as a parameter is dispatched in the function + // TODO: specifically, it can be dispatched in another function that is called in this function, and + // this algorithm cannot track that. But I'm tired of thinking about that for now. + for _, b := range f.Blocks { + for _, instr := range b.Instrs { + switch v := instr.(type) { + case *ssa.Call: + if inspectDispatchInFunction(v.Common()) { + shouldReturn = false + } + case *ssa.Defer: + if inspectDispatchInFunction(v.Common()) { + shouldReturn = false + } + } + } + } + } + } + if shouldReturn { + return + } + } for _, arg := range c.Args { if isZerologEvent(arg) { - if isDispatchMethod(*c) { - val := getRootSsaValue(arg) - // if there's branch, remove both ways from the set - if phi, ok := val.(*ssa.Phi); ok { - for _, edge := range phi.Edges { - delete(*set, edge) - } - } else { - delete(*set, val) + val := getRootSsaValue(arg) + // if there's branch, remove both ways from the set + if phi, ok := val.(*ssa.Phi); ok { + for _, edge := range phi.Edges { + delete(*set, edge) } + } else { + delete(*set, val) } } } } +func inspectDispatchInFunction(cc *ssa.CallCommon) bool { + if isDispatchMethod(cc.StaticCallee()) { + for _, arg := range cc.Args { + if isZerologEvent(arg) { + return true + } + } + } + return false +} + func isInLogPkg(c ssa.CallCommon) bool { switch v := c.Value.(type) { case ssa.Member: @@ -106,9 +144,18 @@ func isInLogPkg(c ssa.CallCommon) bool { return false } return strings.HasSuffix(p.Pkg.Path(), "github.com/rs/zerolog/log") - default: - return false } + return false +} + +func isLoggerRecv(c ssa.CallCommon) bool { + switch f := c.Value.(type) { + case *ssa.Function: + if recv := f.Signature.Recv(); recv != nil { + return strings.HasSuffix(types.TypeString(recv.Type(), nil), "zerolog.Logger") + } + } + return false } func isZerologEvent(v ssa.Value) bool { @@ -116,8 +163,11 @@ func isZerologEvent(v ssa.Value) bool { return strings.HasSuffix(ts, "github.com/rs/zerolog.Event") } -func isDispatchMethod(c ssa.CallCommon) bool { - m := c.StaticCallee().Name() +func isDispatchMethod(f *ssa.Function) bool { + if f == nil { + return false + } + m := f.Name() if m == "Send" || m == "Msg" || m == "Msgf" || m == "MsgFunc" { return true } @@ -127,15 +177,23 @@ func isDispatchMethod(c ssa.CallCommon) bool { func getRootSsaValue(v ssa.Value) ssa.Value { if c, ok := v.(*ssa.Call); ok { v := c.Value() + // When there is no receiver, that's the block of zerolog.Event - // eg. Error() method in log.Error().Str("foo", "bar"). Send() + // eg. Error() method in log.Error().Str("foo", "bar").Send() if len(v.Call.Args) == 0 { return v } + // Even when there is a receiver, if it's a zerolog.Logger instance, return this block + // eg. Info() method in zerolog.New(os.Stdout).Info() + root := v.Call.Args[0] + if !isZerologEvent(root) { + return v + } + // Ok to just return the receiver because all the method in this // chain is zerolog.Event at this point. - return getRootSsaValue(v.Call.Args[0]) + return getRootSsaValue(root) } return v } -- cgit mrf-deployment