package testpkg

import (
	"fmt"
	"os"
	"sync"
)

type T int

func Fn() T {
	return T(0)
}

func (*T) Close() {}

func fn() {
	a := Fn()
	b := Fn()

	defer a.Close()
	defer b.Close()

	a = Fn()
	defer a.Close()

	b = Fn()
	defer b.Close()

	a = Fn()
	defer a.Close()
	b = Fn() // want `missing whitespace above this line \(invalid statement above assign\)`
	defer b.Close()

	a = Fn()
	b = Fn()
	defer a.Close() // want `missing whitespace above this line \(no shared variables above defer\)`
	defer b.Close()

	m := sync.Mutex{}

	m.Lock()
	defer m.Unlock()

	c := true
	defer func(b bool) { // want `missing whitespace above this line \(no shared variables above defer\)`
		fmt.Printf("%v", b)
	}(false)

	_ = c
}

func fn2() {
	a := 1
	b := Fn() // want `missing whitespace above this line \(too many statements above defer\)`
	defer b.Close()

	_ = a
}

func fn3() {
	f, err := os.Open("x")
	if err != nil {
		panic(err)
	}
	defer f.Close()
}

func fn4() {
	// Cuddling allowed: variable is used inside the anonymous function body.
	sharedVar := "hello"
	defer func() {
		fmt.Println(sharedVar)
	}()

	// Cuddling not allowed: variable is not used inside the anonymous function body.
	notShared := "world"
	defer func() { // want `missing whitespace above this line \(no shared variables above defer\)`
		fmt.Println("not using notShared")
	}()

	_ = notShared
}

// Regression: an `*ast.ExprStmt` cuddled above a `defer` is allowed when the
// two share an identifier (the trigger being `defer` relaxes the previous
// statement's type rule).
func fn5() {
	a := Fn()

	a.Close()
	defer a.Close()
}

func fn6() {
	a := Fn()

	fmt.Println("unrelated")
	defer a.Close() // want `missing whitespace above this line \(no shared variables above defer\)`
}
