This test verifies various behaviors of function extraction.

-- go.mod --
module mod.test/extract

go 1.18

-- basic.go --
package extract

func _() { //@codeaction("{", "refactor.extract.function", end=closeBracket, result=outer)
	a := 1    //@codeaction("a", "refactor.extract.function", end=end, result=inner)
	_ = a + 4 //@loc(end, "4")
} //@loc(closeBracket, "}")

-- @outer/basic.go --
package extract

func _() { //@codeaction("{", "refactor.extract.function", end=closeBracket, result=outer)
	newFunction() //@loc(end, "4")
}

func newFunction() {
	a := 1 //@codeaction("a", "refactor.extract.function", end=end, result=inner)
	_ = a + 4
} //@loc(closeBracket, "}")

-- @inner/basic.go --
package extract

func _() { //@codeaction("{", "refactor.extract.function", end=closeBracket, result=outer)
	newFunction() //@loc(end, "4")
}

func newFunction() {
	a := 1 //@codeaction("a", "refactor.extract.function", end=end, result=inner)
	_ = a + 4
} //@loc(closeBracket, "}")

-- return.go --
package extract

func _() bool {
	x := 1
	if x == 0 { //@codeaction("if", "refactor.extract.function", end=ifend, result=return)
		return true
	} //@loc(ifend, "}")
	return false
}

-- @return/return.go --
package extract

func _() bool {
	x := 1
	shouldReturn, b := newFunction(x)
	if shouldReturn {
		return b
	} //@loc(ifend, "}")
	return false
}

func newFunction(x int) (bool, bool) {
	if x == 0 { //@codeaction("if", "refactor.extract.function", end=ifend, result=return)
		return true, true
	}
	return false, false
}

-- return_nonnested.go --
package extract

func _() bool {
	x := 1 //@codeaction("x", "refactor.extract.function", end=rnnEnd, result=rnn)
	if x == 0 {
		return true
	}
	return false //@loc(rnnEnd, "false")
}

-- @rnn/return_nonnested.go --
package extract

func _() bool {
	return newFunction() //@loc(rnnEnd, "false")
}

func newFunction() bool {
	x := 1 //@codeaction("x", "refactor.extract.function", end=rnnEnd, result=rnn)
	if x == 0 {
		return true
	}
	return false
}

-- return_complex.go --
package extract

import "fmt"

func _() (int, string, error) {
	x := 1
	y := "hello"
	z := "bye" //@codeaction("z", "refactor.extract.function", end=rcEnd, result=rc)
	if y == z {
		return x, y, fmt.Errorf("same")
	} else if false {
		z = "hi"
		return x, z, nil
	} //@loc(rcEnd, "}")
	return x, z, nil
}

-- @rc/return_complex.go --
package extract

import "fmt"

func _() (int, string, error) {
	x := 1
	y := "hello"
	z, shouldReturn, i, s, err := newFunction(y, x)
	if shouldReturn {
		return i, s, err
	} //@loc(rcEnd, "}")
	return x, z, nil
}

func newFunction(y string, x int) (string, bool, int, string, error) {
	z := "bye" //@codeaction("z", "refactor.extract.function", end=rcEnd, result=rc)
	if y == z {
		return "", true, x, y, fmt.Errorf("same")
	} else if false {
		z = "hi"
		return "", true, x, z, nil
	}
	return z, false, 0, "", nil
}

-- return_complex_nonnested.go --
package extract

import "fmt"

func _() (int, string, error) {
	x := 1
	y := "hello"
	z := "bye" //@codeaction("z", "refactor.extract.function", end=rcnnEnd, result=rcnn)
	if y == z {
		return x, y, fmt.Errorf("same")
	} else if false {
		z = "hi"
		return x, z, nil
	}
	return x, z, nil //@loc(rcnnEnd, "nil")
}

-- @rcnn/return_complex_nonnested.go --
package extract

import "fmt"

func _() (int, string, error) {
	x := 1
	y := "hello"
	return newFunction(y, x) //@loc(rcnnEnd, "nil")
}

func newFunction(y string, x int) (int, string, error) {
	z := "bye" //@codeaction("z", "refactor.extract.function", end=rcnnEnd, result=rcnn)
	if y == z {
		return x, y, fmt.Errorf("same")
	} else if false {
		z = "hi"
		return x, z, nil
	}
	return x, z, nil
}

-- return_func_lit.go --
package extract

import "go/ast"

func _() {
	ast.Inspect(ast.NewIdent("a"), func(n ast.Node) bool {
		if n == nil { //@codeaction("if", "refactor.extract.function", end=rflEnd, result=rfl)
			return true
		} //@loc(rflEnd, "}")
		return false
	})
}

-- @rfl/return_func_lit.go --
package extract

import "go/ast"

func _() {
	ast.Inspect(ast.NewIdent("a"), func(n ast.Node) bool {
		shouldReturn, b := newFunction(n)
		if shouldReturn {
			return b
		} //@loc(rflEnd, "}")
		return false
	})
}

