add FieldsOf to inject fields of a struct directly (#138)

This commit is contained in:
shantuo
2019-03-01 13:52:07 -08:00
committed by GitHub
parent 58e5de342a
commit 327f42724c
37 changed files with 869 additions and 56 deletions

View File

@@ -32,6 +32,7 @@ const (
funcProviderCall callKind = iota
structProvider
valueExpr
selectorExpr
)
// A call represents a step of an injector function. It may be either a
@@ -44,17 +45,21 @@ type call struct {
// out is the type this step produces.
out types.Type
// pkg and name identify the provider to call for kind ==
// funcProviderCall or the type to construct for kind ==
// structProvider.
// pkg and name identify one of the following:
// 1) the provider to call for kind == funcProviderCall;
// 2) the type to construct for kind == structProvider;
// 3) the name to select for kind == selectorExpr.
pkg *types.Package
name string
// args is a list of arguments to call the provider with. Each element is:
// a) one of the givens (args[i] < len(given)), or
// args is a list of arguments to call the provider with. Each element is:
// a) one of the givens (args[i] < len(given)),
// b) the result of a previous provider call (args[i] >= len(given))
//
// This will be nil for kind == valueExpr.
//
// If kind == selectorExpr, then the length of this slice will be 1 and the
// "argument" will be the value to access fields from.
args []int
// varargs is true if the provider function is variadic.
@@ -207,6 +212,29 @@ dfs:
valueExpr: v.expr,
valueTypeInfo: v.info,
})
case pv.IsField():
f := pv.Field()
if index.At(f.Parent) == nil {
// Fields have one dependency which is the parent struct. Make
// sure to visit it first if it is not already visited.
stk = append(stk, curr, frame{t: f.Parent, from: curr.t, up: &curr})
continue
}
index.Set(curr.t, given.Len()+len(calls))
v := index.At(f.Parent)
if v == errAbort {
index.Set(curr.t, errAbort)
continue dfs
}
// Use the args[0] to store the position of the parent struct.
args := []int{v.(int)}
calls = append(calls, call{
kind: selectorExpr,
pkg: f.Pkg,
name: f.Name,
out: curr.t,
args: args,
})
default:
panic("unknown return value from ProviderSet.For")
}
@@ -275,11 +303,24 @@ func verifyArgsUsed(set *ProviderSet, used []*providerSetSrc) []error {
errs = append(errs, fmt.Errorf("unused interface binding to type %s", types.TypeString(b.Iface, nil)))
}
}
for _, f := range set.Fields {
found := false
for _, u := range used {
if u.Field == f {
found = true
break
}
}
if !found {
errs = append(errs, fmt.Errorf("unused field %q.%s", f.Parent, f.Name))
}
}
return errs
}
// buildProviderMap creates the providerMap and srcMap fields for a given provider set.
// The given provider set's providerMap and srcMap fields are ignored.
// buildProviderMap creates the providerMap and srcMap fields for a given
// provider set. The given provider set's providerMap and srcMap fields are
// ignored.
func buildProviderMap(fset *token.FileSet, hasher typeutil.Hasher, set *ProviderSet) (*typeutil.Map, *typeutil.Map, []error) {
providerMap := new(typeutil.Map)
providerMap.SetHasher(hasher)
@@ -339,6 +380,15 @@ func buildProviderMap(fset *token.FileSet, hasher typeutil.Hasher, set *Provider
providerMap.Set(v.Out, &ProvidedType{t: v.Out, v: v})
srcMap.Set(v.Out, src)
}
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
}
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
}
@@ -398,38 +448,49 @@ func verifyAcyclic(providerMap *typeutil.Map, hasher typeutil.Hasher) []error {
continue
}
pt := x.(*ProvidedType)
if pt.IsValue() {
switch {
case pt.IsValue():
// Leaf: values do not have dependencies.
continue
}
if pt.IsArg() {
case pt.IsArg():
// Injector arguments do not have dependencies.
continue
}
if !pt.IsProvider() {
panic("invalid provider map value")
}
for _, arg := range pt.Provider().Args {
a := arg.Type
hasCycle := false
for i, b := range curr {
if types.Identical(a, b) {
sb := new(strings.Builder)
fmt.Fprintf(sb, "cycle for %s:\n", types.TypeString(a, nil))
for j := i; j < len(curr); j++ {
p := providerMap.At(curr[j]).(*ProvidedType).Provider()
fmt.Fprintf(sb, "%s (%s.%s) ->\n", types.TypeString(curr[j], nil), p.Pkg.Path(), p.Name)
case pt.IsProvider() || pt.IsField():
var args []types.Type
if pt.IsProvider() {
for _, arg := range pt.Provider().Args {
args = append(args, arg.Type)
}
} else {
args = append(args, pt.Field().Parent)
}
for _, a := range args {
hasCycle := false
for i, b := range curr {
if types.Identical(a, b) {
sb := new(strings.Builder)
fmt.Fprintf(sb, "cycle for %s:\n", types.TypeString(a, nil))
for j := i; j < len(curr); j++ {
t := providerMap.At(curr[j]).(*ProvidedType)
if t.IsProvider() {
p := t.Provider()
fmt.Fprintf(sb, "%s (%s.%s) ->\n", types.TypeString(curr[j], nil), p.Pkg.Path(), p.Name)
} else {
p := t.Field()
fmt.Fprintf(sb, "%s (%s.%s) ->\n", types.TypeString(curr[j], nil), p.Parent, p.Name)
}
}
fmt.Fprintf(sb, "%s", types.TypeString(a, nil))
ec.add(errors.New(sb.String()))
hasCycle = true
break
}
fmt.Fprintf(sb, "%s", types.TypeString(a, nil))
ec.add(errors.New(sb.String()))
hasCycle = true
break
}
if !hasCycle {
next := append(append([]types.Type(nil), curr...), a)
stk = append(stk, next)
}
}
if !hasCycle {
next := append(append([]types.Type(nil), curr...), a)
stk = append(stk, next)
}
default:
panic("invalid provider map value")
}
}
}

View File

@@ -37,6 +37,7 @@ type providerSetSrc struct {
Value *Value
Import *ProviderSet
InjectorArg *InjectorArg
Field *Field
}
// description returns a string describing the source of p, including line numbers.
@@ -63,6 +64,8 @@ func (p *providerSetSrc) description(fset *token.FileSet, typ types.Type) string
case p.InjectorArg != nil:
args := p.InjectorArg.Args
return fmt.Sprintf("argument %s to injector function %s (%s)", args.Tuple.At(p.InjectorArg.Index).Name(), args.Name, fset.Position(args.Pos))
case p.Field != nil:
return fmt.Sprintf("wire.FieldsOf (%s)", fset.Position(p.Field.Pos))
}
panic("providerSetSrc with no fields set")
}
@@ -96,6 +99,7 @@ type ProviderSet struct {
Providers []*Provider
Bindings []*IfaceBinding
Values []*Value
Fields []*Field
Imports []*ProviderSet
// InjectorArgs is only filled in for wire.Build.
InjectorArgs *InjectorArgs
@@ -214,6 +218,21 @@ type InjectorArgs struct {
Pos token.Pos
}
// Field describes a list of fields from a struct.
type Field struct {
// Parent is the struct or pointer to the struct that the field belongs to.
Parent types.Type
// Name is the field name.
Name string
// Pkg is the package that the struct resides in.
Pkg *types.Package
// Pos is the source position of the field declaration.
// defining these fields.
Pos token.Pos
// Out is the field's type.
Out types.Type
}
// Load finds all the provider sets in the packages that match the given
// patterns, as well as the provider sets' transitive dependencies. It
// may return both errors and Info. The patterns are defined by the
@@ -436,8 +455,8 @@ func newObjectCache(pkgs []*packages.Package) *objectCache {
return oc
}
// get converts a Go object into a Wire structure. It may return a
// *Provider, an *IfaceBinding, a *ProviderSet, or a *Value.
// get converts a Go object into a Wire structure. It may return a *Provider, an
// *IfaceBinding, a *ProviderSet, a *Value, or a *Fields.
func (oc *objectCache) get(obj types.Object) (val interface{}, errs []error) {
ref := objRef{
importPath: obj.Pkg().Path(),
@@ -493,8 +512,8 @@ func (oc *objectCache) varDecl(obj *types.Var) *ast.ValueSpec {
return nil
}
// processExpr converts an expression into a Wire structure. It may
// return a *Provider, an *IfaceBinding, a *ProviderSet, or a *Value.
// processExpr converts an expression into a Wire structure. It may return a
// *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)
@@ -531,6 +550,12 @@ func (oc *objectCache) processExpr(info *types.Info, pkgPath string, expr ast.Ex
return nil, []error{notePosition(exprPos, err)}
}
return v, nil
case "FieldsOf":
v, err := processFieldsOf(oc.fset, info, call)
if err != nil {
return nil, []error{notePosition(exprPos, err)}
}
return v, nil
default:
return nil, []error{notePosition(exprPos, errors.New("unknown pattern"))}
}
@@ -570,6 +595,8 @@ func (oc *objectCache) processNewSet(info *types.Info, pkgPath string, call *ast
pset.Bindings = append(pset.Bindings, item)
case *Value:
pset.Values = append(pset.Values, item)
case []*Field:
pset.Fields = append(pset.Fields, item...)
default:
panic("unknown item type")
}
@@ -845,6 +872,73 @@ func processInterfaceValue(fset *token.FileSet, info *types.Info, call *ast.Call
}, nil
}
// processFieldsOf creates a list 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.
if len(call.Args) < 2 {
return nil, notePosition(fset.Position(call.Pos()),
errors.New("call to FieldsOf must specify fields to be extracted"))
}
const firstArgReqFormat = "first argument to FieldsOf must be a pointer to a struct or a pointer to a pointer to a struct; found %s"
structType := info.TypeOf(call.Args[0])
structPtr, ok := structType.(*types.Pointer)
if !ok {
return nil, notePosition(fset.Position(call.Pos()),
fmt.Errorf(firstArgReqFormat, types.TypeString(structType, nil)))
}
var struc *types.Struct
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)))
}
case *types.Struct:
struc = t
default:
return nil, notePosition(fset.Position(call.Pos()),
fmt.Errorf(firstArgReqFormat, types.TypeString(t, nil)))
}
if struc.NumFields() < len(call.Args)-1 {
return nil, notePosition(fset.Position(call.Pos()),
fmt.Errorf("fields number exceeds the number available in the struct which has %d fields", struc.NumFields()))
}
fields := make([]*Field, 0, len(call.Args)-1)
for i := 1; i < len(call.Args); i++ {
v, err := checkField(call.Args[i], struc)
if err != nil {
return nil, notePosition(fset.Position(call.Pos()), err)
}
fields = append(fields, &Field{
Parent: structPtr.Elem(),
Name: v.Name(),
Pkg: v.Pkg(),
Pos: v.Pos(),
Out: v.Type(),
})
}
return fields, nil
}
// checkField reports whether f is a field of st. f should be a string with the
// field name.
func checkField(f ast.Expr, st *types.Struct) (*types.Var, error) {
b, ok := f.(*ast.BasicLit)
if !ok {
return nil, fmt.Errorf("%v must be a string with the field name", f)
}
for i := 0; i < st.NumFields(); i++ {
if strings.EqualFold(strconv.Quote(st.Field(i).Name()), b.Value) {
return st.Field(i), nil
}
}
return nil, fmt.Errorf("%s is not a field of %s", b.Value, st.String())
}
// findInjectorBuild returns the wire.Build call if fn is an injector template.
// It returns nil if the function is not an injector template.
func findInjectorBuild(info *types.Info, fn *ast.FuncDecl) (*ast.CallExpr, error) {
@@ -928,11 +1022,12 @@ type ProvidedType struct {
p *Provider
v *Value
a *InjectorArg
f *Field
}
// IsNil reports whether pt is the zero value.
func (pt ProvidedType) IsNil() bool {
return pt.p == nil && pt.v == nil && pt.a == nil
return pt.p == nil && pt.v == nil && pt.a == nil && pt.f == nil
}
// Type returns the output type.
@@ -961,6 +1056,11 @@ func (pt ProvidedType) IsArg() bool {
return pt.a != nil
}
// IsField reports whether pt points to a Fields.
func (pt ProvidedType) IsField() bool {
return pt.f != nil
}
// Provider returns pt as a Provider pointer. It panics if pt does not point
// to a Provider.
func (pt ProvidedType) Provider() *Provider {
@@ -987,3 +1087,12 @@ func (pt ProvidedType) Arg() *InjectorArg {
}
return pt.a
}
// Field returns pt as a Field pointer. It panics if pt does not point to a
// struct Field.
func (pt ProvidedType) Field() *Field {
if pt.f == nil {
panic("ProvidedType does not hold a Field")
}
return pt.f
}

View File

@@ -0,0 +1,38 @@
// 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(injectedBaz())
}
type Foo int
type Baz int
type Bar struct {
Bz Baz
}
func provideFoo(_ Baz) Foo {
return 0
}
func provideBar(_ Foo) Bar {
return Bar{}
}

