Skip to content

go generate struct as tuple failed to pack elements count when allownil #395

@zjubird

Description

@zjubird

For a user defined struct as follows


//go:generate msgp

//msgp:tuple User

type User struct {

	ID       []byte `msgpack:"id,allownil"`

	Name     string `msgpack:"name"`

	Email    string `msgpack:"email"`

	IsActive bool   `msgpack:"is_active"`
}

The ID is tagged as allownil

i want to pack it like tuples, so i use msgp:tuple directive

after i execute go generate i get a MarshalMsg As Follows


// MarshalMsg implements msgp.Marshaler
func (z *User) MarshalMsg(b []byte) (o []byte, err error) {
	o = msgp.Require(b, z.Msgsize())
	// array header, size 4
	if z.ID == nil { // allownil: if nil
		o = msgp.AppendNil(o)
	} else {
		o = append(o, 0x94)
		o = msgp.AppendBytes(o, z.ID)
	}
	o = msgp.AppendString(o, z.Name)
	o = msgp.AppendString(o, z.Email)
	o = msgp.AppendBool(o, z.IsActive)
	return
}

Just as you can see, only when ID is not nil, the number of elements 0x94 is packed into the results.
But if ID is nil, then the number of elements is not packed, which is obviously wrong.

I think the problem lies in gen/marshal.go

func (m *marshalGen) tuple(s *Struct) {
	data := make([]byte, 0, 5)
	data = msgp.AppendArrayHeader(data, uint32(len(s.Fields)))
	m.p.printf("\n// array header, size %d", len(s.Fields))
	m.Fuse(data)

	if len(s.Fields) == 0 {
		m.fuseHook()
		return
	}
	for i := range s.Fields {
		if !m.p.ok() {
			return
		}
		fieldElem := s.Fields[i].FieldElem
		anField := s.Fields[i].HasTagPart("allownil") && fieldElem.AllowNil()
		if anField {
			m.p.printf("\nif %s { // allownil: if nil", fieldElem.IfZeroExpr())
			m.p.printf("\no = msgp.AppendNil(o)")
			m.p.printf("\n} else {")
		}
		m.ctx.PushString(s.Fields[i].FieldName)
		SetIsAllowNil(fieldElem, anField)
		next(m, fieldElem)
		m.ctx.Pop()
		if anField {
			m.p.printf("\n}") // close if statement
		}
	}
}

if i change code snippet

	m.Fuse(data)

	if len(s.Fields) == 0 {
		m.fuseHook()
		return
	}

to

	m.Fuse(data)
        m.fuseHook()
	if len(s.Fields) == 0 {	
		return
	}

it works! The generated MarshalMsg is as follows

// MarshalMsg implements msgp.Marshaler
func (z *User) MarshalMsg(b []byte) (o []byte, err error) {
	o = msgp.Require(b, z.Msgsize())
	// array header, size 4
	o = append(o, 0x94)
	if z.ID == nil { // allownil: if nil
		o = msgp.AppendNil(o)
	} else {
		o = msgp.AppendBytes(o, z.ID)
	}
	o = msgp.AppendString(o, z.Name)
	o = msgp.AppendString(o, z.Email)
	o = msgp.AppendBool(o, z.IsActive)
	return
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions