diff --git a/docs/guide.md b/docs/guide.md index bbd69e23..36264603 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -423,7 +423,8 @@ func injectedMessage() string { ``` You can add as many field names to a `wire.FieldsOf` function as you like. -For a given field type `T`, `FieldsOf` provides both `T` and `*T`. +For a given field type `T`, `FieldsOf` provides at least `T`; if the struct +argument is a pointer to a struct, then `FieldsOf` also provides `*T`. ### Cleanup functions diff --git a/internal/wire/analyze.go b/internal/wire/analyze.go index d5d15ff8..9650ef16 100644 --- a/internal/wire/analyze.go +++ b/internal/wire/analyze.go @@ -232,9 +232,9 @@ dfs: } // Use args[0] to store the position of the parent struct. args := []int{v.(int)} - // len(f.Out) is always 2; if curr.t is the 2nd one, then the call must + // If f.Out has 2 elements and curr.t is the 2nd one, then the call must // provide a pointer to the field. - ptrToField := types.Identical(curr.t, f.Out[1]) + ptrToField := len(f.Out) == 2 && types.Identical(curr.t, f.Out[1]) calls = append(calls, call{ kind: selectorExpr, pkg: f.Pkg, diff --git a/internal/wire/parse.go b/internal/wire/parse.go index be6f50e8..0af96294 100644 --- a/internal/wire/parse.go +++ b/internal/wire/parse.go @@ -232,7 +232,8 @@ type Field struct { // defining these fields. Pos token.Pos // Out is the field's provided types. The first element provides the - // field type, the second element provides a pointer to it. + // field type. If the field is coming from a pointer to a struct, + // there will be a second element providing a pointer to the field. Out []types.Type } @@ -1006,6 +1007,7 @@ func processFieldsOf(fset *token.FileSet, info *types.Info, call *ast.CallExpr) } var struc *types.Struct + isPtrToStruct := false switch t := structPtr.Elem().Underlying().(type) { case *types.Pointer: struc, ok = t.Elem().Underlying().(*types.Struct) @@ -1013,6 +1015,7 @@ func processFieldsOf(fset *token.FileSet, info *types.Info, call *ast.CallExpr) return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf(firstArgReqFormat, types.TypeString(struc, nil))) } + isPtrToStruct = true case *types.Struct: struc = t default: @@ -1030,12 +1033,18 @@ func processFieldsOf(fset *token.FileSet, info *types.Info, call *ast.CallExpr) if err != nil { return nil, notePosition(fset.Position(call.Pos()), err) } + out := []types.Type{v.Type()} + if isPtrToStruct { + // If the field is from a pointer to a struct, then + // wire.Fields also provides a pointer to the field. + out = append(out, types.NewPointer(v.Type())) + } fields = append(fields, &Field{ Parent: structPtr.Elem(), Name: v.Name(), Pkg: v.Pkg(), Pos: v.Pos(), - Out: []types.Type{v.Type(), types.NewPointer(v.Type())}, + Out: out, }) } return fields, nil diff --git a/internal/wire/testdata/FieldsOfStruct/foo/foo.go b/internal/wire/testdata/FieldsOfStruct/foo/foo.go index 9229923f..09e0eb06 100644 --- a/internal/wire/testdata/FieldsOfStruct/foo/foo.go +++ b/internal/wire/testdata/FieldsOfStruct/foo/foo.go @@ -26,5 +26,4 @@ func provideS() S { func main() { fmt.Println(injectedMessage()) - fmt.Println("pointer to " + *injectedMessagePtr()) } diff --git a/internal/wire/testdata/FieldsOfStruct/foo/wire.go b/internal/wire/testdata/FieldsOfStruct/foo/wire.go index 7debbfd2..94aa3165 100644 --- a/internal/wire/testdata/FieldsOfStruct/foo/wire.go +++ b/internal/wire/testdata/FieldsOfStruct/foo/wire.go @@ -26,10 +26,3 @@ func injectedMessage() string { wire.FieldsOf(new(S), "Foo")) return "" } - -func injectedMessagePtr() *string { - wire.Build( - provideS, - wire.FieldsOf(new(S), "Foo")) - return nil -} diff --git a/internal/wire/testdata/FieldsOfStruct/want/program_out.txt b/internal/wire/testdata/FieldsOfStruct/want/program_out.txt index 82baaaf7..8ab686ea 100644 --- a/internal/wire/testdata/FieldsOfStruct/want/program_out.txt +++ b/internal/wire/testdata/FieldsOfStruct/want/program_out.txt @@ -1,2 +1 @@ Hello, World! -pointer to Hello, World! diff --git a/internal/wire/testdata/FieldsOfStruct/want/wire_gen.go b/internal/wire/testdata/FieldsOfStruct/want/wire_gen.go index b01acbeb..2092a3db 100644 --- a/internal/wire/testdata/FieldsOfStruct/want/wire_gen.go +++ b/internal/wire/testdata/FieldsOfStruct/want/wire_gen.go @@ -12,9 +12,3 @@ func injectedMessage() string { string2 := s.Foo return string2 } - -func injectedMessagePtr() *string { - s := provideS() - string2 := &s.Foo - return string2 -} diff --git a/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/foo/foo.go b/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/foo/foo.go new file mode 100644 index 00000000..fee0f71a --- /dev/null +++ b/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/foo/foo.go @@ -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. + +package main + +import "fmt" + +type S struct { + Foo string +} + +func provideS() S { + return S{Foo: "Hello, World!"} +} + +func main() { + fmt.Println("pointer to " + *injectedMessagePtr()) +} diff --git a/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/foo/wire.go b/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/foo/wire.go new file mode 100644 index 00000000..5f25df71 --- /dev/null +++ b/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/foo/wire.go @@ -0,0 +1,32 @@ +// 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 injectedMessagePtr() *string { + // This shouldn't work; FieldsOf provides a pointer to the + // field only when the struct type is a pointer to a struct. + // See FieldsOfStructPointer for a working example using + // a pointer to a struct. + wire.Build( + provideS, + wire.FieldsOf(new(S), "Foo")) + return nil +} diff --git a/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/pkg b/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/pkg new file mode 100644 index 00000000..f7a5c8ce --- /dev/null +++ b/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/want/wire_errs.txt b/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/want/wire_errs.txt new file mode 100644 index 00000000..deee37e9 --- /dev/null +++ b/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/wire.go:x:y: inject injectedMessagePtr: no provider found for *string, output of injector \ No newline at end of file diff --git a/internal/wire/testdata/FieldsOfStructPointer/foo/foo.go b/internal/wire/testdata/FieldsOfStructPointer/foo/foo.go index a5621577..213d871f 100644 --- a/internal/wire/testdata/FieldsOfStructPointer/foo/foo.go +++ b/internal/wire/testdata/FieldsOfStructPointer/foo/foo.go @@ -26,4 +26,5 @@ func provideS() *S { func main() { fmt.Println(injectedMessage()) + fmt.Println("pointer to " + *injectedMessagePtr()) } diff --git a/internal/wire/testdata/FieldsOfStructPointer/foo/wire.go b/internal/wire/testdata/FieldsOfStructPointer/foo/wire.go index ac9a81a9..5d16378c 100644 --- a/internal/wire/testdata/FieldsOfStructPointer/foo/wire.go +++ b/internal/wire/testdata/FieldsOfStructPointer/foo/wire.go @@ -26,3 +26,10 @@ func injectedMessage() string { wire.FieldsOf(new(*S), "Foo")) return "" } + +func injectedMessagePtr() *string { + wire.Build( + provideS, + wire.FieldsOf(new(*S), "Foo")) + return nil +} diff --git a/internal/wire/testdata/FieldsOfStructPointer/want/program_out.txt b/internal/wire/testdata/FieldsOfStructPointer/want/program_out.txt index 8ab686ea..82baaaf7 100644 --- a/internal/wire/testdata/FieldsOfStructPointer/want/program_out.txt +++ b/internal/wire/testdata/FieldsOfStructPointer/want/program_out.txt @@ -1 +1,2 @@ Hello, World! +pointer to Hello, World! diff --git a/internal/wire/testdata/FieldsOfStructPointer/want/wire_gen.go b/internal/wire/testdata/FieldsOfStructPointer/want/wire_gen.go index 2092a3db..b01acbeb 100644 --- a/internal/wire/testdata/FieldsOfStructPointer/want/wire_gen.go +++ b/internal/wire/testdata/FieldsOfStructPointer/want/wire_gen.go @@ -12,3 +12,9 @@ func injectedMessage() string { string2 := s.Foo return string2 } + +func injectedMessagePtr() *string { + s := provideS() + string2 := &s.Foo + return string2 +} diff --git a/wire.go b/wire.go index 1ef77908..fe8edc8c 100644 --- a/wire.go +++ b/wire.go @@ -173,11 +173,11 @@ type StructFields struct{} // to provide the types of those fields. The structType argument must be a // pointer to the struct or a pointer to a pointer to the struct it wishes to reference. // -// The following example would provide *Foo and *Bar using S.MyFoo and S.MyBar respectively: +// The following example would provide Foo and Bar using S.MyFoo and S.MyBar respectively: // // type S struct { -// MyFoo *Foo -// MyBar *Bar +// MyFoo Foo +// MyBar Bar // } // // func NewStruct() S { /* ... */ } @@ -187,6 +187,10 @@ type StructFields struct{} // // func NewStruct() *S { /* ... */ } // var Set = wire.NewSet(wire.FieldsOf(new(*S), "MyFoo", "MyBar")) +// +// If the structType argument is a pointer to a pointer to a struct, then FieldsOf +// additionally provides a pointer to each field type (e.g., *Foo and *Bar in the +// example above). func FieldsOf(structType interface{}, fieldNames ...string) StructFields { return StructFields{} }