mirror of
https://github.com/mgechev/revive.git
synced 2025-03-29 21:47:12 +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)
|
||||
- [Recommended Configuration](#recommended-configuration)
|
||||
- [Available Rules](#available-rules)
|
||||
- [Configurable rules](#configurable-rules)
|
||||
- [`var-naming`](#var-naming)
|
||||
- [Available Formatters](#available-formatters)
|
||||
- [Friendly](#friendly)
|
||||
- [Stylish](#stylish)
|
||||
@ -51,7 +53,7 @@ Here's how `revive` is different from `golint`:
|
||||
- [Custom Formatter](#custom-formatter)
|
||||
- [Speed Comparison](#speed-comparison)
|
||||
- [golint](#golint)
|
||||
- [revive](#revive-1)
|
||||
- [revive](#revive)
|
||||
- [Contributors](#contributors)
|
||||
- [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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|
||||
## Configurable rules
|
||||
|
@ -67,6 +67,10 @@ var allRules = append([]lint.Rule{
|
||||
&rule.ImportsBlacklistRule{},
|
||||
&rule.FunctionResultsLimitRule{},
|
||||
&rule.MaxPublicStructsRule{},
|
||||
&rule.RangeValInClosureRule{},
|
||||
&rule.WaitGroupByValueRule{},
|
||||
&rule.AtomicRule{},
|
||||
&rule.EmptyLinesRule{},
|
||||
}, defaultRules...)
|
||||
|
||||
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.
|
||||
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 {
|
||||
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.
|
||||
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 {
|
||||
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()
|
||||
}
|
||||
|
||||
// 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{
|
||||
types.UntypedBool: "bool",
|
||||
types.UntypedInt: "int",
|
||||
|
@ -77,7 +77,9 @@ func (p *Package) TypeCheck() error {
|
||||
anyFile = f
|
||||
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,
|
||||
// since we will get partial information.
|
||||
p.TypesPkg = typesPkg
|
||||
@ -86,6 +88,20 @@ func (p *Package) TypeCheck() error {
|
||||
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.
|
||||
func (p *Package) TypeOf(expr ast.Expr) types.Type {
|
||||
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{}
|
||||
|
||||
// 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
|
||||
|
||||
fileAst := file.AST
|
||||
@ -38,7 +38,7 @@ type lintBlankImports struct {
|
||||
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.
|
||||
if w.file.Pkg.IsMain() || w.file.IsTest() {
|
||||
return nil
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
type BoolLiteralRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
onFailure := func(failure lint.Failure) {
|
||||
|
@ -49,7 +49,7 @@ var allPkgs = packages{pkgs: make([]pkgMethods, 1)}
|
||||
type ConfusingNamingRule struct{}
|
||||
|
||||
// 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
|
||||
fileAst := file.AST
|
||||
pkgm := allPkgs.methodNames(file.Pkg)
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
type ConfusingResultsRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
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",
|
||||
})
|
||||
break
|
||||
} else {
|
||||
lastType = t.Name
|
||||
}
|
||||
lastType = t.Name
|
||||
|
||||
}
|
||||
|
||||
return w
|
||||
|
@ -1,11 +1,8 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/mgechev/revive/lint"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
@ -13,7 +10,7 @@ import (
|
||||
type ConstantLogicalExprRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
onFailure := func(failure lint.Failure) {
|
||||
@ -43,7 +40,7 @@ func (w *lintConstantLogicalExpr) Visit(node ast.Node) ast.Visitor {
|
||||
return w
|
||||
}
|
||||
|
||||
if !w.areEqual(n.X, n.Y) {
|
||||
if gofmt(n.X) != gofmt(n.Y) { // check if subexpressions are the same
|
||||
return w
|
||||
}
|
||||
|
||||
@ -81,21 +78,6 @@ func (w *lintConstantLogicalExpr) isInequalityOperator(t token.Token) bool {
|
||||
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) {
|
||||
w.onFailure(lint.Failure{
|
||||
Confidence: 1,
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
type ContextAsArgumentRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
fileAst := file.AST
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
type ContextKeysType struct{}
|
||||
|
||||
// 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
|
||||
|
||||
fileAst := file.AST
|
||||
|
@ -47,7 +47,7 @@ type lintCyclomatic struct {
|
||||
onFailure func(lint.Failure)
|
||||
}
|
||||
|
||||
func (w lintCyclomatic) Visit(n ast.Node) ast.Visitor {
|
||||
func (w lintCyclomatic) Visit(_ ast.Node) ast.Visitor {
|
||||
f := w.file
|
||||
for _, decl := range f.AST.Decls {
|
||||
if fn, ok := decl.(*ast.FuncDecl); ok {
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
type DeepExitRule struct{}
|
||||
|
||||
// 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
|
||||
onFailure := func(failure lint.Failure) {
|
||||
failures = append(failures, failure)
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
type DotImportsRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
fileAst := file.AST
|
||||
@ -38,7 +38,7 @@ type lintImports struct {
|
||||
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 {
|
||||
_ = i
|
||||
if is.Name != nil && is.Name.Name == "." && !w.file.IsTest() {
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
type EmptyBlockRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
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{}
|
||||
|
||||
// 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
|
||||
|
||||
fileAst := file.AST
|
||||
@ -41,7 +41,7 @@ type lintErrors struct {
|
||||
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 {
|
||||
gd, ok := decl.(*ast.GenDecl)
|
||||
if !ok || gd.Tok != token.VAR {
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
type ErrorReturnRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
fileAst := file.AST
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
type ErrorStringsRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
fileAst := file.AST
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
type ErrorfRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
fileAst := file.AST
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
type ExportedRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
if isTest(file) {
|
||||
|
@ -51,7 +51,7 @@ type lintFileHeader struct {
|
||||
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]
|
||||
failure := lint.Failure{
|
||||
Node: w.fileAst,
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
type FlagParamRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
onFailure := func(failure lint.Failure) {
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
type GetReturnRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
onFailure := func(failure lint.Failure) {
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
type IfReturnRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
onFailure := func(failure lint.Failure) {
|
||||
|
@ -54,7 +54,7 @@ type blacklistedImports struct {
|
||||
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 {
|
||||
if is.Path != nil && !w.file.IsTest() && w.blacklist[is.Path.Value] {
|
||||
w.onFailure(lint.Failure{
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
type IncrementDecrementRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
fileAst := file.AST
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
type IndentErrorFlowRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
onFailure := func(failure lint.Failure) {
|
||||
@ -77,4 +77,3 @@ func (w lintElse) Visit(node ast.Node) ast.Visitor {
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
type ModifiesParamRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
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[")
|
||||
}
|
||||
|
||||
func (_ lintModifiesValRecRule) getNameFromExpr(ie ast.Expr) string {
|
||||
func (lintModifiesValRecRule) getNameFromExpr(ie ast.Expr) string {
|
||||
ident, ok := ie.(*ast.Ident)
|
||||
if !ok {
|
||||
return ""
|
||||
|
@ -17,7 +17,7 @@ import (
|
||||
type PackageCommentsRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
if isTest(file) {
|
||||
@ -45,7 +45,7 @@ type lintPackageComments struct {
|
||||
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() {
|
||||
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{}
|
||||
|
||||
// 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
|
||||
|
||||
onFailure := func(failure lint.Failure) {
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
type ReceiverNamingRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
fileAst := file.AST
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
type RedefinesBuiltinIDRule struct{}
|
||||
|
||||
// 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 builtInConstAndVars = map[string]bool{
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
type SuperfluousElseRule struct{}
|
||||
|
||||
// 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
|
||||
onFailure := func(failure lint.Failure) {
|
||||
failures = append(failures, failure)
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
type TimeNamingRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
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 {
|
||||
typ = pt.Elem()
|
||||
}
|
||||
if !isNamedType(w.file.Pkg, typ, "time", "Duration") {
|
||||
if !isNamedType(typ, "time", "Duration") {
|
||||
continue
|
||||
}
|
||||
suffix := ""
|
||||
@ -83,7 +83,7 @@ var timeSuffixes = []string{
|
||||
"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)
|
||||
if !ok {
|
||||
return false
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
type UnexportedReturnRule struct{}
|
||||
|
||||
// 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
|
||||
|
||||
fileAst := file.AST
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
type UnnecessaryStmtRule struct{}
|
||||
|
||||
// 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
|
||||
onFailure := func(failure lint.Failure) {
|
||||
failures = append(failures, failure)
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
type UnreachableCodeRule struct{}
|
||||
|
||||
// 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
|
||||
onFailure := func(failure lint.Failure) {
|
||||
failures = append(failures, failure)
|
||||
|
@ -1,8 +1,10 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"regexp"
|
||||
@ -179,3 +181,11 @@ func isExprABooleanLit(n ast.Node) (lexeme string, ok bool) {
|
||||
|
||||
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{}
|
||||
|
||||
// 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
|
||||
|
||||
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]
|
||||
}
|
||||
if parseInstructions(t, filename, src) == nil {
|
||||
assertSuccess(t, baseDir, stat, src, []lint.Rule{rule}, c)
|
||||
assertSuccess(t, baseDir, stat, []lint.Rule{rule}, c)
|
||||
return
|
||||
}
|
||||
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) {
|
||||
return ioutil.ReadFile(baseDir + file)
|
||||
})
|
||||
@ -220,7 +220,8 @@ func srcLine(src []byte, p token.Position) string {
|
||||
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 {
|
||||
src string
|
||||
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 {
|
||||
name, want string
|
||||
}{
|
||||
@ -301,7 +303,8 @@ func exportedType(typ types.Type) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func TestExportedType(t *testing.T) {
|
||||
// TestExportedType tests exportedType function
|
||||
func TestExportedType(t *testing.T) { //revive:disable-line:exported
|
||||
tests := []struct {
|
||||
typString string
|
||||
exp bool
|
||||
@ -356,7 +359,8 @@ func isGenerated(src []byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func TestIsGenerated(t *testing.T) {
|
||||
// TestIsGenerated tests isGenerated function
|
||||
func TestIsGenerated(t *testing.T) { //revive:disable-line:exported
|
||||
tests := []struct {
|
||||
source string
|
||||
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.indent-error-flow]
|
||||
[rule.empty-block]
|
||||
[rule.range-val-in-closure]
|
||||
[rule.waitgroup-by-value]
|
||||
[rule.atomic]
|
||||
|
Loading…
x
Reference in New Issue
Block a user