From a39aa2d6d38af3f5cfd7c4150bd9df364029d9cf Mon Sep 17 00:00:00 2001 From: a2not <31874975+a2not@users.noreply.github.com> Date: Sat, 8 Nov 2025 11:04:48 +0900 Subject: [PATCH 1/4] fix(codegen): preserve *assert.CollectT in Require Update replace funcmap to replace "assert." with "require." while preserving "*assert.CollectT" references. This prevents unintended replacement of pointer references to CollectT, ensuring correct code generation. Signed-off-by: a2not <31874975+a2not@users.noreply.github.com> --- _codegen/main.go | 16 +++++++++++++--- require/require.go | 4 ++-- require/require.go.tmpl | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/_codegen/main.go b/_codegen/main.go index 574985181..fa07f1d33 100644 --- a/_codegen/main.go +++ b/_codegen/main.go @@ -106,9 +106,7 @@ func parseTemplates() (*template.Template, *template.Template, error) { } funcTemplate = string(f) } - tmpl, err := template.New("function").Funcs(template.FuncMap{ - "replace": strings.ReplaceAll, - }).Parse(funcTemplate) + tmpl, err := template.New("function").Parse(funcTemplate) if err != nil { return nil, nil, err } @@ -298,6 +296,18 @@ func (f *testFunc) CommentWithoutT(receiver string) string { return strings.Replace(f.Comment(), search, replace, -1) } +func (f *testFunc) Replace(comment, search, replace string) string { + // replace strings, but preserve "*assert.CollectT" + const ( + assertCollectT = "*assert.CollectT" + assertCollectTPlaceholder = "__COLLECT_T_PLACEHOLDER__" + ) + + protected := strings.ReplaceAll(comment, assertCollectT, assertCollectTPlaceholder) + result := strings.ReplaceAll(protected, search, replace) + return strings.ReplaceAll(result, assertCollectTPlaceholder, assertCollectT) +} + // Standard header https://go.dev/s/generatedcode. var headerTemplate = `// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT. diff --git a/require/require.go b/require/require.go index 23a3be780..b57d6f8bc 100644 --- a/require/require.go +++ b/require/require.go @@ -429,7 +429,7 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t // time.Sleep(8*time.Second) // externalValue = true // }() -// require.EventuallyWithT(t, func(c *require.CollectT) { +// require.EventuallyWithT(t, func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // require.True(c, externalValue, "expected 'externalValue' to be true") // }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") @@ -457,7 +457,7 @@ func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitF // time.Sleep(8*time.Second) // externalValue = true // }() -// require.EventuallyWithTf(t, func(c *require.CollectT, "error message %s", "formatted") { +// require.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { // // add assertions as needed; any assertion failure will fail the current tick // require.True(c, externalValue, "expected 'externalValue' to be true") // }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") diff --git a/require/require.go.tmpl b/require/require.go.tmpl index 8b3283685..d4c236c5c 100644 --- a/require/require.go.tmpl +++ b/require/require.go.tmpl @@ -1,4 +1,4 @@ -{{ replace .Comment "assert." "require."}} +{{ .Replace .Comment "assert." "require."}} func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } From 4bbb9c92ab43c663efcb1964bf9cb9c5a17bb513 Mon Sep 17 00:00:00 2001 From: a2not <31874975+a2not@users.noreply.github.com> Date: Sat, 8 Nov 2025 12:36:04 +0900 Subject: [PATCH 2/4] refactor(codegen): generalize identifier preservation logic Refactored the Replace function in _codegen/main.go to support preserving multiple identifiers, not just "*assert.CollectT". Updated usage in require/require.go to reflect new placeholder format. This improves extensibility and maintainability for future identifier additions. Signed-off-by: a2not <31874975+a2not@users.noreply.github.com> --- _codegen/main.go | 30 +++++++++++++++++++++--------- require/require.go | 4 ++-- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/_codegen/main.go b/_codegen/main.go index fa07f1d33..63fe000a2 100644 --- a/_codegen/main.go +++ b/_codegen/main.go @@ -297,15 +297,27 @@ func (f *testFunc) CommentWithoutT(receiver string) string { } func (f *testFunc) Replace(comment, search, replace string) string { - // replace strings, but preserve "*assert.CollectT" - const ( - assertCollectT = "*assert.CollectT" - assertCollectTPlaceholder = "__COLLECT_T_PLACEHOLDER__" - ) - - protected := strings.ReplaceAll(comment, assertCollectT, assertCollectTPlaceholder) - result := strings.ReplaceAll(protected, search, replace) - return strings.ReplaceAll(result, assertCollectTPlaceholder, assertCollectT) + // replace strings, while preserving some identifiers + identifiersToBePreserved := []string{ + "*assert.CollectT", + } + placeholderFromIdentifier := func(ident string) string { + return fmt.Sprintf("__%s_PLACEHOLDER___", ident) + } + + for _, ident := range identifiersToBePreserved { + placeholder := placeholderFromIdentifier(ident) + comment = strings.ReplaceAll(comment, ident, placeholder) + } + + comment = strings.ReplaceAll(comment, search, replace) + + for _, ident := range identifiersToBePreserved { + placeholder := placeholderFromIdentifier(ident) + comment = strings.ReplaceAll(comment, placeholder, ident) + } + + return comment } // Standard header https://go.dev/s/generatedcode. diff --git a/require/require.go b/require/require.go index b57d6f8bc..071138ebc 100644 --- a/require/require.go +++ b/require/require.go @@ -429,7 +429,7 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t // time.Sleep(8*time.Second) // externalValue = true // }() -// require.EventuallyWithT(t, func(c *assert.CollectT) { +// require.EventuallyWithT(t, func(c __*require.CollectT_PLACEHOLDER___) { // // add assertions as needed; any assertion failure will fail the current tick // require.True(c, externalValue, "expected 'externalValue' to be true") // }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") @@ -457,7 +457,7 @@ func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitF // time.Sleep(8*time.Second) // externalValue = true // }() -// require.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { +// require.EventuallyWithTf(t, func(c __*require.CollectT_PLACEHOLDER___, "error message %s", "formatted") { // // add assertions as needed; any assertion failure will fail the current tick // require.True(c, externalValue, "expected 'externalValue' to be true") // }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") From 187957dc693a10c9fe498e8a2dcf04ee4ee22081 Mon Sep 17 00:00:00 2001 From: a2not <31874975+a2not@users.noreply.github.com> Date: Sat, 8 Nov 2025 12:39:56 +0900 Subject: [PATCH 3/4] fix(codegen): correct placeholder casing for preserved identifiers Update placeholder generation to use uppercase and replace dots with underscores, ensuring consistency and preventing accidental replacement of preserved identifiers. Also update documentation examples in require.go to use correct types. Signed-off-by: a2not <31874975+a2not@users.noreply.github.com> --- _codegen/main.go | 3 ++- require/require.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/_codegen/main.go b/_codegen/main.go index 63fe000a2..bf54651bd 100644 --- a/_codegen/main.go +++ b/_codegen/main.go @@ -302,7 +302,8 @@ func (f *testFunc) Replace(comment, search, replace string) string { "*assert.CollectT", } placeholderFromIdentifier := func(ident string) string { - return fmt.Sprintf("__%s_PLACEHOLDER___", ident) + // assuming none of the identifiers to be replaced is ALL CAPS + return fmt.Sprintf("__%s_PLACEHOLDER___", strings.ToUpper(strings.ReplaceAll(ident, ".", "_"))) } for _, ident := range identifiersToBePreserved { diff --git a/require/require.go b/require/require.go index 071138ebc..b57d6f8bc 100644 --- a/require/require.go +++ b/require/require.go @@ -429,7 +429,7 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t // time.Sleep(8*time.Second) // externalValue = true // }() -// require.EventuallyWithT(t, func(c __*require.CollectT_PLACEHOLDER___) { +// require.EventuallyWithT(t, func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // require.True(c, externalValue, "expected 'externalValue' to be true") // }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") @@ -457,7 +457,7 @@ func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitF // time.Sleep(8*time.Second) // externalValue = true // }() -// require.EventuallyWithTf(t, func(c __*require.CollectT_PLACEHOLDER___, "error message %s", "formatted") { +// require.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { // // add assertions as needed; any assertion failure will fail the current tick // require.True(c, externalValue, "expected 'externalValue' to be true") // }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") From ef2c4029084a2d3add18b39857e18d54d0818369 Mon Sep 17 00:00:00 2001 From: a2not <31874975+a2not@users.noreply.github.com> Date: Tue, 11 Nov 2025 18:39:45 +0900 Subject: [PATCH 4/4] fix(codegen): preserve identifier without pointer in Replace Remove pointer from "assert.CollectT" in identifiersToBePreserved to ensure correct string replacement behavior in testFunc.Replace. This prevents unintended placeholder substitution for pointer types. Signed-off-by: a2not <31874975+a2not@users.noreply.github.com> --- _codegen/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_codegen/main.go b/_codegen/main.go index bf54651bd..deb6b768a 100644 --- a/_codegen/main.go +++ b/_codegen/main.go @@ -299,7 +299,7 @@ func (f *testFunc) CommentWithoutT(receiver string) string { func (f *testFunc) Replace(comment, search, replace string) string { // replace strings, while preserving some identifiers identifiersToBePreserved := []string{ - "*assert.CollectT", + "assert.CollectT", } placeholderFromIdentifier := func(ident string) string { // assuming none of the identifiers to be replaced is ALL CAPS