wire: add info from the dependency graph when a type is not provided

This commit is contained in:
Robert van Gent
2018-10-17 14:14:08 -07:00
committed by Ross Light
parent b84ad6154f
commit a8825fef58
5 changed files with 72 additions and 24 deletions

View File

@@ -122,6 +122,7 @@ func solve(fset *token.FileSet, out types.Type, given []types.Type, set *Provide
type frame struct { type frame struct {
t types.Type t types.Type
from types.Type from types.Type
up *frame
} }
stk := []frame{{t: out}} stk := []frame{{t: out}}
dfs: dfs:
@@ -135,12 +136,15 @@ dfs:
switch pv := set.For(curr.t); { switch pv := set.For(curr.t); {
case pv.IsNil(): case pv.IsNil():
if curr.from == nil { if curr.from == nil {
ec.add(fmt.Errorf("no provider found for %s (output of injector)", types.TypeString(curr.t, nil))) ec.add(fmt.Errorf("no provider found for %s, output of injector", types.TypeString(curr.t, nil)))
index.Set(curr.t, errAbort) index.Set(curr.t, errAbort)
continue continue
} }
// TODO(light): Give name of provider. neededBy := []string{types.TypeString(curr.t, nil)}
ec.add(fmt.Errorf("no provider found for %s (required by provider of %s)", types.TypeString(curr.t, nil), types.TypeString(curr.from, nil))) for f := curr.up; f != nil; f = f.up {
neededBy = append(neededBy, fmt.Sprintf("%s in %s", types.TypeString(f.t, nil), set.srcMap.At(f.t).(*providerSetSrc).description(fset, f.t)))
}
ec.add(fmt.Errorf("no provider found for %s", strings.Join(neededBy, ", needed by ")))
index.Set(curr.t, errAbort) index.Set(curr.t, errAbort)
continue continue
case pv.IsProvider(): case pv.IsProvider():
@@ -151,7 +155,7 @@ dfs:
// Interface binding. Don't create a call ourselves. // Interface binding. Don't create a call ourselves.
i := index.At(concrete) i := index.At(concrete)
if i == nil { if i == nil {
stk = append(stk, curr, frame{t: concrete, from: curr.t}) stk = append(stk, curr, frame{t: concrete, from: curr.t, up: &curr})
continue continue
} }
index.Set(curr.t, i) index.Set(curr.t, i)
@@ -169,7 +173,7 @@ dfs:
stk = append(stk, curr) stk = append(stk, curr)
visitedArgs = false visitedArgs = false
} }
stk = append(stk, frame{t: a.Type, from: curr.t}) stk = append(stk, frame{t: a.Type, from: curr.t, up: &curr})
} }
} }
if !visitedArgs { if !visitedArgs {
@@ -208,7 +212,7 @@ dfs:
// Interface binding. Don't create a call ourselves. // Interface binding. Don't create a call ourselves.
i := index.At(v.Out) i := index.At(v.Out)
if i == nil { if i == nil {
stk = append(stk, curr, frame{t: v.Out, from: curr.t}) stk = append(stk, curr, frame{t: v.Out, from: curr.t, up: &curr})
continue continue
} }
index.Set(curr.t, i) index.Set(curr.t, i)

View File

@@ -38,34 +38,42 @@ type providerSetSrc struct {
Import *ProviderSet Import *ProviderSet
} }
// trace returns a slice of strings describing the (possibly recursive) source // description returns a string describing the source of p, including line numbers.
// of p, including line numbers. func (p *providerSetSrc) description(fset *token.FileSet, typ types.Type) string {
func (p *providerSetSrc) trace(fset *token.FileSet, typ types.Type) []string {
quoted := func(s string) string { quoted := func(s string) string {
if s == "" { if s == "" {
return "" return ""
} }
return fmt.Sprintf("%q ", s) return fmt.Sprintf("%q ", s)
} }
var retval []string switch {
if p.Provider != nil { case p.Provider != nil:
kind := "provider" kind := "provider"
if p.Provider.IsStruct { if p.Provider.IsStruct {
kind = "struct provider" kind = "struct provider"
} }
retval = append(retval, fmt.Sprintf("%s %s(%s)", kind, quoted(p.Provider.Name), fset.Position(p.Provider.Pos))) return fmt.Sprintf("%s %s(%s)", kind, quoted(p.Provider.Name), fset.Position(p.Provider.Pos))
} else if p.Binding != nil { case p.Binding != nil:
retval = append(retval, fmt.Sprintf("wire.Bind (%s)", fset.Position(p.Binding.Pos))) return fmt.Sprintf("wire.Bind (%s)", fset.Position(p.Binding.Pos))
} else if p.Value != nil { case p.Value != nil:
retval = append(retval, fmt.Sprintf("wire.Value (%s)", fset.Position(p.Value.Pos))) return fmt.Sprintf("wire.Value (%s)", fset.Position(p.Value.Pos))
} else if p.Import != nil { case p.Import != nil:
return fmt.Sprintf("provider set %s(%s)", quoted(p.Import.VarName), fset.Position(p.Import.Pos))
}
panic("providerSetSrc with no fields set")
}
// trace returns a slice of strings describing the (possibly recursive) source
// of p, including line numbers.
func (p *providerSetSrc) trace(fset *token.FileSet, typ types.Type) []string {
var retval []string
// Only Imports need recursion.
if p.Import != nil {
if parent := p.Import.srcMap.At(typ); parent != nil { if parent := p.Import.srcMap.At(typ); parent != nil {
retval = append(retval, parent.(*providerSetSrc).trace(fset, typ)...) retval = append(retval, parent.(*providerSetSrc).trace(fset, typ)...)
} }
retval = append(retval, fmt.Sprintf("provider set %s(%s)", quoted(p.Import.VarName), fset.Position(p.Import.Pos)))
} else {
panic("providerSetSrc with no fields set")
} }
retval = append(retval, p.description(fset, typ))
return retval return retval
} }