View File

@@ -0,0 +1,26 @@
// 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 injectedBaz() Baz {
wire.Build(provideFoo, provideBar, wire.FieldsOf(new(Bar), "Bz"))
return 0
}

View File

@@ -0,0 +1 @@
example.com/foo

View File

@@ -0,0 +1,5 @@
example.com/foo/wire.go:x:y: cycle for example.com/foo.Bar:
example.com/foo.Bar (example.com/foo.provideBar) ->
example.com/foo.Foo (example.com/foo.provideFoo) ->
example.com/foo.Baz (example.com/foo.Bar.Bz) ->
example.com/foo.Bar

View File

@@ -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.
package bar
import (
"example.com/foo"
)
type Config struct {
V int
}
type Service struct {
Cfg *Config
F *foo.Service
}
func New(cfg *Config, f *foo.Service) *Service {
return &Service{Cfg: cfg, F: f}
}

View File

@@ -0,0 +1,36 @@
// 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 baz
import (
"fmt"
"example.com/bar"
"example.com/foo"
)
type Config struct {
Foo *foo.Config
Bar *bar.Config
}
type Service struct {
Foo *foo.Service
Bar *bar.Service
}
func (m *Service) String() string {
return fmt.Sprintf("%d %d", m.Foo.Cfg.V, m.Bar.Cfg.V)
}

