Skip to content

Commit

Permalink
feat: simplify rules for receiver with type parameters
Browse files Browse the repository at this point in the history
Fix #38

Signed-off-by: Timon Wong <[email protected]>
  • Loading branch information
timonwong committed Sep 1, 2022
1 parent d2fadc4 commit 6fbae4d
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 8 deletions.
52 changes: 48 additions & 4 deletions internal/rules/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,52 @@ func (rs *Ruleset) Match(fn *types.Func) bool {

func emptyQualifier(*types.Package) string { return "" }

func receiverTypeOf(recvType types.Type) string {
buf := bytebufferpool.Get()
defer bytebufferpool.Put(buf)

var recvElemType *types.Named
switch recvType := recvType.(type) {
case *types.Pointer:
buf.WriteByte('*')
if elem, ok := recvType.Elem().(*types.Named); ok {
recvElemType = elem
}
case *types.Named:
recvElemType = recvType
}

if recvElemType == nil {
buf.Reset() // Reset in case we have modifications
types.WriteType(buf, recvType, emptyQualifier)
return buf.String()
}

buf.WriteString(recvElemType.Obj().Name())
typeParams := recvElemType.TypeParams()
typeParamsLen := typeParams.Len()

if typeParamsLen > 0 {
buf.WriteByte('[')
for i := 0; i < typeParamsLen; i++ {
if i > 0 {
// comma as separator
buf.WriteByte(',')
}
p := typeParams.At(i)
if p == nil {
// Just in case
buf.WriteString("nil")
continue
}
buf.WriteString(p.Obj().Name())
}
buf.WriteByte(']')
}

return buf.String()
}

func matchRule(p *FuncRule, sig *types.Signature) bool {
// we do not check package import here since it's already checked in Match()
recv := sig.Recv()
Expand All @@ -55,10 +101,8 @@ func matchRule(p *FuncRule, sig *types.Signature) bool {

if isReceiver {
recvType := recv.Type()
recvTypeBuf := bytebufferpool.Get()
defer bytebufferpool.Put(recvTypeBuf)
types.WriteType(recvTypeBuf, recvType, emptyQualifier)
if recvTypeBuf.String() != p.ReceiverType {
receiverType := receiverTypeOf(recvType)
if receiverType != p.ReceiverType {
return false
}
}
Expand Down
2 changes: 1 addition & 1 deletion testdata/custom-rules-generic.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# Generic methods
(*a/custom-generic.Logger[any]).Infow
(*a/custom-generic.Logger[T,W]).Infow
17 changes: 14 additions & 3 deletions testdata/src/a/custom-generic/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,28 @@ package custom_generic

import "go.uber.org/zap"

type Logger[T any] struct {
type Signed interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}

type Unsigned interface {
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}

type Integer interface {
Signed | Unsigned
}
type Logger[T, W Integer] struct {
*zap.SugaredLogger
}

func (l *Logger[T]) Infow(message string, keysAndValues ...any) {
func (l *Logger[T, W]) Infow(message string, keysAndValues ...any) {
l.SugaredLogger.Infow(message, keysAndValues...)
}

func ExampleGenericCustomOnly() {
l := zap.NewExample().Sugar()
logger := &Logger[any]{l}
logger := &Logger[int8, int]{l}

logger.Infow("custom message", "hello") // want `odd number of arguments passed as key-value pairs for logging`
logger.Debugw("embedded sugar log also works without custom rules", "key1") // want `odd number of arguments passed as key-value pairs for logging`
Expand Down

0 comments on commit 6fbae4d

Please sign in to comment.