package gotypes

import (
	
	
)

// WalkNamed visits every named type reachable from t, invoking visit for each type
// name. It does not descend into a type parameter's constraint; a caller that needs
// the constraint walks it separately. When visit returns false for a name, the walk does
// not descend into that name's type arguments (it prunes below the node); it
// still visits the rest of the tree, so this controls descent, not whole-walk
// termination. A caller that wants to stop entirely self-guards inside visit.
func ( types.Type,  func(*types.TypeName) bool) {
	walkNamed(, &Set[types.Type]{}, )
}

// walkNamed is WalkNamed's recursion, threading the cycle-guard seen set that the
// exported entry point seeds fresh.
func ( types.Type,  *Set[types.Type],  func(*types.TypeName) bool) {
	// Rendering names an alias, not its expansion, so the alias's own name is
	// what reachability must judge, and it is judged before the seen check:
	// the seen set keys on types.Identical, under which an alias equals its
	// target, so whichever spelling were walked first would swallow the other
	// (an exported alias hiding its unexported target, or vice versa). Aliases
	// bypass the dedup entirely; they cannot cycle (the type checker rejects
	// alias cycles), so the walk still covers only the finite spelling tree.
	// Only the rendered type arguments are walked, never Unalias(u).
	if ,  := .(*types.Alias);  {
		if !(.Obj()) {
			return
		}
		if  := .TypeArgs();  != nil {
			for  := range .Types() {
				(, , )
			}
		}
		return
	}
	if !.Add() {
		return
	}
	switch u := .(type) {
	case *types.Named:
		if !(.Obj()) {
			return
		}
		if  := .TypeArgs();  != nil {
			for  := range .Types() {
				(, , )
			}
		}
	case *types.Pointer:
		(.Elem(), , )
	case *types.Slice:
		(.Elem(), , )
	case *types.Array:
		(.Elem(), , )
	case *types.Chan:
		(.Elem(), , )
	case *types.Map:
		(.Key(), , )
		(.Elem(), , )
	case *types.Signature:
		walkTuple(.Params(), , )
		walkTuple(.Results(), , )
	case *types.Struct:
		for  := range .Fields() {
			(.Type(), , )
		}
	case *types.Interface:
		for  := range .EmbeddedTypes() {
			(, , )
		}
		for  := range .ExplicitMethods() {
			(.Type(), , )
		}
	case *types.Union:
		for  := range .Terms() {
			(.Type(), , )
		}
	case *types.Basic:
		// A predeclared or basic type names no type; nothing to visit or descend.
	case *types.TypeParam:
		// constraints are handled separately for lifted params
	default:
		panic(fmt.Sprintf("plumb: WalkNamed: unhandled type kind %T", ))
	}
}

func ( *types.Tuple,  *Set[types.Type],  func(*types.TypeName) bool) {
	for  := range .Variables() {
		walkNamed(.Type(), , )
	}
}

// ContainsInvalid reports whether t mentions the invalid type anywhere, meaning
// it failed to type-check and plumb must not render it.
func ( types.Type) bool {
	return typeContains(, &Set[types.Type]{}, func( types.Type) bool {
		,  := .(*types.Basic)
		return  && .Kind() == types.Invalid
	})
}

// ContainsTypeParam reports whether t mentions any type parameter anywhere:
// the test for a pinning that cannot survive a resolution restart, since lifted
// parameters are per-run objects.
func ( types.Type) bool {
	return typeContains(, &Set[types.Type]{}, func( types.Type) bool {
		,  := .(*types.TypeParam)
		return 
	})
}

// typeContains walks t (guarding against cycles) and reports whether pred holds
// for any node. The switch is exhaustive over every types.Type kind: leaf kinds
// terminate explicitly and any unhandled kind panics, never silently reports
// false. A silent false here is precisely how an unrendered invalid type would
// slip past ContainsInvalid and reach the emitter.
func ( types.Type,  *Set[types.Type],  func(types.Type) bool) bool {
	 = types.Unalias()
	if !.Add() {
		return false
	}
	if () {
		return true
	}
	switch u := .(type) {
	case *types.Basic, *types.TypeParam:
		// Leaf types: nothing to descend into. A type parameter renders as its
		// name; its constraint is validated separately (see checks.go).
		return false
	case *types.Pointer:
		return (.Elem(), , )
	case *types.Slice:
		return (.Elem(), , )
	case *types.Array:
		return (.Elem(), , )
	case *types.Chan:
		return (.Elem(), , )
	case *types.Map:
		return (.Key(), , ) || (.Elem(), , )
	case *types.Named:
		if  := .TypeArgs();  != nil {
			for  := range .Types() {
				if (, , ) {
					return true
				}
			}
		}
		return false
	case *types.Signature:
		return tupleContains(.Params(), , ) || tupleContains(.Results(), , )
	case *types.Struct:
		for  := range .Fields() {
			if (.Type(), , ) {
				return true
			}
		}
		return false
	case *types.Interface:
		for  := range .EmbeddedTypes() {
			if (, , ) {
				return true
			}
		}
		for  := range .ExplicitMethods() {
			if (.Type(), , ) {
				return true
			}
		}
		return false
	case *types.Union:
		for  := range .Terms() {
			if (.Type(), , ) {
				return true
			}
		}
		return false
	default:
		panic(fmt.Sprintf("plumb: typeContains: unhandled type kind %T", ))
	}
}

func ( *types.Tuple,  *Set[types.Type],  func(types.Type) bool) bool {
	for  := range .Variables() {
		if typeContains(.Type(), , ) {
			return true
		}
	}
	return false
}

// universeNames is the set of predeclared identifiers, backing IsUniverseName.
var universeNames = func() map[string]bool {
	 := map[string]bool{}
	for ,  := range types.Universe.Names() {
		[] = true
	}
	return 
}()

// IsUniverseName reports whether name is a predeclared identifier.
func ( string) bool { return universeNames[] }