package solve

import (
	
	

	
	
	
)

// checkReachability enforces that everything the generated code must name across
// a package boundary is exported and reachable.
func ( *solver) ( *Plan) *diag.Error {
	for ,  := range .instances {
		 := .Prov
		// A type plumb must render that does not type-check is fatal (distinct
		// from a tolerated stale-output type error, which never reaches here).
		for ,  := range .Targs {
			if gotypes.ContainsInvalid() {
				return diag.Errorf(.Pos, diag.ErrInvalidType, "provider %s is instantiated at %s", .Name, gotypes.TypeName())
			}
		}
		if .Kind == discover.KindConvert && gotypes.ContainsInvalid(.ConvertTo) {
			return diag.Errorf(.Pos, diag.ErrInvalidType, "conversion %s", .Name)
		}
		// A provider referenced across the boundary must itself be exported. A
		// same-package symbol is named unqualified, so this gate is correct; but
		// the type checks below are NOT gated, because a type a provider renders
		// (a type argument, a conversion target) may come from a third package
		// even when the provider itself is in the destination.
		if .Pkg != nil && .Pkg.Path() != .destPath {
			if  := crossBoundarySymbol();  != nil && !.Exported() {
				return diag.Errorf(.Pos, diag.ErrUnexportedProvider, "provider %s referenced from package %q; export it or generate into its own package", .Name, .destPath)
			}
		}
		// Type arguments are written in the body only for the kinds whose render
		// names them: a function call (Provider[Targ](...)) and a struct literal
		// (Declared[Targ]{...}). A method or field renders against an already-typed
		// receiver value (cache.Get(), foo.Bar) and writes no type arguments, so a
		// method/field type argument that never surfaces in the injector signature
		// is never named; one that does surface is caught by the signature walk
		// below. Checking it here regardless would reject valid programs.
		if .Kind == discover.KindFunc || .Kind == discover.KindStruct {
			for ,  := range .Targs {
				if ,  := findUnreachable(, .destPath);  {
					return diag.Errorf(.Pos, diag.ErrUnreachableType, "type argument %s of provider %s, from package %q (%s is unexported)", gotypes.TypeName(), .Name, .Pkg().Path(), .Name())
				}
			}
		}
		// The conversion's target type is written in the body.
		if .Kind == discover.KindConvert {
			if ,  := findUnreachable(.ConvertTo, .destPath);  {
				return diag.Errorf(.Pos, diag.ErrUnreachableType, "conversion target type %s from package %q (%s is unexported)", gotypes.TypeName(.ConvertTo), .Pkg().Path(), .Name())
			}
		}
	}
	// 2. Signature types must type-check and be reachable.
	for ,  := range .Inputs {
		 := .Type
		if gotypes.ContainsInvalid() {
			return diag.Errorf(.pos, diag.ErrInvalidType, "injector input type %s", gotypes.TypeName())
		}
		if ,  := findUnreachable(, .destPath);  {
			return diag.Errorf(.pos, diag.ErrUnreachableType, "injector input type %s, from package %q (%s is unexported)", gotypes.TypeName(), .Pkg().Path(), .Name())
		}
	}
	for ,  := range .Outputs {
		if gotypes.ContainsInvalid() {
			return diag.Errorf(.pos, diag.ErrInvalidType, "injector output type %s", gotypes.TypeName())
		}
		if ,  := findUnreachable(, .destPath);  {
			return diag.Errorf(.pos, diag.ErrUnreachableType, "injector output type %s, from package %q (%s is unexported)", gotypes.TypeName(), .Pkg().Path(), .Name())
		}
	}
	// 3. Lifted free parameters' constraints appear in the header, except one that
	// emit collapses to the bare "any", which names no package (the same predicate
	// gates emit's rendering and import collection, so the two cannot drift).
	for ,  := range .Lifted {
		if gotypes.ConstraintCollapsesToAny(.Constraint()) {
			continue
		}
		// A constraint that did not type-check (a typo'd or undefined name in
		// tolerated-invalid input) would render as "invalid type" in the header and
		// fail to format; catch it here, as inputs and outputs are checked above.
		if gotypes.ContainsInvalid(.Constraint()) {
			return diag.Errorf(.pos, diag.ErrInvalidType, "lifted type parameter %s has a constraint that does not type-check", .Obj().Name())
		}
		if ,  := findUnreachable(.Constraint(), .destPath);  {
			return diag.Errorf(.pos, diag.ErrUnreachableType, "lifted type parameter %s has a constraint from package %q (%s is unexported)", .Obj().Name(), .Pkg().Path(), .Name())
		}
	}
	return nil
}

