@@ -35,6 +35,7 @@ import (
35
35
// Exactly one of the fields will be set.
36
36
type providerSetSrc struct {
37
37
Provider * Provider
38
+ AutoBinding * AutoBinding
38
39
Binding * IfaceBinding
39
40
Value * Value
40
41
Import * ProviderSet
@@ -57,6 +58,8 @@ func (p *providerSetSrc) description(fset *token.FileSet, typ types.Type) string
57
58
kind = "struct provider"
58
59
}
59
60
return fmt .Sprintf ("%s %s(%s)" , kind , quoted (p .Provider .Name ), fset .Position (p .Provider .Pos ))
61
+ case p .AutoBinding != nil :
62
+ return fmt .Sprintf ("wire.AutoBind (%s)" , fset .Position (p .AutoBinding .Pos ))
60
63
case p .Binding != nil :
61
64
return fmt .Sprintf ("wire.Bind (%s)" , fset .Position (p .Binding .Pos ))
62
65
case p .Value != nil :
@@ -98,11 +101,12 @@ type ProviderSet struct {
98
101
// variable.
99
102
VarName string
100
103
101
- Providers []* Provider
102
- Bindings []* IfaceBinding
103
- Values []* Value
104
- Fields []* Field
105
- Imports []* ProviderSet
104
+ Providers []* Provider
105
+ Bindings []* IfaceBinding
106
+ AutoBindings []* AutoBinding
107
+ Values []* Value
108
+ Fields []* Field
109
+ Imports []* ProviderSet
106
110
// InjectorArgs is only filled in for wire.Build.
107
111
InjectorArgs * InjectorArgs
108
112
@@ -125,6 +129,22 @@ func (set *ProviderSet) Outputs() []types.Type {
125
129
func (set * ProviderSet ) For (t types.Type ) ProvidedType {
126
130
pt := set .providerMap .At (t )
127
131
if pt == nil {
132
+ // if t is an interface, we may have an AutoBinding that implements it.
133
+ iface , ok := t .Underlying ().(* types.Interface )
134
+ if ! ok {
135
+ return ProvidedType {}
136
+ }
137
+
138
+ for _ , ab := range set .AutoBindings {
139
+ if types .Implements (ab .Concrete , iface ) {
140
+ // cache for later
141
+ pt := & ProvidedType {t : ab .Concrete , ab : ab }
142
+ set .providerMap .Set (t , pt )
143
+ set .srcMap .Set (t , & providerSetSrc {AutoBinding : ab })
144
+ return * pt
145
+ }
146
+ }
147
+
128
148
return ProvidedType {}
129
149
}
130
150
return * pt .(* ProvidedType )
@@ -179,6 +199,17 @@ type Provider struct {
179
199
HasErr bool
180
200
}
181
201
202
+ // AutoBinding records the signature of a provider eligible for auto-binding
203
+ // to interfaces it implements. A provider is a single Go object, either a
204
+ // function or a named type.
205
+ type AutoBinding struct {
206
+ // Concrete is always a type that implements N number of interfaces.
207
+ Concrete types.Type
208
+
209
+ // Pos is the position where the binding was declared.
210
+ Pos token.Pos
211
+ }
212
+
182
213
// ProviderInput describes an incoming edge in the provider graph.
183
214
type ProviderInput struct {
184
215
Type types.Type
@@ -520,7 +551,7 @@ func (oc *objectCache) varDecl(obj *types.Var) *ast.ValueSpec {
520
551
}
521
552
522
553
// processExpr converts an expression into a Wire structure. It may return a
523
- // *Provider, an *IfaceBinding, a *ProviderSet, a *Value or a []*Field.
554
+ // *Provider, an *AutoBinding, an * IfaceBinding, a *ProviderSet, a *Value or a []*Field.
524
555
func (oc * objectCache ) processExpr (info * types.Info , pkgPath string , expr ast.Expr , varName string ) (interface {}, []error ) {
525
556
exprPos := oc .fset .Position (expr .Pos ())
526
557
expr = astutil .Unparen (expr )
@@ -546,6 +577,12 @@ func (oc *objectCache) processExpr(info *types.Info, pkgPath string, expr ast.Ex
546
577
case "NewSet" :
547
578
pset , errs := oc .processNewSet (info , pkgPath , call , nil , varName )
548
579
return pset , notePositionAll (exprPos , errs )
580
+ case "AutoBind" :
581
+ abs , err := processAutoBind (oc .fset , info , call )
582
+ if err != nil {
583
+ return nil , []error {notePosition (exprPos , err )}
584
+ }
585
+ return abs , nil
549
586
case "Bind" :
550
587
b , err := processBind (oc .fset , info , call )
551
588
if err != nil {
@@ -607,6 +644,8 @@ func (oc *objectCache) processNewSet(info *types.Info, pkgPath string, call *ast
607
644
continue
608
645
}
609
646
switch item := item .(type ) {
647
+ case * AutoBinding :
648
+ pset .AutoBindings = append (pset .AutoBindings , item )
610
649
case * Provider :
611
650
pset .Providers = append (pset .Providers , item )
612
651
case * ProviderSet :
@@ -880,6 +919,41 @@ func isPrevented(tag string) bool {
880
919
return reflect .StructTag (tag ).Get ("wire" ) == "-"
881
920
}
882
921
922
+ func processAutoBind (fset * token.FileSet , info * types.Info , call * ast.CallExpr ) (* AutoBinding , error ) {
923
+ // Assumes that call.Fun is wire.AutoBind.
924
+
925
+ if len (call .Args ) != 1 {
926
+ return nil , notePosition (fset .Position (call .Pos ()),
927
+ errors .New ("call to AutoBind takes exactly one argument" ))
928
+ }
929
+ const firstArgReqFormat = "first argument to AutoBind must be a pointer to a type; found %s"
930
+ typ := info .TypeOf (call .Args [0 ])
931
+ ptr , ok := typ .(* types.Pointer )
932
+ if ! ok {
933
+ return nil , notePosition (fset .Position (call .Pos ()),
934
+ fmt .Errorf (firstArgReqFormat , types .TypeString (typ , nil )))
935
+ }
936
+
937
+ switch ptr .Elem ().Underlying ().(type ) {
938
+ case * types.Named ,
939
+ * types.Struct ,
940
+ * types.Basic :
941
+ // good!
942
+
943
+ default :
944
+ return nil , notePosition (fset .Position (call .Pos ()),
945
+ fmt .Errorf (firstArgReqFormat , types .TypeString (ptr , nil )))
946
+ }
947
+
948
+ typeExpr := call .Args [0 ].(* ast.CallExpr )
949
+ typeName := qualifiedIdentObject (info , typeExpr .Args [0 ]) // should be either an identifier or selector
950
+ autoBinding := & AutoBinding {
951
+ Concrete : ptr ,
952
+ Pos : typeName .Pos (),
953
+ }
954
+ return autoBinding , nil
955
+ }
956
+
883
957
// processBind creates an interface binding from a wire.Bind call.
884
958
func processBind (fset * token.FileSet , info * types.Info , call * ast.CallExpr ) (* IfaceBinding , error ) {
885
959
// Assumes that call.Fun is wire.Bind.
@@ -1122,7 +1196,6 @@ func findInjectorBuild(info *types.Info, fn *ast.FuncDecl) (*ast.CallExpr, error
1122
1196
default :
1123
1197
invalid = true
1124
1198
}
1125
-
1126
1199
}
1127
1200
if wireBuildCall == nil {
1128
1201
return nil , nil
@@ -1157,16 +1230,17 @@ func isProviderSetType(t types.Type) bool {
1157
1230
// none of the above, and returns true for IsNil.
1158
1231
type ProvidedType struct {
1159
1232
// t is the provided concrete type.
1160
- t types.Type
1161
- p * Provider
1162
- v * Value
1163
- a * InjectorArg
1164
- f * Field
1233
+ t types.Type
1234
+ p * Provider
1235
+ ab * AutoBinding
1236
+ v * Value
1237
+ a * InjectorArg
1238
+ f * Field
1165
1239
}
1166
1240
1167
1241
// IsNil reports whether pt is the zero value.
1168
1242
func (pt ProvidedType ) IsNil () bool {
1169
- return pt .p == nil && pt .v == nil && pt .a == nil && pt .f == nil
1243
+ return pt .p == nil && pt .ab == nil && pt . v == nil && pt .a == nil && pt .f == nil
1170
1244
}
1171
1245
1172
1246
// Type returns the output type.
@@ -1185,6 +1259,11 @@ func (pt ProvidedType) IsProvider() bool {
1185
1259
return pt .p != nil
1186
1260
}
1187
1261
1262
+ // IsAutoBinding reports whether pt points to an AutoBinding.
1263
+ func (pt ProvidedType ) IsAutoBinding () bool {
1264
+ return pt .ab != nil
1265
+ }
1266
+
1188
1267
// IsValue reports whether pt points to a Value.
1189
1268
func (pt ProvidedType ) IsValue () bool {
1190
1269
return pt .v != nil
@@ -1209,6 +1288,15 @@ func (pt ProvidedType) Provider() *Provider {
1209
1288
return pt .p
1210
1289
}
1211
1290
1291
+ // AutoBinding returns pt as a AutoBinding pointer. It panics if pt does not point
1292
+ // to a AutoBinding.
1293
+ func (pt ProvidedType ) AutoBinding () * AutoBinding {
1294
+ if pt .ab == nil {
1295
+ panic ("ProvidedType does not hold an AutoBinding" )
1296
+ }
1297
+ return pt .ab
1298
+ }
1299
+
1212
1300
// Value returns pt as a Value pointer. It panics if pt does not point
1213
1301
// to a Value.
1214
1302
func (pt ProvidedType ) Value () * Value {
0 commit comments