Repository: uber-go/dig
Pull request #393 (2023-08-02 21:35:52)

Build value group values using their original scope
We call simple providers with their original scope. ([ref](https://github.com/uber-go/dig/blob/master/param.go#L287)) This allows decorators to be applied even if the constructor is exported via `dig.Export(true)`.

However, we call value group value providers with whatever scope we found them in: ([ref](https://github.com/uber-go/dig/blob/master/param.go#L609))

This has the consequence of causing exported value group value providers within a module to not be able to be decorated by decorators within the same module, when using `dig.Export(true)`, unlike their simple provider counterpart.

For example ([playground](https://go.dev/play/p/R2uqaTvT57P)):
```go
type Foo struct{}
type FooResults struct {
	dig.Out

	Foo Foo `group:"foos"`
}

func NewFoo(s string) FooResults {
	fmt.Printf("String in NewFoo: %q\n", s)
	return FooResults{
		Foo: Foo{},
	}
}

type Bar struct{}

func NewBar(s string) Bar {
	fmt.Printf("String in NewBar: %q\n", s)
	return Bar{}
}

type UseFooAndBarParams struct {
	dig.In

	Foos []Foo `group:"foos"`
	Bar  Bar
}

func UseFooAndBar(UseFooAndBarParams) {}

func main() {
	c := dig.New()
	c.Provide(func() string { return "base" })
	child := c.Scope("child")
	child.Decorate(func(s string) string {
		return s + "-decorated"
	})
	child.Provide(NewFoo, dig.Export(true))
	child.Provide(NewBar, dig.Export(true))
}

// Output:
// String in NewFoo: "base"
// String in NewBar: "base-decorated"
```

Since we use `dig.Export(true)` by default in Fx, this is the default behavior for value group providers, see https://github.com/uber-go/fx/issues/1104

This commit changes `callGroupProviders` to use the value group provider's original scope as well, and adds a test to verify the behavior is fixed.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/uber-go/dig/pull/393?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uber-go) Report
> Merging [#393](https://app.codecov.io/gh/uber-go/dig/pull/393?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uber-go) (cc4ce88) into [master](https://app.codecov.io/gh/uber-go/dig/commit/a30081d72915995cb8f934ba9393256c74df9143?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uber-go) (a30081d) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##           master     #393   +/-   ##
=======================================
  Coverage   98.39%   98.39%           
=======================================
  Files          22       22           
  Lines        1492     1492           
=======================================
  Hits         1468     1468           
  Misses         15       15           
  Partials        9        9           
```


| [Files Changed](https://app.codecov.io/gh/uber-go/dig/pull/393?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uber-go) | Coverage Δ | |
|---|---|---|
| [param.go](https://app.codecov.io/gh/uber-go/dig/pull/393?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uber-go#diff-cGFyYW0uZ28=) | `97.34% <100.00%> (ø)` | |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uber-go)


Patch:
diff --git a/param.go b/param.go
index d584fc2..ca06897 100644
--- a/param.go
+++ b/param.go
@@ -606,7 +606,7 @@ func (pt paramGroupedSlice) callGroupProviders(c containerStore) (int, error) {
 		providers := c.getGroupProviders(pt.Group, pt.Type.Elem())
 		itemCount += len(providers)
 		for _, n := range providers {
-			if err := n.Call(c); err != nil {
+			if err := n.Call(n.OrigScope()); err != nil {
 				return 0, errParamGroupFailed{
 					CtorID: n.ID(),
 					Key:    key{group: pt.Group, t: pt.Type.Elem()},


Test patch:
diff --git a/dig_test.go b/dig_test.go
index f85241b..120083a 100644
--- a/dig_test.go
+++ b/dig_test.go
@@ -37,6 +37,51 @@ import (
 	"go.uber.org/dig/internal/digtest"
 )
 
+func TestValueGroupValuesGetProperlyDecorated(t *testing.T) {
+	type Foo struct{}
+	type FooResults struct {
+		dig.Out
+
+		Foo Foo `group:"foos"`
+	}
+	type Bar struct{}
+	type UseBarAndFooParams struct {
+		dig.In
+
+		Foos []Foo `group:"foos"`
+		Bar  Bar
+	}
+
+	var FooCtrRun, BarCtrRun bool
+	c := digtest.New(t)
+	c.RequireProvide(func() string { return "base" })
+
+	child := c.Scope("child")
+	child.RequireDecorate(func(s string) string {
+		return s + "-decorated"
+	})
+	child.RequireProvide(func(s string) FooResults {
+		FooCtrRun = true
+		// Previously, this would have just been "base",
+		// because we weren't looking in the constructor's
+		// original scope for decorators to apply
+		// when the value is a value group value.
+		assert.Equal(t, "base-decorated", s)
+		return FooResults{
+			Foo: Foo{},
+		}
+	}, dig.Export(true))
+	child.RequireProvide(func(s string) Bar {
+		BarCtrRun = true
+		assert.Equal(t, "base-decorated", s)
+		return Bar{}
+	}, dig.Export(true))
+	child.RequireInvoke(func(UseBarAndFooParams) {})
+
+	assert.True(t, FooCtrRun)
+	assert.True(t, BarCtrRun)
+}
+
 func TestEndToEndSuccess(t *testing.T) {
 	t.Parallel()