View File

@@ -0,0 +1,27 @@
// 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 foo
type Config struct {
V int
}
type Service struct {
Cfg *Config
}
func New(cfg *Config) *Service {
return &Service{Cfg: cfg}
}

View File

@@ -0,0 +1,49 @@
// 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 (
"fmt"
"example.com/bar"
"example.com/baz"
"example.com/foo"
"github.com/google/wire"
)
func newBazService(*baz.Config) *baz.Service {
wire.Build(
baz.Service{},
wire.FieldsOf(
new(*baz.Config),
"Foo",
"Bar",
),
foo.New,
bar.New,
)
return nil
}
func main() {
cfg := &baz.Config{
Foo: &foo.Config{1},
Bar: &bar.Config{2},
}
svc := newBazService(cfg)
fmt.Println(svc.String())
}

View File

@@ -0,0 +1 @@
example.com/main

View File

@@ -0,0 +1 @@
1 2

View File

@@ -0,0 +1,38 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
import (
"example.com/bar"
"example.com/baz"
"example.com/foo"
"fmt"
)
// Injectors from wire.go:
func newBazService(config *baz.Config) *baz.Service {
fooConfig := config.Foo
service := foo.New(fooConfig)
barConfig := config.Bar
barService := bar.New(barConfig, service)
bazService := &baz.Service{
Foo: service,
Bar: barService,
}
return bazService
}
// wire.go:
func main() {
cfg := &baz.Config{
Foo: &foo.Config{1},
Bar: &bar.Config{2},
}
svc := newBazService(cfg)
fmt.Println(svc.String())
}

