Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 75 additions & 10 deletions cmd/internal/migrations/v3/logger_generics.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ package v3

import (
"fmt"
"go/parser"
"go/token"
"regexp"
"strconv"
"strings"

semver "github.com/Masterminds/semver/v3"
"github.com/spf13/cobra"
Expand All @@ -11,18 +15,79 @@ import (
)

func MigrateLoggerGenerics(cmd *cobra.Command, cwd string, _, _ *semver.Version) error {
reAllLogger := regexp.MustCompile(`(\w+)\.AllLogger([^\w\[]|$)`)
reConfigurableLogger := regexp.MustCompile(`(\w+)\.ConfigurableLogger([^\w\[]|$)`)
reDefaultLogger := regexp.MustCompile(`(\w+)\.DefaultLogger\(\)`)
reSetLogger := regexp.MustCompile(`(\w+)\.SetLogger\(`)
reLoggerToWriter := regexp.MustCompile(`(\w+)\.LoggerToWriter\(`)
fiberImport := regexp.MustCompile(`^github\.com/gofiber/fiber/v\d+/log$`)
reLoggerType := regexp.MustCompile(`(?m)^func\s+(?:\(\w+\s+\*?\w+\)\s+)?Logger\(\)\s*(\*?\w+(?:\.\w+)*)\s*{`)

changed, err := internal.ChangeFileContent(cwd, func(content string) string {
content = reAllLogger.ReplaceAllString(content, `$1.AllLogger[any]$2`)
content = reConfigurableLogger.ReplaceAllString(content, `$1.ConfigurableLogger[any]$2`)
content = reDefaultLogger.ReplaceAllString(content, `$1.DefaultLogger[any]()`)
content = reSetLogger.ReplaceAllString(content, `$1.SetLogger[any](`)
content = reLoggerToWriter.ReplaceAllString(content, `$1.LoggerToWriter[any](`)
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "", content, parser.ImportsOnly)
if err != nil {
return content
}

aliases := make([]string, 0, len(file.Imports))
for _, imp := range file.Imports {
path, err := strconv.Unquote(imp.Path.Value)
if err != nil {
continue
}
if !fiberImport.MatchString(path) {
continue
}
alias := "log"
if imp.Name != nil {
alias = imp.Name.Name
} else if idx := strings.LastIndex(path, "/"); idx != -1 {
alias = path[idx+1:]
}
aliases = append(aliases, regexp.QuoteMeta(alias))
}
Comment on lines +37 to +44
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Skip dot-import and blank identifier aliases to avoid unusable matchers.

Dot-imports expose identifiers without alias and _ aliases are not referenced. Including them generates patterns that never match ((\.)\.AllLogger...). Filter them out.

-			alias := "log"
-			if imp.Name != nil {
-				alias = imp.Name.Name
+			alias := "log"
+			if imp.Name != nil {
+				// ignore dot-imports and blank identifier imports
+				if imp.Name.Name == "." || imp.Name.Name == "_" {
+					continue
+				}
+				alias = imp.Name.Name
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
alias := "log"
if imp.Name != nil {
alias = imp.Name.Name
} else if idx := strings.LastIndex(path, "/"); idx != -1 {
alias = path[idx+1:]
}
aliases = append(aliases, regexp.QuoteMeta(alias))
}
alias := "log"
if imp.Name != nil {
// ignore dot-imports and blank identifier imports
if imp.Name.Name == "." || imp.Name.Name == "_" {
continue
}
alias = imp.Name.Name
} else if idx := strings.LastIndex(path, "/"); idx != -1 {
alias = path[idx+1:]
}
aliases = append(aliases, regexp.QuoteMeta(alias))
🤖 Prompt for AI Agents
In cmd/internal/migrations/v3/logger_generics.go around lines 37 to 44, the
current logic adds aliases for imports but doesn't skip dot-imports (Name ==
".") or blank identifier aliases ("_"), which produces unusable regex fragments;
update the conditional so if imp.Name != nil and imp.Name.Name is "." or "_" you
continue/skip and do not derive or append an alias, otherwise keep the existing
logic to use imp.Name.Name or the last path segment and then append
regexp.QuoteMeta(alias).


if len(aliases) == 0 {
return content
}

aliasPattern := strings.Join(aliases, "|")
reAllLogger := regexp.MustCompile(fmt.Sprintf("(%s)\\.AllLogger([^\\w\\[]|$)", aliasPattern))
reConfigurableLogger := regexp.MustCompile(fmt.Sprintf("(%s)\\.ConfigurableLogger([^\\w\\[]|$)", aliasPattern))
reDefaultLogger := regexp.MustCompile(fmt.Sprintf("(%s)\\.DefaultLogger\\(\\)", aliasPattern))
reSetLogger := regexp.MustCompile(fmt.Sprintf("(%s)\\.SetLogger\\(", aliasPattern))
reLoggerToWriter := regexp.MustCompile(fmt.Sprintf("(%s)\\.LoggerToWriter\\(", aliasPattern))

loggerType := "any"
if m := reLoggerType.FindStringSubmatch(content); len(m) == 2 {
loggerType = m[1]
}

type replacement struct {
re *regexp.Regexp
format string
expectedSubmatch int
}

replacements := []replacement{
{reAllLogger, "%s.AllLogger[%s]%s", 3},
{reConfigurableLogger, "%s.ConfigurableLogger[%s]%s", 3},
{reDefaultLogger, "%s.DefaultLogger[%s]()", 2},
{reSetLogger, "%s.SetLogger[%s](", 2},
{reLoggerToWriter, "%s.LoggerToWriter[%s](", 2},
}

for _, r := range replacements {
content = r.re.ReplaceAllStringFunc(content, func(m string) string {
sub := r.re.FindStringSubmatch(m)
if len(sub) != r.expectedSubmatch {
return m
}

args := []any{sub[1], loggerType}
if len(sub) > 2 {
args = append(args, sub[2])
}

return fmt.Sprintf(r.format, args...)
})
}
return content
})
if err != nil {
Expand Down
82 changes: 76 additions & 6 deletions cmd/internal/migrations/v3/logger_generics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,53 @@ func Test_MigrateLoggerGenerics(t *testing.T) {

file := writeTempFile(t, dir, `package main
import (
"github.com/gofiber/fiber/v2/log"
)
var _ log.AllLogger = (*customLogger)(nil)
var _ log.ConfigurableLogger = (*customLogger)(nil)
func main() {
logger := log.DefaultLogger()
log.SetLogger(logger)
_ = log.LoggerToWriter(logger, log.LevelInfo)
}
`)

var buf bytes.Buffer
cmd := newCmd(&buf)
require.NoError(t, v3.MigrateLoggerGenerics(cmd, dir, nil, nil))

content := readFile(t, file)
assert.NotContains(t, content, "AllLogger =")
assert.NotContains(t, content, "ConfigurableLogger =")
assert.Contains(t, content, "AllLogger[any]")
assert.Contains(t, content, "ConfigurableLogger[any]")
assert.Contains(t, content, "log.DefaultLogger[any]()")
assert.Contains(t, content, "log.SetLogger[any](")
assert.Contains(t, content, "log.LoggerToWriter[any](")
assert.Contains(t, buf.String(), "Migrating logger generics")
}

func Test_MigrateLoggerGenerics_Zap(t *testing.T) {
t.Parallel()

dir, err := os.MkdirTemp("", "mloggenericstestzap")
require.NoError(t, err)
defer func() { require.NoError(t, os.RemoveAll(dir)) }()

file := writeTempFile(t, dir, `package main
import (
fiberlog "github.com/gofiber/fiber/v2/log"
"go.uber.org/zap"
)
type customLogger struct{}
func (c *customLogger) Logger() *zap.Logger { return nil }
var _ fiberlog.AllLogger = (*customLogger)(nil)
var _ fiberlog.ConfigurableLogger = (*customLogger)(nil)
func main() {
logger := fiberlog.DefaultLogger()
logger := &customLogger{}
fiberlog.SetLogger(logger)
_ = fiberlog.LoggerToWriter(logger, fiberlog.LevelInfo)
_ = fiberlog.DefaultLogger().Logger()
}
`)

Expand All @@ -38,10 +77,41 @@ func main() {
content := readFile(t, file)
assert.NotContains(t, content, "AllLogger =")
assert.NotContains(t, content, "ConfigurableLogger =")
assert.Contains(t, content, "AllLogger[any]")
assert.Contains(t, content, "ConfigurableLogger[any]")
assert.Contains(t, content, "DefaultLogger[any]()")
assert.Contains(t, content, "SetLogger[any](")
assert.Contains(t, content, "LoggerToWriter[any](")
assert.Contains(t, content, "AllLogger[*zap.Logger]")
assert.Contains(t, content, "ConfigurableLogger[*zap.Logger]")
assert.Contains(t, content, "SetLogger[*zap.Logger](")
assert.Contains(t, content, "LoggerToWriter[*zap.Logger](")
assert.Contains(t, content, "DefaultLogger[*zap.Logger]()")
assert.Contains(t, buf.String(), "Migrating logger generics")
}

func Test_MigrateLoggerGenerics_SkipNonFiber(t *testing.T) {
t.Parallel()

dir, err := os.MkdirTemp("", "mloggenericstestskip")
require.NoError(t, err)
defer func() { require.NoError(t, os.RemoveAll(dir)) }()

file := writeTempFile(t, dir, `package main
import (
otherlog "example.com/other/log"
)
var _ otherlog.AllLogger = (*customLogger)(nil)
var _ otherlog.ConfigurableLogger = (*customLogger)(nil)
func main() {
logger := otherlog.DefaultLogger()
otherlog.SetLogger(logger)
_ = otherlog.LoggerToWriter(logger, otherlog.LevelInfo)
}
`)

var buf bytes.Buffer
cmd := newCmd(&buf)
require.NoError(t, v3.MigrateLoggerGenerics(cmd, dir, nil, nil))

content := readFile(t, file)
assert.Contains(t, content, "otherlog.DefaultLogger()")
assert.NotContains(t, content, "AllLogger[")
assert.NotContains(t, content, "ConfigurableLogger[")
assert.NotContains(t, buf.String(), "Migrating logger generics")
}
Loading