func newFunction(n ast.Node) (bool, bool) {
	if n == nil { //@codeaction("if", "refactor.extract.function", end=rflEnd, result=rfl)
		return true, true
	}
	return false, false
}

-- return_func_lit_nonnested.go --
package extract

import "go/ast"

func _() {
	ast.Inspect(ast.NewIdent("a"), func(n ast.Node) bool {
		if n == nil { //@codeaction("if", "refactor.extract.function", end=rflnnEnd, result=rflnn)
			return true
		}
		return false //@loc(rflnnEnd, "false")
	})
}

-- @rflnn/return_func_lit_nonnested.go --
package extract

import "go/ast"

func _() {
	ast.Inspect(ast.NewIdent("a"), func(n ast.Node) bool {
		return newFunction(n) //@loc(rflnnEnd, "false")
	})
}

func newFunction(n ast.Node) bool {
	if n == nil { //@codeaction("if", "refactor.extract.function", end=rflnnEnd, result=rflnn)
		return true
	}
	return false
}

-- return_init.go --
package extract

func _() string {
	x := 1
	if x == 0 { //@codeaction("if", "refactor.extract.function", end=riEnd, result=ri)
		x = 3
		return "a"
	} //@loc(riEnd, "}")
	x = 2
	return "b"
}

-- @ri/return_init.go --
package extract

func _() string {
	x := 1
	shouldReturn, s := newFunction(x)
	if shouldReturn {
		return s
	} //@loc(riEnd, "}")
	x = 2
	return "b"
}

func newFunction(x int) (bool, string) {
	if x == 0 { //@codeaction("if", "refactor.extract.function", end=riEnd, result=ri)
		x = 3
		return true, "a"
	}
	return false, ""
}

-- return_init_nonnested.go --
package extract

func _() string {
	x := 1
	if x == 0 { //@codeaction("if", "refactor.extract.function", end=rinnEnd, result=rinn)
		x = 3
		return "a"
	}
	x = 2
	return "b" //@loc(rinnEnd, "\"b\"")
}

-- @rinn/return_init_nonnested.go --
package extract

func _() string {
	x := 1
	return newFunction(x) //@loc(rinnEnd, "\"b\"")
}

func newFunction(x int) string {
	if x == 0 { //@codeaction("if", "refactor.extract.function", end=rinnEnd, result=rinn)
		x = 3
		return "a"
	}
	x = 2
	return "b"
}

-- args_returns.go --
package extract

func _() {
	a := 1
	a = 5     //@codeaction("a", "refactor.extract.function", end=araend, result=ara)
	a = a + 2 //@loc(araend, "2")

	b := a * 2 //@codeaction("b", "refactor.extract.function", end=arbend, result=arb)
	_ = b + 4  //@loc(arbend, "4")
}

-- @ara/args_returns.go --
package extract

func _() {
	a := 1
	a = newFunction(a) //@loc(araend, "2")

	b := a * 2 //@codeaction("b", "refactor.extract.function", end=arbend, result=arb)
	_ = b + 4  //@loc(arbend, "4")
}

func newFunction(a int) int {
	a = 5 //@codeaction("a", "refactor.extract.function", end=araend, result=ara)
	a = a + 2
	return a
}

-- @arb/args_returns.go --
package extract

func _() {
	a := 1
	a = 5     //@codeaction("a", "refactor.extract.function", end=araend, result=ara)
	a = a + 2 //@loc(araend, "2")

	newFunction(a)  //@loc(arbend, "4")
}

func newFunction(a int) {
	b := a * 2 //@codeaction("b", "refactor.extract.function", end=arbend, result=arb)
	_ = b + 4
}

-- scope.go --
package extract

func _() {
	newFunction := 1
	a := newFunction //@codeaction("a", "refactor.extract.function", end="newFunction", result=scope)
	_ = a // avoid diagnostic
}

func newFunction1() int {
	return 1
}

-- @scope/scope.go --
package extract

func _() {
	newFunction := 1
	a := newFunction2(newFunction) //@codeaction("a", "refactor.extract.function", end="newFunction", result=scope)
	_ = a // avoid diagnostic
}

func newFunction2(newFunction int) int {
	a := newFunction
	return a
}

func newFunction1() int {
	return 1
}

-- smart_initialization.go --
package extract

func _() {
	var a []int
	a = append(a, 2) //@codeaction("a", "refactor.extract.function", end=siEnd, result=si)
	b := 4           //@loc(siEnd, "4")
	a = append(a, b)
}

-- @si/smart_initialization.go --
package extract

func _() {
	var a []int
	a, b := newFunction(a)           //@loc(siEnd, "4")
	a = append(a, b)
}

func newFunction(a []int) ([]int, int) {
	a = append(a, 2) //@codeaction("a", "refactor.extract.function", end=siEnd, result=si)
	b := 4
	return a, b
}

-- smart_return.go --
package extract

