Skip to content

Commit 1e84708

Browse files
authored
Add LocationForPC ProvideOption (uber-go#282)
Add `LocationForPC` ProvideOption which overrides the function inspection done by `digreflect` for a specific program counter. This helps with debugging errors from constructors that are created from reflection.
1 parent 4350cd3 commit 1e84708

File tree

3 files changed

+53
-6
lines changed

3 files changed

+53
-6
lines changed

dig.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ type optionFunc func(*Container)
6060
func (f optionFunc) applyOption(c *Container) { f(c) }
6161

6262
type provideOptions struct {
63-
Name string
64-
Group string
65-
Info *ProvideInfo
63+
Name string
64+
Group string
65+
Info *ProvideInfo
66+
Location *digreflect.Func
6667
}
6768

6869
func (o *provideOptions) Validate() error {
@@ -198,6 +199,18 @@ func FillProvideInfo(info *ProvideInfo) ProvideOption {
198199
})
199200
}
200201

202+
// LocationForPC is a ProvideOption which specifies an alternate function program
203+
// counter address to be used for debug information. The package, name, file and
204+
// line number of this alternate function address will be used in error messages
205+
// and DOT graphs. This option is intended to be used with functions created
206+
// with the reflect.MakeFunc method whose error messages are otherwise hard to
207+
// understand
208+
func LocationForPC(pc uintptr) ProvideOption {
209+
return provideOptionFunc(func(opts *provideOptions) {
210+
opts.Location = digreflect.InspectFuncPC(pc)
211+
})
212+
}
213+
201214
// An InvokeOption modifies the default behavior of Invoke. It's included for
202215
// future functionality; currently, there are no concrete implementations.
203216
type InvokeOption interface {
@@ -538,6 +551,7 @@ func (c *Container) provide(ctor interface{}, opts provideOptions) error {
538551
nodeOptions{
539552
ResultName: opts.Name,
540553
ResultGroup: opts.Group,
554+
Location: opts.Location,
541555
},
542556
)
543557
if err != nil {
@@ -742,6 +756,7 @@ type nodeOptions struct {
742756
// or belong to the specified value group
743757
ResultName string
744758
ResultGroup string
759+
Location *digreflect.Func
745760
}
746761

747762
func newNode(ctor interface{}, opts nodeOptions) (*node, error) {
@@ -765,10 +780,15 @@ func newNode(ctor interface{}, opts nodeOptions) (*node, error) {
765780
return nil, err
766781
}
767782

783+
location := opts.Location
784+
if location == nil {
785+
location = digreflect.InspectFunc(ctor)
786+
}
787+
768788
return &node{
769789
ctor: ctor,
770790
ctype: ctype,
771-
location: digreflect.InspectFunc(ctor),
791+
location: location,
772792
id: dot.CtorID(cptr),
773793
paramList: params,
774794
resultList: results,

dig_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,24 @@ func TestProvideGroupAndName(t *testing.T) {
15491549
"name:\"bar\" provided with group:\"foo\"")
15501550
}
15511551

1552+
type testStruct struct{}
1553+
1554+
func (testStruct) TestMethod(x int) float64 { return float64(x) }
1555+
1556+
func TestProvideLocation(t *testing.T) {
1557+
t.Parallel()
1558+
1559+
c := New()
1560+
err := c.Provide(func(x int) float64 {
1561+
return testStruct{}.TestMethod(x)
1562+
}, LocationForPC(reflect.TypeOf(testStruct{}).Method(0).Func.Pointer()))
1563+
require.NoError(t, err)
1564+
err = c.Invoke(func(y float64) {})
1565+
require.Error(t, err)
1566+
require.Contains(t, err.Error(), `"go.uber.org/dig".testStruct.TestMethod`)
1567+
require.Contains(t, err.Error(), `dig/dig_test.go`)
1568+
}
1569+
15521570
func TestCantProvideUntypedNil(t *testing.T) {
15531571
t.Parallel()
15541572
c := New()

internal/digreflect/func.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,18 @@ func (f *Func) Format(w fmt.State, c rune) {
6666
// function.
6767
func InspectFunc(function interface{}) *Func {
6868
fptr := reflect.ValueOf(function).Pointer()
69-
f := runtime.FuncForPC(fptr)
69+
return InspectFuncPC(fptr)
70+
}
71+
72+
// InspectFuncPC inspects and returns runtime information about the function
73+
// at the given program counter address.
74+
func InspectFuncPC(pc uintptr) *Func {
75+
f := runtime.FuncForPC(pc)
76+
if f == nil {
77+
return nil
78+
}
7079
pkgName, funcName := splitFuncName(f.Name())
71-
fileName, lineNum := f.FileLine(fptr)
80+
fileName, lineNum := f.FileLine(pc)
7281
return &Func{
7382
Name: funcName,
7483
Package: pkgName,

0 commit comments

Comments
 (0)