Repository: google/wire (go)

Issue:
Allow `FieldsOf` to provide a pointer type
### Is your feature request related to a problem? Please describe.

I am trying to wire a non-pointer field from a struct to a provider which requires a pointer of the same type

Consider the following
```go
type Config struct {
    UserConfig UserConfig
}

func initialize(ctx context.Context, cfg *Config) (*st, func(), error) {
    panic( // panic to be replaced by generated code
        wire.Build(
            InitializeUser,
            wire.FieldsOf(new(Config), "UserConfig"),
        ),
    )
}

func InitializeUser(config *UserConfig) *User {
    // initialize user from user config
}
```

I am unable to wire the `UserConfig` field in my `Config` for the `InitializeUser` provider without writing a wrapper provider by hand.

### Describe the solution you'd like

`FieldsOf` should provide both a non-pointer and a pointer type of that field. This seems to already be done today by the `Struct Providers` as per the documentation. It would be useful (and consistent) if `FieldsOf` would do the same

### Describe alternatives you've considered

The alternative it to write your own simple wrapper provider such as

```go
func InitializeUserConfig(cfg *Config) *UserConfig {
   return &cfg.UserConfig
}
```


Pull request description:
wire: wire.FieldsOf should not provide pointer to field type for non-pointer structs
@zombiezen pointed out (hah!) that `FieldsOf` should not provide a pointer to field types in cases where the struct itself is not a pointer; the struct is then a temporary variable.

I moved the previously-working "pointer to field type" test from `FieldsOfStruct` to `FieldsOfStructPointer`, and added a new non-working testcase attempting to provide a ptr-to-field from a non-pointer struct (expected to fail).

Fixes #208.

Patch:
diff --git a/docs/guide.md b/docs/guide.md
index bbd69e2..3626460 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 d5d15ff..9650ef1 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 be6f50e..0af9629 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/wire.go b/wire.go
index 1ef7790..fe8edc8 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{}
 }


Test patch:
diff --git a/internal/wire/testdata/FieldsOfStruct/foo/foo.go b/internal/wire/testdata/FieldsOfStruct/foo/foo.go
index 9229923..09e0eb0 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 7debbfd..94aa316 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 82baaaf..8ab686e 100644
--- a/internal/wire/testdata/FieldsOfStruct/want/program_out.txt
+++ b/internal/wire/testdata/FieldsOfStruct/want/program_out.txt
@@ -1,2 +1,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 b01acbe..2092a3d 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 0000000..fee0f71
--- /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 0000000..5f25df7
--- /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 0000000..f7a5c8c
--- /dev/null
+++ b/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/pkg
@@ -0,0 +1,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 0000000..deee37e
--- /dev/null
+++ b/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/want/wire_errs.txt
@@ -0,0 +1,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 a562157..213d871 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 ac9a81a..5d16378 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 8ab686e..82baaaf 100644
--- a/internal/wire/testdata/FieldsOfStructPointer/want/program_out.txt
+++ b/internal/wire/testdata/FieldsOfStructPointer/want/program_out.txt
@@ -1,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 2092a3d..b01acbe 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
+}