wire: Add wire.InterfaceValue, required instead of wire.Value if the value is an interface value (google/go-cloud#322)

* Add wire.InterfaceValue, required instead of wire.Value if the value is an interface value.

* Update guestbook sample to use InterfaceValue where appropriate.

* Remove unnecessary ok := true

* Addressing comments from code review.
This commit is contained in:
Robert van Gent
2018-08-14 14:55:28 -07:00
committed by Ross Light
parent eedae3d8d0
commit cd32a686b1
20 changed files with 296 additions and 3 deletions

View File

@@ -454,6 +454,12 @@ func (oc *objectCache) processExpr(pkg *loader.PackageInfo, expr ast.Expr, varNa
return nil, []error{notePosition(exprPos, err)}
}
return v, nil
case "InterfaceValue":
v, err := processInterfaceValue(oc.prog.Fset, &pkg.Info, call)
if err != nil {
return nil, []error{notePosition(exprPos, err)}
}
return v, nil
default:
return nil, []error{notePosition(exprPos, errors.New("unknown pattern"))}
}
@@ -739,6 +745,11 @@ func processValue(fset *token.FileSet, info *types.Info, call *ast.CallExpr) (*V
if !ok {
return nil, notePosition(fset.Position(call.Pos()), errors.New("argument to Value is too complex"))
}
// Result type can't be an interface type; use wire.InterfaceValue for that.
argType := info.TypeOf(call.Args[0])
if _, isInterfaceType := argType.Underlying().(*types.Interface); isInterfaceType {
return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf("argument to Value may not be an interface value (found %s); use InterfaceValue instead", types.TypeString(argType, nil)))
}
return &Value{
Pos: call.Args[0].Pos(),
Out: info.TypeOf(call.Args[0]),
@@ -747,6 +758,35 @@ func processValue(fset *token.FileSet, info *types.Info, call *ast.CallExpr) (*V
}, nil
}
// processInterfaceValue creates a value from a wire.InterfaceValue call.
func processInterfaceValue(fset *token.FileSet, info *types.Info, call *ast.CallExpr) (*Value, error) {
// Assumes that call.Fun is wire.InterfaceValue.
if len(call.Args) != 2 {
return nil, notePosition(fset.Position(call.Pos()), errors.New("call to InterfaceValue takes exactly two arguments"))
}
ifaceArgType := info.TypeOf(call.Args[0])
ifacePtr, ok := ifaceArgType.(*types.Pointer)
if !ok {
return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf("first argument to InterfaceValue must be a pointer to an interface type; found %s", types.TypeString(ifaceArgType, nil)))
}
iface := ifacePtr.Elem()
methodSet, ok := iface.Underlying().(*types.Interface)
if !ok {
return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf("first argument to InterfaceValue must be a pointer to an interface type; found %s", types.TypeString(ifaceArgType, nil)))
}
provided := info.TypeOf(call.Args[1])
if !types.Implements(provided, methodSet) {
return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf("%s does not implement %s", types.TypeString(provided, nil), types.TypeString(ifaceArgType, nil)))
}
return &Value{
Pos: call.Args[1].Pos(),
Out: iface,
expr: call.Args[1],
info: info,
}, nil
}
// isInjector checks whether a given function declaration is an
// injector template, returning the wire.Build call. It returns nil if
// the function is not an injector template.
@@ -845,7 +885,7 @@ func (pv ProviderOrValue) Provider() *Provider {
return pv.p
}
// Provider returns pv as a Value pointer. It panics if pv points to a
// Value returns pv as a Value pointer. It panics if pv points to a
// Provider.
func (pv ProviderOrValue) Value() *Value {
if pv.p != nil {