diff --git a/README.md b/README.md index d60c856..1eff7ed 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,23 @@ interface type and the second argument is a zero value of the concrete type. Any set that includes an interface binding must also have a provider in the same set that provides the concrete type. +If necessary, you can also bind one interface to another: + +```go +type FooerPlus interface { + Fooer + Bar() String +} + +func ProviderFooerPlus() FooerPlus { + ... +} + +var FooerPlusAsFooer = wire.NewSet( + ProvideFooerPlus, + wire.Bind(new(Fooer), *new(FooerPlus))) +``` + [type identity]: https://golang.org/ref/spec#Type_identity [return concrete types]: https://github.com/golang/go/wiki/CodeReviewComments#interfaces diff --git a/internal/wire/parse.go b/internal/wire/parse.go index 1b65312..2450100 100644 --- a/internal/wire/parse.go +++ b/internal/wire/parse.go @@ -680,22 +680,23 @@ func processBind(fset *token.FileSet, info *types.Info, call *ast.CallExpr) (*If ifaceArgType := info.TypeOf(call.Args[0]) ifacePtr, ok := ifaceArgType.(*types.Pointer) if !ok { - return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf("first argument to bind must be a pointer to an interface type; found %s", types.TypeString(ifaceArgType, nil))) + return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf("first argument to Bind must be a pointer to an interface type; found %s", types.TypeString(ifaceArgType, nil))) } - methodSet, ok := ifacePtr.Elem().Underlying().(*types.Interface) + iface := ifacePtr.Elem() + methodSet, ok := iface.Underlying().(*types.Interface) if !ok { - return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf("first argument to bind must be a pointer to an interface type; found %s", types.TypeString(ifaceArgType, nil))) + return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf("first argument to Bind must be a pointer to an interface type; found %s", types.TypeString(ifaceArgType, nil))) } provided := info.TypeOf(call.Args[1]) - if types.Identical(ifacePtr.Elem(), provided) { + if types.Identical(iface, provided) { return nil, notePosition(fset.Position(call.Pos()), errors.New("cannot bind interface to itself")) } if !types.Implements(provided, methodSet) { - return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf("%s does not implement %s", types.TypeString(provided, nil), types.TypeString(ifaceArgType, nil))) + return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf("%s does not implement %s", types.TypeString(provided, nil), types.TypeString(iface, nil))) } return &IfaceBinding{ Pos: call.Pos(), - Iface: ifacePtr.Elem(), + Iface: iface, Provided: provided, }, nil } @@ -764,7 +765,7 @@ func processInterfaceValue(fset *token.FileSet, info *types.Info, call *ast.Call } provided := info.TypeOf(call.Args[1]) if !types.Implements(provided, methodSet) { - return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf("%s does not implement %s", types.TypeString(provided, nil), types.TypeString(ifaceArgType, nil))) + return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf("%s does not implement %s", types.TypeString(provided, nil), types.TypeString(iface, nil))) } return &Value{ Pos: call.Args[1].Pos(), diff --git a/internal/wire/testdata/InterfaceBindingDoesntImplement/foo/foo.go b/internal/wire/testdata/InterfaceBindingDoesntImplement/foo/foo.go new file mode 100644 index 0000000..4942cb8 --- /dev/null +++ b/internal/wire/testdata/InterfaceBindingDoesntImplement/foo/foo.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Go Cloud Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" +) + +func main() { + fmt.Println(injectFooer().Foo()) +} + +type Fooer interface { + Foo() string +} diff --git a/internal/wire/testdata/InterfaceBindingDoesntImplement/foo/wire.go b/internal/wire/testdata/InterfaceBindingDoesntImplement/foo/wire.go new file mode 100644 index 0000000..454f839 --- /dev/null +++ b/internal/wire/testdata/InterfaceBindingDoesntImplement/foo/wire.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Go Cloud Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//+build wireinject + +package main + +import ( + "github.com/google/go-cloud/wire" +) + +func injectFooer() Fooer { + // wrong: string doesn't implement Fooer. + wire.Build(wire.Bind((*Fooer)(nil), "foo")) + return nil +} diff --git a/internal/wire/testdata/InterfaceBindingDoesntImplement/pkg b/internal/wire/testdata/InterfaceBindingDoesntImplement/pkg new file mode 100644 index 0000000..f7a5c8c --- /dev/null +++ b/internal/wire/testdata/InterfaceBindingDoesntImplement/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/internal/wire/testdata/InterfaceBindingDoesntImplement/want/wire_errs.txt b/internal/wire/testdata/InterfaceBindingDoesntImplement/want/wire_errs.txt new file mode 100644 index 0000000..c92114a --- /dev/null +++ b/internal/wire/testdata/InterfaceBindingDoesntImplement/want/wire_errs.txt @@ -0,0 +1 @@ +string does not implement example.com/foo.Fooer diff --git a/internal/wire/testdata/InterfaceBindingInvalidArg0/foo/foo.go b/internal/wire/testdata/InterfaceBindingInvalidArg0/foo/foo.go new file mode 100644 index 0000000..4942cb8 --- /dev/null +++ b/internal/wire/testdata/InterfaceBindingInvalidArg0/foo/foo.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Go Cloud Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" +) + +func main() { + fmt.Println(injectFooer().Foo()) +} + +type Fooer interface { + Foo() string +} diff --git a/internal/wire/testdata/InterfaceBindingInvalidArg0/foo/wire.go b/internal/wire/testdata/InterfaceBindingInvalidArg0/foo/wire.go new file mode 100644 index 0000000..6a504e5 --- /dev/null +++ b/internal/wire/testdata/InterfaceBindingInvalidArg0/foo/wire.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Go Cloud Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//+build wireinject + +package main + +import ( + "github.com/google/go-cloud/wire" +) + +func injectFooer() Fooer { + // wrong: arg0 must be a pointer to an interface. + wire.Build(wire.Bind("foo", "bar")) + return nil +} diff --git a/internal/wire/testdata/InterfaceBindingInvalidArg0/pkg b/internal/wire/testdata/InterfaceBindingInvalidArg0/pkg new file mode 100644 index 0000000..f7a5c8c --- /dev/null +++ b/internal/wire/testdata/InterfaceBindingInvalidArg0/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/internal/wire/testdata/InterfaceBindingInvalidArg0/want/wire_errs.txt b/internal/wire/testdata/InterfaceBindingInvalidArg0/want/wire_errs.txt new file mode 100644 index 0000000..6c4aa11 --- /dev/null +++ b/internal/wire/testdata/InterfaceBindingInvalidArg0/want/wire_errs.txt @@ -0,0 +1 @@ +first argument to Bind must be a pointer to an interface type; found string diff --git a/internal/wire/testdata/InterfaceBindingNotEnoughArgs/foo/foo.go b/internal/wire/testdata/InterfaceBindingNotEnoughArgs/foo/foo.go new file mode 100644 index 0000000..4942cb8 --- /dev/null +++ b/internal/wire/testdata/InterfaceBindingNotEnoughArgs/foo/foo.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Go Cloud Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" +) + +func main() { + fmt.Println(injectFooer().Foo()) +} + +type Fooer interface { + Foo() string +} diff --git a/internal/wire/testdata/InterfaceBindingNotEnoughArgs/foo/wire.go b/internal/wire/testdata/InterfaceBindingNotEnoughArgs/foo/wire.go new file mode 100644 index 0000000..b443d9a --- /dev/null +++ b/internal/wire/testdata/InterfaceBindingNotEnoughArgs/foo/wire.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Go Cloud Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//+build wireinject + +package main + +import ( + "github.com/google/go-cloud/wire" +) + +func injectFooer() Fooer { + // wrong: wire.Bind requires 2 args. + wire.Build(wire.Bind((*Fooer)(nil))) + return nil +} diff --git a/internal/wire/testdata/InterfaceBindingNotEnoughArgs/pkg b/internal/wire/testdata/InterfaceBindingNotEnoughArgs/pkg new file mode 100644 index 0000000..f7a5c8c --- /dev/null +++ b/internal/wire/testdata/InterfaceBindingNotEnoughArgs/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/internal/wire/testdata/InterfaceBindingNotEnoughArgs/want/wire_errs.txt b/internal/wire/testdata/InterfaceBindingNotEnoughArgs/want/wire_errs.txt new file mode 100644 index 0000000..ffd395a --- /dev/null +++ b/internal/wire/testdata/InterfaceBindingNotEnoughArgs/want/wire_errs.txt @@ -0,0 +1 @@ +too few arguments in call to wire.Bind diff --git a/internal/wire/testdata/InterfaceValueDoesntImplement/foo/foo.go b/internal/wire/testdata/InterfaceValueDoesntImplement/foo/foo.go new file mode 100644 index 0000000..4289d9f --- /dev/null +++ b/internal/wire/testdata/InterfaceValueDoesntImplement/foo/foo.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Go Cloud Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import "fmt" + +func main() { + fmt.Println(injectedMessage()) +} diff --git a/internal/wire/testdata/InterfaceValueDoesntImplement/foo/wire.go b/internal/wire/testdata/InterfaceValueDoesntImplement/foo/wire.go new file mode 100644 index 0000000..58d28e1 --- /dev/null +++ b/internal/wire/testdata/InterfaceValueDoesntImplement/foo/wire.go @@ -0,0 +1,28 @@ +// Copyright 2018 The Go Cloud Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//+build wireinject + +package main + +import ( + "github.com/google/go-cloud/wire" + "io" +) + +func injectedMessage() string { + // wrong: string doesn't implement io.Reader. + wire.Build(wire.InterfaceValue(new(io.Reader), "bar")) + return "" +} diff --git a/internal/wire/testdata/InterfaceValueDoesntImplement/pkg b/internal/wire/testdata/InterfaceValueDoesntImplement/pkg new file mode 100644 index 0000000..f7a5c8c --- /dev/null +++ b/internal/wire/testdata/InterfaceValueDoesntImplement/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/internal/wire/testdata/InterfaceValueDoesntImplement/want/wire_errs.txt b/internal/wire/testdata/InterfaceValueDoesntImplement/want/wire_errs.txt new file mode 100644 index 0000000..d88c9ce --- /dev/null +++ b/internal/wire/testdata/InterfaceValueDoesntImplement/want/wire_errs.txt @@ -0,0 +1 @@ +string does not implement io.Reader diff --git a/internal/wire/testdata/InterfaceValueInvalidArg0/foo/wire.go b/internal/wire/testdata/InterfaceValueInvalidArg0/foo/wire.go index fa64bec..5906a6d 100644 --- a/internal/wire/testdata/InterfaceValueInvalidArg0/foo/wire.go +++ b/internal/wire/testdata/InterfaceValueInvalidArg0/foo/wire.go @@ -21,6 +21,7 @@ import ( ) func injectedMessage() string { + // wrong: arg0 must be a pointer to an interface. wire.Build(wire.InterfaceValue("foo", "bar")) return "" } diff --git a/internal/wire/testdata/InterfaceValueNotEnoughArgs/foo/wire.go b/internal/wire/testdata/InterfaceValueNotEnoughArgs/foo/wire.go index 17576ae..718322f 100644 --- a/internal/wire/testdata/InterfaceValueNotEnoughArgs/foo/wire.go +++ b/internal/wire/testdata/InterfaceValueNotEnoughArgs/foo/wire.go @@ -21,6 +21,7 @@ import ( ) func injectedMessage() string { + // wrong: InterfaceValue requires 2 args. wire.Build(wire.InterfaceValue("foo")) return "" }