package main

import "errors"

func simple() (err error) {
	defer func() {
		err = nil
	}()
	return
}

func twoReturnParams() (i int, err error) { // want `named return "i" with type "int" found`
	defer func() {
		i = 0
		err = nil
	}()
	return
}

func allUnderscoresExceptError() (_ int, err error) {
	defer func() {
		err = nil
	}()
	return
}

func customName() (myName error) {
	defer func() {
		myName = nil
	}()
	return
}

func errorIsNoAssigned() (err error) { // want `named return "err" with type "error" found`
	defer func() {
		_ = err
		processError(err)
		if err == nil {
		}
		switch err {
		case nil:
		default:
		}
	}()
	return
}

func shadowVariable() (err error) { // want `named return "err" with type "error" found`
	defer func() {
		err := errors.New("xxx")
		_ = err
	}()
	return
}

func shadowVariableButAssign() (err error) {
	defer func() {
		{
			err := errors.New("xxx")
			_ = err
		}
		err = nil
	}()
	return
}

func shadowVariable2() (err error) { // want `named return "err" with type "error" found`
	defer func() {
		a, err := doSomething()
		_ = a
		_ = err
	}()
	return
}

type errorAlias = error

func errorAliasIsTheSame() (err errorAlias) {
	defer func() {
		err = nil
	}()
	return
}

type myError error // linter doesn't check underlying type (yet?)

func customTypeWithErrorUnderline() (err myError) { // want `named return "err" with type "myError" found`
	defer func() {
		err = nil
	}()
	return
}

type myError2 interface{ error } // linter doesn't check interfaces

func customTypeWithTheSameInterface() (err myError2) { // want `named return "err" with type "myError2" found`
	defer func() {
		err = nil
	}()
	return
}

var _ error = myError3{}

type myError3 struct{} // linter doesn't check interfaces

func (m myError3) Error() string { return "" }

func customTypeImplementingErrorInterface() (err myError3) { // want `named return "err" with type "myError3" found`
	defer func() {
		err = struct{}{}
	}()
	return
}

func shadowErrorType() {
	type error interface { // linter understands that this is not built-in error, even if it has the same name
		Error() string
	}
	do := func() (err error) { // want `named return "err" with type "error" found`
		defer func() {
			err = nil
		}()
		return
	}
	do()
}

func notTheLast() (err error, _ int) {
	defer func() {
		err = nil
	}()
	return
}

func twoErrorsCombined() (err1, err2 error) {
	defer func() {
		err1 = nil
		err2 = nil
	}()
	return
}

func twoErrorsSeparated() (err1 error, err2 error) {
	defer func() {
		err1 = nil
		err2 = nil
	}()
	return
}

func errorSlice() (err []error) { // want `named return "err" with type "\[\]error" found`
	defer func() {
		err = nil
	}()
	return
}

func deferWithVariable() (err error) { // want `named return "err" with type "error" found`
	f := func() {
		err = nil
	}
	defer f() // linter can't catch closure passed via variable (yet?)
	return
}

func uberMultierr() (err error) { // want `named return "err" with type "error" found`
	defer func() {
		multierrAppendInto(&err, nil) // linter doesn't allow it (yet?)
	}()
	return
}

func deferInDefer() (err error) {
	defer func() {
		defer func() {
			err = nil
		}()
	}()
	return
}

func twoDefers() (err error) {
	defer func() {}()
	defer func() {
		err = nil
	}()
	return
}

func callFunction() (err error) {
	defer func() {
		_, err = doSomething()
	}()
	return
}

func callFunction2() (err error) {
	defer func() {
		var a int
		a, err = doSomething()
		_ = a
	}()
	return
}

func deepInside() (err error) {
	if true {
		switch true {
		case false:
			for i := 0; i < 10; i++ {
				go func() {
					select {
					default:
						defer func() {
							if true {
								switch true {
								case false:
									for j := 0; j < 10; j++ {
										go func() {
											select {
											default:
												err = nil
											}
										}()
									}
								}
							}
						}()
					}
				}()
			}
		}
	}
	return
}

var goodFuncLiteral = func() (err error) {
	defer func() {
		err = nil
	}()
	return
}

var badFuncLiteral = func() (err error) { // want `named return "err" with type "error" found`
	defer func() {
		_ = err
	}()
	return
}

func funcLiteralInsideFunc() error {
	do := func() (err error) {
		defer func() {
			err = nil
		}()
		return
	}
	return do()
}

type x struct{}

func (x) goodMethod() (err error) {
	defer func() {
		err = nil
	}()
	return
}

func (x) badMethod() (err error) { // want `named return "err" with type "error" found`
	defer func() {
		_ = err
	}()
	return
}

func processError(error)                    {}
func doSomething() (int, error)             { return 10, nil }
func multierrAppendInto(*error, error) bool { return false } // https://pkg.go.dev/go.uber.org/multierr#AppendInto
