65 lines
1.6 KiB
Go
65 lines
1.6 KiB
Go
package checkers
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/token"
|
|
|
|
"github.com/go-lintpack/lintpack"
|
|
"github.com/go-lintpack/lintpack/astwalk"
|
|
"github.com/go-toolsmith/astcast"
|
|
"github.com/go-toolsmith/astequal"
|
|
)
|
|
|
|
func init() {
|
|
var info lintpack.CheckerInfo
|
|
info.Name = "valSwap"
|
|
info.Tags = []string{"style", "experimental"}
|
|
info.Summary = "Detects value swapping code that are not using parallel assignment"
|
|
info.Before = `
|
|
tmp := *x
|
|
*x = *y
|
|
*y = tmp`
|
|
info.After = `*x, *y = *y, *x`
|
|
|
|
collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker {
|
|
return astwalk.WalkerForStmtList(&valSwapChecker{ctx: ctx})
|
|
})
|
|
}
|
|
|
|
type valSwapChecker struct {
|
|
astwalk.WalkHandler
|
|
ctx *lintpack.CheckerContext
|
|
}
|
|
|
|
func (c *valSwapChecker) VisitStmtList(list []ast.Stmt) {
|
|
for len(list) >= 3 {
|
|
tmpAssign := astcast.ToAssignStmt(list[0])
|
|
assignX := astcast.ToAssignStmt(list[1])
|
|
assignY := astcast.ToAssignStmt(list[2])
|
|
|
|
cond := c.isSimpleAssign(tmpAssign) &&
|
|
c.isSimpleAssign(assignX) &&
|
|
c.isSimpleAssign(assignY) &&
|
|
assignX.Tok == token.ASSIGN &&
|
|
assignY.Tok == token.ASSIGN &&
|
|
astequal.Expr(assignX.Lhs[0], tmpAssign.Rhs[0]) &&
|
|
astequal.Expr(assignX.Rhs[0], assignY.Lhs[0]) &&
|
|
astequal.Expr(assignY.Rhs[0], tmpAssign.Lhs[0])
|
|
if cond {
|
|
c.warn(tmpAssign, assignX.Lhs[0], assignY.Lhs[0])
|
|
list = list[3:]
|
|
} else {
|
|
list = list[1:]
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *valSwapChecker) isSimpleAssign(x *ast.AssignStmt) bool {
|
|
return len(x.Lhs) == 1 && len(x.Rhs) == 1
|
|
}
|
|
|
|
func (c *valSwapChecker) warn(cause, x, y ast.Node) {
|
|
c.ctx.Warn(cause, "can re-write as `%s, %s = %s, %s`",
|
|
x, y, y, x)
|
|
}
|