View File

@@ -0,0 +1,29 @@
// Copyright 2018 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(injectedMessage())
}

View File

@@ -0,0 +1,28 @@
// Copyright 2018 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 injectedMessage() string {
wire.Build(
provideS,
wire.FieldsOf(new(S), "Foo"))
return ""
}

View File

@@ -0,0 +1 @@
example.com/foo

View File

@@ -0,0 +1 @@
Hello, World!

View File

@@ -0,0 +1,14 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectedMessage() string {
s := provideS()
string2 := s.Foo
return string2
}

View File

@@ -0,0 +1,29 @@
// Copyright 2018 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(injectedMessage())
}

View File

@@ -0,0 +1,28 @@
// Copyright 2018 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 injectedMessage() string {
wire.Build(
provideS,
wire.FieldsOf(new(*S), "Foo"))
return ""
}

View File

@@ -0,0 +1 @@
example.com/foo

View File

@@ -0,0 +1 @@
Hello, World!

View File

@@ -0,0 +1,14 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectedMessage() string {
s := provideS()
string2 := s.Foo
return string2
}

View File

@@ -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.
package bar
import (
"example.com/foo"
)
type Config struct {
V int
}
type Service struct {
Cfg *Config
F *foo.Service
}
func New(cfg *Config, f *foo.Service) *Service {
return &Service{Cfg: cfg, F: f}
}

