Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wire: FieldsOf now provides a pointer to the field type as well as the actual field type #209

Merged
merged 2 commits into from Sep 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 17 additions & 5 deletions docs/guide.md
Expand Up @@ -233,11 +233,11 @@ have a provider in the same set that provides the concrete type.

### Struct Providers

Structs can also be marked as providers. Use the `wire.Struct` function to
inject a struct type and tell the injector which field(s) should be injected.
Structs can be constructed using provided types. Use the `wire.Struct` function
to construct a struct type and tell the injector which field(s) should be injected.
The injector will fill in each field using the provider for the field's type.
For a given struct type `S`, this would provide both `S` and `*S`. For example,
given the following providers:
For the resulting struct type `S`, `wire.Struct` provides both `S` and `*S`. For
example, given the following providers:

```go
type Foo int
Expand Down Expand Up @@ -298,7 +298,18 @@ func injectFooBar() FooBar {
}
```

And similarly if the injector needed a `*FooBar`.
If the injector returned a `*FooBar` instead of a `FooBar`, the generated injector
would look like this:

```go
func injectFooBar() *FooBar {
foo := ProvideFoo()
fooBar := &FooBar{
MyFoo: foo,
}
return fooBar
}
```

It is sometimes useful to prevent certain fields from being filled in by the
injector, especially when passing `*` to `wire.Struct`. You can tag a field with
Expand Down Expand Up @@ -412,6 +423,7 @@ 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`.

### Cleanup functions

Expand Down
32 changes: 21 additions & 11 deletions internal/wire/analyze.go
Expand Up @@ -84,6 +84,10 @@ type call struct {

valueExpr ast.Expr
valueTypeInfo *types.Info

// The following are only set for kind == selectorExpr:

ptrToField bool
}

// solve finds the sequence of calls required to produce an output type
Expand Down Expand Up @@ -226,14 +230,18 @@ dfs:
index.Set(curr.t, errAbort)
continue dfs
}
// Use the args[0] to store the position of the parent struct.
// 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
// provide a pointer to the field.
ptrToField := types.Identical(curr.t, f.Out[1])
calls = append(calls, call{
kind: selectorExpr,
pkg: f.Pkg,
name: f.Name,
out: curr.t,
args: args,
kind: selectorExpr,
pkg: f.Pkg,
name: f.Name,
out: curr.t,
args: args,
ptrToField: ptrToField,
})
default:
panic("unknown return value from ProviderSet.For")
Expand Down Expand Up @@ -382,12 +390,14 @@ func buildProviderMap(fset *token.FileSet, hasher typeutil.Hasher, set *Provider
}
for _, f := range set.Fields {
src := &providerSetSrc{Field: f}
if prevSrc := srcMap.At(f.Out); prevSrc != nil {
ec.add(bindingConflictError(fset, f.Out, set, src, prevSrc.(*providerSetSrc)))
continue
for _, typ := range f.Out {
if prevSrc := srcMap.At(typ); prevSrc != nil {
ec.add(bindingConflictError(fset, typ, set, src, prevSrc.(*providerSetSrc)))
continue
}
providerMap.Set(typ, &ProvidedType{t: typ, f: f})
srcMap.Set(typ, src)
}
providerMap.Set(f.Out, &ProvidedType{t: f.Out, f: f})
srcMap.Set(f.Out, src)
}
if len(ec.errors) > 0 {
return nil, nil, ec.errors
Expand Down
15 changes: 8 additions & 7 deletions internal/wire/parse.go
Expand Up @@ -220,7 +220,7 @@ type InjectorArgs struct {
Pos token.Pos
}

// Field describes a list of fields from a struct.
// Field describes a specific field selected from a struct.
type Field struct {
// Parent is the struct or pointer to the struct that the field belongs to.
Parent types.Type
Expand All @@ -231,8 +231,9 @@ type Field struct {
// Pos is the source position of the field declaration.
// defining these fields.
Pos token.Pos
// Out is the field's type.
Out types.Type
// Out is the field's provided types. The first element provides the
// field type, the second element provides a pointer to it.
Out []types.Type
vangent marked this conversation as resolved.
Show resolved Hide resolved
}

// Load finds all the provider sets in the packages that match the given
Expand Down Expand Up @@ -458,7 +459,7 @@ func newObjectCache(pkgs []*packages.Package) *objectCache {
}

// get converts a Go object into a Wire structure. It may return a *Provider, an
// *IfaceBinding, a *ProviderSet, a *Value, or a *Fields.
// *IfaceBinding, a *ProviderSet, a *Value, or a []*Field.
vangent marked this conversation as resolved.
Show resolved Hide resolved
func (oc *objectCache) get(obj types.Object) (val interface{}, errs []error) {
ref := objRef{
importPath: obj.Pkg().Path(),
Expand Down Expand Up @@ -515,7 +516,7 @@ func (oc *objectCache) varDecl(obj *types.Var) *ast.ValueSpec {
}

// processExpr converts an expression into a Wire structure. It may return a
// *Provider, an *IfaceBinding, a *ProviderSet, a *Value or a *Field.
// *Provider, an *IfaceBinding, a *ProviderSet, a *Value or a []*Field.
func (oc *objectCache) processExpr(info *types.Info, pkgPath string, expr ast.Expr, varName string) (interface{}, []error) {
exprPos := oc.fset.Position(expr.Pos())
expr = astutil.Unparen(expr)
Expand Down Expand Up @@ -988,7 +989,7 @@ func processInterfaceValue(fset *token.FileSet, info *types.Info, call *ast.Call
}, nil
}

// processFieldsOf creates a list of fields from a wire.FieldsOf call.
// processFieldsOf creates a slice of fields from a wire.FieldsOf call.
func processFieldsOf(fset *token.FileSet, info *types.Info, call *ast.CallExpr) ([]*Field, error) {
// Assumes that call.Fun is wire.FieldsOf.

Expand Down Expand Up @@ -1034,7 +1035,7 @@ func processFieldsOf(fset *token.FileSet, info *types.Info, call *ast.CallExpr)
Name: v.Name(),
Pkg: v.Pkg(),
Pos: v.Pos(),
Out: v.Type(),
Out: []types.Type{v.Type(), types.NewPointer(v.Type())},
})
}
return fields, nil
Expand Down
1 change: 1 addition & 0 deletions internal/wire/testdata/FieldsOfStruct/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/FieldsOfStruct/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 change: 1 addition & 0 deletions internal/wire/testdata/FieldsOfStruct/want/program_out.txt
@@ -1 +1,2 @@
Hello, World!
pointer to Hello, World!
6 changes: 6 additions & 0 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.

8 changes: 6 additions & 2 deletions internal/wire/wire.go
Expand Up @@ -731,10 +731,14 @@ func (ig *injectorGen) valueExpr(lname string, c *call) {

func (ig *injectorGen) fieldExpr(lname string, c *call) {
a := c.args[0]
ig.p("\t%s := ", lname)
if c.ptrToField {
ig.p("&")
}
if a < len(ig.paramNames) {
ig.p("\t%s := %s.%s\n", lname, ig.paramNames[a], c.name)
ig.p("%s.%s\n", ig.paramNames[a], c.name)
} else {
ig.p("\t%s := %s.%s\n", lname, ig.localNames[a-len(ig.paramNames)], c.name)
ig.p("%s.%s\n", ig.localNames[a-len(ig.paramNames)], c.name)
}
}

Expand Down
3 changes: 2 additions & 1 deletion wire.go
Expand Up @@ -146,7 +146,8 @@ func InterfaceValue(typ interface{}, x interface{}) ProvidedValue {
// A StructProvider represents a named struct.
type StructProvider struct{}

// Struct specifies that the given struct type will be provided by filling in the fields in the struct that have the names given.
// Struct specifies that the given struct type will be provided by filling in
// the fields in the struct that have the names given.
//
// The first argument must be a pointer to the struct type. For a struct type
// Foo, Wire will use field-filling to provide both Foo and *Foo. The remaining
Expand Down