goose: require pointer for first argument to goose.Bind (google/go-cloud#31)

Fixes google/go-cloud#15
This commit is contained in:
Ross Light
2018-05-29 08:45:14 -07:00
parent 7a864edab7
commit e9a61ba66b
6 changed files with 22 additions and 17 deletions

View File

@@ -247,13 +247,13 @@ func ProvideBar() *Bar {
var BarFooer = goose.NewSet( var BarFooer = goose.NewSet(
ProvideBar, ProvideBar,
goose.Bind(Fooer(nil), (*Bar)(nil))) goose.Bind(new(Fooer), new(Bar)))
``` ```
The first argument to `goose.Bind` is a nil value for the interface type and the The first argument to `goose.Bind` is a pointer to a value of the desired
second argument is a zero value of the concrete type. An interface binding does interface type and the second argument is a zero value of the concrete type. An
not necessarily need to have a provider in the same set that provides the interface binding does not necessarily need to have a provider in the same set
concrete type. that provides the concrete type.
[type identity]: https://golang.org/ref/spec#Type_identity [type identity]: https://golang.org/ref/spec#Type_identity
[return concrete types]: https://github.com/golang/go/wiki/CodeReviewComments#interfaces [return concrete types]: https://github.com/golang/go/wiki/CodeReviewComments#interfaces

View File

@@ -43,11 +43,12 @@ func Build(...interface{}) string {
type Binding struct{} type Binding struct{}
// Bind declares that a concrete type should be used to satisfy a // Bind declares that a concrete type should be used to satisfy a
// dependency on iface. // dependency on the type of iface, which must be a pointer to an
// interface type.
// //
// Example: // Example:
// //
// var MySet = goose.NewSet(goose.Bind(MyInterface(nil), new(MyStruct))) // var MySet = goose.NewSet(goose.Bind(new(MyInterface), new(MyStruct)))
func Bind(iface, to interface{}) Binding { func Bind(iface, to interface{}) Binding {
return Binding{} return Binding{}
} }

View File

@@ -496,21 +496,25 @@ func processBind(fset *token.FileSet, info *types.Info, call *ast.CallExpr) (*If
return nil, fmt.Errorf("%v: call to Bind takes exactly two arguments", fset.Position(call.Pos())) return nil, fmt.Errorf("%v: call to Bind takes exactly two arguments", fset.Position(call.Pos()))
} }
// TODO(light): Verify that arguments are simple expressions. // TODO(light): Verify that arguments are simple expressions.
iface := info.TypeOf(call.Args[0]) ifaceArgType := info.TypeOf(call.Args[0])
methodSet, ok := iface.Underlying().(*types.Interface) ifacePtr, ok := ifaceArgType.(*types.Pointer)
if !ok { if !ok {
return nil, fmt.Errorf("%v: first argument to bind must be of interface type; found %s", fset.Position(call.Pos()), types.TypeString(iface, nil)) return nil, fmt.Errorf("%v: first argument to bind must be a pointer to an interface type; found %s", fset.Position(call.Pos()), types.TypeString(ifaceArgType, nil))
}
methodSet, ok := ifacePtr.Elem().Underlying().(*types.Interface)
if !ok {
return nil, fmt.Errorf("%v: first argument to bind must be a pointer to an interface type; found %s", fset.Position(call.Pos()), types.TypeString(ifaceArgType, nil))
} }
provided := info.TypeOf(call.Args[1]) provided := info.TypeOf(call.Args[1])
if types.Identical(iface, provided) { if types.Identical(ifacePtr.Elem(), provided) {
return nil, fmt.Errorf("%v: cannot bind interface to itself", fset.Position(call.Pos())) return nil, fmt.Errorf("%v: cannot bind interface to itself", fset.Position(call.Pos()))
} }
if !types.Implements(provided, methodSet) { if !types.Implements(provided, methodSet) {
return nil, fmt.Errorf("%v: %s does not implement %s", fset.Position(call.Pos()), types.TypeString(provided, nil), types.TypeString(iface, nil)) return nil, fmt.Errorf("%v: %s does not implement %s", fset.Position(call.Pos()), types.TypeString(provided, nil), types.TypeString(ifaceArgType, nil))
} }
return &IfaceBinding{ return &IfaceBinding{
Pos: call.Pos(), Pos: call.Pos(),
Iface: iface, Iface: ifacePtr.Elem(),
Provided: provided, Provided: provided,
}, nil }, nil
} }

View File

@@ -17,8 +17,8 @@ package main
import ( import (
"fmt" "fmt"
"github.com/google/go-cloud/goose"
"foo" "foo"
"github.com/google/go-cloud/goose"
) )
func main() { func main() {
@@ -39,4 +39,4 @@ func provideBar() *Bar {
var Set = goose.NewSet( var Set = goose.NewSet(
provideBar, provideBar,
goose.Bind(foo.Fooer(nil), (*Bar)(nil))) goose.Bind((*foo.Fooer)(nil), (*Bar)(nil)))

View File

@@ -42,4 +42,4 @@ func provideBar() *Bar {
var Set = goose.NewSet( var Set = goose.NewSet(
provideBar, provideBar,
goose.Bind(Fooer(nil), (*Bar)(nil))) goose.Bind((*Fooer)(nil), (*Bar)(nil)))

View File

@@ -24,5 +24,5 @@ func injectFooBar() FooBar {
panic(goose.Build( panic(goose.Build(
provideBar, provideBar,
provideFooBar, provideFooBar,
goose.Bind(Fooer(nil), (*Bar)(nil)))) goose.Bind((*Fooer)(nil), (*Bar)(nil))))
} }