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
|
# 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.
|
# with golangci-lint call it on a directory with the changed file.
|
||||||
check-exported: false
|
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:
|
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]
|
misspell: Finds commonly misspelled English words in comments [fast: true]
|
||||||
lll: Reports long lines [fast: true]
|
lll: Reports long lines [fast: true]
|
||||||
unparam: Reports unused function parameters [fast: false]
|
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:
|
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
|
- [misspell](https://github.com/client9/misspell) - Finds commonly misspelled English words in comments
|
||||||
- [lll](https://github.com/walle/lll) - Reports long lines
|
- [lll](https://github.com/walle/lll) - Reports long lines
|
||||||
- [unparam](https://github.com/mvdan/unparam) - Reports unused function parameters
|
- [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
|
# Configuration
|
||||||
The config file has lower priority than command-line options. If the same bool/string/int option is provided on the command-line
|
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
|
# 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.
|
# with golangci-lint call it on a directory with the changed file.
|
||||||
check-exported: false
|
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:
|
linters:
|
||||||
@ -636,6 +641,7 @@ Thanks to developers and authors of used linters:
|
|||||||
- [OpenPeeDeeP](https://github.com/OpenPeeDeeP)
|
- [OpenPeeDeeP](https://github.com/OpenPeeDeeP)
|
||||||
- [client9](https://github.com/client9)
|
- [client9](https://github.com/client9)
|
||||||
- [walle](https://github.com/walle)
|
- [walle](https://github.com/walle)
|
||||||
|
- [alexkohler](https://github.com/alexkohler)
|
||||||
|
|
||||||
# Future Plans
|
# Future Plans
|
||||||
1. Upstream all changes of forked linters.
|
1. Upstream all changes of forked linters.
|
||||||
|
@ -160,14 +160,24 @@ type LintersSettings struct {
|
|||||||
CheckExported bool `mapstructure:"check-exported"`
|
CheckExported bool `mapstructure:"check-exported"`
|
||||||
}
|
}
|
||||||
|
|
||||||
Lll LllSettings
|
Lll LllSettings
|
||||||
Unparam UnparamSettings
|
Unparam UnparamSettings
|
||||||
|
Nakedret NakedretSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
type LllSettings struct {
|
type LllSettings struct {
|
||||||
LineLength int `mapstructure:"line-length"`
|
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{
|
var defaultLintersSettings = LintersSettings{
|
||||||
Lll: LllSettings{
|
Lll: LllSettings{
|
||||||
LineLength: 120,
|
LineLength: 120,
|
||||||
@ -175,11 +185,9 @@ var defaultLintersSettings = LintersSettings{
|
|||||||
Unparam: UnparamSettings{
|
Unparam: UnparamSettings{
|
||||||
Algo: "cha",
|
Algo: "cha",
|
||||||
},
|
},
|
||||||
}
|
Nakedret: NakedretSettings{
|
||||||
|
MaxFuncLines: 30,
|
||||||
type UnparamSettings struct {
|
},
|
||||||
CheckExported bool `mapstructure:"check-exported"`
|
|
||||||
Algo string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Linters struct {
|
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().
|
WithFullImport().
|
||||||
WithSSA().
|
WithSSA().
|
||||||
WithURL("https://github.com/mvdan/unparam"),
|
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" {
|
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.Misspell{}.Name(): true, // unsure about false-positives number
|
||||||
golinters.Lll{}.Name(): true, // annoying
|
golinters.Lll{}.Name(): true, // annoying
|
||||||
golinters.Unparam{}.Name(): true, // need to check it first
|
golinters.Unparam{}.Name(): true, // need to check it first
|
||||||
|
golinters.Nakedret{}.Name(): true, // annoying
|
||||||
}
|
}
|
||||||
return enableLinterConfigs(lcs, func(lc *linter.Config) bool {
|
return enableLinterConfigs(lcs, func(lc *linter.Config) bool {
|
||||||
return !disabled[lc.Linter.Name()]
|
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