Support embedded fields in go-marshal.
PiperOrigin-RevId: 334437990
This commit is contained in:
parent
7d64bc1fdf
commit
44c7d55074
|
@ -113,3 +113,18 @@ The following are some guidelines for modifying the `go_marshal` tool:
|
|||
- No runtime reflection in the code generated for the marshallable interface.
|
||||
The entire point of the tool is to avoid runtime reflection. The generated
|
||||
tests may use reflection.
|
||||
|
||||
## Debugging
|
||||
|
||||
To enable debugging output from the go-marshal tool, use one of the following
|
||||
options, depending on how go-marshal is being invoked:
|
||||
|
||||
- Pass `--define gomarshal=verbose` to the bazel command. Note that this can
|
||||
generate a lot of output depending on what's being compiled, as this will
|
||||
enable debugging for all packages built by the command.
|
||||
|
||||
- Set `marshal_debug = True` on the top-level `go_library` BUILD rule.
|
||||
|
||||
- Set `debug = True` on the `go_marshal` BUILD rule.
|
||||
|
||||
- Pass `-debug` to the go-marshal tool invocation.
|
||||
|
|
|
@ -43,8 +43,8 @@ type interfaceGenerator struct {
|
|||
// of t's interfaces.
|
||||
ms map[string]struct{}
|
||||
|
||||
// as records embedded fields in t that are potentially not packed. The key
|
||||
// is the accessor for the field.
|
||||
// as records fields in t that are potentially not packed. The key is the
|
||||
// accessor for the field.
|
||||
as map[string]struct{}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,12 +50,6 @@ func (g *interfaceGenerator) areFieldsPackedExpression() (string, bool) {
|
|||
// checks are done ahead of time and in one place so we can make assumptions
|
||||
// later.
|
||||
func (g *interfaceGenerator) validateStruct(ts *ast.TypeSpec, st *ast.StructType) {
|
||||
forEachStructField(st, func(f *ast.Field) {
|
||||
if len(f.Names) == 0 {
|
||||
g.abortAt(f.Pos(), "Cannot marshal structs with embedded fields, give the field a name; use '_' for anonymous fields such as padding fields")
|
||||
}
|
||||
})
|
||||
|
||||
forEachStructField(st, func(f *ast.Field) {
|
||||
fieldDispatcher{
|
||||
primitive: func(_, t *ast.Ident) {
|
||||
|
@ -101,7 +95,7 @@ func (g *interfaceGenerator) emitMarshallableForStruct(st *ast.StructType) {
|
|||
var dynamicSizeTerms []string
|
||||
|
||||
forEachStructField(st, fieldDispatcher{
|
||||
primitive: func(n, t *ast.Ident) {
|
||||
primitive: func(_, t *ast.Ident) {
|
||||
if size, dynamic := g.scalarSize(t); !dynamic {
|
||||
primitiveSize += size
|
||||
} else {
|
||||
|
@ -109,13 +103,13 @@ func (g *interfaceGenerator) emitMarshallableForStruct(st *ast.StructType) {
|
|||
dynamicSizeTerms = append(dynamicSizeTerms, fmt.Sprintf("(*%s)(nil).SizeBytes()", t.Name))
|
||||
}
|
||||
},
|
||||
selector: func(n, tX, tSel *ast.Ident) {
|
||||
selector: func(_, tX, tSel *ast.Ident) {
|
||||
tName := fmt.Sprintf("%s.%s", tX.Name, tSel.Name)
|
||||
g.recordUsedImport(tX.Name)
|
||||
g.recordUsedMarshallable(tName)
|
||||
dynamicSizeTerms = append(dynamicSizeTerms, fmt.Sprintf("(*%s)(nil).SizeBytes()", tName))
|
||||
},
|
||||
array: func(n *ast.Ident, a *ast.ArrayType, t *ast.Ident) {
|
||||
array: func(_ *ast.Ident, a *ast.ArrayType, t *ast.Ident) {
|
||||
lenExpr := g.arrayLenExpr(a)
|
||||
if size, dynamic := g.scalarSize(t); !dynamic {
|
||||
dynamicSizeTerms = append(dynamicSizeTerms, fmt.Sprintf("%d*%s", size, lenExpr))
|
||||
|
|
|
@ -79,7 +79,7 @@ type fieldDispatcher struct {
|
|||
}
|
||||
|
||||
// Precondition: All dispatch callbacks that will be invoked must be
|
||||
// provided. Embedded fields are not allowed, len(f.Names) >= 1.
|
||||
// provided.
|
||||
func (fd fieldDispatcher) dispatch(f *ast.Field) {
|
||||
// Each field declaration may actually be multiple declarations of the same
|
||||
// type. For example, consider:
|
||||
|
@ -88,12 +88,24 @@ func (fd fieldDispatcher) dispatch(f *ast.Field) {
|
|||
// x, y, z int
|
||||
// }
|
||||
//
|
||||
// We invoke the call-backs once per such instance. Embedded fields are not
|
||||
// allowed, and results in a panic.
|
||||
// We invoke the call-backs once per such instance.
|
||||
|
||||
// Handle embedded fields. Embedded fields have no names, but can be
|
||||
// referenced by the type name.
|
||||
if len(f.Names) < 1 {
|
||||
panic("Precondition not met: attempted to dispatch on embedded field")
|
||||
switch v := f.Type.(type) {
|
||||
case *ast.Ident:
|
||||
fd.primitive(v, v)
|
||||
case *ast.SelectorExpr:
|
||||
fd.selector(v.Sel, v.X.(*ast.Ident), v.Sel)
|
||||
default:
|
||||
// Note: Arrays can't be embedded, which is handled here.
|
||||
panic(fmt.Sprintf("Attempted to dispatch on embedded field of unsupported kind: %#v", f.Type))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Non-embedded field.
|
||||
for _, name := range f.Names {
|
||||
switch v := f.Type.(type) {
|
||||
case *ast.Ident:
|
||||
|
|
|
@ -174,3 +174,27 @@ type Type9 struct {
|
|||
x int64
|
||||
y [sizeA]int32
|
||||
}
|
||||
|
||||
// Type10Embed is a test data type which is be embedded into another type.
|
||||
//
|
||||
// +marshal
|
||||
type Type10Embed struct {
|
||||
x int64
|
||||
}
|
||||
|
||||
// Type10 is a test data type which contains an embedded struct.
|
||||
//
|
||||
// +marshal
|
||||
type Type10 struct {
|
||||
Type10Embed
|
||||
y int64
|
||||
}
|
||||
|
||||
// Type11 is a test data type which contains an embedded struct from an external
|
||||
// package.
|
||||
//
|
||||
// +marshal
|
||||
type Type11 struct {
|
||||
ex.External
|
||||
y int64
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue