Bind: takes a pointer for the second argument (#152)
This commit is contained in:
@@ -337,7 +337,7 @@ func Load(ctx context.Context, wd string, env []string, patterns []string) (*Inf
|
|||||||
}
|
}
|
||||||
|
|
||||||
// load typechecks the packages that match the given patterns and
|
// load typechecks the packages that match the given patterns and
|
||||||
// includes source for all transitive dependencies. The patterns are
|
// includes source for all transitive dependencies. The patterns are
|
||||||
// defined by the underlying build system. For the go tool, this is
|
// defined by the underlying build system. For the go tool, this is
|
||||||
// described at https://golang.org/cmd/go/#hdr-Package_lists_and_patterns
|
// described at https://golang.org/cmd/go/#hdr-Package_lists_and_patterns
|
||||||
//
|
//
|
||||||
@@ -860,25 +860,39 @@ func processBind(fset *token.FileSet, info *types.Info, call *ast.CallExpr) (*If
|
|||||||
// Assumes that call.Fun is wire.Bind.
|
// Assumes that call.Fun is wire.Bind.
|
||||||
|
|
||||||
if len(call.Args) != 2 {
|
if len(call.Args) != 2 {
|
||||||
return nil, notePosition(fset.Position(call.Pos()), errors.New("call to Bind takes exactly two arguments"))
|
return nil, notePosition(fset.Position(call.Pos()),
|
||||||
|
errors.New("call to Bind takes exactly two arguments"))
|
||||||
}
|
}
|
||||||
// TODO(light): Verify that arguments are simple expressions.
|
// TODO(light): Verify that arguments are simple expressions.
|
||||||
ifaceArgType := info.TypeOf(call.Args[0])
|
ifaceArgType := info.TypeOf(call.Args[0])
|
||||||
ifacePtr, ok := ifaceArgType.(*types.Pointer)
|
ifacePtr, ok := ifaceArgType.(*types.Pointer)
|
||||||
if !ok {
|
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)))
|
||||||
}
|
}
|
||||||
iface := ifacePtr.Elem()
|
iface := ifacePtr.Elem()
|
||||||
methodSet, ok := iface.Underlying().(*types.Interface)
|
methodSet, ok := iface.Underlying().(*types.Interface)
|
||||||
if !ok {
|
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])
|
provided := info.TypeOf(call.Args[1])
|
||||||
|
if bindShouldUsePointer(info, call) {
|
||||||
|
providedPtr, ok := provided.(*types.Pointer)
|
||||||
|
if !ok {
|
||||||
|
return nil, notePosition(fset.Position(call.Args[0].Pos()),
|
||||||
|
fmt.Errorf("second argument to Bind must be a pointer or a pointer to a pointer; found %s", types.TypeString(provided, nil)))
|
||||||
|
}
|
||||||
|
provided = providedPtr.Elem()
|
||||||
|
}
|
||||||
if types.Identical(iface, provided) {
|
if types.Identical(iface, provided) {
|
||||||
return nil, notePosition(fset.Position(call.Pos()), errors.New("cannot bind interface to itself"))
|
return nil, notePosition(fset.Position(call.Pos()),
|
||||||
|
errors.New("cannot bind interface to itself"))
|
||||||
}
|
}
|
||||||
if !types.Implements(provided, methodSet) {
|
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(iface, 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{
|
return &IfaceBinding{
|
||||||
Pos: call.Pos(),
|
Pos: call.Pos(),
|
||||||
@@ -1185,3 +1199,13 @@ func (pt ProvidedType) Field() *Field {
|
|||||||
}
|
}
|
||||||
return pt.f
|
return pt.f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bindShouldUsePointer loads the wire package the user is importing from their
|
||||||
|
// injector. The call is a wire marker function call.
|
||||||
|
func bindShouldUsePointer(info *types.Info, call *ast.CallExpr) bool {
|
||||||
|
// These type assertions should not fail, otherwise panic.
|
||||||
|
fun := call.Fun.(*ast.SelectorExpr) // wire.Bind
|
||||||
|
pkgName := fun.X.(*ast.Ident) // wire
|
||||||
|
wireName := info.ObjectOf(pkgName).(*types.PkgName) // wire package
|
||||||
|
return wireName.Imported().Scope().Lookup("bindToUsePointer") != nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Println(inject(&Foo{"hello"}).Name)
|
fmt.Println(inject(Foo{"hello"}).Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Fooer interface {
|
type Fooer interface {
|
||||||
@@ -30,7 +30,7 @@ type Foo struct {
|
|||||||
f string
|
f string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Foo) Foo() string {
|
func (f Foo) Foo() string {
|
||||||
return f.f
|
return f.f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,11 +20,10 @@ import (
|
|||||||
"github.com/google/wire"
|
"github.com/google/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
func inject(foo *Foo) *Bar {
|
func inject(foo Foo) *Bar {
|
||||||
// Currently fails because wire.Bind can't see injector args (#547).
|
|
||||||
wire.Build(
|
wire.Build(
|
||||||
NewBar,
|
NewBar,
|
||||||
wire.Bind(new(Fooer), &Foo{}),
|
wire.Bind(new(Fooer), new(Foo)),
|
||||||
)
|
)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package main
|
|||||||
|
|
||||||
// Injectors from wire.go:
|
// Injectors from wire.go:
|
||||||
|
|
||||||
func inject(foo *Foo) *Bar {
|
func inject(foo Foo) *Bar {
|
||||||
bar := NewBar(foo)
|
bar := NewBar(foo)
|
||||||
return bar
|
return bar
|
||||||
}
|
}
|
||||||
|
|||||||
43
internal/wire/testdata/BindInjectorArgPointer/foo/foo.go
vendored
Normal file
43
internal/wire/testdata/BindInjectorArgPointer/foo/foo.go
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2019 The Wire 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(inject(&Foo{"hello"}).Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fooer interface {
|
||||||
|
Foo() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Foo struct {
|
||||||
|
f string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Foo) Foo() string {
|
||||||
|
return f.f
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bar struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBar(fooer Fooer) *Bar {
|
||||||
|
return &Bar{Name: fooer.Foo()}
|
||||||
|
}
|
||||||
29
internal/wire/testdata/BindInjectorArgPointer/foo/wire.go
vendored
Normal file
29
internal/wire/testdata/BindInjectorArgPointer/foo/wire.go
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2019 The Wire 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/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func inject(foo *Foo) *Bar {
|
||||||
|
wire.Build(
|
||||||
|
NewBar,
|
||||||
|
wire.Bind(new(Fooer), new(*Foo)),
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
1
internal/wire/testdata/BindInjectorArgPointer/pkg
vendored
Normal file
1
internal/wire/testdata/BindInjectorArgPointer/pkg
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
example.com/foo
|
||||||
1
internal/wire/testdata/BindInjectorArgPointer/want/program_out.txt
vendored
Normal file
1
internal/wire/testdata/BindInjectorArgPointer/want/program_out.txt
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
hello
|
||||||
13
internal/wire/testdata/BindInjectorArgPointer/want/wire_gen.go
vendored
Normal file
13
internal/wire/testdata/BindInjectorArgPointer/want/wire_gen.go
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate wire
|
||||||
|
//+build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Injectors from wire.go:
|
||||||
|
|
||||||
|
func inject(foo *Foo) *Bar {
|
||||||
|
bar := NewBar(foo)
|
||||||
|
return bar
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ import (
|
|||||||
func inject() io.Writer {
|
func inject() io.Writer {
|
||||||
wire.Build(
|
wire.Build(
|
||||||
wire.Value(os.Stdout),
|
wire.Value(os.Stdout),
|
||||||
wire.Bind(new(io.Writer), new(os.File)),
|
wire.Bind(new(io.Writer), new(*os.File)),
|
||||||
)
|
)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ var mockAppSet = wire.NewSet(
|
|||||||
// For each mocked dependency, add a provider and use wire.Bind to bind
|
// For each mocked dependency, add a provider and use wire.Bind to bind
|
||||||
// the concrete type to the relevant interface.
|
// the concrete type to the relevant interface.
|
||||||
newMockTimer,
|
newMockTimer,
|
||||||
wire.Bind(new(timer), new(mockTimer)),
|
wire.Bind(new(timer), new(*mockTimer)),
|
||||||
)
|
)
|
||||||
|
|
||||||
type timer interface {
|
type timer interface {
|
||||||
|
|||||||
@@ -39,4 +39,4 @@ func provideBar() *Bar {
|
|||||||
|
|
||||||
var Set = wire.NewSet(
|
var Set = wire.NewSet(
|
||||||
provideBar,
|
provideBar,
|
||||||
wire.Bind((*foo.Fooer)(nil), (*Bar)(nil)))
|
wire.Bind(new(foo.Fooer), new(*Bar)))
|
||||||
|
|||||||
@@ -42,4 +42,4 @@ func provideBar() *Bar {
|
|||||||
|
|
||||||
var Set = wire.NewSet(
|
var Set = wire.NewSet(
|
||||||
provideBar,
|
provideBar,
|
||||||
wire.Bind((*Fooer)(nil), (*Bar)(nil)))
|
wire.Bind(new(Fooer), new(*Bar)))
|
||||||
|
|||||||
@@ -22,6 +22,6 @@ import (
|
|||||||
|
|
||||||
func injectFooer() Fooer {
|
func injectFooer() Fooer {
|
||||||
// wrong: string doesn't implement Fooer.
|
// wrong: string doesn't implement Fooer.
|
||||||
wire.Build(wire.Bind((*Fooer)(nil), "foo"))
|
wire.Build(wire.Bind(new(Fooer), new(string)))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,6 @@ import (
|
|||||||
|
|
||||||
func injectFooer() Fooer {
|
func injectFooer() Fooer {
|
||||||
// wrong: wire.Bind requires 2 args.
|
// wrong: wire.Bind requires 2 args.
|
||||||
wire.Build(wire.Bind((*Fooer)(nil)))
|
wire.Build(wire.Bind(new(Fooer)))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func injectFooBar() FooBar {
|
|||||||
wire.Build(
|
wire.Build(
|
||||||
provideBar,
|
provideBar,
|
||||||
provideFooBar,
|
provideFooBar,
|
||||||
wire.Bind((*Fooer)(nil), (*Bar)(nil)),
|
wire.Bind(new(Fooer), new(*Bar)),
|
||||||
)
|
)
|
||||||
return FooBar{}
|
return FooBar{}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,5 +49,5 @@ func injectDuplicateValues() Foo {
|
|||||||
|
|
||||||
func injectDuplicateInterface() Bar {
|
func injectDuplicateInterface() Bar {
|
||||||
// fail: provideBar and wire.Bind both provide Bar.
|
// fail: provideBar and wire.Bind both provide Bar.
|
||||||
panic(wire.Build(provideBar, wire.Bind(new(Bar), strings.NewReader("hello"))))
|
panic(wire.Build(provideBar, wire.Bind(new(Bar), new(*strings.Reader))))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,6 @@ var (
|
|||||||
// From the user guide:
|
// From the user guide:
|
||||||
// Any set that includes an interface binding must also have a provider in
|
// Any set that includes an interface binding must also have a provider in
|
||||||
// the same set that provides the concrete type.
|
// the same set that provides the concrete type.
|
||||||
setB = wire.NewSet(wire.Bind(new(fooer), new(foo)))
|
setB = wire.NewSet(wire.Bind(new(fooer), new(*foo)))
|
||||||
setC = wire.NewSet(setA, setB)
|
setC = wire.NewSet(setA, setB)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -22,14 +22,14 @@ import (
|
|||||||
|
|
||||||
func injectBar() Bar {
|
func injectBar() Bar {
|
||||||
wire.Build(
|
wire.Build(
|
||||||
provideFoo, // needed as input for provideBar
|
provideFoo, // needed as input for provideBar
|
||||||
provideBar, // needed for Bar
|
provideBar, // needed for Bar
|
||||||
partiallyUsedSet, // 1/2 providers in the set are needed
|
partiallyUsedSet, // 1/2 providers in the set are needed
|
||||||
provideUnused, // not needed -> error
|
provideUnused, // not needed -> error
|
||||||
wire.Value("unused"), // not needed -> error
|
wire.Value("unused"), // not needed -> error
|
||||||
unusedSet, // nothing in set is needed -> error
|
unusedSet, // nothing in set is needed -> error
|
||||||
wire.Bind((*Fooer)(nil), (*Foo)(nil)), // binding to Fooer is not needed -> error
|
wire.Bind(new(Fooer), new(*Foo)), // binding to Fooer is not needed -> error
|
||||||
wire.FieldsOf(new(S), "Cfg"), // S.Cfg not needed -> error
|
wire.FieldsOf(new(S), "Cfg"), // S.Cfg not needed -> error
|
||||||
)
|
)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
12
wire.go
12
wire.go
@@ -93,9 +93,9 @@ func Build(...interface{}) string {
|
|||||||
// A Binding maps an interface to a concrete type.
|
// A Binding maps an interface to a concrete type.
|
||||||
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
|
||||||
// dependency on the type of iface, which must be a pointer to an
|
// the type of iface. iface must be a pointer to an interface type, to must be a
|
||||||
// interface type.
|
// pointer to a concrete type.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
@@ -108,12 +108,16 @@ type Binding struct{}
|
|||||||
// func (MyFoo) Foo() {}
|
// func (MyFoo) Foo() {}
|
||||||
//
|
//
|
||||||
// var MySet = wire.NewSet(
|
// var MySet = wire.NewSet(
|
||||||
// MyFoo{},
|
// wire.Struct(new(MyFoo))
|
||||||
// wire.Bind(new(Fooer), new(MyFoo)))
|
// wire.Bind(new(Fooer), new(MyFoo)))
|
||||||
func Bind(iface, to interface{}) Binding {
|
func Bind(iface, to interface{}) Binding {
|
||||||
return Binding{}
|
return Binding{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bindToUsePointer is detected by the wire tool to indicate that Bind's second argument should take a pointer.
|
||||||
|
// See https://github.com/google/wire/issues/120 for details.
|
||||||
|
const bindToUsePointer = true
|
||||||
|
|
||||||
// A ProvidedValue is an expression that is copied to the generated injector.
|
// A ProvidedValue is an expression that is copied to the generated injector.
|
||||||
type ProvidedValue struct{}
|
type ProvidedValue struct{}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user