Fix #98: nakedret linter support
This commit is contained in:
parent
a6b91ccc77
commit
facc3a096a
@ -125,6 +125,9 @@ linters-settings:
|
||||
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
check-exported: false
|
||||
nakedret:
|
||||
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
|
||||
max-func-lines: 30
|
||||
|
||||
|
||||
linters:
|
||||
|
@ -123,6 +123,7 @@ depguard: Go linter that checks if package imports are in a list of acceptable p
|
||||
misspell: Finds commonly misspelled English words in comments [fast: true]
|
||||
lll: Reports long lines [fast: true]
|
||||
unparam: Reports unused function parameters [fast: false]
|
||||
nakedret: Finds naked returns in functions greater than a specified function length [fast: true]
|
||||
```
|
||||
|
||||
Pass `-E/--enable` to enable linter and `-D/--disable` to disable:
|
||||
@ -231,6 +232,7 @@ golangci-lint linters
|
||||
- [misspell](https://github.com/client9/misspell) - Finds commonly misspelled English words in comments
|
||||
- [lll](https://github.com/walle/lll) - Reports long lines
|
||||
- [unparam](https://github.com/mvdan/unparam) - Reports unused function parameters
|
||||
- [nakedret](https://github.com/alexkohler/nakedret) - Finds naked returns in functions greater than a specified function length
|
||||
|
||||
# Configuration
|
||||
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:
|
||||
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
check-exported: false
|
||||
nakedret:
|
||||
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
|
||||
max-func-lines: 30
|
||||
|
||||
|
||||
linters:
|
||||
@ -636,6 +641,7 @@ Thanks to developers and authors of used linters:
|
||||
- [OpenPeeDeeP](https://github.com/OpenPeeDeeP)
|
||||
- [client9](https://github.com/client9)
|
||||
- [walle](https://github.com/walle)
|
||||
- [alexkohler](https://github.com/alexkohler)
|
||||
|
||||
# Future Plans
|
||||
1. Upstream all changes of forked linters.
|
||||
|
@ -160,14 +160,24 @@ type LintersSettings struct {
|
||||
CheckExported bool `mapstructure:"check-exported"`
|
||||
}
|
||||
|
||||
Lll LllSettings
|
||||
Unparam UnparamSettings
|
||||
Lll LllSettings
|
||||
Unparam UnparamSettings
|
||||
Nakedret NakedretSettings
|
||||
}
|
||||
|
||||
type LllSettings struct {
|
||||
LineLength int `mapstructure:"line-length"`
|
||||
}
|
||||
|
||||
type NakedretSettings struct {
|
||||
MaxFuncLines int `mapstructure:"max-func-lines"`
|
||||
}
|
||||
|
||||
type UnparamSettings struct {
|
||||
CheckExported bool `mapstructure:"check-exported"`
|
||||
Algo string
|
||||
}
|
||||
|
||||
var defaultLintersSettings = LintersSettings{
|
||||
Lll: LllSettings{
|
||||
LineLength: 120,
|
||||
@ -175,11 +185,9 @@ var defaultLintersSettings = LintersSettings{
|
||||
Unparam: UnparamSettings{
|
||||
Algo: "cha",
|
||||
},
|
||||
}
|
||||
|
||||
type UnparamSettings struct {
|
||||
CheckExported bool `mapstructure:"check-exported"`
|
||||
Algo string
|
||||
Nakedret: NakedretSettings{
|
||||
MaxFuncLines: 30,
|
||||
},
|
||||
}
|
||||
|
||||
type Linters struct {
|
||||
|
100
pkg/golinters/nakedret.go
Normal file
100
pkg/golinters/nakedret.go
Normal file
@ -0,0 +1,100 @@
|
||||
package golinters
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
)
|
||||
|
||||
type Nakedret struct{}
|
||||
|
||||
func (Nakedret) Name() string {
|
||||
return "nakedret"
|
||||
}
|
||||
|
||||
func (Nakedret) Desc() string {
|
||||
return "Finds naked returns in functions greater than a specified function length"
|
||||
}
|
||||
|
||||
type nakedretVisitor struct {
|
||||
maxLength int
|
||||
f *token.FileSet
|
||||
issues []result.Issue
|
||||
}
|
||||
|
||||
func (v *nakedretVisitor) processFuncDecl(funcDecl *ast.FuncDecl) {
|
||||
file := v.f.File(funcDecl.Pos())
|
||||
functionLineLength := file.Position(funcDecl.End()).Line - file.Position(funcDecl.Pos()).Line
|
||||
|
||||
// Scan the body for usage of the named returns
|
||||
for _, stmt := range funcDecl.Body.List {
|
||||
s, ok := stmt.(*ast.ReturnStmt)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(s.Results) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
file := v.f.File(s.Pos())
|
||||
if file == nil || functionLineLength <= v.maxLength {
|
||||
continue
|
||||
}
|
||||
if funcDecl.Name == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
v.issues = append(v.issues, result.Issue{
|
||||
FromLinter: Nakedret{}.Name(),
|
||||
Text: fmt.Sprintf("naked return in func `%s` with %d lines of code",
|
||||
funcDecl.Name.Name, functionLineLength),
|
||||
Pos: v.f.Position(s.Pos()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (v *nakedretVisitor) Visit(node ast.Node) ast.Visitor {
|
||||
funcDecl, ok := node.(*ast.FuncDecl)
|
||||
if !ok {
|
||||
return v
|
||||
}
|
||||
|
||||
var namedReturns []*ast.Ident
|
||||
|
||||
// We've found a function
|
||||
if funcDecl.Type != nil && funcDecl.Type.Results != nil {
|
||||
for _, field := range funcDecl.Type.Results.List {
|
||||
for _, ident := range field.Names {
|
||||
if ident != nil {
|
||||
namedReturns = append(namedReturns, ident)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(namedReturns) == 0 || funcDecl.Body == nil {
|
||||
return v
|
||||
}
|
||||
|
||||
v.processFuncDecl(funcDecl)
|
||||
return v
|
||||
}
|
||||
|
||||
func (lint Nakedret) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
|
||||
var res []result.Issue
|
||||
for _, f := range lintCtx.ASTCache.GetAllValidFiles() {
|
||||
v := nakedretVisitor{
|
||||
maxLength: lintCtx.Settings().Nakedret.MaxFuncLines,
|
||||
f: f.Fset,
|
||||
}
|
||||
ast.Walk(&v, f.F)
|
||||
res = append(res, v.issues...)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
@ -177,6 +177,10 @@ func GetAllSupportedLinterConfigs() []linter.Config {
|
||||
WithFullImport().
|
||||
WithSSA().
|
||||
WithURL("https://github.com/mvdan/unparam"),
|
||||
linter.NewConfig(golinters.Nakedret{}).
|
||||
WithPresets(linter.PresetComplexity).
|
||||
WithSpeed(10).
|
||||
WithURL("https://github.com/alexkohler/nakedret"),
|
||||
}
|
||||
|
||||
if os.Getenv("GOLANGCI_COM_RUN") == "1" {
|
||||
@ -188,6 +192,7 @@ func GetAllSupportedLinterConfigs() []linter.Config {
|
||||
golinters.Misspell{}.Name(): true, // unsure about false-positives number
|
||||
golinters.Lll{}.Name(): true, // annoying
|
||||
golinters.Unparam{}.Name(): true, // need to check it first
|
||||
golinters.Nakedret{}.Name(): true, // annoying
|
||||
}
|
||||
return enableLinterConfigs(lcs, func(lc *linter.Config) bool {
|
||||
return !disabled[lc.Linter.Name()]
|
||||
|
67
test/testdata/nakedret.go
vendored
Normal file
67
test/testdata/nakedret.go
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
//args: -Enakedret
|
||||
package testdata
|
||||
|
||||
func NakedretIssue() (a int, b string) {
|
||||
if a > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if b == "" {
|
||||
return 0, "0"
|
||||
}
|
||||
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
|
||||
// len of this function is 31
|
||||
return // ERROR "naked return in func `NakedretIssue` with 31 lines of code"
|
||||
}
|
||||
|
||||
func NoNakedretIssue() (a int, b string) {
|
||||
if a > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if b == "" {
|
||||
return 0, "0"
|
||||
}
|
||||
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
|
||||
// len of this function is 30
|
||||
return
|
||||
}
|
21
third_party/nakedret/LICENSE
vendored
Normal file
21
third_party/nakedret/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Alex Kohler
|
||||
|
||||
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.
|
Loading…
x
Reference in New Issue
Block a user