View File

@@ -0,0 +1,36 @@
// 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 baz
import (
"fmt"
"example.com/bar"
"example.com/foo"
)
type Config struct {
Foo *foo.Config
Bar *bar.Config
}
type Service struct {
Foo *foo.Service
Bar *bar.Service
}
func (m *Service) String() string {
return fmt.Sprintf("%d %d", m.Foo.Cfg.V, m.Bar.Cfg.V)
}

View File

@@ -0,0 +1,27 @@
// 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 foo
type Config struct {
V int
}
type Service struct {
Cfg *Config
}
func New(cfg *Config) *Service {
return &Service{Cfg: cfg}
}

View File

@@ -0,0 +1,49 @@
// 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 (
"fmt"
"example.com/bar"
"example.com/baz"
"example.com/foo"
"github.com/google/wire"
)
func newBazService() *baz.Service {
wire.Build(
baz.Service{},
wire.Value(&baz.Config{
Foo: &foo.Config{1},
Bar: &bar.Config{2},
}),
wire.FieldsOf(
new(*baz.Config),
"Foo",
"Bar",
),
foo.New,
bar.New,
)
return nil
}
func main() {
svc := newBazService()
fmt.Println(svc.String())
}

View File

@@ -0,0 +1 @@
example.com/main

View File

@@ -0,0 +1 @@
1 2

View File

@@ -0,0 +1,42 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
import (
"example.com/bar"
"example.com/baz"
"example.com/foo"
"fmt"
)
// Injectors from wire.go:
func newBazService() *baz.Service {
config := _wireConfigValue
fooConfig := config.Foo
service := foo.New(fooConfig)
barConfig := config.Bar
barService := bar.New(barConfig, service)
bazService := &baz.Service{
Foo: service,
Bar: barService,
}
return bazService
}
var (
_wireConfigValue = &baz.Config{
Foo: &foo.Config{1},
Bar: &bar.Config{2},
}
)
// wire.go:
func main() {
svc := newBazService()
fmt.Println(svc.String())
}

View File

