Repository: uber-go/dig
Pull request #332 (2022-03-15 21:58:40)

Bubble location from LocationForPC's ProvideOption up to errors
Previously, if a location was passed to Provide via the
LocationForPC ProvideOption, the location would not be taken
into account in the case of an error where a constructor could
not be Provided.

This change updates the error returned, making it more readable by
basing the error message on alternate function provided by
LocationForPC rather than the constructor.

For example, the following error:
```
Failed: cannot provide function "reflect".makeFuncStub (/usr/local/Cellar/go/1.17.6/libexec/src/reflect/asm_amd64.s:30):
cannot provide *fx_test.a[name="a"] from [0].Field0: already provided by
"go.uber.org/fx_test".TestAnnotate.func1 (/gocode/src/github.com/uber-go/fx/annotated_test.go:515)
```

Becomes:
```
Failed: cannot provide function "go.uber.org/fx_test".TestAnnotate.func1 (/gocode/src/github.com/uber-go/fx/annotated_test.go:515):
cannot provide *fx_test.a[name="a"] from [0].Field0: already provided by
"go.uber.org/fx_test".TestAnnotate.func1 (/gocode/src/github.com/uber-go/fx/annotated_test.go:515)
```

codecov[bot]: # [Codecov](https://codecov.io/gh/uber-go/dig/pull/332?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uber-go) Report
> Merging [#332](https://codecov.io/gh/uber-go/dig/pull/332?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uber-go) (d8735f5) into [master](https://codecov.io/gh/uber-go/dig/commit/1988af94c5751c46572a001fcb4cf9f4c4e04fda?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uber-go) (1988af9) will **decrease** coverage by `0.13%`.
> The diff coverage is `66.66%`.

```diff
@@            Coverage Diff             @@
##           master     #332      +/-   ##
==========================================
- Coverage   98.17%   98.04%   -0.14%     
==========================================
  Files          21       21              
  Lines        1428     1433       +5     
==========================================
+ Hits         1402     1405       +3     
- Misses         16       17       +1     
- Partials       10       11       +1     
```


| [Impacted Files](https://codecov.io/gh/uber-go/dig/pull/332?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uber-go) | Coverage Δ | |
|---|---|---|
| [provide.go](https://codecov.io/gh/uber-go/dig/pull/332/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uber-go#diff-cHJvdmlkZS5nbw==) | `98.91% <66.66%> (-1.09%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/uber-go/dig/pull/332?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uber-go).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uber-go)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/uber-go/dig/pull/332?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uber-go). Last update [1988af9...d8735f5](https://codecov.io/gh/uber-go/dig/pull/332?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uber-go). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uber-go).


Patch:
diff --git a/provide.go b/provide.go
index a706637..3f5937e 100644
--- a/provide.go
+++ b/provide.go
@@ -417,8 +417,15 @@ func (s *Scope) Provide(constructor interface{}, opts ...ProvideOption) error {
 	}
 
 	if err := s.provide(constructor, options); err != nil {
+		var errFunc *digreflect.Func
+		if options.Location == nil {
+			errFunc = digreflect.InspectFunc(constructor)
+		} else {
+			errFunc = options.Location
+		}
+
 		return errProvide{
-			Func:   digreflect.InspectFunc(constructor),
+			Func:   errFunc,
 			Reason: err,
 		}
 	}


Test patch:
diff --git a/dig_test.go b/dig_test.go
index 2709466..4761f83 100644
--- a/dig_test.go
+++ b/dig_test.go
@@ -2417,6 +2417,30 @@ func testProvideFailures(t *testing.T, dryRun bool) {
 		assert.Contains(t, err.Error(), "cannot provide io.Reader")
 		assert.Contains(t, err.Error(), "already provided")
 	})
+
+	t.Run("error should refer to location given by LocationForPC ProvideOption", func(t *testing.T) {
+		c := digtest.New(t)
+		type A struct{ idx int }
+		type ret struct {
+			dig.Out
+
+			A1 A // same type A provided twice
+			A2 A
+		}
+
+		locationFn := func() {}
+
+		err := c.Provide(func() ret {
+			return ret{
+				A1: A{idx: 1},
+				A2: A{idx: 2},
+			}
+		}, dig.LocationForPC(reflect.ValueOf(locationFn).Pointer()))
+		require.Error(t, err, "provide must return error")
+		dig.AssertErrorMatches(t, err,
+			`cannot provide function "go.uber.org/dig_test".testProvideFailures.func\d+.1`,
+		)
+	})
 }
 
 func TestInvokeFailures(t *testing.T) {
@@ -3515,6 +3539,20 @@ func TestEndToEndSuccessWithAliases(t *testing.T) {
 		)
 	})
 
+	t.Run("duplicate provide with LocationForPC", func(t *testing.T) {
+		c := digtest.New(t)
+		c.RequireProvide(func(x int) float64 {
+			return testStruct{}.TestMethod(x)
+		}, dig.LocationForPC(reflect.TypeOf(testStruct{}).Method(0).Func.Pointer()))
+		err := c.Provide(func(x int) float64 {
+			return testStruct{}.TestMethod(x)
+		}, dig.LocationForPC(reflect.TypeOf(testStruct{}).Method(0).Func.Pointer()))
+
+		require.Error(t, err)
+		require.Contains(t, err.Error(), `cannot provide function "go.uber.org/dig_test".testStruct.TestMethod`)
+		require.Contains(t, err.Error(), `already provided by "go.uber.org/dig_test".testStruct.TestMethod`)
+	})
+
 	t.Run("named instances", func(t *testing.T) {
 		c := digtest.New(t)
 		type A1 struct{ s string }