Denis Isaev 7705f82591 Update megacheck to the latest version
Also do following improvements:
  - show proper sublinter name for megacheck sublinters
  - refactor and make more simple and robust megacheck
  merging/optimizing
  - improve handling of unknown linter names in //nolint directives
  - minimize diff of our megacheck version from the upstream,
  https://github.com/golang/go/issues/29612 blocks usage of the upstream
  version
  - support the new `stylecheck` linter
  - improve tests coverage for megacheck and nolint related cases
  - update and use upstream versions of unparam and interfacer instead of forked
  ones
  - don't use golangci/tools repo anymore
  - fix newly found issues after updating linters

Also should be noted that megacheck works much faster and consumes less
memory in the newest release, therefore golangci-lint works noticeably
faster and consumes less memory for large repos.

Relates: #314
2019-01-08 21:16:15 +03:00

124 lines
2.7 KiB
Go

package functions
import (
"go/token"
"go/types"
"github.com/golangci/go-tools/callgraph"
"github.com/golangci/go-tools/lint/lintdsl"
"github.com/golangci/go-tools/ssa"
)
// IsStub reports whether a function is a stub. A function is
// considered a stub if it has no instructions or exactly one
// instruction, which must be either returning only constant values or
// a panic.
func (d *Descriptions) IsStub(fn *ssa.Function) bool {
if len(fn.Blocks) == 0 {
return true
}
if len(fn.Blocks) > 1 {
return false
}
instrs := lintdsl.FilterDebug(fn.Blocks[0].Instrs)
if len(instrs) != 1 {
return false
}
switch instrs[0].(type) {
case *ssa.Return:
// Since this is the only instruction, the return value must
// be a constant. We consider all constants as stubs, not just
// the zero value. This does not, unfortunately, cover zero
// initialised structs, as these cause additional
// instructions.
return true
case *ssa.Panic:
return true
default:
return false
}
}
func (d *Descriptions) IsPure(fn *ssa.Function) bool {
if fn.Signature.Results().Len() == 0 {
// A function with no return values is empty or is doing some
// work we cannot see (for example because of build tags);
// don't consider it pure.
return false
}
for _, param := range fn.Params {
if _, ok := param.Type().Underlying().(*types.Basic); !ok {
return false
}
}
if fn.Blocks == nil {
return false
}
checkCall := func(common *ssa.CallCommon) bool {
if common.IsInvoke() {
return false
}
builtin, ok := common.Value.(*ssa.Builtin)
if !ok {
if common.StaticCallee() != fn {
if common.StaticCallee() == nil {
return false
}
// TODO(dh): ideally, IsPure wouldn't be responsible
// for avoiding infinite recursion, but
// FunctionDescriptions would be.
node := d.CallGraph.CreateNode(common.StaticCallee())
if callgraph.PathSearch(node, func(other *callgraph.Node) bool {
return other.Func == fn
}) != nil {
return false
}
if !d.Get(common.StaticCallee()).Pure {
return false
}
}
} else {
switch builtin.Name() {
case "len", "cap", "make", "new":
default:
return false
}
}
return true
}
for _, b := range fn.Blocks {
for _, ins := range b.Instrs {
switch ins := ins.(type) {
case *ssa.Call:
if !checkCall(ins.Common()) {
return false
}
case *ssa.Defer:
if !checkCall(&ins.Call) {
return false
}
case *ssa.Select:
return false
case *ssa.Send:
return false
case *ssa.Go:
return false
case *ssa.Panic:
return false
case *ssa.Store:
return false
case *ssa.FieldAddr:
return false
case *ssa.UnOp:
if ins.Op == token.MUL || ins.Op == token.AND {
return false
}
}
}
}
return true
}