@@ -28,22 +28,28 @@ import (
type MainConfig struct {
Foo *foo.Config
Bar *bar.Config
Baz *baz.Config
baz *baz.Config
}
type MainService struct {
Foo *foo.Service
Bar *bar.Service
Baz *baz.Service
baz *baz.Service
}
func (m *MainService) String() string {
return fmt.Sprintf("%d %d %d", m.Foo.Cfg.V, m.Bar.Cfg.V, m.Baz.Cfg.V)
return fmt.Sprintf("%d %d %d", m.Foo.Cfg.V, m.Bar.Cfg.V, m.baz.Cfg.V)
}
func newMainService(*foo.Config, *bar.Config, *baz.Config) *MainService {
func newMainService(MainConfig) *MainService {
wire.Build(
MainService{},
wire.FieldsOf(
new(MainConfig),
"Foo",
"Bar",
"baz",
),
foo.New,
bar.New,
baz.New,
@@ -52,11 +58,11 @@ func newMainService(*foo.Config, *bar.Config, *baz.Config) *MainService {
}
func main() {
cfg := &MainConfig{
cfg := MainConfig{
Foo: &foo.Config{1},
Bar: &bar.Config{2},
Baz: &baz.Config{3},
baz: &baz.Config{3},
}
svc := newMainService(cfg.Foo, cfg.Bar, cfg.Baz)
svc := newMainService(cfg)
fmt.Println(svc.String())
}

View File

@@ -14,14 +14,17 @@ import (
// Injectors from wire.go:
func newMainService(config *foo.Config, barConfig *bar.Config, bazConfig *baz.Config) *MainService {
func newMainService(mainConfig MainConfig) *MainService {
config := mainConfig.Foo
service := foo.New(config)
barConfig := mainConfig.Bar
barService := bar.New(barConfig, service)
bazConfig := mainConfig.baz
bazService := baz.New(bazConfig, barService)
mainService := &MainService{
Foo: service,
Bar: barService,
Baz: bazService,
baz: bazService,
}
return mainService
}
@@ -31,25 +34,25 @@ func newMainService(config *foo.Config, barConfig *bar.Config, bazConfig *baz.Co
type MainConfig struct {
Foo *foo.Config
Bar *bar.Config
Baz *baz.Config
baz *baz.Config
}
type MainService struct {
Foo *foo.Service
Bar *bar.Service
Baz *baz.Service
baz *baz.Service
}
func (m *MainService) String() string {
return fmt.Sprintf("%d %d %d", m.Foo.Cfg.V, m.Bar.Cfg.V, m.Baz.Cfg.V)
return fmt.Sprintf("%d %d %d", m.Foo.Cfg.V, m.Bar.Cfg.V, m.baz.Cfg.V)
}
func main() {
cfg := &MainConfig{
cfg := MainConfig{
Foo: &foo.Config{1},
Bar: &bar.Config{2},
Baz: &baz.Config{3},
baz: &baz.Config{3},
}
svc := newMainService(cfg.Foo, cfg.Bar, cfg.Baz)
svc := newMainService(cfg)
fmt.Println(svc.String())
}

View File

@@ -69,3 +69,9 @@ func provideOneOfTwo() OneOfTwo {
func provideTwoOfTwo() TwoOfTwo {
return 1
}
type S struct {
Cfg Config
}
type Config int

View File

@@ -29,6 +29,7 @@ func injectBar() Bar {
wire.Value("unused"), // not needed -> error
unusedSet, // nothing in set is needed -> error
wire.Bind((*Fooer)(nil), (*Foo)(nil)), // binding to Fooer is not needed -> error
wire.FieldsOf(new(S), "Cfg"), // S.Cfg not needed -> error
)
return 0
}

View File

@@ -4,4 +4,6 @@ example.com/foo/wire.go:x:y: inject injectBar: unused provider "provideUnused"
example.com/foo/wire.go:x:y: inject injectBar: unused value of type string
example.com/foo/wire.go:x:y: inject injectBar: unused interface binding to type example.com/foo.Fooer
example.com/foo/wire.go:x:y: inject injectBar: unused interface binding to type example.com/foo.Fooer
example.com/foo/wire.go:x:y: inject injectBar: unused field "example.com/foo.S".Cfg

View File

@@ -623,6 +623,8 @@ func injectPass(name string, sig *types.Signature, calls []call, ig *injectorGen
ig.funcProviderCall(lname, c, injectSig)
case valueExpr:
ig.valueExpr(lname, c)
case selectorExpr:
ig.fieldExpr(lname, c)
default:
panic("unknown kind")
}
@@ -715,6 +717,15 @@ func (ig *injectorGen) valueExpr(lname string, c *call) {
ig.p("\t%s := %s\n", lname, ig.g.values[c.valueExpr])
}
func (ig *injectorGen) fieldExpr(lname string, c *call) {
a := c.args[0]
if a < len(ig.paramNames) {
ig.p("\t%s := %s.%s\n", lname, ig.paramNames[a], c.name)
} else {
ig.p("\t%s := %s.%s\n", lname, ig.localNames[a-len(ig.paramNames)], c.name)
}
}
// nameInInjector reports whether name collides with any other identifier
// in the current injector.
func (ig *injectorGen) nameInInjector(name string) bool {