func isLocallyDefined(scopes depScopes, name string) bool {
	for i := len(scopes) - 1; i >= 0; i-- {
		if _, ok := scopes[i][name]; ok {
			return true
		}
	}
	return false
}
isLocallyDefined reports whether name is locally defined in scopes.
package compiler
import (
	"fmt"
	"github.com/open2b/scriggo/ast"
)
type packageDeclsDeps map[*ast.Identifier][]*ast.Identifier
type deps struct {
	d packageDeclsDeps
	itea                     map[string][]*ast.Identifier
	analyzingVarExprWithItea *ast.Identifier
}
func (d *deps) addDepsToGlobal(ident *ast.Identifier, node ast.Node, scopes depScopes) {
	if scopes == nil {
		scopes = depScopes{map[string]struct{}{}}
	}
	if d.d[ident] == nil {
		d.d[ident] = []*ast.Identifier{}
	}
	for _, dep := range d.nodeDeps(node, scopes) {
		if dep.Name == "_" {
			continue
		}
		alreadyAdded := false
		for _, d := range d.d[ident] {
			if d.Name == dep.Name {
				alreadyAdded = true
				break
			}
		}
		if !alreadyAdded {
			d.d[ident] = append(d.d[ident], dep)
		}
	}
}
func (d *deps) hasIteaInItsExpression(varLh *ast.Identifier) bool {
	for _, v := range d.itea {
		for _, ident := range v {
			if ident == varLh {
				return true
			}
		}
	}
	return false
}
func (d *deps) analyzeGlobalVar(n *ast.Var) {
	if len(n.Lhs) == len(n.Rhs) {
		for i := range n.Lhs {
			d.addDepsToGlobal(n.Lhs[i], n.Type, nil)
			if d.hasIteaInItsExpression(n.Lhs[i]) {
				d.analyzingVarExprWithItea = n.Lhs[i]
				d.addDepsToGlobal(n.Lhs[i], n.Rhs[i], nil)
				d.analyzingVarExprWithItea = nil
			} else {
				d.addDepsToGlobal(n.Lhs[i], n.Rhs[i], nil)
			}
		}
		return
	}
	for _, left := range n.Lhs {
		d.addDepsToGlobal(left, n.Type, nil)
		for _, right := range n.Rhs {
			d.addDepsToGlobal(left, right, nil)
		}
	}
}
func (d *deps) analyzeGlobalConst(n *ast.Const) {
	for i := range n.Lhs {
		d.addDepsToGlobal(n.Lhs[i], n.Type, nil)
		d.addDepsToGlobal(n.Lhs[i], n.Rhs[i], nil)
	}
}
func (d *deps) analyzeGlobalDeclarationAssignment(n *ast.Assignment) {
	if len(n.Lhs) == len(n.Rhs) {
		for i := range n.Lhs {
			if ident, ok := n.Lhs[i].(*ast.Identifier); ok {
				d.addDepsToGlobal(ident, n.Rhs[i], nil)
			}
		}
		return
	}
	for _, left := range n.Lhs {
		for _, right := range n.Rhs {
			d.addDepsToGlobal(left.(*ast.Identifier), right, nil)
		}
	}
}
func (d *deps) analyzeGlobalFunc(n *ast.Func) {
	scopes := depScopes{map[string]struct{}{}}
	for _, f := range n.Type.Parameters {
		if f.Ident != nil {
			scopes = declareLocally(scopes, f.Ident.Name)
		}
	}
	for _, f := range n.Type.Result {
		if f.Ident != nil {
			scopes = declareLocally(scopes, f.Ident.Name)
		}
	}
	d.addDepsToGlobal(n.Ident, n.Type, scopes)
	if n.Body != nil {
		d.addDepsToGlobal(n.Ident, n.Body, scopes)
	}
}
func (d *deps) analyzeGlobalTypeDeclaration(td *ast.TypeDeclaration) {
	d.addDepsToGlobal(td.Ident, td.Type, nil)
}
func analyzeTree(pkg *ast.Package) packageDeclsDeps {
	d := &deps{
		d:    packageDeclsDeps{},
		itea: pkg.IR.IteaNameToVarIdents,
	}
	for _, n := range pkg.Declarations {
		switch n := n.(type) {
		case *ast.Var:
			d.analyzeGlobalVar(n)
		case *ast.Const:
			d.analyzeGlobalConst(n)
		case *ast.Func:
			d.analyzeGlobalFunc(n)
		case *ast.TypeDeclaration:
			d.analyzeGlobalTypeDeclaration(n)
		}
	}
	return packageDeclsDeps(d.d)
}
type depScopes []map[string]struct{}
func enterScope(scopes depScopes) depScopes {
	return append(scopes, map[string]struct{}{})
}
func exitScope(scopes depScopes) depScopes {
	return scopes[:len(scopes)-1]
}
func declareLocally(scopes depScopes, name string) depScopes {
	scopes[len(scopes)-1][name] = struct{}{}
	return scopes
}