diff --git a/internal/wire/analyze.go b/internal/wire/analyze.go index 8d2c3dc..0b823aa 100644 --- a/internal/wire/analyze.go +++ b/internal/wire/analyze.go @@ -122,6 +122,7 @@ func solve(fset *token.FileSet, out types.Type, given []types.Type, set *Provide type frame struct { t types.Type from types.Type + up *frame } stk := []frame{{t: out}} dfs: @@ -135,12 +136,15 @@ dfs: switch pv := set.For(curr.t); { case pv.IsNil(): 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) continue } - // TODO(light): Give name of provider. - ec.add(fmt.Errorf("no provider found for %s (required by provider of %s)", types.TypeString(curr.t, nil), types.TypeString(curr.from, nil))) + neededBy := []string{types.TypeString(curr.t, 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) continue case pv.IsProvider(): @@ -151,7 +155,7 @@ dfs: // Interface binding. Don't create a call ourselves. i := index.At(concrete) 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 } index.Set(curr.t, i) @@ -169,7 +173,7 @@ dfs: stk = append(stk, curr) 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 { @@ -208,7 +212,7 @@ dfs: // Interface binding. Don't create a call ourselves. i := index.At(v.Out) 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 } index.Set(curr.t, i) diff --git a/internal/wire/parse.go b/internal/wire/parse.go index 6b9c32f..b701b40 100644 --- a/internal/wire/parse.go +++ b/internal/wire/parse.go @@ -38,34 +38,42 @@ type providerSetSrc struct { Import *ProviderSet } -// 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 { +// description returns a string describing the source of p, including line numbers. +func (p *providerSetSrc) description(fset *token.FileSet, typ types.Type) string { quoted := func(s string) string { if s == "" { return "" } return fmt.Sprintf("%q ", s) } - var retval []string - if p.Provider != nil { + switch { + case p.Provider != nil: kind := "provider" if p.Provider.IsStruct { kind = "struct provider" } - retval = append(retval, fmt.Sprintf("%s %s(%s)", kind, quoted(p.Provider.Name), fset.Position(p.Provider.Pos))) - } else if p.Binding != nil { - retval = append(retval, fmt.Sprintf("wire.Bind (%s)", fset.Position(p.Binding.Pos))) - } else if p.Value != nil { - retval = append(retval, fmt.Sprintf("wire.Value (%s)", fset.Position(p.Value.Pos))) - } else if p.Import != nil { + return fmt.Sprintf("%s %s(%s)", kind, quoted(p.Provider.Name), fset.Position(p.Provider.Pos)) + case p.Binding != nil: + return fmt.Sprintf("wire.Bind (%s)", fset.Position(p.Binding.Pos)) + case p.Value != nil: + return fmt.Sprintf("wire.Value (%s)", fset.Position(p.Value.Pos)) + 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 { 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 } diff --git a/internal/wire/testdata/MultipleMissingInputs/foo/foo.go b/internal/wire/testdata/MultipleMissingInputs/foo/foo.go index 91d6f88..6aedca9 100644 --- a/internal/wire/testdata/MultipleMissingInputs/foo/foo.go +++ b/internal/wire/testdata/MultipleMissingInputs/foo/foo.go @@ -17,7 +17,9 @@ package main import "fmt" func main() { - fmt.Println(injectBaz()) + fmt.Println(injectMissingOutputType()) + fmt.Println(injectMultipleMissingTypes()) + fmt.Println(injectMissingRecursiveType()) } type Foo int @@ -27,3 +29,19 @@ type Baz int func provideBaz(foo Foo, bar Bar) Baz { 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 +} diff --git a/internal/wire/testdata/MultipleMissingInputs/foo/wire.go b/internal/wire/testdata/MultipleMissingInputs/foo/wire.go index f8b2140..64ffdbb 100644 --- a/internal/wire/testdata/MultipleMissingInputs/foo/wire.go +++ b/internal/wire/testdata/MultipleMissingInputs/foo/wire.go @@ -20,7 +20,23 @@ import ( "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) 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) +} diff --git a/internal/wire/testdata/MultipleMissingInputs/want/wire_errs.txt b/internal/wire/testdata/MultipleMissingInputs/want/wire_errs.txt index 1d3dce6..9a42fd1 100644 --- a/internal/wire/testdata/MultipleMissingInputs/want/wire_errs.txt +++ b/internal/wire/testdata/MultipleMissingInputs/want/wire_errs.txt @@ -1,2 +1,4 @@ -no provider found for example.com/foo.Foo -no provider found for example.com/foo.Bar +inject injectMissingOutputType: no provider found for example.com/foo.Foo, output of injector +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)