func (c *Checker) LintRedundantNilCheckWithLen(j *lint.Job) {
	isConstZero := func(expr ast.Expr) (isConst bool, isZero bool) {
		_, ok := expr.(*ast.BasicLit)
		if ok {
			return true, IsZero(expr)
		}
		id, ok := expr.(*ast.Ident)
		if !ok {
			return false, false
		}
		c, ok := ObjectOf(j, id).(*types.Const)
		if !ok {
			return false, false
		}
		return true, c.Val().Kind() == constant.Int && c.Val().String() == "0"
	}

	fn := func(node ast.Node) bool {
		// check that expr is "x || y" or "x && y"
		expr, ok := node.(*ast.BinaryExpr)
		if !ok {
			return true
		}
		if expr.Op != token.LOR && expr.Op != token.LAND {
			return true
		}
		eqNil := expr.Op == token.LOR

		// check that x is "xx == nil" or "xx != nil"
		x, ok := expr.X.(*ast.BinaryExpr)
		if !ok {
			return true
		}
		if eqNil && x.Op != token.EQL {
			return true
		}
		if !eqNil && x.Op != token.NEQ {
			return true
		}
		xx, ok := x.X.(*ast.Ident)
		if !ok {
			return true
		}
		if !IsNil(j, x.Y) {
			return true
		}

		// check that y is "len(xx) == 0" or "len(xx) ... "
		y, ok := expr.Y.(*ast.BinaryExpr)
		if !ok {
			return true
		}
		if eqNil && y.Op != token.EQL { // must be len(xx) *==* 0
			return false
		}
		yx, ok := y.X.(*ast.CallExpr)
		if !ok {
			return true
		}
		yxFun, ok := yx.Fun.(*ast.Ident)
		if !ok || yxFun.Name != "len" || len(yx.Args) != 1 {
			return true
		}
		yxArg, ok := yx.Args[Arg("len.v")].(*ast.Ident)
		if !ok {
			return true
		}
		if yxArg.Name != xx.Name {
			return true
		}

		if eqNil && !IsZero(y.Y) { // must be len(x) == *0*
			return true
		}

		if !eqNil {
			isConst, isZero := isConstZero(y.Y)
			if !isConst {
				return true
			}
			switch y.Op {
			case token.EQL:
				// avoid false positive for "xx != nil && len(xx) == 0"
				if isZero {
					return true
				}
			case token.GEQ:
				// avoid false positive for "xx != nil && len(xx) >= 0"
				if isZero {
					return true
				}
			case token.NEQ:
				// avoid false positive for "xx != nil && len(xx) != <non-zero>"
				if !isZero {
					return true
				}
			case token.GTR:
				// ok
			default:
				return true
			}
		}

		// finally check that xx type is one of array, slice, map or chan
		// this is to prevent false positive in case if xx is a pointer to an array
		var nilType string
		switch TypeOf(j, xx).(type) {
		case *types.Slice:
			nilType = "nil slices"
		case *types.Map:
			nilType = "nil maps"
		case *types.Chan:
			nilType = "nil channels"
		default:
			return true
		}
		j.Errorf(expr, "should omit nil check; len() for %s is defined as zero", nilType)
		return true
	}
	for _, f := range j.Program.Files {
		ast.Inspect(f, fn)
	}
}
func (c *Checker) LintRedundantNilCheckWithLen(j *lint.Job) {
	isConstZero := func(expr ast.Expr) (isConst bool, isZero bool) {
		_, ok := expr.(*ast.BasicLit)
		if ok {
			return true, IsZero(expr)
		}
		id, ok := expr.(*ast.Ident)
		if !ok {
			return false, false
		}
		c, ok := ObjectOf(j, id).(*types.Const)
		if !ok {
			return false, false
		}
		return true, c.Val().Kind() == constant.Int && c.Val().String() == "0"
	}

	fn := func(node ast.Node) bool {
		
		expr, ok := node.(*ast.BinaryExpr)
		if !ok {
			return true
		}
		if expr.Op != token.LOR && expr.Op != token.LAND {
			return true
		}
		eqNil := expr.Op == token.LOR

		
		x, ok := expr.X.(*ast.BinaryExpr)
		if !ok {
			return true
		}
		if eqNil && x.Op != token.EQL {
			return true
		}
		if !eqNil && x.Op != token.NEQ {
			return true
		}
		xx, ok := x.X.(*ast.Ident)
		if !ok {
			return true
		}
		if !IsNil(j, x.Y) {
			return true
		}

		
		y, ok := expr.Y.(*ast.BinaryExpr)
		if !ok {
			return true
		}
		if eqNil && y.Op != token.EQL { 
			return false
		}
		yx, ok := y.X.(*ast.CallExpr)
		if !ok {
			return true
		}
		yxFun, ok := yx.Fun.(*ast.Ident)
		if !ok || yxFun.Name != "len" || len(yx.Args) != 1 {
			return true
		}
		yxArg, ok := yx.Args[Arg("len.v")].(*ast.Ident)
		if !ok {
			return true
		}
		if yxArg.Name != xx.Name {
			return true
		}

		if eqNil && !IsZero(y.Y) { 
			return true
		}

		if !eqNil {
			isConst, isZero := isConstZero(y.Y)
			if !isConst {
				return true
			}
			switch y.Op {
			case token.EQL:
				
				if isZero {
					return true
				}
			case token.GEQ:
				
				if isZero {
					return true
				}
			case token.NEQ:
				
				if !isZero {
					return true
				}
			case token.GTR:
				
			default:
				return true
			}
		}

		
		
		var nilType string
		switch TypeOf(j, xx).(type) {
		case *types.Slice:
			nilType = "nil slices"
		case *types.Map:
			nilType = "nil maps"
		case *types.Chan:
			nilType = "nil channels"
		default:
			return true
		}
		j.Errorf(expr, "should omit nil check; len() for %s is defined as zero", nilType)
		return true
	}
	for _, f := range j.Program.Files {
		ast.Inspect(f, fn)
	}
}
func ( c * Checker ) LintRedundantNilCheckWithLen ( j * lint . Job ) { isConstZero := func ( expr ast . Expr ) ( isConst bool , isZero bool ) { _ , ok := expr . ( * ast . BasicLit ) if ok { return true , IsZero ( expr ) } id , ok := expr . ( * ast . Ident ) if ! ok { return false , false } c , ok := ObjectOf ( j , id ) . ( * types . Const ) if ! ok { return false , false } return true , c . Val ( ) . Kind ( ) == constant . Int && c . Val ( ) . String ( ) == " " } fn := func ( node ast . Node ) bool { // check that expr is "x || y" or "x && y" expr , ok := node . ( * ast . BinaryExpr ) if ! ok { return true } if expr . Op != token . LOR && expr . Op != token . LAND { return true } eqNil := expr . Op == token . LOR // check that x is "xx == nil" or "xx != nil" x , ok := expr . X . ( * ast . BinaryExpr ) if ! ok { return true } if eqNil && x . Op != token . EQL { return true } if ! eqNil && x . Op != token . NEQ { return true } xx , ok := x . X . ( * ast . Ident ) if ! ok { return true } if ! IsNil ( j , x . Y ) { return true } // check that y is "len(xx) == 0" or "len(xx) ... " y , ok := expr . Y . ( * ast . BinaryExpr ) if ! ok { return true } if eqNil && y . Op != token . EQL { // must be len(xx) *==* 0 return false } yx , ok := y . X . ( * ast . CallExpr ) if ! ok { return true } yxFun , ok := yx . Fun . ( * ast . Ident ) if ! ok || yxFun . Name != " " || len ( yx . Args ) != 1 { return true } yxArg , ok := yx . Args [ Arg ( " " ) ] . ( * ast . Ident ) if ! ok { return true } if yxArg . Name != xx . Name { return true } if eqNil && ! IsZero ( y . Y ) { // must be len(x) == *0* return true } if ! eqNil { isConst , isZero := isConstZero ( y . Y ) if ! isConst { return true } switch y . Op { case token . EQL : // avoid false positive for "xx != nil && len(xx) == 0" if isZero { return true } case token . GEQ : // avoid false positive for "xx != nil && len(xx) >= 0" if isZero { return true } case token . NEQ : // avoid false positive for "xx != nil && len(xx) != <non-zero>" if ! isZero { return true } case token . GTR : // ok default : return true } } // finally check that xx type is one of array, slice, map or chan // this is to prevent false positive in case if xx is a pointer to an array var nilType string switch TypeOf ( j , xx ) . ( type ) { case * types . Slice : nilType = " " case * types . Map : nilType = " " case * types . Chan : nilType = " " default : return true } j . Errorf ( expr , " " , nilType ) return true } for _ , f := range j . Program . Files { ast . Inspect ( f , fn ) } }
// LintRedundantNilCheckWithLen checks for the following reduntant nil-checks:
//
//   if x == nil || len(x) == 0 {}
//   if x != nil && len(x) != 0 {}
//   if x != nil && len(x) == N {} (where N != 0)
//   if x != nil && len(x) > N {}
//   if x != nil && len(x) >= N {} (where N != 0)
//
LintRedundantNilCheckWithLen checks for the following reduntant nil - checks : if x == nil || len ( x ) == 0 {} if x ! = nil && len ( x ) ! = 0 {} if x ! = nil && len ( x ) == N {} ( where N ! = 0 ) if x ! = nil && len ( x ) > N {} if x ! = nil && len ( x ) > = N {} ( where N ! = 0 )