Skip to content

Commit

Permalink
wire: wire.FieldsOf should not provide pointer to field type for non-…
Browse files Browse the repository at this point in the history
…pointer structs (#210)
  • Loading branch information
vangent committed Sep 5, 2019
1 parent f85ec5c commit c385f07
Show file tree
Hide file tree
Showing 16 changed files with 100 additions and 23 deletions.
3 changes: 2 additions & 1 deletion docs/guide.md
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions internal/wire/analyze.go
Expand Up @@ -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,
Expand Down
13 changes: 11 additions & 2 deletions internal/wire/parse.go
Expand Up @@ -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
}

Expand Down Expand Up @@ -1006,13 +1007,15 @@ 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)
if !ok {
return nil, notePosition(fset.Position(call.Pos()),
fmt.Errorf(firstArgReqFormat, types.TypeString(struc, nil)))
}
isPtrToStruct = true
case *types.Struct:
struc = t
default:
Expand All @@ -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
Expand Down
1 change: 0 additions & 1 deletion internal/wire/testdata/FieldsOfStruct/foo/foo.go
Expand Up @@ -26,5 +26,4 @@ func provideS() S {

func main() {
fmt.Println(injectedMessage())
fmt.Println("pointer to " + *injectedMessagePtr())
}
7 changes: 0 additions & 7 deletions internal/wire/testdata/FieldsOfStruct/foo/wire.go
Expand Up @@ -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
}
1 change: 0 additions & 1 deletion internal/wire/testdata/FieldsOfStruct/want/program_out.txt
@@ -1,2 +1 @@
Hello, World!
pointer to Hello, World!
6 changes: 0 additions & 6 deletions internal/wire/testdata/FieldsOfStruct/want/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

@@ -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())
}
@@ -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
}
@@ -0,0 +1 @@
example.com/foo
@@ -0,0 +1 @@
example.com/foo/wire.go:x:y: inject injectedMessagePtr: no provider found for *string, output of injector
1 change: 1 addition & 0 deletions internal/wire/testdata/FieldsOfStructPointer/foo/foo.go
Expand Up @@ -26,4 +26,5 @@ func provideS() *S {

func main() {
fmt.Println(injectedMessage())
fmt.Println("pointer to " + *injectedMessagePtr())
}
7 changes: 7 additions & 0 deletions internal/wire/testdata/FieldsOfStructPointer/foo/wire.go
Expand Up @@ -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
}
@@ -1 +1,2 @@
Hello, World!
pointer to Hello, World!
6 changes: 6 additions & 0 deletions internal/wire/testdata/FieldsOfStructPointer/want/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions wire.go
Expand Up @@ -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 { /* ... */ }
Expand All @@ -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{}
}

0 comments on commit c385f07

Please sign in to comment.