// crossBoundarySymbol returns the named symbol a provider renders across the
// boundary, or nil if the provider renders no qualified symbol of its own.
func ( *discover.Provider) types.Object {
	switch .Kind {
	case discover.KindFunc, discover.KindMethod:
		return .Fn
	case discover.KindSymbol, discover.KindField:
		return .Sym
	case discover.KindStruct:
		return gotypes.TypeNameOf(.Declared)
	case discover.KindConvert:
		// The conversion's only foreign reference is the target type, checked by
		// the type walk; it renders no qualified symbol of its own.
		return nil
	}
	panic(fmt.Sprintf("plumb: unhandled provider kind %d", .Kind))
}

// findUnreachable walks t and returns the first named type from a non-destination
// package that is unexported (and therefore cannot be named from the destination).
// The offending type lives in the returned TypeName's own package, so diagnostics
// name obj.Pkg().Path(), not destPath, the one package it is guaranteed not in.
// Its Pkg() is never nil: the match requires a non-nil package below.
func ( types.Type,  string) (*types.TypeName, bool) {
	var  *types.TypeName
	gotypes.WalkNamed(, func( *types.TypeName) bool {
		if  != nil {
			return false
		}
		if  := .Pkg();  != nil && .Path() !=  && !.Exported() {
			 = 
			return false
		}
		return true
	})
	if  != nil {
		return , true
	}
	return nil, false
}

// checkReservedAndCollision enforces the reserved-name and set-name collision
// rules.
func ( *solver) ( *Plan) *diag.Error {
	 := len(.Inputs) > 0 || len(.Outputs) > 0 || .AnyCleanup || .Fallible || len(.Lifted) > 0

	if .name == "init" &&  {
		return diag.Errorf(.pos, diag.ErrReservedName, "set %q would generate a func init; init must take no parameters and return nothing", .name)
	}
	if .name == "main" && .dest.PkgName == "main" &&  {
		return diag.Errorf(.pos, diag.ErrReservedName, "set %q would generate a func main in package main", .name)
	}

	// A generated function is a package-block declaration, so naming a set after a
	// predeclared identifier (int, new, error, ...) shadows that identifier for the
	// entire destination package, breaking the generated body and any sibling file
	// that uses the builtin. In separate-package mode there are no siblings today,
	// but the name remains a latent trap, and the function name cannot be aliased.
	// So reject it outright in both modes, regardless of whether the generated
	// signature happens to mention the identifier.
	if gotypes.IsUniverseName(.name) {
		return diag.Errorf(.pos, diag.ErrShadowsPredeclared, "set %q would generate a top-level func %q shadowing the builtin for the whole destination package; rename the set", .name, .name)
	}

	// The reverse shadowing direction (a destination declaration capturing a
	// predeclared name the generated body spells unqualified) is a property of
	// the destination, not of any one set, so gen checks it once before solving
	// (see buildDestInfo).

	// init is never entered into the package scope, so it never collides.
	if .name == "init" || !.dest.Scanned {
		return nil
	}
	// Collision with an existing package-level declaration. gen already drops the
	// overwritten file's own declarations from dest.Names, so a hit here is always a
	// genuine collision; but for standard output no file is overwritten and every
	// declaration is recorded, so the check cannot tell a real collision from the
	// set's own future output and is skipped.
	if .outputBase != "" {
		if ,  := .dest.Names[.name];  {
			return diag.Errorf(.pos, diag.ErrSetNameCollision, "set %q in package %q (%s); rename the set", .name, .destPath, )
		}
	}
	// Collision with an import qualifier (file block) in a hand-written file: Go
	// forbids one identifier in both the file and package block. gen drops the
	// overwritten file's own qualifiers from dest.Imports (plumb rewrites them), so
	// a hit is a hand-written sibling's qualifier, a genuine collision, checked
	// even for standard output.
	if ,  := .dest.Imports[.name];  {
		return diag.Errorf(.pos, diag.ErrSetNameCollision, "set %q matches an import qualifier in package %q (%s); rename the set", .name, .destPath, )
	}
	return nil
}