diff --git a/README.md b/README.md index 88aec23..d47ab72 100644 --- a/README.md +++ b/README.md @@ -304,6 +304,33 @@ func injectFooBar() FooBar { And similarly if the injector needed a `*FooBar`. +### Binding Values + +Occasionally, it is useful to bind a basic value (usually `nil`) to a type. +Instead of having injectors depend on a throwaway provider function, you can +add a value expression to a provider set. + +```go +type Foo int + +func injectFoo() Foo { + panic(goose.Use(goose.Value(Foo(42)))) +} +``` + +The generated injector would look like this: + +```go +func injectFoo() Foo { + foo := Foo(42) + return foo +} +``` + +It's important to note that the expression will be copied, so references to +variables will be evaluated during the call to the injector. goose will emit +an error if the expression calls any functions. + ### Cleanup functions If a provider creates a value that needs to be cleaned up (e.g. closing a file), diff --git a/cmd/goose/main.go b/cmd/goose/main.go index ad6b1a2..479e621 100644 --- a/cmd/goose/main.go +++ b/cmd/goose/main.go @@ -132,6 +132,8 @@ func show(pkgs ...string) error { switch v := v.(type) { case *goose.Provider: out[types.TypeString(t, nil)] = v.Pos + case *goose.Value: + out[types.TypeString(t, nil)] = v.Pos case *goose.IfaceBinding: out[types.TypeString(t, nil)] = v.Pos default: @@ -150,7 +152,7 @@ func show(pkgs ...string) error { type outGroup struct { name string inputs *typeutil.Map // values are not important - outputs *typeutil.Map // values are either *goose.Provider or *goose.IfaceBinding + outputs *typeutil.Map // values are *goose.Provider, *goose.Value, or *goose.IfaceBinding } // gather flattens a provider set into outputs grouped by the inputs @@ -180,6 +182,9 @@ func gather(info *goose.Info, key goose.ProviderSetID) (_ []outGroup, imports ma for _, b := range curr.Bindings { pm.Set(b.Iface, b) } + for _, v := range curr.Values { + pm.Set(v.Out, v) + } for _, imp := range curr.Imports { next = append(next, imp) } @@ -253,6 +258,24 @@ func gather(info *goose.Info, key goose.ProviderSetID) (_ []outGroup, imports ma inputs: in, outputs: out, }) + case *goose.Value: + for i := range groups { + if groups[i].inputs.Len() == 0 { + groups[i].outputs.Set(p.Out, p) + inputVisited.Set(p.Out, i) + continue dfs + } + } + in := new(typeutil.Map) + in.SetHasher(hash) + out := new(typeutil.Map) + out.SetHasher(hash) + out.Set(p.Out, p) + inputVisited.Set(p.Out, len(groups)) + groups = append(groups, outGroup{ + inputs: in, + outputs: out, + }) case *goose.IfaceBinding: i, ok := inputVisited.At(p.Provided).(int) if !ok { diff --git a/goose.go b/goose.go index 86d7d4b..0332f0b 100644 --- a/goose.go +++ b/goose.go @@ -50,3 +50,15 @@ type Binding struct{} func Bind(iface, to interface{}) Binding { return Binding{} } + +// A ProvidedValue is an expression that is copied to the generated injector. +type ProvidedValue struct{} + +// Value binds an expression to provide the type of the expression. +// +// Example: +// +// var MySet = goose.NewSet(goose.Value([]string(nil))) +func Value(interface{}) ProvidedValue { + return ProvidedValue{} +} diff --git a/internal/goose/analyze.go b/internal/goose/analyze.go index 9592b49..4c4bc73 100644 --- a/internal/goose/analyze.go +++ b/internal/goose/analyze.go @@ -16,17 +16,34 @@ package goose import ( "fmt" + "go/ast" "go/token" "go/types" "golang.org/x/tools/go/types/typeutil" ) +type callKind int + +const ( + funcProviderCall callKind = iota + structProvider + valueExpr +) + // A call represents a step of an injector function. It may be either a // function call or a composite struct literal, depending on the value -// of isStruct. +// of kind. type call struct { - // importPath and name identify the provider to call. + // kind indicates the code pattern to use. + kind callKind + + // out is the type this step produces. + out types.Type + + // importPath and name identify the provider to call for kind == + // funcProviderCall or the type to construct for kind == + // structProvider. importPath string name string @@ -34,24 +51,29 @@ type call struct { // a) one of the givens (args[i] < len(given)), // b) the result of a previous provider call (args[i] >= len(given)), or // c) the zero value for the type (args[i] == -1). + // + // This will be nil for kind == valueExpr. args []int - // isStruct indicates whether this should generate a struct composite - // literal instead of a function call. - isStruct bool - // fieldNames maps the arguments to struct field names. - // This will only be set if isStruct is true. + // This will only be set if kind == structProvider. fieldNames []string // ins is the list of types this call receives as arguments. + // This will be nil for kind == valueExpr. ins []types.Type - // out is the type produced by this provider call. - out types.Type + + // The following are only set for kind == funcProviderCall: + // hasCleanup is true if the provider call returns a cleanup function. hasCleanup bool // hasErr is true if the provider call returns an error. hasErr bool + + // The following are only set for kind == valueExpr: + + valueExpr ast.Expr + valueTypeInfo *types.Info } // solve finds the sequence of calls required to produce an output type @@ -97,50 +119,69 @@ func solve(fset *token.FileSet, out types.Type, given []types.Type, set *Provide } } - p, _ := providers.At(typ).(*Provider) - if p == nil { + switch p := providers.At(typ).(type) { + case nil: if len(trail) == 1 { return fmt.Errorf("no provider found for %s (output of injector)", types.TypeString(typ, nil)) } // TODO(light): Give name of provider. return fmt.Errorf("no provider found for %s (required by provider of %s)", types.TypeString(typ, nil), types.TypeString(trail[len(trail)-2].Type, nil)) - } - if !types.Identical(p.Out, typ) { - // Interface binding. Don't create a call ourselves. - if err := visit(append(trail, ProviderInput{Type: p.Out})); err != nil { - return err + case *Provider: + if !types.Identical(p.Out, typ) { + // Interface binding. Don't create a call ourselves. + if err := visit(append(trail, ProviderInput{Type: p.Out})); err != nil { + return err + } + index.Set(typ, index.At(p.Out)) + return nil } - index.Set(typ, index.At(p.Out)) - return nil - } - for _, a := range p.Args { - // TODO(light): This will discard grown trail arrays. - if err := visit(append(trail, a)); err != nil { - return err + for _, a := range p.Args { + // TODO(light): This will discard grown trail arrays. + if err := visit(append(trail, a)); err != nil { + return err + } } - } - args := make([]int, len(p.Args)) - ins := make([]types.Type, len(p.Args)) - for i := range p.Args { - ins[i] = p.Args[i].Type - if x := index.At(p.Args[i].Type); x != nil { - args[i] = x.(int) - } else { - args[i] = -1 + args := make([]int, len(p.Args)) + ins := make([]types.Type, len(p.Args)) + for i := range p.Args { + ins[i] = p.Args[i].Type + args[i] = index.At(p.Args[i].Type).(int) } + index.Set(typ, len(given)+len(calls)) + kind := funcProviderCall + if p.IsStruct { + kind = structProvider + } + calls = append(calls, call{ + kind: kind, + importPath: p.ImportPath, + name: p.Name, + args: args, + fieldNames: p.Fields, + ins: ins, + out: typ, + hasCleanup: p.HasCleanup, + hasErr: p.HasErr, + }) + case *Value: + if !types.Identical(p.Out, typ) { + // Interface binding. Don't create a call ourselves. + if err := visit(append(trail, ProviderInput{Type: p.Out})); err != nil { + return err + } + index.Set(typ, index.At(p.Out)) + return nil + } + index.Set(typ, len(given)+len(calls)) + calls = append(calls, call{ + kind: valueExpr, + out: typ, + valueExpr: p.expr, + valueTypeInfo: p.info, + }) + default: + panic("unknown provider map value type") } - index.Set(typ, len(given)+len(calls)) - calls = append(calls, call{ - importPath: p.ImportPath, - name: p.Name, - args: args, - isStruct: p.IsStruct, - fieldNames: p.Fields, - ins: ins, - out: typ, - hasCleanup: p.HasCleanup, - hasErr: p.HasErr, - }) return nil } if err := visit([]ProviderInput{{Type: out}}); err != nil { @@ -155,7 +196,7 @@ func buildProviderMap(fset *token.FileSet, set *ProviderSet) (*typeutil.Map, err set *ProviderSet } - providerMap := new(typeutil.Map) // to *Provider + providerMap := new(typeutil.Map) // to *Provider or *Value setMap := new(typeutil.Map) // to *ProviderSet, for error messages var bindings []binding visited := make(map[*ProviderSet]struct{}) @@ -175,6 +216,13 @@ func buildProviderMap(fset *token.FileSet, set *ProviderSet) (*typeutil.Map, err providerMap.Set(p.Out, p) setMap.Set(p.Out, curr) } + for _, v := range curr.Values { + if providerMap.At(v.Out) != nil { + return nil, bindingConflictError(fset, v.Pos, v.Out, setMap.At(v.Out).(*ProviderSet)) + } + providerMap.Set(v.Out, v) + setMap.Set(v.Out, curr) + } for _, b := range curr.Bindings { bindings = append(bindings, binding{ IfaceBinding: b, diff --git a/internal/goose/goose.go b/internal/goose/goose.go index 679cddf..5cb9ae8 100644 --- a/internal/goose/goose.go +++ b/internal/goose/goose.go @@ -244,15 +244,24 @@ func (g *gen) inject(fset *token.FileSet, name string, sig *types.Signature, set paramTypes[i] = types.TypeString(params.At(i).Type(), g.qualifyPkg) } for _, c := range calls { - g.qualifyImport(c.importPath) - if !c.isStruct { - // Struct providers just omit zero-valued fields. - continue - } - for i := range c.args { - if c.args[i] == -1 { - zeroValue(c.ins[i], g.qualifyPkg) + switch c.kind { + case funcProviderCall: + g.qualifyImport(c.importPath) + for i := range c.args { + if c.args[i] == -1 { + zeroValue(c.ins[i], g.qualifyPkg) + } } + case structProvider: + g.qualifyImport(c.importPath) + case valueExpr: + if err := accessibleFrom(c.valueTypeInfo, c.valueExpr, g.currPackage); err != nil { + // TODO(light): Display line number of value expression. + ts := types.TypeString(c.out, nil) + return fmt.Errorf("inject %s: value %s can't be used: %v", name, ts, err) + } + default: + panic("unknown kind") } } outTypeString := types.TypeString(outType, g.qualifyPkg) @@ -326,7 +335,8 @@ func (g *gen) inject(fset *token.FileSet, name string, sig *types.Signature, set g.p(", %s", errVar) } g.p(" := ") - if c.isStruct { + switch c.kind { + case structProvider: if _, ok := c.out.(*types.Pointer); ok { g.p("&") } @@ -345,7 +355,7 @@ func (g *gen) inject(fset *token.FileSet, name string, sig *types.Signature, set g.p(",\n") } g.p("\t}\n") - } else { + case funcProviderCall: g.p("%s(", g.qualifiedID(c.importPath, c.name)) for j, a := range c.args { if j > 0 { @@ -360,6 +370,11 @@ func (g *gen) inject(fset *token.FileSet, name string, sig *types.Signature, set } } g.p(")\n") + case valueExpr: + g.writeAST(fset, c.valueTypeInfo, c.valueExpr) + g.p("\n") + default: + panic("unknown kind") } if c.hasErr { g.p("\tif %s != nil {\n", errVar) @@ -656,6 +671,32 @@ func disambiguate(name string, collides func(string) bool) string { } } +// accessibleFrom reports whether node can be copied to wantPkg without +// violating Go visibility rules. +func accessibleFrom(info *types.Info, node ast.Node, wantPkg string) error { + var unexportError error + ast.Inspect(node, func(node ast.Node) bool { + if unexportError != nil { + return false + } + ident, ok := node.(*ast.Ident) + if !ok { + return true + } + obj := info.ObjectOf(ident) + if _, ok := obj.(*types.PkgName); ok { + // Local package names are fine, since we can just reimport them. + return true + } + if pkg := obj.Pkg(); pkg != nil && !ast.IsExported(ident.Name) && pkg.Path() != wantPkg { + unexportError = fmt.Errorf("uses unexported identifier %s", obj.Name()) + return false + } + return true + }) + return unexportError +} + var ( errorType = types.Universe.Lookup("error").Type() cleanupType = types.NewSignature(nil, nil, nil, false) diff --git a/internal/goose/parse.go b/internal/goose/parse.go index 897fee0..0b29d20 100644 --- a/internal/goose/parse.go +++ b/internal/goose/parse.go @@ -41,6 +41,7 @@ type ProviderSet struct { Providers []*Provider Bindings []*IfaceBinding + Values []*Value Imports []*ProviderSet } @@ -100,6 +101,21 @@ type ProviderInput struct { // TODO(light): Move field name into this struct. } +// Value describes a value expression. +type Value struct { + // Pos is the source position of the expression defining this value. + Pos token.Pos + + // Out is the type this value produces. + Out types.Type + + // expr is the expression passed to goose.Value. + expr ast.Expr + + // info is the type info for the expression. + info *types.Info +} + // Load finds all the provider sets in the given packages, as well as // the provider sets' transitive dependencies. func Load(bctx *build.Context, wd string, pkgs []string) (*Info, error) { @@ -163,7 +179,7 @@ func (id ProviderSetID) String() string { // objectCache is a lazily evaluated mapping of objects to goose structures. type objectCache struct { prog *loader.Program - objects map[objRef]interface{} // *Provider or *ProviderSet + objects map[objRef]interface{} // *Provider, *ProviderSet, *IfaceBinding, or *Value } type objRef struct { @@ -179,7 +195,8 @@ func newObjectCache(prog *loader.Program) *objectCache { } // get converts a Go object into a goose structure. It may return a -// *Provider, a structProviderPair, an *IfaceBinding, or a *ProviderSet. +// *Provider, a structProviderPair, an *IfaceBinding, a *ProviderSet, +// or a *Value. func (oc *objectCache) get(obj types.Object) (interface{}, error) { ref := objRef{ importPath: obj.Pkg().Path(), @@ -239,8 +256,8 @@ func (oc *objectCache) varDecl(obj *types.Var) *ast.ValueSpec { } // processExpr converts an expression into a goose structure. It may -// return a *Provider, a structProviderPair, an *IfaceBinding, or a -// *ProviderSet. +// return a *Provider, a structProviderPair, an *IfaceBinding, a +// *ProviderSet, or a *Value. func (oc *objectCache) processExpr(pkg *loader.PackageInfo, expr ast.Expr) (interface{}, error) { exprPos := oc.prog.Fset.Position(expr.Pos()) expr = astutil.Unparen(expr) @@ -269,6 +286,12 @@ func (oc *objectCache) processExpr(pkg *loader.PackageInfo, expr ast.Expr) (inte return nil, fmt.Errorf("%v: %v", exprPos, err) } return b, nil + case "Value": + v, err := processValue(oc.prog.Fset, &pkg.Info, call) + if err != nil { + return nil, fmt.Errorf("%v: %v", exprPos, err) + } + return v, nil default: return nil, fmt.Errorf("%v: unknown pattern", exprPos) } @@ -312,6 +335,8 @@ func (oc *objectCache) processNewSet(pkg *loader.PackageInfo, call *ast.CallExpr pset.Bindings = append(pset.Bindings, item) case structProviderPair: pset.Providers = append(pset.Providers, item.provider, item.ptrProvider) + case *Value: + pset.Values = append(pset.Values, item) default: panic("unknown item type") } @@ -471,6 +496,48 @@ func processBind(fset *token.FileSet, info *types.Info, call *ast.CallExpr) (*If }, nil } +// processValue creates a value from a goose.Value call. +func processValue(fset *token.FileSet, info *types.Info, call *ast.CallExpr) (*Value, error) { + // Assumes that call.Fun is goose.Value. + + if len(call.Args) != 1 { + return nil, fmt.Errorf("%v: call to Value takes exactly one argument", fset.Position(call.Pos())) + } + ok := true + ast.Inspect(call.Args[0], func(node ast.Node) bool { + switch node.(type) { + case nil, *ast.ArrayType, *ast.BasicLit, *ast.BinaryExpr, *ast.ChanType, *ast.CompositeLit, *ast.FuncType, *ast.Ident, *ast.IndexExpr, *ast.InterfaceType, *ast.KeyValueExpr, *ast.MapType, *ast.ParenExpr, *ast.SelectorExpr, *ast.SliceExpr, *ast.StarExpr, *ast.StructType, *ast.TypeAssertExpr: + // Good! + case *ast.UnaryExpr: + expr := node.(*ast.UnaryExpr) + if expr.Op == token.ARROW { + ok = false + return false + } + case *ast.CallExpr: + // Only acceptable if it's a type conversion. + call := node.(*ast.CallExpr) + if _, isFunc := info.TypeOf(call.Fun).(*types.Signature); isFunc { + ok = false + return false + } + default: + ok = false + return false + } + return true + }) + if !ok { + return nil, fmt.Errorf("%v: argument to Value is too complex", fset.Position(call.Pos())) + } + return &Value{ + Pos: call.Args[0].Pos(), + Out: info.TypeOf(call.Args[0]), + expr: call.Args[0], + info: info, + }, nil +} + // isInjector checks whether a given function declaration is an // injector template, returning the goose.Use call. It returns nil if // the function is not an injector template. diff --git a/internal/goose/testdata/ExportedValue/bar/bar.go b/internal/goose/testdata/ExportedValue/bar/bar.go new file mode 100644 index 0000000..9507812 --- /dev/null +++ b/internal/goose/testdata/ExportedValue/bar/bar.go @@ -0,0 +1,21 @@ +// Copyright 2018 Google LLC +// +// 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 "github.com/google/go-cloud/goose" + +var Value = goose.Value(PublicMsg) + +var PublicMsg = "Hello, World!" diff --git a/internal/goose/testdata/ExportedValue/foo/foo.go b/internal/goose/testdata/ExportedValue/foo/foo.go new file mode 100644 index 0000000..f8216df --- /dev/null +++ b/internal/goose/testdata/ExportedValue/foo/foo.go @@ -0,0 +1,21 @@ +// Copyright 2018 Google LLC +// +// 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(injectedMessage()) +} diff --git a/internal/goose/testdata/ExportedValue/foo/goose.go b/internal/goose/testdata/ExportedValue/foo/goose.go new file mode 100644 index 0000000..0cdcb8f --- /dev/null +++ b/internal/goose/testdata/ExportedValue/foo/goose.go @@ -0,0 +1,26 @@ +// Copyright 2018 Google LLC +// +// 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 gooseinject + +package main + +import ( + "bar" + "github.com/google/go-cloud/goose" +) + +func injectedMessage() string { + panic(goose.Use(bar.Value)) +} diff --git a/internal/goose/testdata/ExportedValue/out.txt b/internal/goose/testdata/ExportedValue/out.txt new file mode 100644 index 0000000..8ab686e --- /dev/null +++ b/internal/goose/testdata/ExportedValue/out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/internal/goose/testdata/ExportedValue/pkg b/internal/goose/testdata/ExportedValue/pkg new file mode 100644 index 0000000..257cc56 --- /dev/null +++ b/internal/goose/testdata/ExportedValue/pkg @@ -0,0 +1 @@ +foo diff --git a/internal/goose/testdata/ExportedValueDifferentPackage/bar/bar.go b/internal/goose/testdata/ExportedValueDifferentPackage/bar/bar.go new file mode 100644 index 0000000..4e7b163 --- /dev/null +++ b/internal/goose/testdata/ExportedValueDifferentPackage/bar/bar.go @@ -0,0 +1,23 @@ +// Copyright 2018 Google LLC +// +// 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 ( + "os" + + "github.com/google/go-cloud/goose" +) + +var Value = goose.Value(os.Stdout) diff --git a/internal/goose/testdata/ExportedValueDifferentPackage/foo/foo.go b/internal/goose/testdata/ExportedValueDifferentPackage/foo/foo.go new file mode 100644 index 0000000..b23b644 --- /dev/null +++ b/internal/goose/testdata/ExportedValueDifferentPackage/foo/foo.go @@ -0,0 +1,21 @@ +// Copyright 2018 Google LLC +// +// 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.Fprintln(injectedFile(), "Hello, World!") +} diff --git a/internal/goose/testdata/ExportedValueDifferentPackage/foo/goose.go b/internal/goose/testdata/ExportedValueDifferentPackage/foo/goose.go new file mode 100644 index 0000000..af2916c --- /dev/null +++ b/internal/goose/testdata/ExportedValueDifferentPackage/foo/goose.go @@ -0,0 +1,28 @@ +// Copyright 2018 Google LLC +// +// 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 gooseinject + +package main + +import ( + "os" + + "bar" + "github.com/google/go-cloud/goose" +) + +func injectedFile() *os.File { + panic(goose.Use(bar.Value)) +} diff --git a/internal/goose/testdata/ExportedValueDifferentPackage/out.txt b/internal/goose/testdata/ExportedValueDifferentPackage/out.txt new file mode 100644 index 0000000..8ab686e --- /dev/null +++ b/internal/goose/testdata/ExportedValueDifferentPackage/out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/internal/goose/testdata/ExportedValueDifferentPackage/pkg b/internal/goose/testdata/ExportedValueDifferentPackage/pkg new file mode 100644 index 0000000..257cc56 --- /dev/null +++ b/internal/goose/testdata/ExportedValueDifferentPackage/pkg @@ -0,0 +1 @@ +foo diff --git a/internal/goose/testdata/NiladicValue/foo/foo.go b/internal/goose/testdata/NiladicValue/foo/foo.go new file mode 100644 index 0000000..f8216df --- /dev/null +++ b/internal/goose/testdata/NiladicValue/foo/foo.go @@ -0,0 +1,21 @@ +// Copyright 2018 Google LLC +// +// 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(injectedMessage()) +} diff --git a/internal/goose/testdata/NiladicValue/foo/goose.go b/internal/goose/testdata/NiladicValue/foo/goose.go new file mode 100644 index 0000000..9fad6f7 --- /dev/null +++ b/internal/goose/testdata/NiladicValue/foo/goose.go @@ -0,0 +1,25 @@ +// Copyright 2018 Google LLC +// +// 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 gooseinject + +package main + +import ( + "github.com/google/go-cloud/goose" +) + +func injectedMessage() string { + panic(goose.Use(goose.Value("Hello, World!"))) +} diff --git a/internal/goose/testdata/NiladicValue/out.txt b/internal/goose/testdata/NiladicValue/out.txt new file mode 100644 index 0000000..8ab686e --- /dev/null +++ b/internal/goose/testdata/NiladicValue/out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/internal/goose/testdata/NiladicValue/pkg b/internal/goose/testdata/NiladicValue/pkg new file mode 100644 index 0000000..257cc56 --- /dev/null +++ b/internal/goose/testdata/NiladicValue/pkg @@ -0,0 +1 @@ +foo diff --git a/internal/goose/testdata/UnexportedValue/bar/bar.go b/internal/goose/testdata/UnexportedValue/bar/bar.go new file mode 100644 index 0000000..b19ed9a --- /dev/null +++ b/internal/goose/testdata/UnexportedValue/bar/bar.go @@ -0,0 +1,21 @@ +// Copyright 2018 Google LLC +// +// 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 "github.com/google/go-cloud/goose" + +var Value = goose.Value(privateMsg) + +var privateMsg = "Hello, World!" diff --git a/internal/goose/testdata/UnexportedValue/foo/foo.go b/internal/goose/testdata/UnexportedValue/foo/foo.go new file mode 100644 index 0000000..f8216df --- /dev/null +++ b/internal/goose/testdata/UnexportedValue/foo/foo.go @@ -0,0 +1,21 @@ +// Copyright 2018 Google LLC +// +// 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(injectedMessage()) +} diff --git a/internal/goose/testdata/UnexportedValue/foo/goose.go b/internal/goose/testdata/UnexportedValue/foo/goose.go new file mode 100644 index 0000000..0cdcb8f --- /dev/null +++ b/internal/goose/testdata/UnexportedValue/foo/goose.go @@ -0,0 +1,26 @@ +// Copyright 2018 Google LLC +// +// 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 gooseinject + +package main + +import ( + "bar" + "github.com/google/go-cloud/goose" +) + +func injectedMessage() string { + panic(goose.Use(bar.Value)) +} diff --git a/internal/goose/testdata/UnexportedValue/out.txt b/internal/goose/testdata/UnexportedValue/out.txt new file mode 100644 index 0000000..5df7507 --- /dev/null +++ b/internal/goose/testdata/UnexportedValue/out.txt @@ -0,0 +1 @@ +ERROR diff --git a/internal/goose/testdata/UnexportedValue/pkg b/internal/goose/testdata/UnexportedValue/pkg new file mode 100644 index 0000000..257cc56 --- /dev/null +++ b/internal/goose/testdata/UnexportedValue/pkg @@ -0,0 +1 @@ +foo diff --git a/internal/goose/testdata/ValueChain/foo/foo.go b/internal/goose/testdata/ValueChain/foo/foo.go new file mode 100644 index 0000000..19c1b37 --- /dev/null +++ b/internal/goose/testdata/ValueChain/foo/foo.go @@ -0,0 +1,36 @@ +// Copyright 2018 Google LLC +// +// 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" + + "github.com/google/go-cloud/goose" +) + +func main() { + fmt.Println(injectFooBar()) +} + +type Foo int +type FooBar int + +var Set = goose.NewSet( + goose.Value(Foo(41)), + provideFooBar) + +func provideFooBar(foo Foo) FooBar { + return FooBar(foo) + 1 +} diff --git a/internal/goose/testdata/ValueChain/foo/goose.go b/internal/goose/testdata/ValueChain/foo/goose.go new file mode 100644 index 0000000..8f9227a --- /dev/null +++ b/internal/goose/testdata/ValueChain/foo/goose.go @@ -0,0 +1,25 @@ +// Copyright 2018 Google LLC +// +// 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 gooseinject + +package main + +import ( + "github.com/google/go-cloud/goose" +) + +func injectFooBar() FooBar { + panic(goose.Use(Set)) +} diff --git a/internal/goose/testdata/ValueChain/out.txt b/internal/goose/testdata/ValueChain/out.txt new file mode 100644 index 0000000..d81cc07 --- /dev/null +++ b/internal/goose/testdata/ValueChain/out.txt @@ -0,0 +1 @@ +42 diff --git a/internal/goose/testdata/ValueChain/pkg b/internal/goose/testdata/ValueChain/pkg new file mode 100644 index 0000000..257cc56 --- /dev/null +++ b/internal/goose/testdata/ValueChain/pkg @@ -0,0 +1 @@ +foo diff --git a/internal/goose/testdata/ValueConversion/foo/foo.go b/internal/goose/testdata/ValueConversion/foo/foo.go new file mode 100644 index 0000000..17e752a --- /dev/null +++ b/internal/goose/testdata/ValueConversion/foo/foo.go @@ -0,0 +1,23 @@ +// Copyright 2018 Google LLC +// +// 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(injectedMessage()) +} + +type Foo string diff --git a/internal/goose/testdata/ValueConversion/foo/goose.go b/internal/goose/testdata/ValueConversion/foo/goose.go new file mode 100644 index 0000000..1ae73ab --- /dev/null +++ b/internal/goose/testdata/ValueConversion/foo/goose.go @@ -0,0 +1,25 @@ +// Copyright 2018 Google LLC +// +// 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 gooseinject + +package main + +import ( + "github.com/google/go-cloud/goose" +) + +func injectedMessage() Foo { + panic(goose.Use(goose.Value(Foo("Hello, World!")))) +} diff --git a/internal/goose/testdata/ValueConversion/out.txt b/internal/goose/testdata/ValueConversion/out.txt new file mode 100644 index 0000000..8ab686e --- /dev/null +++ b/internal/goose/testdata/ValueConversion/out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/internal/goose/testdata/ValueConversion/pkg b/internal/goose/testdata/ValueConversion/pkg new file mode 100644 index 0000000..257cc56 --- /dev/null +++ b/internal/goose/testdata/ValueConversion/pkg @@ -0,0 +1 @@ +foo diff --git a/internal/goose/testdata/VarValue/foo/foo.go b/internal/goose/testdata/VarValue/foo/foo.go new file mode 100644 index 0000000..a81b517 --- /dev/null +++ b/internal/goose/testdata/VarValue/foo/foo.go @@ -0,0 +1,26 @@ +// Copyright 2018 Google LLC +// +// 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() { + // Value should be deferred until function call. + msg = "Hello, World!" + + fmt.Println(injectedMessage()) +} + +var msg string diff --git a/internal/goose/testdata/VarValue/foo/goose.go b/internal/goose/testdata/VarValue/foo/goose.go new file mode 100644 index 0000000..4c5ec31 --- /dev/null +++ b/internal/goose/testdata/VarValue/foo/goose.go @@ -0,0 +1,25 @@ +// Copyright 2018 Google LLC +// +// 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 gooseinject + +package main + +import ( + "github.com/google/go-cloud/goose" +) + +func injectedMessage() string { + panic(goose.Use(goose.Value(msg))) +} diff --git a/internal/goose/testdata/VarValue/out.txt b/internal/goose/testdata/VarValue/out.txt new file mode 100644 index 0000000..8ab686e --- /dev/null +++ b/internal/goose/testdata/VarValue/out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/internal/goose/testdata/VarValue/pkg b/internal/goose/testdata/VarValue/pkg new file mode 100644 index 0000000..257cc56 --- /dev/null +++ b/internal/goose/testdata/VarValue/pkg @@ -0,0 +1 @@ +foo