package solveimport ()// 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 {ifgotypes.ContainsInvalid() {returndiag.Errorf(.Pos, diag.ErrInvalidType, "provider %s is instantiated at %s", .Name, gotypes.TypeName()) } }if .Kind == discover.KindConvert && gotypes.ContainsInvalid(.ConvertTo) {returndiag.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() {returndiag.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); {returndiag.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); {returndiag.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 { := .Typeifgotypes.ContainsInvalid() {returndiag.Errorf(.pos, diag.ErrInvalidType, "injector input type %s", gotypes.TypeName()) }if , := findUnreachable(, .destPath); {returndiag.Errorf(.pos, diag.ErrUnreachableType, "injector input type %s, from package %q (%s is unexported)", gotypes.TypeName(), .Pkg().Path(), .Name()) } }for , := range .Outputs {ifgotypes.ContainsInvalid() {returndiag.Errorf(.pos, diag.ErrInvalidType, "injector output type %s", gotypes.TypeName()) }if , := findUnreachable(, .destPath); {returndiag.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 {ifgotypes.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.ifgotypes.ContainsInvalid(.Constraint()) {returndiag.Errorf(.pos, diag.ErrInvalidType, "lifted type parameter %s has a constraint that does not type-check", .Obj().Name()) }if , := findUnreachable(.Constraint(), .destPath); {returndiag.Errorf(.pos, diag.ErrUnreachableType, "lifted type parameter %s has a constraint from package %q (%s is unexported)", .Obj().Name(), .Pkg().Path(), .Name()) } }returnnil}// 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 {casediscover.KindFunc, discover.KindMethod:return .Fncasediscover.KindSymbol, discover.KindField:return .Symcasediscover.KindStruct:returngotypes.TypeNameOf(.Declared)casediscover.KindConvert:// The conversion's only foreign reference is the target type, checked by // the type walk; it renders no qualified symbol of its own.returnnil }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.TypeNamegotypes.WalkNamed(, func( *types.TypeName) bool {if != nil {returnfalse }if := .Pkg(); != nil && .Path() != && !.Exported() { = returnfalse }returntrue })if != nil {return , true }returnnil, 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) > 0if .name == "init" && {returndiag.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" && {returndiag.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.ifgotypes.IsUniverseName(.name) {returndiag.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 {returnnil }// 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]; {returndiag.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]; {returndiag.Errorf(.pos, diag.ErrSetNameCollision, "set %q matches an import qualifier in package %q (%s); rename the set", .name, .destPath, ) }returnnil}
The pages are generated with Goldsv0.8.4. (GOOS=linux GOARCH=amd64)