package a

import (
	"log"
	"os"
)

type X struct{ f, g int }

func f(x, y *X) {
	if x == nil {
		print(x.f) // want "nil dereference in field selection"
	} else {
		print(x.f)
	}

	if x == nil {
		if nil != y {
			print(1)
			panic(0)
		}
		x.f = 1 // want "nil dereference in field selection"
		y.f = 1 // want "nil dereference in field selection"
	}

	var f func()
	if f == nil { // want "tautological condition: nil == nil"
		go f() // want "nil dereference in dynamic function call"
	} else {
		// This block is unreachable,
		// so we don't report an error for the
		// nil dereference in the call.
		defer f()
	}
}

func f2(ptr *[3]int, i interface{}) {
	if ptr != nil {
		print(ptr[:])
		*ptr = [3]int{}
		print(*ptr)
	} else {
		print(ptr[:])   // want "nil dereference in slice operation"
		*ptr = [3]int{} // want "nil dereference in store"
		print(*ptr)     // want "nil dereference in load"

		if ptr != nil { // want "impossible condition: nil != nil"
			// Dominated by ptr==nil and ptr!=nil,
			// this block is unreachable.
			// We do not report errors within it.
			print(*ptr)
		}
	}

	if i != nil {
		print(i.(interface{ f() }))
	} else {
		print(i.(interface{ f() })) // want "nil dereference in type assertion"
	}
}

func g() error { return nil }

func f3() error {
	err := g()
	if err != nil {
		return err
	}
	if err != nil && err.Error() == "foo" { // want "impossible condition: nil != nil"
		print(0)
	}
	ch := make(chan int)
	if ch == nil { // want "impossible condition: non-nil == nil"
		print(0)
	}
	if ch != nil { // want "tautological condition: non-nil != nil"
		print(0)
	}
	return nil
}

func h(err error, b bool) {
	if err != nil && b {
		return
	} else if err != nil {
		panic(err)
	}
}

func i(*int) error {
	for {
		if err := g(); err != nil {
			return err
		}
	}
}

func f4(x *X) {
	if x == nil {
		panic(x)
	}
}

func f5(x *X) {
	panic(nil) // want "panic with nil value"
}

func f6(x *X) {
	var err error
	panic(err) // want "panic with nil value"
}

func f7() {
	x, err := bad()
	if err != nil {
		panic(0)
	}
	if x == nil {
		panic(err) // want "panic with nil value"
	}
}

func bad() (*X, error) {
	return nil, nil
}

func f8() {
	var e error
	v, _ := e.(interface{})
	print(v)
}

func f9(x interface {
	a()
	b()
	c()
}) {
	x.b() // we don't catch this panic because we don't have any facts yet
	xx := interface {
		a()
		b()
	}(x)
	if xx != nil {
		return
	}
	x.c()  // want "nil dereference in dynamic method call"
	xx.b() // want "nil dereference in dynamic method call"
	xxx := interface{ a() }(xx)
	xxx.a() // want "nil dereference in dynamic method call"

	if unknown() {
		panic(x) // want "panic with nil value"
	}
	if unknown() {
		panic(xx) // want "panic with nil value"
	}
	if unknown() {
		panic(xxx) // want "panic with nil value"
	}
}

func f10() {
	s0 := make([]string, 0)
	if s0 == nil { // want "impossible condition: non-nil == nil"
		print(0)
	}

	var s1 []string
	if s1 == nil { // want "tautological condition: nil == nil"
		print(0)
	}
	s2 := s1[:][:]
	if s2 == nil { // want "tautological condition: nil == nil"
		print(0)
	}
}

func unknown() bool {
	return false
}

func f11(a interface{}) {
	switch a.(type) {
	case nil:
		return
	}
	switch a.(type) {
	case nil: // want "impossible condition: non-nil == nil"
		return
	}
}

func f12(a interface{}) {
	switch a {
	case nil:
		return
	}
	switch a {
	case 5,
		nil: // want "impossible condition: non-nil == nil"
		return
	}
}

type Y struct {
	innerY
}

type innerY struct {
	value int
}

func f13() {
	var d *Y
	print(d.value) // want "nil dereference in field selection"
}

func f14() {
	var x struct{ f string }
	if x == struct{ f string }{} { // we don't catch this tautology as we restrict to reference types
		print(x)
	}
}

func f15(x any) {
	ptr, ok := x.(*int)
	if ok {
		return
	}
	println(*ptr) // want "nil dereference in load"
}

func f16(x any) {
	ptr, ok := x.(*int)
	if !ok {
		println(*ptr) // want "nil dereference in load"
		return
	}
	println(*ptr)
}

func f18(x any) {
	ptr, ok := x.(*int)
	if ok {
		println(ptr)
		// falls through
	}
	println(*ptr)
}

// Regression test for https://github.com/golang/go/issues/65674:
// spurious "nil deference in slice index operation" when the
// index was subject to a range loop.
func f19(slice []int, array *[2]int, m map[string]int, ch chan int) {
	if slice == nil {
		// A range over a nil slice is dynamically benign,
		// but still signifies a programmer mistake.
		//
		// Since SSA has melted down the control structure,
		// so we can only report a diagnostic about the
		// index operation, with heuristics for "range".

		for range slice { // nothing to report here
		}
		for _, v := range slice { // want "range of nil slice"
			_ = v
		}
		for i := range slice {
			_ = slice[i] // want "range of nil slice"
		}
		{
			var i int
			for i = range slice {
			}
			_ = slice[i] // want "index of nil slice"
		}
		for i := range slice {
			if i < len(slice) {
				_ = slice[i] // want "range of nil slice"
			}
		}
		if len(slice) > 3 {
			_ = slice[2] // want "index of nil slice"
		}
		for i := 0; i < len(slice); i++ {
			_ = slice[i] // want "index of nil slice"
		}
	}

	if array == nil {
		// (The v var is necessary, otherwise the SSA
		// code doesn't dereference the pointer.)
		for _, v := range array { // want "nil dereference in array index operation"
			_ = v
		}
	}

	if m == nil {
		for range m { // want "range over nil map"
		}
		m["one"] = 1 // want "nil dereference in map update"
	}

	if ch == nil {
		for range ch { // want "receive from nil channel"
		}
		<-ch    // want "receive from nil channel"
		ch <- 0 // want "send to nil channel"
	}
}

func f20() {
	f, err := os.Open("")
	if err != nil {
		log.Fatal(err) // noreturn analysis proves this call doesn't return
	}
	if err != nil { // want "impossible condition"
		log.Fatal(err)
	}
	f.Close()
}
