David López 37e6995b45 lintpack/gocritic: update lintpack & gocritic versions
update lintpack & gocritic versions to support all the new gocritic checks
2018-12-22 15:34:16 +03:00

128 lines
2.9 KiB
Go

package checkers
import (
"go/ast"
"go/types"
"github.com/go-critic/checkers/internal/lintutil"
"github.com/go-lintpack/lintpack"
"github.com/go-lintpack/lintpack/astwalk"
"github.com/go-toolsmith/astp"
)
func init() {
var info lintpack.CheckerInfo
info.Name = "underef"
info.Tags = []string{"style"}
info.Params = lintpack.CheckerParams{
"skipRecvDeref": {
Value: true,
Usage: "whether to skip (*x).method() calls where x is a pointer receiver",
},
}
info.Summary = "Detects dereference expressions that can be omitted"
info.Before = `
(*k).field = 5
v := (*a)[5] // only if a is array`
info.After = `
k.field = 5
v := a[5]`
collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker {
c := &underefChecker{ctx: ctx}
c.skipRecvDeref = info.Params.Bool("skipRecvDeref")
return astwalk.WalkerForExpr(c)
})
}
type underefChecker struct {
astwalk.WalkHandler
ctx *lintpack.CheckerContext
skipRecvDeref bool
}
func (c *underefChecker) VisitExpr(expr ast.Expr) {
switch n := expr.(type) {
case *ast.SelectorExpr:
expr := lintutil.AsParenExpr(n.X)
if c.skipRecvDeref && c.isPtrRecvMethodCall(n.Sel) {
return
}
if expr, ok := expr.X.(*ast.StarExpr); ok {
if c.checkStarExpr(expr) {
c.warnSelect(n)
}
}
case *ast.IndexExpr:
expr := lintutil.AsParenExpr(n.X)
if expr, ok := expr.X.(*ast.StarExpr); ok {
if !c.checkStarExpr(expr) {
return
}
if c.checkArray(expr) {
c.warnArray(n)
}
}
}
}
func (c *underefChecker) isPtrRecvMethodCall(fn *ast.Ident) bool {
typ, ok := c.ctx.TypesInfo.TypeOf(fn).(*types.Signature)
if ok && typ != nil && typ.Recv() != nil {
_, ok := typ.Recv().Type().(*types.Pointer)
return ok
}
return false
}
func (c *underefChecker) underef(x *ast.ParenExpr) ast.Expr {
// If there is only 1 deref, can remove parenthesis,
// otherwise can remove StarExpr only.
dereferenced := x.X.(*ast.StarExpr).X
if astp.IsStarExpr(dereferenced) {
return &ast.ParenExpr{X: dereferenced}
}
return dereferenced
}
func (c *underefChecker) warnSelect(expr *ast.SelectorExpr) {
// TODO: add () to function output.
c.ctx.Warn(expr, "could simplify %s to %s.%s",
expr,
c.underef(expr.X.(*ast.ParenExpr)),
expr.Sel.Name)
}
func (c *underefChecker) warnArray(expr *ast.IndexExpr) {
c.ctx.Warn(expr, "could simplify %s to %s[%s]",
expr,
c.underef(expr.X.(*ast.ParenExpr)),
expr.Index)
}
// checkStarExpr checks if ast.StarExpr could be simplified.
func (c *underefChecker) checkStarExpr(expr *ast.StarExpr) bool {
typ, ok := c.ctx.TypesInfo.TypeOf(expr.X).Underlying().(*types.Pointer)
if !ok {
return false
}
switch typ.Elem().Underlying().(type) {
case *types.Pointer, *types.Interface:
return false
default:
return true
}
}
func (c *underefChecker) checkArray(expr *ast.StarExpr) bool {
typ, ok := c.ctx.TypesInfo.TypeOf(expr.X).(*types.Pointer)
if !ok {
return false
}
_, ok = typ.Elem().(*types.Array)
return ok
}