package pkg

import (
	"errors"
	"os/exec"
)

type T struct{ x *int }

func fn1() *int             { return nil }
func fn2() (int, *int, int) { return 0, nil, 0 }

func fn3() (int, error) { return 0, nil }
func fn4() error        { return nil }

func gen1() interface{} {
	// don't flag, returning a concrete value
	return 0
}

func gen2() interface{} {
	// don't flag, returning a concrete value
	return &T{}
}

func gen3() interface{} {
	// flag, always returns a typed value
	m := map[int]*int{}
	return m[0]
}

func gen4() (int, interface{}, *int) {
	// flag ret[1], always a typed value
	m := map[int]*int{}
	return 0, m[0], nil
}

func gen5() interface{} {
	// flag, propagate gen3
	return gen3()
}

func gen6(b bool) interface{} {
	// don't flag, sometimes returns untyped nil
	if b {
		m := map[int]*int{}
		return m[0]
	} else {
		return nil
	}
}

func gen7() interface{} {
	// flag, always returns a typed value
	return fn1()
}

func gen8(x *int) interface{} {
	// flag
	if x == nil {
		return x
	}
	return x
}

func gen9() interface{} {
	// flag
	var x *int
	return x
}

func gen10() interface{} {
	// flag
	var x *int
	if x == nil {
		return x
	}
	return errors.New("")

	// This is a tricky one. we should flag this, because it never
	// returns a nil error, but if errors.New could return untyped
	// nils, then we shouldn't flag it. we need to consider the
	// implementation of the called function.
}

func gen11() interface{} {
	// don't flag, we sometimes return untyped nil
	if true {
		return nil
	} else {
		return (*int)(nil)
	}
}

func gen12(b bool) interface{} {
	// flag, all branches return typed nils
	var x interface{}
	if b {
		x = (*int)(nil)
	} else {
		x = (*string)(nil)
	}
	return x
}

func gen13() interface{} {
	// flag, always returns a typed value
	_, x, _ := fn2()
	return x
}

func gen14(ch chan *int) interface{} {
	// flag
	return <-ch
}

func gen15() interface{} {
	// flag
	t := &T{}
	return t.x
}

var g *int = new(int)

func gen16() interface{} {
	// don't flag. returning a global is akin to returning &T{}.
	return g
}

func gen17(x interface{}) interface{} {
	// don't flag
	if x != nil {
		return x
	}
	return x
}

func gen18() (int, error) {
	// don't flag
	_, err := fn3()
	if err != nil {
		return 0, errors.New("yo")
	}
	return 0, err
}

func gen19() (out interface{}) {
	// don't flag
	if true {
		return (*int)(nil)
	}
	return
}

func gen20() (out interface{}) {
	// don't flag
	if true {
		return (*int)(nil)
	}
	return
}

func gen21() error {
	if false {
		return (*exec.Error)(nil)
	}
	return fn4()
}

func gen22() interface{} {
	if true {
		return g
	}
	return (*int)(nil)
}

func test() {
	_ = gen1() == nil
	_ = gen2() == nil
	_ = gen3() == nil //@ diag(`never true`)
	{
		_, r2, r3 := gen4()
		_ = r2 == nil //@ diag(`never true`)
		_ = r3 == nil
	}
	_ = gen5() == nil //@ diag(`never true`)
	_ = gen6(false) == nil
	_ = gen7() == nil    //@ diag(`never true`)
	_ = gen8(nil) == nil //@ diag(`never true`)
	_ = gen9() == nil    //@ diag(`never true`)
	_ = gen10() == nil   //@ diag(`never true`)
	_ = gen11() == nil
	_ = gen12(true) == nil //@ diag(`never true`)
	_ = gen13() == nil     //@ diag(`never true`)
	_ = gen14(nil) == nil  //@ diag(`never true`)
	_ = gen15() == nil     //@ diag(`never true`)
	_ = gen16() == nil
	_ = gen17(nil) == nil
	{
		_, r2 := gen18()
		_ = r2 == nil
	}
	_ = gen19() == nil
	_ = gen20() == nil
	_ = gen21() == nil
	_ = gen22() == nil //@ diag(`never true`)

	var v1 interface{} = 0
	_ = v1 == nil //@ diag(`never true; the lhs`)

	_ = any((*int)(nil)) == nil //@ diag(`never true`)
	_ = any((error)(nil)) == nil
}

type any = interface{}
