Skip to content

Commit 2ea496f

Browse files
authored
new-linter: ireturn (checks for function return type) (#2219)
1 parent 813ba7d commit 2ea496f

11 files changed

+130
-0
lines changed

.golangci.example.yml

+23
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,29 @@ linters-settings:
447447
- pkg: knative.dev/serving/pkg/apis/(\w+)/(v[\w\d]+)
448448
alias: $1$2
449449

450+
ireturn:
451+
# ireturn allows using `allow` and `reject` settings at the same time.
452+
# Both settings are lists of the keywords and regular expressions matched to interface or package names.
453+
# keywords:
454+
# - `empty` for `interface{}`
455+
# - `error` for errors
456+
# - `stdlib` for standard library
457+
# - `anon` for anonymous interfaces
458+
459+
# By default, it allows using errors, empty interfaces, anonymous interfaces,
460+
# and interfaces provided by the standard library.
461+
allow:
462+
- anon
463+
- error
464+
- empty
465+
- stdlib
466+
# You can specify idiomatic endings for interface
467+
- (or|er)$
468+
469+
# Reject patterns
470+
reject:
471+
- github.com\/user\/package\/v4\.Type
472+
450473
lll:
451474
# max line length, lines longer will be reported. Default is 120.
452475
# '\t' is counted as 1 character by default, and can be changed with the tab-width option

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ require (
1313
github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde
1414
github.com/bkielbasa/cyclop v1.2.0
1515
github.com/bombsimon/wsl/v3 v3.3.0
16+
github.com/butuzov/ireturn v0.1.0
1617
github.com/charithe/durationcheck v0.0.8
1718
github.com/daixiang0/gci v0.2.9
1819
github.com/denis-tingajkin/go-header v0.4.2

go.sum

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/config/linters_settings.go

+6
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ type LintersSettings struct {
110110
Gosimple StaticCheckSettings
111111
Govet GovetSettings
112112
Ifshort IfshortSettings
113+
Ireturn IreturnSettings
113114
ImportAs ImportAsSettings
114115
Lll LllSettings
115116
Makezero MakezeroSettings
@@ -186,6 +187,11 @@ type ExhaustiveStructSettings struct {
186187
StructPatterns []string `mapstructure:"struct-patterns"`
187188
}
188189

190+
type IreturnSettings struct {
191+
Allow []string `mapstructure:"allow"`
192+
Reject []string `mapstructure:"reject"`
193+
}
194+
189195
type ForbidigoSettings struct {
190196
Forbid []string `mapstructure:"forbid"`
191197
ExcludeGodocExamples bool `mapstructure:"exclude-godoc-examples"`

pkg/golinters/ireturn.go

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package golinters
2+
3+
import (
4+
"strings"
5+
6+
"github.com/golangci/golangci-lint/pkg/config"
7+
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
8+
9+
"github.com/butuzov/ireturn/analyzer"
10+
"golang.org/x/tools/go/analysis"
11+
)
12+
13+
func NewIreturn(settings *config.IreturnSettings) *goanalysis.Linter {
14+
a := analyzer.NewAnalyzer()
15+
16+
cfg := map[string]map[string]interface{}{}
17+
if settings != nil {
18+
cfg[a.Name] = map[string]interface{}{
19+
"allow": strings.Join(settings.Allow, ","),
20+
"reject": strings.Join(settings.Reject, ","),
21+
}
22+
}
23+
24+
return goanalysis.NewLinter(
25+
a.Name,
26+
a.Doc,
27+
[]*analysis.Analyzer{a},
28+
cfg,
29+
).WithLoadMode(goanalysis.LoadModeTypesInfo)
30+
}

pkg/lint/lintersdb/manager.go

+7
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
110110
var reviveCfg *config.ReviveSettings
111111
var cyclopCfg *config.Cyclop
112112
var importAsCfg *config.ImportAsSettings
113+
var ireturnCfg *config.IreturnSettings
113114
var goModDirectivesCfg *config.GoModDirectivesSettings
114115
var tagliatelleCfg *config.TagliatelleSettings
115116
var gosecCfg *config.GoSecSettings
@@ -131,6 +132,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
131132
reviveCfg = &m.cfg.LintersSettings.Revive
132133
cyclopCfg = &m.cfg.LintersSettings.Cyclop
133134
importAsCfg = &m.cfg.LintersSettings.ImportAs
135+
ireturnCfg = &m.cfg.LintersSettings.Ireturn
134136
goModDirectivesCfg = &m.cfg.LintersSettings.GoModDirectives
135137
tagliatelleCfg = &m.cfg.LintersSettings.Tagliatelle
136138
gosecCfg = &m.cfg.LintersSettings.Gosec
@@ -506,6 +508,11 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
506508
WithLoadForGoAnalysis().
507509
WithURL("https://github.com/Antonboom/errname").
508510
WithSince("v1.42.0"),
511+
linter.NewConfig(golinters.NewIreturn(ireturnCfg)).
512+
WithSince("v1.43.0").
513+
WithPresets(linter.PresetStyle).
514+
WithLoadForGoAnalysis().
515+
WithURL("https://github.com/butuzov/ireturn"),
509516

510517
// nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives
511518
linter.NewConfig(golinters.NewNoLintLint()).

test/testdata/configs/ireturn.yml

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
linters-settings:
2+
ireturn:
3+
allow:
4+
- IreturnAllowDoer
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
linters-settings:
2+
ireturn:
3+
reject:
4+
- stdlib

test/testdata/ireturn_allow.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// args: -Eireturn
2+
// config_path: testdata/configs/ireturn.yml
3+
package testdata
4+
5+
type (
6+
IreturnAllowDoer interface{ Do() }
7+
ireturnAllowDoer struct{}
8+
)
9+
10+
func NewAllowDoer() IreturnAllowDoer { return new(ireturnAllowDoer) }
11+
func (d *ireturnAllowDoer) Do() { /*...*/ }
12+
13+
func NewerAllowDoer() *ireturnAllowDoer { return new(ireturnAllowDoer) }

test/testdata/ireturn_default.go

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// args: -Eireturn
2+
package testdata
3+
4+
type (
5+
IreturnDoer interface{ Do() }
6+
ireturnDoer struct{}
7+
)
8+
9+
func New() IreturnDoer { return new(ireturnDoer) } // ERROR `New returns interface \(command-line-arguments.IreturnDoer\)`
10+
func (d *ireturnDoer) Do() { /*...*/ }
11+
12+
func Newer() *ireturnDoer { return new(ireturnDoer) }
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// args: -Eireturn
2+
// config_path: testdata/configs/ireturn_stdlib_reject.yml
3+
package testdata
4+
5+
import (
6+
"bytes"
7+
"io"
8+
)
9+
10+
func NewWriter() io.Writer { // ERROR `NewWriter returns interface \(io.Writer\)`
11+
var buf bytes.Buffer
12+
return &buf
13+
}
14+
15+
func TestError() error {
16+
return nil
17+
}
18+
19+
type Foo interface {
20+
Foo()
21+
}
22+
type foo int
23+
24+
func (f foo) Foo() {}
25+
26+
func NewFoo() Foo {
27+
return foo(1)
28+
}

0 commit comments

Comments
 (0)