Skip to content

Commit facc3a0

Browse files
committed
Fix #98: nakedret linter support
1 parent a6b91cc commit facc3a0

File tree

7 files changed

+217
-7
lines changed

7 files changed

+217
-7
lines changed

Diff for: .golangci.example.yml

+3
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ linters-settings:
125125
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
126126
# with golangci-lint call it on a directory with the changed file.
127127
check-exported: false
128+
nakedret:
129+
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
130+
max-func-lines: 30
128131

129132

130133
linters:

Diff for: README.md

+6
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ depguard: Go linter that checks if package imports are in a list of acceptable p
123123
misspell: Finds commonly misspelled English words in comments [fast: true]
124124
lll: Reports long lines [fast: true]
125125
unparam: Reports unused function parameters [fast: false]
126+
nakedret: Finds naked returns in functions greater than a specified function length [fast: true]
126127
```
127128

128129
Pass `-E/--enable` to enable linter and `-D/--disable` to disable:
@@ -231,6 +232,7 @@ golangci-lint linters
231232
- [misspell](https://github.com/client9/misspell) - Finds commonly misspelled English words in comments
232233
- [lll](https://github.com/walle/lll) - Reports long lines
233234
- [unparam](https://github.com/mvdan/unparam) - Reports unused function parameters
235+
- [nakedret](https://github.com/alexkohler/nakedret) - Finds naked returns in functions greater than a specified function length
234236

235237
# Configuration
236238
The config file has lower priority than command-line options. If the same bool/string/int option is provided on the command-line
@@ -454,6 +456,9 @@ linters-settings:
454456
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
455457
# with golangci-lint call it on a directory with the changed file.
456458
check-exported: false
459+
nakedret:
460+
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
461+
max-func-lines: 30
457462

458463

459464
linters:
@@ -636,6 +641,7 @@ Thanks to developers and authors of used linters:
636641
- [OpenPeeDeeP](https://github.com/OpenPeeDeeP)
637642
- [client9](https://github.com/client9)
638643
- [walle](https://github.com/walle)
644+
- [alexkohler](https://github.com/alexkohler)
639645

640646
# Future Plans
641647
1. Upstream all changes of forked linters.

Diff for: pkg/config/config.go

+15-7
Original file line numberDiff line numberDiff line change
@@ -160,26 +160,34 @@ type LintersSettings struct {
160160
CheckExported bool `mapstructure:"check-exported"`
161161
}
162162

163-
Lll LllSettings
164-
Unparam UnparamSettings
163+
Lll LllSettings
164+
Unparam UnparamSettings
165+
Nakedret NakedretSettings
165166
}
166167

167168
type LllSettings struct {
168169
LineLength int `mapstructure:"line-length"`
169170
}
170171

172+
type NakedretSettings struct {
173+
MaxFuncLines int `mapstructure:"max-func-lines"`
174+
}
175+
176+
type UnparamSettings struct {
177+
CheckExported bool `mapstructure:"check-exported"`
178+
Algo string
179+
}
180+
171181
var defaultLintersSettings = LintersSettings{
172182
Lll: LllSettings{
173183
LineLength: 120,
174184
},
175185
Unparam: UnparamSettings{
176186
Algo: "cha",
177187
},
178-
}
179-
180-
type UnparamSettings struct {
181-
CheckExported bool `mapstructure:"check-exported"`
182-
Algo string
188+
Nakedret: NakedretSettings{
189+
MaxFuncLines: 30,
190+
},
183191
}
184192

185193
type Linters struct {

Diff for: pkg/golinters/nakedret.go

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package golinters
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"go/ast"
7+
"go/token"
8+
9+
"github.com/golangci/golangci-lint/pkg/lint/linter"
10+
"github.com/golangci/golangci-lint/pkg/result"
11+
)
12+
13+
type Nakedret struct{}
14+
15+
func (Nakedret) Name() string {
16+
return "nakedret"
17+
}
18+
19+
func (Nakedret) Desc() string {
20+
return "Finds naked returns in functions greater than a specified function length"
21+
}
22+
23+
type nakedretVisitor struct {
24+
maxLength int
25+
f *token.FileSet
26+
issues []result.Issue
27+
}
28+
29+
func (v *nakedretVisitor) processFuncDecl(funcDecl *ast.FuncDecl) {
30+
file := v.f.File(funcDecl.Pos())
31+
functionLineLength := file.Position(funcDecl.End()).Line - file.Position(funcDecl.Pos()).Line
32+
33+
// Scan the body for usage of the named returns
34+
for _, stmt := range funcDecl.Body.List {
35+
s, ok := stmt.(*ast.ReturnStmt)
36+
if !ok {
37+
continue
38+
}
39+
40+
if len(s.Results) != 0 {
41+
continue
42+
}
43+
44+
file := v.f.File(s.Pos())
45+
if file == nil || functionLineLength <= v.maxLength {
46+
continue
47+
}
48+
if funcDecl.Name == nil {
49+
continue
50+
}
51+
52+
v.issues = append(v.issues, result.Issue{
53+
FromLinter: Nakedret{}.Name(),
54+
Text: fmt.Sprintf("naked return in func `%s` with %d lines of code",
55+
funcDecl.Name.Name, functionLineLength),
56+
Pos: v.f.Position(s.Pos()),
57+
})
58+
}
59+
}
60+
61+
func (v *nakedretVisitor) Visit(node ast.Node) ast.Visitor {
62+
funcDecl, ok := node.(*ast.FuncDecl)
63+
if !ok {
64+
return v
65+
}
66+
67+
var namedReturns []*ast.Ident
68+
69+
// We've found a function
70+
if funcDecl.Type != nil && funcDecl.Type.Results != nil {
71+
for _, field := range funcDecl.Type.Results.List {
72+
for _, ident := range field.Names {
73+
if ident != nil {
74+
namedReturns = append(namedReturns, ident)
75+
}
76+
}
77+
}
78+
}
79+
80+
if len(namedReturns) == 0 || funcDecl.Body == nil {
81+
return v
82+
}
83+
84+
v.processFuncDecl(funcDecl)
85+
return v
86+
}
87+
88+
func (lint Nakedret) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
89+
var res []result.Issue
90+
for _, f := range lintCtx.ASTCache.GetAllValidFiles() {
91+
v := nakedretVisitor{
92+
maxLength: lintCtx.Settings().Nakedret.MaxFuncLines,
93+
f: f.Fset,
94+
}
95+
ast.Walk(&v, f.F)
96+
res = append(res, v.issues...)
97+
}
98+
99+
return res, nil
100+
}

Diff for: pkg/lint/lintersdb/lintersdb.go

+5
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ func GetAllSupportedLinterConfigs() []linter.Config {
177177
WithFullImport().
178178
WithSSA().
179179
WithURL("https://github.com/mvdan/unparam"),
180+
linter.NewConfig(golinters.Nakedret{}).
181+
WithPresets(linter.PresetComplexity).
182+
WithSpeed(10).
183+
WithURL("https://github.com/alexkohler/nakedret"),
180184
}
181185

182186
if os.Getenv("GOLANGCI_COM_RUN") == "1" {
@@ -188,6 +192,7 @@ func GetAllSupportedLinterConfigs() []linter.Config {
188192
golinters.Misspell{}.Name(): true, // unsure about false-positives number
189193
golinters.Lll{}.Name(): true, // annoying
190194
golinters.Unparam{}.Name(): true, // need to check it first
195+
golinters.Nakedret{}.Name(): true, // annoying
191196
}
192197
return enableLinterConfigs(lcs, func(lc *linter.Config) bool {
193198
return !disabled[lc.Linter.Name()]

Diff for: test/testdata/nakedret.go

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//args: -Enakedret
2+
package testdata
3+
4+
func NakedretIssue() (a int, b string) {
5+
if a > 0 {
6+
return
7+
}
8+
9+
if b == "" {
10+
return 0, "0"
11+
}
12+
13+
// ...
14+
// ...
15+
// ...
16+
// ...
17+
// ...
18+
// ...
19+
// ...
20+
// ...
21+
// ...
22+
// ...
23+
// ...
24+
// ...
25+
// ...
26+
// ...
27+
// ...
28+
// ...
29+
// ...
30+
// ...
31+
// ...
32+
33+
// len of this function is 31
34+
return // ERROR "naked return in func `NakedretIssue` with 31 lines of code"
35+
}
36+
37+
func NoNakedretIssue() (a int, b string) {
38+
if a > 0 {
39+
return
40+
}
41+
42+
if b == "" {
43+
return 0, "0"
44+
}
45+
46+
// ...
47+
// ...
48+
// ...
49+
// ...
50+
// ...
51+
// ...
52+
// ...
53+
// ...
54+
// ...
55+
// ...
56+
// ...
57+
// ...
58+
// ...
59+
// ...
60+
// ...
61+
// ...
62+
// ...
63+
// ...
64+
65+
// len of this function is 30
66+
return
67+
}

Diff for: third_party/nakedret/LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Alex Kohler
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)