mirror of
https://github.com/mgechev/revive.git
synced 2025-03-31 21:55:29 +02:00
Merge master
This commit is contained in:
commit
69f012a7d9
12
PULL_REQUEST_TEMPLATE.md
Normal file
12
PULL_REQUEST_TEMPLATE.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!-- ### IMPORTANT ### -->
|
||||||
|
<!-- Please do not create a Pull Request without creating an issue first.** -->
|
||||||
|
<!-- If you're fixing a typo or improving the documentation, you may not have to open an issue. -->
|
||||||
|
|
||||||
|
<!-- ### CHECKLIST ### -->
|
||||||
|
<!-- Please, describe in details what's your motivation for this PR -->
|
||||||
|
<!-- Did you add tests? -->
|
||||||
|
<!-- Does your code follows the coding style of the rest of the repository? -->
|
||||||
|
<!-- Does the Travis build passes? -->
|
||||||
|
|
||||||
|
<!-- ### FOOTER (OPTIONAL) ### -->
|
||||||
|
<!-- If you're closing an issue add "Closes #XXXX" in your comment. This way, the PR will be linked to the issue automatically. -->
|
@ -41,6 +41,8 @@ Here's how `revive` is different from `golint`:
|
|||||||
- [Custom Configuration](#custom-configuration)
|
- [Custom Configuration](#custom-configuration)
|
||||||
- [Recommended Configuration](#recommended-configuration)
|
- [Recommended Configuration](#recommended-configuration)
|
||||||
- [Available Rules](#available-rules)
|
- [Available Rules](#available-rules)
|
||||||
|
- [Configurable rules](#configurable-rules)
|
||||||
|
- [`var-naming`](#var-naming)
|
||||||
- [Available Formatters](#available-formatters)
|
- [Available Formatters](#available-formatters)
|
||||||
- [Friendly](#friendly)
|
- [Friendly](#friendly)
|
||||||
- [Stylish](#stylish)
|
- [Stylish](#stylish)
|
||||||
@ -51,7 +53,7 @@ Here's how `revive` is different from `golint`:
|
|||||||
- [Custom Formatter](#custom-formatter)
|
- [Custom Formatter](#custom-formatter)
|
||||||
- [Speed Comparison](#speed-comparison)
|
- [Speed Comparison](#speed-comparison)
|
||||||
- [golint](#golint)
|
- [golint](#golint)
|
||||||
- [revive](#revive-1)
|
- [revive](#revive)
|
||||||
- [Contributors](#contributors)
|
- [Contributors](#contributors)
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
|
|
||||||
@ -271,6 +273,9 @@ List of all available rules. The rules ported from `golint` are left unchanged a
|
|||||||
| `redefines-builtin-id`| n/a | Warns on redefinitions of builtin identifiers | no | no |
|
| `redefines-builtin-id`| n/a | Warns on redefinitions of builtin identifiers | no | no |
|
||||||
| `function-result-limit` | int | Specifies the maximum number of results a function can return | no | no |
|
| `function-result-limit` | int | Specifies the maximum number of results a function can return | no | no |
|
||||||
| `imports-blacklist` | []string | Disallows importing the specified packages | no | no |
|
| `imports-blacklist` | []string | Disallows importing the specified packages | no | no |
|
||||||
|
| `range-val-in-closure`| n/a | Warns if range value is used in a closure dispatched as goroutine| no | no |
|
||||||
|
| `waitgroup-by-value` | n/a | Warns on functions taking sync.WaitGroup as a by-value parameter | no | no |
|
||||||
|
| `atomic` | n/a | Check for common mistaken usages of the `sync/atomic` package | no | no |
|
||||||
| `empty-lines` | n/a | Warns when there are heading or trailing newlines in a block | no | no |
|
| `empty-lines` | n/a | Warns when there are heading or trailing newlines in a block | no | no |
|
||||||
|
|
||||||
## Configurable rules
|
## Configurable rules
|
||||||
|
@ -67,6 +67,10 @@ var allRules = append([]lint.Rule{
|
|||||||
&rule.ImportsBlacklistRule{},
|
&rule.ImportsBlacklistRule{},
|
||||||
&rule.FunctionResultsLimitRule{},
|
&rule.FunctionResultsLimitRule{},
|
||||||
&rule.MaxPublicStructsRule{},
|
&rule.MaxPublicStructsRule{},
|
||||||
|
&rule.RangeValInClosureRule{},
|
||||||
|
&rule.WaitGroupByValueRule{},
|
||||||
|
&rule.AtomicRule{},
|
||||||
|
&rule.EmptyLinesRule{},
|
||||||
}, defaultRules...)
|
}, defaultRules...)
|
||||||
|
|
||||||
var allFormatters = []lint.Formatter{
|
var allFormatters = []lint.Formatter{
|
||||||
|
45
fixtures/atomic.go
Normal file
45
fixtures/atomic.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package fixtures
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Counter uint64
|
||||||
|
|
||||||
|
func AtomicTests() {
|
||||||
|
x := uint64(1)
|
||||||
|
x = atomic.AddUint64(&x, 1) // MATCH /direct assignment to atomic value/
|
||||||
|
_, x = 10, atomic.AddUint64(&x, 1) // MATCH /direct assignment to atomic value/
|
||||||
|
x, _ = atomic.AddUint64(&x, 1), 10 // MATCH /direct assignment to atomic value/
|
||||||
|
|
||||||
|
y := &x
|
||||||
|
*y = atomic.AddUint64(y, 1) // MATCH /direct assignment to atomic value/
|
||||||
|
|
||||||
|
var su struct{ Counter uint64 }
|
||||||
|
su.Counter = atomic.AddUint64(&su.Counter, 1) // MATCH /direct assignment to atomic value/
|
||||||
|
z1 := atomic.AddUint64(&su.Counter, 1)
|
||||||
|
_ = z1 // Avoid err "z declared and not used"
|
||||||
|
|
||||||
|
var sp struct{ Counter *uint64 }
|
||||||
|
*sp.Counter = atomic.AddUint64(sp.Counter, 1) // MATCH /direct assignment to atomic value/
|
||||||
|
z2 := atomic.AddUint64(sp.Counter, 1)
|
||||||
|
_ = z2 // Avoid err "z declared and not used"
|
||||||
|
|
||||||
|
au := []uint64{10, 20}
|
||||||
|
au[0] = atomic.AddUint64(&au[0], 1) // MATCH /direct assignment to atomic value/
|
||||||
|
au[1] = atomic.AddUint64(&au[0], 1)
|
||||||
|
|
||||||
|
ap := []*uint64{&au[0], &au[1]}
|
||||||
|
*ap[0] = atomic.AddUint64(ap[0], 1) // MATCH /direct assignment to atomic value/
|
||||||
|
*ap[1] = atomic.AddUint64(ap[0], 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
type T struct{}
|
||||||
|
|
||||||
|
func (T) AddUint64(addr *uint64, delta uint64) uint64 { return 0 }
|
||||||
|
|
||||||
|
func NonAtomic() {
|
||||||
|
x := uint64(1)
|
||||||
|
var atomic T
|
||||||
|
x = atomic.AddUint64(&x, 1) // MATCH /direct assignment to atomic value/
|
||||||
|
}
|
59
fixtures/empty-lines.go
Normal file
59
fixtures/empty-lines.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Test of empty-lines.
|
||||||
|
|
||||||
|
package fixtures
|
||||||
|
|
||||||
|
func f1(x *int) bool { // MATCH /extra empty line at the start of a block/
|
||||||
|
|
||||||
|
return x > 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func f2(x *int) bool {
|
||||||
|
return x > 2 // MATCH /extra empty line at the end of a block/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func f3(x *int) bool { // MATCH /extra empty line at the start of a block/
|
||||||
|
|
||||||
|
return x > 2 // MATCH /extra empty line at the end of a block/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func f4(x *int) bool {
|
||||||
|
// This is fine.
|
||||||
|
return x > 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func f5(x *int) bool { // MATCH /extra empty line at the start of a block/
|
||||||
|
|
||||||
|
// This is _not_ fine.
|
||||||
|
return x > 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func f6(x *int) bool {
|
||||||
|
return x > 2
|
||||||
|
// This is fine.
|
||||||
|
}
|
||||||
|
|
||||||
|
func f7(x *int) bool {
|
||||||
|
return x > 2 // MATCH /extra empty line at the end of a block/
|
||||||
|
// This is _not_ fine.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func f8(*int) bool {
|
||||||
|
if x > 2 { // MATCH /extra empty line at the start of a block/
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func f9(*int) bool {
|
||||||
|
if x > 2 {
|
||||||
|
return true // MATCH /extra empty line at the end of a block/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
37
fixtures/range-val-in-closure.go
Normal file
37
fixtures/range-val-in-closure.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package fixtures
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func foo() {
|
||||||
|
mySlice := []string{"A", "B", "C"}
|
||||||
|
for index, value := range mySlice {
|
||||||
|
go func() {
|
||||||
|
fmt.Printf("Index: %d\n", index) // MATCH /loop variable index captured by func literal/
|
||||||
|
fmt.Printf("Value: %s\n", value) // MATCH /loop variable value captured by func literal/
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
myDict := make(map[string]int)
|
||||||
|
myDict["A"] = 1
|
||||||
|
myDict["B"] = 2
|
||||||
|
myDict["C"] = 3
|
||||||
|
for key, value := range myDict {
|
||||||
|
defer func() {
|
||||||
|
fmt.Printf("Index: %d\n", key) // MATCH /loop variable key captured by func literal/
|
||||||
|
fmt.Printf("Value: %s\n", value) // MATCH /loop variable value captured by func literal/
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, newg := range groups {
|
||||||
|
go func(newg int) {
|
||||||
|
newg.run(m.opts.Context,i) // MATCH /loop variable i captured by func literal/
|
||||||
|
}(newg)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, newg := range groups {
|
||||||
|
newg := newg
|
||||||
|
go func() {
|
||||||
|
newg.run(m.opts.Context,i) // MATCH /loop variable i captured by func literal/
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
21
fixtures/waitgroup-by-value.go
Normal file
21
fixtures/waitgroup-by-value.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package fixtures
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func foo(a int, b float32, c char, d sync.WaitGroup) { // MATCH /sync.WaitGroup passed by value, the function will get a copy of the original one/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func bar(a, b sync.WaitGroup) { // MATCH /sync.WaitGroup passed by value, the function will get a copy of the original one/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func baz(zz sync.WaitGroup) { // MATCH /sync.WaitGroup passed by value, the function will get a copy of the original one/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ok(zz *sync.WaitGroup) {
|
||||||
|
|
||||||
|
}
|
@ -18,7 +18,7 @@ func (f *Default) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Format formats the failures gotten from the lint.
|
// Format formats the failures gotten from the lint.
|
||||||
func (f *Default) Format(failures <-chan lint.Failure, config lint.RulesConfig) (string, error) {
|
func (f *Default) Format(failures <-chan lint.Failure, _ lint.RulesConfig) (string, error) {
|
||||||
for failure := range failures {
|
for failure := range failures {
|
||||||
fmt.Printf("%v: %s\n", failure.Position.Start, failure.Failure)
|
fmt.Printf("%v: %s\n", failure.Position.Start, failure.Failure)
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ func (f *Unix) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Format formats the failures gotten from the lint.
|
// Format formats the failures gotten from the lint.
|
||||||
func (f *Unix) Format(failures <-chan lint.Failure, config lint.RulesConfig) (string, error) {
|
func (f *Unix) Format(failures <-chan lint.Failure, _ lint.RulesConfig) (string, error) {
|
||||||
for failure := range failures {
|
for failure := range failures {
|
||||||
fmt.Printf("%v: [%s] %s\n", failure.Position.Start, failure.RuleName, failure.Failure)
|
fmt.Printf("%v: [%s] %s\n", failure.Position.Start, failure.RuleName, failure.Failure)
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,11 @@ func (f *File) Render(x interface{}) string {
|
|||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommentMap builds a comment map for the file.
|
||||||
|
func (f *File) CommentMap() ast.CommentMap {
|
||||||
|
return ast.NewCommentMap(f.Pkg.fset, f.AST, f.AST.Comments)
|
||||||
|
}
|
||||||
|
|
||||||
var basicTypeKinds = map[types.BasicKind]string{
|
var basicTypeKinds = map[types.BasicKind]string{
|
||||||
types.UntypedBool: "bool",
|
types.UntypedBool: "bool",
|
||||||
types.UntypedInt: "int",
|
types.UntypedInt: "int",
|
||||||
|
@ -77,7 +77,9 @@ func (p *Package) TypeCheck() error {
|
|||||||
anyFile = f
|
anyFile = f
|
||||||
astFiles = append(astFiles, f.AST)
|
astFiles = append(astFiles, f.AST)
|
||||||
}
|
}
|
||||||
typesPkg, err := config.Check(anyFile.AST.Name.Name, p.fset, astFiles, info)
|
|
||||||
|
typesPkg, err := check(config, anyFile.AST.Name.Name, p.fset, astFiles, info)
|
||||||
|
|
||||||
// Remember the typechecking info, even if config.Check failed,
|
// Remember the typechecking info, even if config.Check failed,
|
||||||
// since we will get partial information.
|
// since we will get partial information.
|
||||||
p.TypesPkg = typesPkg
|
p.TypesPkg = typesPkg
|
||||||
@ -86,6 +88,20 @@ func (p *Package) TypeCheck() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check function encapsulates the call to go/types.Config.Check method and
|
||||||
|
// recovers if the called method panics (see issue #59)
|
||||||
|
func check(config *types.Config, n string, fset *token.FileSet, astFiles []*ast.File, info *types.Info) (p *types.Package, err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err, _ = r.(error)
|
||||||
|
p = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return config.Check(n, fset, astFiles, info)
|
||||||
|
}
|
||||||
|
|
||||||
// TypeOf returns the type of an expression.
|
// TypeOf returns the type of an expression.
|
||||||
func (p *Package) TypeOf(expr ast.Expr) types.Type {
|
func (p *Package) TypeOf(expr ast.Expr) types.Type {
|
||||||
if p.TypesInfo == nil {
|
if p.TypesInfo == nil {
|
||||||
|
94
rule/atomic.go
Normal file
94
rule/atomic.go
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package rule
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
|
||||||
|
"github.com/mgechev/revive/lint"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AtomicRule lints given else constructs.
|
||||||
|
type AtomicRule struct{}
|
||||||
|
|
||||||
|
// Apply applies the rule to given file.
|
||||||
|
func (r *AtomicRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
|
var failures []lint.Failure
|
||||||
|
walker := atomic{
|
||||||
|
pkgTypesInfo: file.Pkg.TypesInfo,
|
||||||
|
onFailure: func(failure lint.Failure) {
|
||||||
|
failures = append(failures, failure)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ast.Walk(walker, file.AST)
|
||||||
|
|
||||||
|
return failures
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the rule name.
|
||||||
|
func (r *AtomicRule) Name() string {
|
||||||
|
return "atomic"
|
||||||
|
}
|
||||||
|
|
||||||
|
type atomic struct {
|
||||||
|
pkgTypesInfo *types.Info
|
||||||
|
onFailure func(lint.Failure)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w atomic) Visit(node ast.Node) ast.Visitor {
|
||||||
|
n, ok := node.(*ast.AssignStmt)
|
||||||
|
if !ok {
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(n.Lhs) != len(n.Rhs) {
|
||||||
|
return nil // skip assignment sub-tree
|
||||||
|
}
|
||||||
|
if len(n.Lhs) == 1 && n.Tok == token.DEFINE {
|
||||||
|
return nil // skip assignment sub-tree
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, right := range n.Rhs {
|
||||||
|
call, ok := right.(*ast.CallExpr)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sel, ok := call.Fun.(*ast.SelectorExpr)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pkgIdent, _ := sel.X.(*ast.Ident)
|
||||||
|
if w.pkgTypesInfo != nil {
|
||||||
|
pkgName, ok := w.pkgTypesInfo.Uses[pkgIdent].(*types.PkgName)
|
||||||
|
if !ok || pkgName.Imported().Path() != "sync/atomic" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch sel.Sel.Name {
|
||||||
|
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
|
||||||
|
left := n.Lhs[i]
|
||||||
|
if len(call.Args) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
arg := call.Args[0]
|
||||||
|
broken := false
|
||||||
|
|
||||||
|
if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND {
|
||||||
|
broken = gofmt(left) == gofmt(uarg.X)
|
||||||
|
} else if star, ok := left.(*ast.StarExpr); ok {
|
||||||
|
broken = gofmt(star.X) == gofmt(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if broken {
|
||||||
|
w.onFailure(lint.Failure{
|
||||||
|
Confidence: 1,
|
||||||
|
Failure: "direct assignment to atomic value",
|
||||||
|
Node: n,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
}
|
@ -10,7 +10,7 @@ import (
|
|||||||
type BlankImportsRule struct{}
|
type BlankImportsRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *BlankImportsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *BlankImportsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
fileAst := file.AST
|
fileAst := file.AST
|
||||||
@ -38,7 +38,7 @@ type lintBlankImports struct {
|
|||||||
onFailure func(lint.Failure)
|
onFailure func(lint.Failure)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w lintBlankImports) Visit(n ast.Node) ast.Visitor {
|
func (w lintBlankImports) Visit(_ ast.Node) ast.Visitor {
|
||||||
// In package main and in tests, we don't complain about blank imports.
|
// In package main and in tests, we don't complain about blank imports.
|
||||||
if w.file.Pkg.IsMain() || w.file.IsTest() {
|
if w.file.Pkg.IsMain() || w.file.IsTest() {
|
||||||
return nil
|
return nil
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
type BoolLiteralRule struct{}
|
type BoolLiteralRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *BoolLiteralRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *BoolLiteralRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
onFailure := func(failure lint.Failure) {
|
onFailure := func(failure lint.Failure) {
|
||||||
|
@ -49,7 +49,7 @@ var allPkgs = packages{pkgs: make([]pkgMethods, 1)}
|
|||||||
type ConfusingNamingRule struct{}
|
type ConfusingNamingRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *ConfusingNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *ConfusingNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
fileAst := file.AST
|
fileAst := file.AST
|
||||||
pkgm := allPkgs.methodNames(file.Pkg)
|
pkgm := allPkgs.methodNames(file.Pkg)
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
type ConfusingResultsRule struct{}
|
type ConfusingResultsRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *ConfusingResultsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *ConfusingResultsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
fileAst := file.AST
|
fileAst := file.AST
|
||||||
@ -59,9 +59,9 @@ func (w lintConfusingResults) Visit(n ast.Node) ast.Visitor {
|
|||||||
Failure: "unnamed results of the same type may be confusing, consider using named results",
|
Failure: "unnamed results of the same type may be confusing, consider using named results",
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
} else {
|
|
||||||
lastType = t.Name
|
|
||||||
}
|
}
|
||||||
|
lastType = t.Name
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return w
|
return w
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
package rule
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"github.com/mgechev/revive/lint"
|
"github.com/mgechev/revive/lint"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/format"
|
|
||||||
"go/token"
|
"go/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,7 +10,7 @@ import (
|
|||||||
type ConstantLogicalExprRule struct{}
|
type ConstantLogicalExprRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *ConstantLogicalExprRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *ConstantLogicalExprRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
onFailure := func(failure lint.Failure) {
|
onFailure := func(failure lint.Failure) {
|
||||||
@ -43,7 +40,7 @@ func (w *lintConstantLogicalExpr) Visit(node ast.Node) ast.Visitor {
|
|||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
if !w.areEqual(n.X, n.Y) {
|
if gofmt(n.X) != gofmt(n.Y) { // check if subexpressions are the same
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,21 +78,6 @@ func (w *lintConstantLogicalExpr) isInequalityOperator(t token.Token) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w lintConstantLogicalExpr) areEqual(x, y ast.Expr) bool {
|
|
||||||
fset := token.NewFileSet()
|
|
||||||
var buf1 bytes.Buffer
|
|
||||||
if err := format.Node(&buf1, fset, x); err != nil {
|
|
||||||
return false // keep going in case of error
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf2 bytes.Buffer
|
|
||||||
if err := format.Node(&buf2, fset, y); err != nil {
|
|
||||||
return false // keep going in case of error
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%s", buf1.Bytes()) == fmt.Sprintf("%s", buf2.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w lintConstantLogicalExpr) newFailure(node ast.Node, msg string) {
|
func (w lintConstantLogicalExpr) newFailure(node ast.Node, msg string) {
|
||||||
w.onFailure(lint.Failure{
|
w.onFailure(lint.Failure{
|
||||||
Confidence: 1,
|
Confidence: 1,
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
type ContextAsArgumentRule struct{}
|
type ContextAsArgumentRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *ContextAsArgumentRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *ContextAsArgumentRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
fileAst := file.AST
|
fileAst := file.AST
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
type ContextKeysType struct{}
|
type ContextKeysType struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *ContextKeysType) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *ContextKeysType) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
fileAst := file.AST
|
fileAst := file.AST
|
||||||
|
@ -47,7 +47,7 @@ type lintCyclomatic struct {
|
|||||||
onFailure func(lint.Failure)
|
onFailure func(lint.Failure)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w lintCyclomatic) Visit(n ast.Node) ast.Visitor {
|
func (w lintCyclomatic) Visit(_ ast.Node) ast.Visitor {
|
||||||
f := w.file
|
f := w.file
|
||||||
for _, decl := range f.AST.Decls {
|
for _, decl := range f.AST.Decls {
|
||||||
if fn, ok := decl.(*ast.FuncDecl); ok {
|
if fn, ok := decl.(*ast.FuncDecl); ok {
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
type DeepExitRule struct{}
|
type DeepExitRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *DeepExitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *DeepExitRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
onFailure := func(failure lint.Failure) {
|
onFailure := func(failure lint.Failure) {
|
||||||
failures = append(failures, failure)
|
failures = append(failures, failure)
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
type DotImportsRule struct{}
|
type DotImportsRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *DotImportsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *DotImportsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
fileAst := file.AST
|
fileAst := file.AST
|
||||||
@ -38,7 +38,7 @@ type lintImports struct {
|
|||||||
onFailure func(lint.Failure)
|
onFailure func(lint.Failure)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w lintImports) Visit(n ast.Node) ast.Visitor {
|
func (w lintImports) Visit(_ ast.Node) ast.Visitor {
|
||||||
for i, is := range w.fileAst.Imports {
|
for i, is := range w.fileAst.Imports {
|
||||||
_ = i
|
_ = i
|
||||||
if is.Name != nil && is.Name.Name == "." && !w.file.IsTest() {
|
if is.Name != nil && is.Name.Name == "." && !w.file.IsTest() {
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
type EmptyBlockRule struct{}
|
type EmptyBlockRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *EmptyBlockRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *EmptyBlockRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
onFailure := func(failure lint.Failure) {
|
onFailure := func(failure lint.Failure) {
|
||||||
|
113
rule/empty-lines.go
Normal file
113
rule/empty-lines.go
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
package rule
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
|
"github.com/mgechev/revive/lint"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EmptyLinesRule lints empty lines in blocks.
|
||||||
|
type EmptyLinesRule struct{}
|
||||||
|
|
||||||
|
// Apply applies the rule to given file.
|
||||||
|
func (r *EmptyLinesRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
||||||
|
var failures []lint.Failure
|
||||||
|
|
||||||
|
onFailure := func(failure lint.Failure) {
|
||||||
|
failures = append(failures, failure)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := lintEmptyLines{file, file.CommentMap(), onFailure}
|
||||||
|
ast.Walk(w, file.AST)
|
||||||
|
return failures
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the rule name.
|
||||||
|
func (r *EmptyLinesRule) Name() string {
|
||||||
|
return "empty-lines"
|
||||||
|
}
|
||||||
|
|
||||||
|
type lintEmptyLines struct {
|
||||||
|
file *lint.File
|
||||||
|
cmap ast.CommentMap
|
||||||
|
onFailure func(lint.Failure)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w lintEmptyLines) Visit(node ast.Node) ast.Visitor {
|
||||||
|
block, ok := node.(*ast.BlockStmt)
|
||||||
|
if !ok {
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
w.checkStart(block)
|
||||||
|
w.checkEnd(block)
|
||||||
|
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w lintEmptyLines) checkStart(block *ast.BlockStmt) {
|
||||||
|
if len(block.List) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
start := w.position(block.Lbrace)
|
||||||
|
firstNode := block.List[0]
|
||||||
|
|
||||||
|
if w.commentBetween(start, firstNode) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
first := w.position(firstNode.Pos())
|
||||||
|
if first.Line-start.Line > 1 {
|
||||||
|
w.onFailure(lint.Failure{
|
||||||
|
Confidence: 1,
|
||||||
|
Node: block,
|
||||||
|
Category: "style",
|
||||||
|
URL: "#empty-lines",
|
||||||
|
Failure: "extra empty line at the start of a block",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w lintEmptyLines) checkEnd(block *ast.BlockStmt) {
|
||||||
|
if len(block.List) < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
end := w.position(block.Rbrace)
|
||||||
|
lastNode := block.List[len(block.List)-1]
|
||||||
|
|
||||||
|
if w.commentBetween(end, lastNode) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
last := w.position(lastNode.Pos())
|
||||||
|
if end.Line-last.Line > 1 {
|
||||||
|
w.onFailure(lint.Failure{
|
||||||
|
Confidence: 1,
|
||||||
|
Node: lastNode,
|
||||||
|
Category: "style",
|
||||||
|
URL: "#empty-lines",
|
||||||
|
Failure: "extra empty line at the end of a block",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w lintEmptyLines) commentBetween(position token.Position, node ast.Node) bool {
|
||||||
|
comments := w.cmap.Filter(node).Comments()
|
||||||
|
if len(comments) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
commentStart := w.position(comments[0].Pos())
|
||||||
|
if commentStart.Line-position.Line == 1 || commentStart.Line-position.Line == -1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w lintEmptyLines) position(pos token.Pos) token.Position {
|
||||||
|
return w.file.ToPosition(pos)
|
||||||
|
}
|
@ -13,7 +13,7 @@ import (
|
|||||||
type ErrorNamingRule struct{}
|
type ErrorNamingRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *ErrorNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *ErrorNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
fileAst := file.AST
|
fileAst := file.AST
|
||||||
@ -41,7 +41,7 @@ type lintErrors struct {
|
|||||||
onFailure func(lint.Failure)
|
onFailure func(lint.Failure)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w lintErrors) Visit(n ast.Node) ast.Visitor {
|
func (w lintErrors) Visit(_ ast.Node) ast.Visitor {
|
||||||
for _, decl := range w.fileAst.Decls {
|
for _, decl := range w.fileAst.Decls {
|
||||||
gd, ok := decl.(*ast.GenDecl)
|
gd, ok := decl.(*ast.GenDecl)
|
||||||
if !ok || gd.Tok != token.VAR {
|
if !ok || gd.Tok != token.VAR {
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
type ErrorReturnRule struct{}
|
type ErrorReturnRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *ErrorReturnRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *ErrorReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
fileAst := file.AST
|
fileAst := file.AST
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
type ErrorStringsRule struct{}
|
type ErrorStringsRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *ErrorStringsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *ErrorStringsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
fileAst := file.AST
|
fileAst := file.AST
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
type ErrorfRule struct{}
|
type ErrorfRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *ErrorfRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *ErrorfRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
fileAst := file.AST
|
fileAst := file.AST
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
type ExportedRule struct{}
|
type ExportedRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *ExportedRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *ExportedRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
if isTest(file) {
|
if isTest(file) {
|
||||||
|
@ -51,7 +51,7 @@ type lintFileHeader struct {
|
|||||||
onFailure func(lint.Failure)
|
onFailure func(lint.Failure)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w lintFileHeader) Visit(n ast.Node) ast.Visitor {
|
func (w lintFileHeader) Visit(_ ast.Node) ast.Visitor {
|
||||||
g := w.fileAst.Comments[0]
|
g := w.fileAst.Comments[0]
|
||||||
failure := lint.Failure{
|
failure := lint.Failure{
|
||||||
Node: w.fileAst,
|
Node: w.fileAst,
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
type FlagParamRule struct{}
|
type FlagParamRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *FlagParamRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *FlagParamRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
onFailure := func(failure lint.Failure) {
|
onFailure := func(failure lint.Failure) {
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
type GetReturnRule struct{}
|
type GetReturnRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *GetReturnRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *GetReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
onFailure := func(failure lint.Failure) {
|
onFailure := func(failure lint.Failure) {
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
type IfReturnRule struct{}
|
type IfReturnRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *IfReturnRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *IfReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
onFailure := func(failure lint.Failure) {
|
onFailure := func(failure lint.Failure) {
|
||||||
|
@ -54,7 +54,7 @@ type blacklistedImports struct {
|
|||||||
blacklist map[string]bool
|
blacklist map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w blacklistedImports) Visit(n ast.Node) ast.Visitor {
|
func (w blacklistedImports) Visit(_ ast.Node) ast.Visitor {
|
||||||
for _, is := range w.fileAst.Imports {
|
for _, is := range w.fileAst.Imports {
|
||||||
if is.Path != nil && !w.file.IsTest() && w.blacklist[is.Path.Value] {
|
if is.Path != nil && !w.file.IsTest() && w.blacklist[is.Path.Value] {
|
||||||
w.onFailure(lint.Failure{
|
w.onFailure(lint.Failure{
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
type IncrementDecrementRule struct{}
|
type IncrementDecrementRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *IncrementDecrementRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *IncrementDecrementRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
fileAst := file.AST
|
fileAst := file.AST
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
type IndentErrorFlowRule struct{}
|
type IndentErrorFlowRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *IndentErrorFlowRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *IndentErrorFlowRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
onFailure := func(failure lint.Failure) {
|
onFailure := func(failure lint.Failure) {
|
||||||
@ -77,4 +77,3 @@ func (w lintElse) Visit(node ast.Node) ast.Visitor {
|
|||||||
}
|
}
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
type ModifiesParamRule struct{}
|
type ModifiesParamRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *ModifiesParamRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *ModifiesParamRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
onFailure := func(failure lint.Failure) {
|
onFailure := func(failure lint.Failure) {
|
||||||
|
@ -124,7 +124,7 @@ func (w lintModifiesValRecRule) skipType(t ast.Expr) bool {
|
|||||||
return strings.HasPrefix(rtName, "[]") || strings.HasPrefix(rtName, "map[")
|
return strings.HasPrefix(rtName, "[]") || strings.HasPrefix(rtName, "map[")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_ lintModifiesValRecRule) getNameFromExpr(ie ast.Expr) string {
|
func (lintModifiesValRecRule) getNameFromExpr(ie ast.Expr) string {
|
||||||
ident, ok := ie.(*ast.Ident)
|
ident, ok := ie.(*ast.Ident)
|
||||||
if !ok {
|
if !ok {
|
||||||
return ""
|
return ""
|
||||||
|
@ -17,7 +17,7 @@ import (
|
|||||||
type PackageCommentsRule struct{}
|
type PackageCommentsRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *PackageCommentsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *PackageCommentsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
if isTest(file) {
|
if isTest(file) {
|
||||||
@ -45,7 +45,7 @@ type lintPackageComments struct {
|
|||||||
onFailure func(lint.Failure)
|
onFailure func(lint.Failure)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *lintPackageComments) Visit(n ast.Node) ast.Visitor {
|
func (l *lintPackageComments) Visit(_ ast.Node) ast.Visitor {
|
||||||
if l.file.IsTest() {
|
if l.file.IsTest() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
111
rule/range-val-in-closure.go
Normal file
111
rule/range-val-in-closure.go
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
package rule
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
|
||||||
|
"github.com/mgechev/revive/lint"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RangeValInClosureRule lints given else constructs.
|
||||||
|
type RangeValInClosureRule struct{}
|
||||||
|
|
||||||
|
// Apply applies the rule to given file.
|
||||||
|
func (r *RangeValInClosureRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
|
var failures []lint.Failure
|
||||||
|
|
||||||
|
walker := rangeValInClosure{
|
||||||
|
onFailure: func(failure lint.Failure) {
|
||||||
|
failures = append(failures, failure)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ast.Walk(walker, file.AST)
|
||||||
|
|
||||||
|
return failures
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the rule name.
|
||||||
|
func (r *RangeValInClosureRule) Name() string {
|
||||||
|
return "range-val-in-closure"
|
||||||
|
}
|
||||||
|
|
||||||
|
type rangeValInClosure struct {
|
||||||
|
onFailure func(lint.Failure)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w rangeValInClosure) Visit(node ast.Node) ast.Visitor {
|
||||||
|
|
||||||
|
// Find the variables updated by the loop statement.
|
||||||
|
var vars []*ast.Ident
|
||||||
|
addVar := func(expr ast.Expr) {
|
||||||
|
if id, ok := expr.(*ast.Ident); ok {
|
||||||
|
vars = append(vars, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var body *ast.BlockStmt
|
||||||
|
switch n := node.(type) {
|
||||||
|
case *ast.RangeStmt:
|
||||||
|
body = n.Body
|
||||||
|
addVar(n.Key)
|
||||||
|
addVar(n.Value)
|
||||||
|
case *ast.ForStmt:
|
||||||
|
body = n.Body
|
||||||
|
switch post := n.Post.(type) {
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
// e.g. for p = head; p != nil; p = p.next
|
||||||
|
for _, lhs := range post.Lhs {
|
||||||
|
addVar(lhs)
|
||||||
|
}
|
||||||
|
case *ast.IncDecStmt:
|
||||||
|
// e.g. for i := 0; i < n; i++
|
||||||
|
addVar(post.X)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if vars == nil {
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inspect a go or defer statement
|
||||||
|
// if it's the last one in the loop body.
|
||||||
|
// (We give up if there are following statements,
|
||||||
|
// because it's hard to prove go isn't followed by wait,
|
||||||
|
// or defer by return.)
|
||||||
|
if len(body.List) == 0 {
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
var last *ast.CallExpr
|
||||||
|
switch s := body.List[len(body.List)-1].(type) {
|
||||||
|
case *ast.GoStmt:
|
||||||
|
last = s.Call
|
||||||
|
case *ast.DeferStmt:
|
||||||
|
last = s.Call
|
||||||
|
default:
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
lit, ok := last.Fun.(*ast.FuncLit)
|
||||||
|
if !ok {
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
if lit.Type == nil {
|
||||||
|
// Not referring to a variable (e.g. struct field name)
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
ast.Inspect(lit.Body, func(n ast.Node) bool {
|
||||||
|
id, ok := n.(*ast.Ident)
|
||||||
|
if !ok || id.Obj == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, v := range vars {
|
||||||
|
if v.Obj == id.Obj {
|
||||||
|
w.onFailure(lint.Failure{
|
||||||
|
Confidence: 1,
|
||||||
|
Failure: fmt.Sprintf("loop variable %v captured by func literal", id.Name),
|
||||||
|
Node: n,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return w
|
||||||
|
}
|
@ -12,7 +12,7 @@ import (
|
|||||||
type RangeRule struct{}
|
type RangeRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *RangeRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *RangeRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
onFailure := func(failure lint.Failure) {
|
onFailure := func(failure lint.Failure) {
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
type ReceiverNamingRule struct{}
|
type ReceiverNamingRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *ReceiverNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *ReceiverNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
fileAst := file.AST
|
fileAst := file.AST
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
type RedefinesBuiltinIDRule struct{}
|
type RedefinesBuiltinIDRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *RedefinesBuiltinIDRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *RedefinesBuiltinIDRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
var builtInConstAndVars = map[string]bool{
|
var builtInConstAndVars = map[string]bool{
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
type SuperfluousElseRule struct{}
|
type SuperfluousElseRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *SuperfluousElseRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *SuperfluousElseRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
onFailure := func(failure lint.Failure) {
|
onFailure := func(failure lint.Failure) {
|
||||||
failures = append(failures, failure)
|
failures = append(failures, failure)
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
type TimeNamingRule struct{}
|
type TimeNamingRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *TimeNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *TimeNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
onFailure := func(failure lint.Failure) {
|
onFailure := func(failure lint.Failure) {
|
||||||
@ -50,7 +50,7 @@ func (w *lintTimeNames) Visit(node ast.Node) ast.Visitor {
|
|||||||
if pt, ok := typ.(*types.Pointer); ok {
|
if pt, ok := typ.(*types.Pointer); ok {
|
||||||
typ = pt.Elem()
|
typ = pt.Elem()
|
||||||
}
|
}
|
||||||
if !isNamedType(w.file.Pkg, typ, "time", "Duration") {
|
if !isNamedType(typ, "time", "Duration") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
suffix := ""
|
suffix := ""
|
||||||
@ -83,7 +83,7 @@ var timeSuffixes = []string{
|
|||||||
"MS", "Ms",
|
"MS", "Ms",
|
||||||
}
|
}
|
||||||
|
|
||||||
func isNamedType(p *lint.Package, typ types.Type, importPath, name string) bool {
|
func isNamedType(typ types.Type, importPath, name string) bool {
|
||||||
n, ok := typ.(*types.Named)
|
n, ok := typ.(*types.Named)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
type UnexportedReturnRule struct{}
|
type UnexportedReturnRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *UnexportedReturnRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *UnexportedReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
fileAst := file.AST
|
fileAst := file.AST
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
type UnnecessaryStmtRule struct{}
|
type UnnecessaryStmtRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *UnnecessaryStmtRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *UnnecessaryStmtRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
onFailure := func(failure lint.Failure) {
|
onFailure := func(failure lint.Failure) {
|
||||||
failures = append(failures, failure)
|
failures = append(failures, failure)
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
type UnreachableCodeRule struct{}
|
type UnreachableCodeRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *UnreachableCodeRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *UnreachableCodeRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
onFailure := func(failure lint.Failure) {
|
onFailure := func(failure lint.Failure) {
|
||||||
failures = append(failures, failure)
|
failures = append(failures, failure)
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package rule
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
|
"go/printer"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -179,3 +181,11 @@ func isExprABooleanLit(n ast.Node) (lexeme string, ok bool) {
|
|||||||
|
|
||||||
return oper.Name, (oper.Name == trueName || oper.Name == falseName)
|
return oper.Name, (oper.Name == trueName || oper.Name == falseName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gofmt returns a string representation of the expression.
|
||||||
|
func gofmt(x ast.Expr) string {
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
fs := token.NewFileSet()
|
||||||
|
printer.Fprint(&buf, fs, x)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
type VarDeclarationsRule struct{}
|
type VarDeclarationsRule struct{}
|
||||||
|
|
||||||
// Apply applies the rule to given file.
|
// Apply applies the rule to given file.
|
||||||
func (r *VarDeclarationsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
func (r *VarDeclarationsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
var failures []lint.Failure
|
var failures []lint.Failure
|
||||||
|
|
||||||
fileAst := file.AST
|
fileAst := file.AST
|
||||||
|
66
rule/waitgroup-by-value.go
Normal file
66
rule/waitgroup-by-value.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package rule
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
|
||||||
|
"github.com/mgechev/revive/lint"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WaitGroupByValueRule lints sync.WaitGroup passed by copy in functions.
|
||||||
|
type WaitGroupByValueRule struct{}
|
||||||
|
|
||||||
|
// Apply applies the rule to given file.
|
||||||
|
func (r *WaitGroupByValueRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||||
|
var failures []lint.Failure
|
||||||
|
|
||||||
|
onFailure := func(failure lint.Failure) {
|
||||||
|
failures = append(failures, failure)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := lintWaitGroupByValueRule{onFailure: onFailure}
|
||||||
|
ast.Walk(w, file.AST)
|
||||||
|
return failures
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the rule name.
|
||||||
|
func (r *WaitGroupByValueRule) Name() string {
|
||||||
|
return "waitgroup-by-value"
|
||||||
|
}
|
||||||
|
|
||||||
|
type lintWaitGroupByValueRule struct {
|
||||||
|
onFailure func(lint.Failure)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w lintWaitGroupByValueRule) Visit(node ast.Node) ast.Visitor {
|
||||||
|
// look for function declarations
|
||||||
|
fd, ok := node.(*ast.FuncDecl)
|
||||||
|
if !ok {
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check all function's parameters
|
||||||
|
for _, field := range fd.Type.Params.List {
|
||||||
|
if !w.isWaitGroup(field.Type) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
w.onFailure(lint.Failure{
|
||||||
|
Confidence: 1,
|
||||||
|
Node: field,
|
||||||
|
Failure: "sync.WaitGroup passed by value, the function will get a copy of the original one",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lintWaitGroupByValueRule) isWaitGroup(ft ast.Expr) bool {
|
||||||
|
se, ok := ft.(*ast.SelectorExpr)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
x, _ := se.X.(*ast.Ident)
|
||||||
|
sel := se.Sel.Name
|
||||||
|
return x.Name == "sync" && sel == "WaitGroup"
|
||||||
|
}
|
12
test/atomic_test.go
Normal file
12
test/atomic_test.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mgechev/revive/rule"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Atomic rule.
|
||||||
|
func TestAtomic(t *testing.T) {
|
||||||
|
testRule(t, "atomic", &rule.AtomicRule{})
|
||||||
|
}
|
12
test/empty-lines_test.go
Normal file
12
test/empty-lines_test.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mgechev/revive/rule"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestEmptyLines rule.
|
||||||
|
func TestEmptyLines(t *testing.T) {
|
||||||
|
testRule(t, "empty-lines", &rule.EmptyLinesRule{})
|
||||||
|
}
|
12
test/range-val-in-closure_test.go
Normal file
12
test/range-val-in-closure_test.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mgechev/revive/lint"
|
||||||
|
"github.com/mgechev/revive/rule"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRangeValInClosure(t *testing.T) {
|
||||||
|
testRule(t, "range-val-in-closure", &rule.RangeValInClosureRule{}, &lint.RuleConfig{})
|
||||||
|
}
|
@ -35,13 +35,13 @@ func testRule(t *testing.T, filename string, rule lint.Rule, config ...*lint.Rul
|
|||||||
c[rule.Name()] = *config[0]
|
c[rule.Name()] = *config[0]
|
||||||
}
|
}
|
||||||
if parseInstructions(t, filename, src) == nil {
|
if parseInstructions(t, filename, src) == nil {
|
||||||
assertSuccess(t, baseDir, stat, src, []lint.Rule{rule}, c)
|
assertSuccess(t, baseDir, stat, []lint.Rule{rule}, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
assertFailures(t, baseDir, stat, src, []lint.Rule{rule}, c)
|
assertFailures(t, baseDir, stat, src, []lint.Rule{rule}, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertSuccess(t *testing.T, baseDir string, fi os.FileInfo, src []byte, rules []lint.Rule, config map[string]lint.RuleConfig) error {
|
func assertSuccess(t *testing.T, baseDir string, fi os.FileInfo, rules []lint.Rule, config map[string]lint.RuleConfig) error {
|
||||||
l := lint.New(func(file string) ([]byte, error) {
|
l := lint.New(func(file string) ([]byte, error) {
|
||||||
return ioutil.ReadFile(baseDir + file)
|
return ioutil.ReadFile(baseDir + file)
|
||||||
})
|
})
|
||||||
@ -220,7 +220,8 @@ func srcLine(src []byte, p token.Position) string {
|
|||||||
return string(src[lo:hi])
|
return string(src[lo:hi])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLine(t *testing.T) {
|
// TestLine tests srcLine function
|
||||||
|
func TestLine(t *testing.T) { //revive:disable-line:exported
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
src string
|
src string
|
||||||
offset int
|
offset int
|
||||||
@ -242,7 +243,8 @@ func TestLine(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLintName(t *testing.T) {
|
// TestLintName tests lint.Name function
|
||||||
|
func TestLintName(t *testing.T) { //revive:disable-line:exported
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name, want string
|
name, want string
|
||||||
}{
|
}{
|
||||||
@ -301,7 +303,8 @@ func exportedType(typ types.Type) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExportedType(t *testing.T) {
|
// TestExportedType tests exportedType function
|
||||||
|
func TestExportedType(t *testing.T) { //revive:disable-line:exported
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
typString string
|
typString string
|
||||||
exp bool
|
exp bool
|
||||||
@ -356,7 +359,8 @@ func isGenerated(src []byte) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsGenerated(t *testing.T) {
|
// TestIsGenerated tests isGenerated function
|
||||||
|
func TestIsGenerated(t *testing.T) { //revive:disable-line:exported
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
source string
|
source string
|
||||||
generated bool
|
generated bool
|
||||||
|
11
test/waitgroup-by-value_test.go
Normal file
11
test/waitgroup-by-value_test.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mgechev/revive/rule"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWaitGroupByValue(t *testing.T) {
|
||||||
|
testRule(t, "waitgroup-by-value", &rule.WaitGroupByValueRule{})
|
||||||
|
}
|
@ -13,3 +13,6 @@
|
|||||||
[rule.receiver-naming]
|
[rule.receiver-naming]
|
||||||
[rule.indent-error-flow]
|
[rule.indent-error-flow]
|
||||||
[rule.empty-block]
|
[rule.empty-block]
|
||||||
|
[rule.range-val-in-closure]
|
||||||
|
[rule.waitgroup-by-value]
|
||||||
|
[rule.atomic]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user