func _() {
	var b []int
	var a int
	a = 2 //@codeaction("a", "refactor.extract.function", end=srEnd, result=sr)
	b = []int{}
	b = append(b, a) //@loc(srEnd, ")")
	b[0] = 1
}

-- @sr/smart_return.go --
package extract

func _() {
	var b []int
	var a int
	b = newFunction(a, b) //@loc(srEnd, ")")
	b[0] = 1
}

func newFunction(a int, b []int) []int {
	a = 2 //@codeaction("a", "refactor.extract.function", end=srEnd, result=sr)
	b = []int{}
	b = append(b, a)
	return b
}

-- unnecessary_param.go --
package extract

func _() {
	var b []int
	a := 2 //@codeaction("a", "refactor.extract.function", end=upEnd, result=up)
	b = []int{}
	b = append(b, a) //@loc(upEnd, ")")
	b[0] = 1
	if a == 2 {
		return
	}
}

-- @up/unnecessary_param.go --
package extract

func _() {
	var b []int
	a, b := newFunction(b) //@loc(upEnd, ")")
	b[0] = 1
	if a == 2 {
		return
	}
}

func newFunction(b []int) (int, []int) {
	a := 2 //@codeaction("a", "refactor.extract.function", end=upEnd, result=up)
	b = []int{}
	b = append(b, a)
	return a, b
}

-- comment.go --
package extract

func _() {
	a := /* comment in the middle of a line */ 1 //@codeaction("a", "refactor.extract.function", end=commentEnd, result=comment1)
	// Comment on its own line  //@codeaction("Comment", "refactor.extract.function", end=commentEnd, result=comment2)
	_ = a + 4 //@loc(commentEnd, "4"),codeaction("_", "refactor.extract.function", end=lastComment, result=comment3)
	// Comment right after 3 + 4

	// Comment after with space //@loc(lastComment, "Comment")
}

-- @comment1/comment.go --
package extract

func _() {
	newFunction() //@loc(commentEnd, "4"),codeaction("_", "refactor.extract.function", end=lastComment, result=comment3)
	// Comment right after 3 + 4

	// Comment after with space //@loc(lastComment, "Comment")
}

func newFunction() {
	a := /* comment in the middle of a line */ 1 //@codeaction("a", "refactor.extract.function", end=commentEnd, result=comment1)
	// Comment on its own line  //@codeaction("Comment", "refactor.extract.function", end=commentEnd, result=comment2)
	_ = a + 4
}

-- @comment2/comment.go --
package extract

func _() {
	a := /* comment in the middle of a line */ 1 //@codeaction("a", "refactor.extract.function", end=commentEnd, result=comment1)
	// Comment on its own line  //@codeaction("Comment", "refactor.extract.function", end=commentEnd, result=comment2)
	newFunction(a) //@loc(commentEnd, "4"),codeaction("_", "refactor.extract.function", end=lastComment, result=comment3)
	// Comment right after 3 + 4

	// Comment after with space //@loc(lastComment, "Comment")
}

func newFunction(a int) {
	_ = a + 4
}

-- @comment3/comment.go --
package extract

func _() {
	a := /* comment in the middle of a line */ 1 //@codeaction("a", "refactor.extract.function", end=commentEnd, result=comment1)
	// Comment on its own line  //@codeaction("Comment", "refactor.extract.function", end=commentEnd, result=comment2)
	newFunction(a) //@loc(commentEnd, "4"),codeaction("_", "refactor.extract.function", end=lastComment, result=comment3)
	// Comment right after 3 + 4

	// Comment after with space //@loc(lastComment, "Comment")
}

func newFunction(a int) {
	_ = a + 4
}

-- redefine.go --
package extract

import "strconv"

func _() {
	i, err := strconv.Atoi("1")
	u, err := strconv.Atoi("2") //@codeaction(re`u.*\)`, "refactor.extract.function", result=redefine)
	if i == u || err == nil {
		return
	}
}

-- @redefine/redefine.go --
package extract

import "strconv"

func _() {
	i, err := strconv.Atoi("1")
	u, err := newFunction() //@codeaction(re`u.*\)`, "refactor.extract.function", result=redefine)
	if i == u || err == nil {
		return
	}
}

func newFunction() (int, error) {
	u, err := strconv.Atoi("2")
	return u, err
}

-- anonymousfunc.go --
package extract
import "cmp"
import "slices"

// issue go#64821
func _() {
	var s []string //@codeaction("var", "refactor.extract.function", end=anonEnd, result=anon1)
	slices.SortFunc(s, func(a, b string) int {
		return cmp.Compare(a, b)
	})
	println(s) //@loc(anonEnd, ")")
}

-- @anon1/anonymousfunc.go --
package extract
import "cmp"
import "slices"

// issue go#64821
func _() {
	newFunction() //@loc(anonEnd, ")")
}

func newFunction() {
	var s []string //@codeaction("var", "refactor.extract.function", end=anonEnd, result=anon1)
	slices.SortFunc(s, func(a, b string) int {
		return cmp.Compare(a, b)
	})
	println(s)
}

