From 08ae8055872f911072dd2c29b1454fc46a914245 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Wed, 12 Feb 2025 18:45:13 +0100 Subject: [PATCH] gci: fix standard packages list for go1.24 (#5402) --- .golangci.yml | 1 + pkg/goformatters/gci/gci.go | 9 +- pkg/goformatters/gci/internal/LICENSE | 29 +++ .../gci/internal/config/config.go | 106 ++++++++++ pkg/goformatters/gci/internal/readme.md | 3 + .../gci/internal/section/parser.go | 51 +++++ .../gci/internal/section/standard.go | 30 +++ .../gci/internal/section/standard_list.go | 182 ++++++++++++++++++ 8 files changed, 409 insertions(+), 2 deletions(-) create mode 100644 pkg/goformatters/gci/internal/LICENSE create mode 100644 pkg/goformatters/gci/internal/config/config.go create mode 100644 pkg/goformatters/gci/internal/readme.md create mode 100644 pkg/goformatters/gci/internal/section/parser.go create mode 100644 pkg/goformatters/gci/internal/section/standard.go create mode 100644 pkg/goformatters/gci/internal/section/standard_list.go diff --git a/.golangci.yml b/.golangci.yml index bf43a0bb6575..b2c0240c7805 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -190,6 +190,7 @@ issues: - test/testdata_etc # test files - internal/go # extracted from Go code - internal/x # extracted from x/tools code + - pkg/goformatters/gci/internal # extracted from gci code exclude-files: - pkg/goanalysis/runner_checker.go # extracted from x/tools code diff --git a/pkg/goformatters/gci/gci.go b/pkg/goformatters/gci/gci.go index f872c3c667f6..f28b5b98a9dd 100644 --- a/pkg/goformatters/gci/gci.go +++ b/pkg/goformatters/gci/gci.go @@ -10,6 +10,7 @@ import ( "github.com/ldez/grignotin/gomod" "github.com/golangci/golangci-lint/pkg/config" + gcicfgi "github.com/golangci/golangci-lint/pkg/goformatters/gci/internal/config" "github.com/golangci/golangci-lint/pkg/goformatters/internal" ) @@ -28,7 +29,7 @@ func New(settings *config.GciSettings) (*Formatter, error) { internal.FormatterLogger.Errorf("gci: %v", err) } - cfg := gcicfg.YamlConfig{ + cfg := gcicfgi.YamlConfig{ Cfg: gcicfg.BoolConfig{ NoInlineComments: settings.NoInlineComments, NoPrefixComments: settings.NoPrefixComments, @@ -53,7 +54,11 @@ func New(settings *config.GciSettings) (*Formatter, error) { return nil, err } - return &Formatter{config: parsedCfg}, nil + return &Formatter{config: &gcicfg.Config{ + BoolConfig: parsedCfg.BoolConfig, + Sections: parsedCfg.Sections, + SectionSeparators: parsedCfg.SectionSeparators, + }}, nil } func (*Formatter) Name() string { diff --git a/pkg/goformatters/gci/internal/LICENSE b/pkg/goformatters/gci/internal/LICENSE new file mode 100644 index 000000000000..e1292f73895f --- /dev/null +++ b/pkg/goformatters/gci/internal/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2020, Xiang Dai +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pkg/goformatters/gci/internal/config/config.go b/pkg/goformatters/gci/internal/config/config.go new file mode 100644 index 000000000000..30cbf5c67a8e --- /dev/null +++ b/pkg/goformatters/gci/internal/config/config.go @@ -0,0 +1,106 @@ +package config + +import ( + "sort" + "strings" + + "gopkg.in/yaml.v3" + + "github.com/daixiang0/gci/pkg/config" + "github.com/daixiang0/gci/pkg/section" +) + +var defaultOrder = map[string]int{ + section.StandardType: 0, + section.DefaultType: 1, + section.CustomType: 2, + section.BlankType: 3, + section.DotType: 4, + section.AliasType: 5, + section.LocalModuleType: 6, +} + +type Config struct { + config.BoolConfig + Sections section.SectionList + SectionSeparators section.SectionList +} + +type YamlConfig struct { + Cfg config.BoolConfig `yaml:",inline"` + SectionStrings []string `yaml:"sections"` + SectionSeparatorStrings []string `yaml:"sectionseparators"` + + // Since history issue, Golangci-lint needs Analyzer to run and GCI add an Analyzer layer to integrate. + // The ModPath param is only from analyzer.go, no need to set it in all other places. + ModPath string `yaml:"-"` +} + +func (g YamlConfig) Parse() (*Config, error) { + var err error + + sections, err := section.Parse(g.SectionStrings) + if err != nil { + return nil, err + } + if sections == nil { + sections = section.DefaultSections() + } + if err := configureSections(sections, g.ModPath); err != nil { + return nil, err + } + + // if default order sorted sections + if !g.Cfg.CustomOrder { + sort.Slice(sections, func(i, j int) bool { + sectionI, sectionJ := sections[i].Type(), sections[j].Type() + + if g.Cfg.NoLexOrder || strings.Compare(sectionI, sectionJ) != 0 { + return defaultOrder[sectionI] < defaultOrder[sectionJ] + } + + return strings.Compare(sections[i].String(), sections[j].String()) < 0 + }) + } + + sectionSeparators, err := section.Parse(g.SectionSeparatorStrings) + if err != nil { + return nil, err + } + if sectionSeparators == nil { + sectionSeparators = section.DefaultSectionSeparators() + } + + return &Config{g.Cfg, sections, sectionSeparators}, nil +} + +func ParseConfig(in string) (*Config, error) { + config := YamlConfig{} + + err := yaml.Unmarshal([]byte(in), &config) + if err != nil { + return nil, err + } + + gciCfg, err := config.Parse() + if err != nil { + return nil, err + } + + return gciCfg, nil +} + +// configureSections now only do golang module path finding. +// Since history issue, Golangci-lint needs Analyzer to run and GCI add an Analyzer layer to integrate. +// The path param is from analyzer.go, in all other places should pass empty string. +func configureSections(sections section.SectionList, path string) error { + for _, sec := range sections { + switch s := sec.(type) { + case *section.LocalModule: + if err := s.Configure(path); err != nil { + return err + } + } + } + return nil +} diff --git a/pkg/goformatters/gci/internal/readme.md b/pkg/goformatters/gci/internal/readme.md new file mode 100644 index 000000000000..c59c3e6c348c --- /dev/null +++ b/pkg/goformatters/gci/internal/readme.md @@ -0,0 +1,3 @@ +Code borrowed from gci and modified to use new std packages. + +https://github.com/daixiang0/gci/pull/227 diff --git a/pkg/goformatters/gci/internal/section/parser.go b/pkg/goformatters/gci/internal/section/parser.go new file mode 100644 index 000000000000..9662cbd1a7b0 --- /dev/null +++ b/pkg/goformatters/gci/internal/section/parser.go @@ -0,0 +1,51 @@ +package section + +import ( + "errors" + "fmt" + "strings" + + "github.com/daixiang0/gci/pkg/section" +) + +func Parse(data []string) (section.SectionList, error) { + if len(data) == 0 { + return nil, nil + } + + var list section.SectionList + var errString string + for _, d := range data { + s := strings.ToLower(d) + if len(s) == 0 { + return nil, nil + } + + if s == "default" { + list = append(list, section.Default{}) + } else if s == "standard" { + list = append(list, Standard{}) + } else if s == "newline" { + list = append(list, section.NewLine{}) + } else if strings.HasPrefix(s, "prefix(") && len(d) > 8 { + list = append(list, section.Custom{Prefix: d[7 : len(d)-1]}) + } else if strings.HasPrefix(s, "commentline(") && len(d) > 13 { + list = append(list, section.Custom{Prefix: d[12 : len(d)-1]}) + } else if s == "dot" { + list = append(list, section.Dot{}) + } else if s == "blank" { + list = append(list, section.Blank{}) + } else if s == "alias" { + list = append(list, section.Alias{}) + } else if s == "localmodule" { + // pointer because we need to mutate the section at configuration time + list = append(list, §ion.LocalModule{}) + } else { + errString += fmt.Sprintf(" %s", s) + } + } + if errString != "" { + return nil, errors.New(fmt.Sprintf("invalid params:%s", errString)) + } + return list, nil +} diff --git a/pkg/goformatters/gci/internal/section/standard.go b/pkg/goformatters/gci/internal/section/standard.go new file mode 100644 index 000000000000..26c7e9dc7dc5 --- /dev/null +++ b/pkg/goformatters/gci/internal/section/standard.go @@ -0,0 +1,30 @@ +package section + +import ( + "github.com/daixiang0/gci/pkg/parse" + "github.com/daixiang0/gci/pkg/specificity" +) + +const StandardType = "standard" + +type Standard struct{} + +func (s Standard) MatchSpecificity(spec *parse.GciImports) specificity.MatchSpecificity { + if isStandard(spec.Path) { + return specificity.StandardMatch{} + } + return specificity.MisMatch{} +} + +func (s Standard) String() string { + return StandardType +} + +func (s Standard) Type() string { + return StandardType +} + +func isStandard(pkg string) bool { + _, ok := standardPackages[pkg] + return ok +} diff --git a/pkg/goformatters/gci/internal/section/standard_list.go b/pkg/goformatters/gci/internal/section/standard_list.go new file mode 100644 index 000000000000..2fddded70cc0 --- /dev/null +++ b/pkg/goformatters/gci/internal/section/standard_list.go @@ -0,0 +1,182 @@ +package section + +// Code generated based on go1.24.0 X:boringcrypto,arenas,synctest. DO NOT EDIT. + +var standardPackages = map[string]struct{}{ + "archive/tar": {}, + "archive/zip": {}, + "arena": {}, + "bufio": {}, + "bytes": {}, + "cmp": {}, + "compress/bzip2": {}, + "compress/flate": {}, + "compress/gzip": {}, + "compress/lzw": {}, + "compress/zlib": {}, + "container/heap": {}, + "container/list": {}, + "container/ring": {}, + "context": {}, + "crypto": {}, + "crypto/aes": {}, + "crypto/boring": {}, + "crypto/cipher": {}, + "crypto/des": {}, + "crypto/dsa": {}, + "crypto/ecdh": {}, + "crypto/ecdsa": {}, + "crypto/ed25519": {}, + "crypto/elliptic": {}, + "crypto/fips140": {}, + "crypto/hkdf": {}, + "crypto/hmac": {}, + "crypto/md5": {}, + "crypto/mlkem": {}, + "crypto/pbkdf2": {}, + "crypto/rand": {}, + "crypto/rc4": {}, + "crypto/rsa": {}, + "crypto/sha1": {}, + "crypto/sha256": {}, + "crypto/sha3": {}, + "crypto/sha512": {}, + "crypto/subtle": {}, + "crypto/tls": {}, + "crypto/tls/fipsonly": {}, + "crypto/x509": {}, + "crypto/x509/pkix": {}, + "database/sql": {}, + "database/sql/driver": {}, + "debug/buildinfo": {}, + "debug/dwarf": {}, + "debug/elf": {}, + "debug/gosym": {}, + "debug/macho": {}, + "debug/pe": {}, + "debug/plan9obj": {}, + "embed": {}, + "encoding": {}, + "encoding/ascii85": {}, + "encoding/asn1": {}, + "encoding/base32": {}, + "encoding/base64": {}, + "encoding/binary": {}, + "encoding/csv": {}, + "encoding/gob": {}, + "encoding/hex": {}, + "encoding/json": {}, + "encoding/pem": {}, + "encoding/xml": {}, + "errors": {}, + "expvar": {}, + "flag": {}, + "fmt": {}, + "go/ast": {}, + "go/build": {}, + "go/build/constraint": {}, + "go/constant": {}, + "go/doc": {}, + "go/doc/comment": {}, + "go/format": {}, + "go/importer": {}, + "go/parser": {}, + "go/printer": {}, + "go/scanner": {}, + "go/token": {}, + "go/types": {}, + "go/version": {}, + "hash": {}, + "hash/adler32": {}, + "hash/crc32": {}, + "hash/crc64": {}, + "hash/fnv": {}, + "hash/maphash": {}, + "html": {}, + "html/template": {}, + "image": {}, + "image/color": {}, + "image/color/palette": {}, + "image/draw": {}, + "image/gif": {}, + "image/jpeg": {}, + "image/png": {}, + "index/suffixarray": {}, + "io": {}, + "io/fs": {}, + "io/ioutil": {}, + "iter": {}, + "log": {}, + "log/slog": {}, + "log/syslog": {}, + "maps": {}, + "math": {}, + "math/big": {}, + "math/bits": {}, + "math/cmplx": {}, + "math/rand": {}, + "math/rand/v2": {}, + "mime": {}, + "mime/multipart": {}, + "mime/quotedprintable": {}, + "net": {}, + "net/http": {}, + "net/http/cgi": {}, + "net/http/cookiejar": {}, + "net/http/fcgi": {}, + "net/http/httptest": {}, + "net/http/httptrace": {}, + "net/http/httputil": {}, + "net/http/pprof": {}, + "net/mail": {}, + "net/netip": {}, + "net/rpc": {}, + "net/rpc/jsonrpc": {}, + "net/smtp": {}, + "net/textproto": {}, + "net/url": {}, + "os": {}, + "os/exec": {}, + "os/signal": {}, + "os/user": {}, + "path": {}, + "path/filepath": {}, + "plugin": {}, + "reflect": {}, + "regexp": {}, + "regexp/syntax": {}, + "runtime": {}, + "runtime/cgo": {}, + "runtime/coverage": {}, + "runtime/debug": {}, + "runtime/metrics": {}, + "runtime/pprof": {}, + "runtime/race": {}, + "runtime/trace": {}, + "slices": {}, + "sort": {}, + "strconv": {}, + "strings": {}, + "structs": {}, + "sync": {}, + "sync/atomic": {}, + "syscall": {}, + "testing": {}, + "testing/fstest": {}, + "testing/iotest": {}, + "testing/quick": {}, + "testing/slogtest": {}, + "testing/synctest": {}, + "text/scanner": {}, + "text/tabwriter": {}, + "text/template": {}, + "text/template/parse": {}, + "time": {}, + "time/tzdata": {}, + "unicode": {}, + "unicode/utf16": {}, + "unicode/utf8": {}, + "unique": {}, + "unsafe": {}, + "weak": {}, +}