diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 32b46fe168b1..ea714629b2d0 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,6 +10,7 @@ updates: # Ignore forked linters because of their versioning issues. - dependency-name: "github.com/golangci/dupl" - dependency-name: "github.com/golangci/gofmt" + - dependency-name: "github.com/golangci/swaggoswag" - dependency-name: "github.com/golangci/unconvert" - package-ecosystem: github-actions directory: "/" diff --git a/.golangci.next.reference.yml b/.golangci.next.reference.yml index dbcc0377a400..9db78da5b4af 100644 --- a/.golangci.next.reference.yml +++ b/.golangci.next.reference.yml @@ -3972,6 +3972,7 @@ formatters: - gofumpt - goimports - golines + - swaggo # Formatters settings. settings: diff --git a/go.mod b/go.mod index 1e7e82b195f4..31966ee07417 100644 --- a/go.mod +++ b/go.mod @@ -51,6 +51,7 @@ require ( github.com/golangci/misspell v0.6.0 github.com/golangci/plugin-module-register v0.1.1 github.com/golangci/revgrep v0.8.0 + github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e github.com/gordonklaus/ineffassign v0.1.0 github.com/gostaticanalysis/forcetypeassert v0.2.0 diff --git a/go.sum b/go.sum index 9875fd835bef..b13c56eb3994 100644 --- a/go.sum +++ b/go.sum @@ -263,6 +263,8 @@ github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+ github.com/golangci/plugin-module-register v0.1.1/go.mod h1:TTpqoB6KkwOJMV8u7+NyXMrkwwESJLOkfl9TxR1DGFc= github.com/golangci/revgrep v0.8.0 h1:EZBctwbVd0aMeRnNUsFogoyayvKHyxlV3CdUA46FX2s= github.com/golangci/revgrep v0.8.0/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k= +github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e h1:ai0EfmVYE2bRA5htgAG9r7s3tHsfjIhN98WshBTJ9jM= +github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e/go.mod h1:Vrn4B5oR9qRwM+f54koyeH3yzphlecwERs0el27Fr/s= github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e h1:gD6P7NEo7Eqtt0ssnqSJNNndxe69DOQ24A5h7+i3KpM= github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e/go.mod h1:h+wZwLjUTJnm/P2rwlbJdRPZXOzaT36/FwnPnY2inzc= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= diff --git a/jsonschema/golangci.next.jsonschema.json b/jsonschema/golangci.next.jsonschema.json index 9af538279cbc..fe33da5f8679 100644 --- a/jsonschema/golangci.next.jsonschema.json +++ b/jsonschema/golangci.next.jsonschema.json @@ -836,7 +836,8 @@ "gofmt", "gofumpt", "goimports", - "golines" + "golines", + "swaggo" ] }, "settings": { diff --git a/pkg/config/linters.go b/pkg/config/linters.go index 590d9448a197..2669f2e64778 100644 --- a/pkg/config/linters.go +++ b/pkg/config/linters.go @@ -49,5 +49,5 @@ func (l *Linters) validateNoFormatters() error { } func getAllFormatterNames() []string { - return []string{"gci", "gofmt", "gofumpt", "goimports", "golines"} + return []string{"gci", "gofmt", "gofumpt", "goimports", "golines", "swaggo"} } diff --git a/pkg/goformatters/meta_formatter.go b/pkg/goformatters/meta_formatter.go index 718caaa96003..dbedcd4cba19 100644 --- a/pkg/goformatters/meta_formatter.go +++ b/pkg/goformatters/meta_formatter.go @@ -12,6 +12,7 @@ import ( "github.com/golangci/golangci-lint/v2/pkg/goformatters/gofumpt" "github.com/golangci/golangci-lint/v2/pkg/goformatters/goimports" "github.com/golangci/golangci-lint/v2/pkg/goformatters/golines" + "github.com/golangci/golangci-lint/v2/pkg/goformatters/swaggo" "github.com/golangci/golangci-lint/v2/pkg/logutils" ) @@ -41,6 +42,10 @@ func NewMetaFormatter(log logutils.Log, cfg *config.Formatters, runCfg *config.R m.formatters = append(m.formatters, goimports.New(&cfg.Settings.GoImports)) } + if slices.Contains(cfg.Enable, swaggo.Name) { + m.formatters = append(m.formatters, swaggo.New()) + } + // gci is a last because the only goal of gci is to handle imports. if slices.Contains(cfg.Enable, gci.Name) { formatter, err := gci.New(&cfg.Settings.Gci) @@ -86,5 +91,5 @@ func (m *MetaFormatter) Format(filename string, src []byte) []byte { } func IsFormatter(name string) bool { - return slices.Contains([]string{gofmt.Name, gofumpt.Name, goimports.Name, gci.Name, golines.Name}, name) + return slices.Contains([]string{gofmt.Name, gofumpt.Name, goimports.Name, gci.Name, golines.Name, swaggo.Name}, name) } diff --git a/pkg/goformatters/swaggo/swaggo.go b/pkg/goformatters/swaggo/swaggo.go new file mode 100644 index 000000000000..2479fb35baba --- /dev/null +++ b/pkg/goformatters/swaggo/swaggo.go @@ -0,0 +1,23 @@ +package swaggo + +import "github.com/golangci/swaggoswag" + +const Name = "swaggo" + +type Formatter struct { + formatter *swaggoswag.Formatter +} + +func New() *Formatter { + return &Formatter{ + formatter: swaggoswag.NewFormatter(), + } +} + +func (*Formatter) Name() string { + return Name +} + +func (f *Formatter) Format(path string, src []byte) ([]byte, error) { + return f.formatter.Format(path, src) +} diff --git a/pkg/golinters/swaggo/swaggo.go b/pkg/golinters/swaggo/swaggo.go new file mode 100644 index 000000000000..8c4eec8e448d --- /dev/null +++ b/pkg/golinters/swaggo/swaggo.go @@ -0,0 +1,23 @@ +package swaggo + +import ( + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/v2/pkg/goanalysis" + "github.com/golangci/golangci-lint/v2/pkg/goformatters" + "github.com/golangci/golangci-lint/v2/pkg/goformatters/swaggo" + "github.com/golangci/golangci-lint/v2/pkg/golinters/internal" +) + +const linterName = "swaggo" + +func New() *goanalysis.Linter { + a := goformatters.NewAnalyzer( + internal.LinterLogger.Child(linterName), + "Check if swaggo comments are formatted", + swaggo.New(), + ) + + return goanalysis.NewLinter(a.Name, a.Doc, []*analysis.Analyzer{a}, nil). + WithLoadMode(goanalysis.LoadModeSyntax) +} diff --git a/pkg/golinters/swaggo/swaggo_integration_test.go b/pkg/golinters/swaggo/swaggo_integration_test.go new file mode 100644 index 000000000000..23fd3f1ff788 --- /dev/null +++ b/pkg/golinters/swaggo/swaggo_integration_test.go @@ -0,0 +1,19 @@ +package swaggo + +import ( + "testing" + + "github.com/golangci/golangci-lint/v2/test/testshared/integration" +) + +func TestFromTestdata(t *testing.T) { + integration.RunTestdata(t) +} + +func TestFix(t *testing.T) { + integration.RunFix(t) +} + +func TestFixPathPrefix(t *testing.T) { + integration.RunFixPathPrefix(t) +} diff --git a/pkg/golinters/swaggo/testdata/fix/in/swaggo.go b/pkg/golinters/swaggo/testdata/fix/in/swaggo.go new file mode 100644 index 000000000000..33c814b2805b --- /dev/null +++ b/pkg/golinters/swaggo/testdata/fix/in/swaggo.go @@ -0,0 +1,18 @@ +//golangcitest:config_path testdata/swaggo.yml +//golangcitest:expected_exitcode 0 +package api + +import "net/http" + +// @Summary Add a new pet to the store +// @Description get string by ID +// @ID get-string-by-int +// @Accept json +// @Produce json +// @Param some_id path int true "Some ID" Format(int64) +// @Param some_id body web.Pet true "Some ID" +// @Success 200 {string} string "ok" +// @Failure 400 {object} web.APIError "We need ID!!" +// @Failure 404 {object} web.APIError "Can not find ID" +// @Router /testapi/get-string-by-int/{some_id} [get] +func GetStringByInt(w http.ResponseWriter, r *http.Request) {} diff --git a/pkg/golinters/swaggo/testdata/fix/out/swaggo.go b/pkg/golinters/swaggo/testdata/fix/out/swaggo.go new file mode 100644 index 000000000000..8c4d2a2cdd40 --- /dev/null +++ b/pkg/golinters/swaggo/testdata/fix/out/swaggo.go @@ -0,0 +1,18 @@ +//golangcitest:config_path testdata/swaggo.yml +//golangcitest:expected_exitcode 0 +package api + +import "net/http" + +// @Summary Add a new pet to the store +// @Description get string by ID +// @ID get-string-by-int +// @Accept json +// @Produce json +// @Param some_id path int true "Some ID" Format(int64) +// @Param some_id body web.Pet true "Some ID" +// @Success 200 {string} string "ok" +// @Failure 400 {object} web.APIError "We need ID!!" +// @Failure 404 {object} web.APIError "Can not find ID" +// @Router /testapi/get-string-by-int/{some_id} [get] +func GetStringByInt(w http.ResponseWriter, r *http.Request) {} diff --git a/pkg/golinters/swaggo/testdata/swaggo.go b/pkg/golinters/swaggo/testdata/swaggo.go new file mode 100644 index 000000000000..db6b7da9dcfc --- /dev/null +++ b/pkg/golinters/swaggo/testdata/swaggo.go @@ -0,0 +1,18 @@ +//golangcitest:config_path testdata/swaggo.yml +package api + +import "net/http" + +// want +1 "File is not properly formatted" +// @Summary Add a new pet to the store +// @Description get string by ID +// @ID get-string-by-int +// @Accept json +// @Produce json +// @Param some_id path int true "Some ID" Format(int64) +// @Param some_id body web.Pet true "Some ID" +// @Success 200 {string} string "ok" +// @Failure 400 {object} web.APIError "We need ID!!" +// @Failure 404 {object} web.APIError "Can not find ID" +// @Router /testapi/get-string-by-int/{some_id} [get] +func GetStringByInt(w http.ResponseWriter, r *http.Request) {} diff --git a/pkg/golinters/swaggo/testdata/swaggo.yml b/pkg/golinters/swaggo/testdata/swaggo.yml new file mode 100644 index 000000000000..458e2fca7eeb --- /dev/null +++ b/pkg/golinters/swaggo/testdata/swaggo.yml @@ -0,0 +1,5 @@ +version: "2" + +formatters: + enable: + - swaggo diff --git a/pkg/lint/lintersdb/builder_linter.go b/pkg/lint/lintersdb/builder_linter.go index 5ac3e5ba2fb0..93ff1d35a5ff 100644 --- a/pkg/lint/lintersdb/builder_linter.go +++ b/pkg/lint/lintersdb/builder_linter.go @@ -94,6 +94,7 @@ import ( "github.com/golangci/golangci-lint/v2/pkg/golinters/spancheck" "github.com/golangci/golangci-lint/v2/pkg/golinters/sqlclosecheck" "github.com/golangci/golangci-lint/v2/pkg/golinters/staticcheck" + "github.com/golangci/golangci-lint/v2/pkg/golinters/swaggo" "github.com/golangci/golangci-lint/v2/pkg/golinters/tagalign" "github.com/golangci/golangci-lint/v2/pkg/golinters/tagliatelle" "github.com/golangci/golangci-lint/v2/pkg/golinters/testableexamples" @@ -581,6 +582,11 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithAutoFix(). WithURL("https://staticcheck.dev/"), + linter.NewConfig(swaggo.New()). + WithSince("v2.2.0"). + WithAutoFix(). + WithURL("https://github.com/swaggo/swaggo"), + linter.NewConfig(tagalign.New(&cfg.Linters.Settings.TagAlign)). WithSince("v1.53.0"). WithAutoFix(). diff --git a/pkg/result/processors/fixer.go b/pkg/result/processors/fixer.go index 14dd3454eab6..d2beaa0ebafe 100644 --- a/pkg/result/processors/fixer.go +++ b/pkg/result/processors/fixer.go @@ -22,6 +22,7 @@ import ( "github.com/golangci/golangci-lint/v2/pkg/goformatters/gofumpt" "github.com/golangci/golangci-lint/v2/pkg/goformatters/goimports" "github.com/golangci/golangci-lint/v2/pkg/goformatters/golines" + "github.com/golangci/golangci-lint/v2/pkg/goformatters/swaggo" "github.com/golangci/golangci-lint/v2/pkg/logutils" "github.com/golangci/golangci-lint/v2/pkg/result" "github.com/golangci/golangci-lint/v2/pkg/timeutils" @@ -79,7 +80,7 @@ func (p Fixer) process(issues []result.Issue) ([]result.Issue, error) { // filenames / linters / edits editsByLinter := make(map[string]map[string][]diff.Edit) - formatters := []string{gofumpt.Name, goimports.Name, gofmt.Name, gci.Name, golines.Name} + formatters := []string{gofumpt.Name, goimports.Name, gofmt.Name, gci.Name, golines.Name, swaggo.Name} var notFixableIssues []result.Issue diff --git a/pkg/result/processors/max_per_file_from_linter.go b/pkg/result/processors/max_per_file_from_linter.go index fdb6bb0278da..2608c22e22ab 100644 --- a/pkg/result/processors/max_per_file_from_linter.go +++ b/pkg/result/processors/max_per_file_from_linter.go @@ -7,6 +7,7 @@ import ( "github.com/golangci/golangci-lint/v2/pkg/goformatters/gofumpt" "github.com/golangci/golangci-lint/v2/pkg/goformatters/goimports" "github.com/golangci/golangci-lint/v2/pkg/goformatters/golines" + "github.com/golangci/golangci-lint/v2/pkg/goformatters/swaggo" "github.com/golangci/golangci-lint/v2/pkg/result" ) @@ -24,7 +25,7 @@ func NewMaxPerFileFromLinter(cfg *config.Config) *MaxPerFileFromLinter { if !cfg.Issues.NeedFix { // if we don't fix we do this limiting to not annoy user; // otherwise we need to fix all issues in the file at once - for _, f := range []string{gofmt.Name, gofumpt.Name, goimports.Name, gci.Name, golines.Name} { + for _, f := range []string{gofmt.Name, gofumpt.Name, goimports.Name, gci.Name, golines.Name, swaggo.Name} { maxPerFileFromLinterConfig[f] = 1 } }