View File

@@ -17,7 +17,9 @@ package main
import "fmt" import "fmt"
func main() { func main() {
fmt.Println(injectBaz()) fmt.Println(injectMissingOutputType())
fmt.Println(injectMultipleMissingTypes())
fmt.Println(injectMissingRecursiveType())
} }
type Foo int type Foo int
@@ -27,3 +29,19 @@ type Baz int
func provideBaz(foo Foo, bar Bar) Baz { func provideBaz(foo Foo, bar Bar) Baz {
return 0 return 0
} }
type Zip int
type Zap int
type Zop int
func provideZip(foo Foo) Zip {
return 0
}
func provideZap(zip Zip) Zap {
return 0
}
func provideZop(zap Zap) Zop {
return 0
}

View File

@@ -20,7 +20,23 @@ import (
"github.com/google/go-cloud/wire" "github.com/google/go-cloud/wire"
) )
func injectBaz() Baz { func injectMissingOutputType() Foo {
// Error: no provider for Foo.
wire.Build()
return Foo(0)
}
func injectMultipleMissingTypes() Baz {
// Error: provideBaz needs Foo and Bar, both missing.
wire.Build(provideBaz) wire.Build(provideBaz)
return Baz(0) return Baz(0)
} }
func injectMissingRecursiveType() Zop {
// Error:
// Zop -> Zap -> Zip -> Foo
// provideZop needs Zap, provideZap needs Zip, provideZip needs Foo,
// which is missing.
wire.Build(provideZop, provideZap, provideZip)
return Zop(0)
}

View File

@@ -1,2 +1,4 @@
no provider found for example.com/foo.Foo inject injectMissingOutputType: no provider found for example.com/foo.Foo, output of injector
no provider found for example.com/foo.Bar inject injectMultipleMissingTypes: no provider found for example.com/foo.Foo, needed by example.com/foo.Baz in provider "provideBaz" (/wire_gopath/src/example.com/foo/foo.go:29:6)
inject injectMultipleMissingTypes: no provider found for example.com/foo.Bar, needed by example.com/foo.Baz in provider "provideBaz" (/wire_gopath/src/example.com/foo/foo.go:29:6)
inject injectMissingRecursiveType: no provider found for example.com/foo.Foo, needed by example.com/foo.Zip in provider "provideZip" (/wire_gopath/src/example.com/foo/foo.go:37:6), needed by example.com/foo.Zap in provider "provideZap" (/wire_gopath/src/example.com/foo/foo.go:41:6), needed by example.com/foo.Zop in provider "provideZop" (/wire_gopath/src/example.com/foo/foo.go:45:6)