recognize named map and slice types

master
Felix Lange 8 years ago
parent 1fd1f70537
commit 788895d3d8
  1. 21
      genmethod.go
  2. 10
      internal/tests/mapconv/input.go
  3. 66
      internal/tests/mapconv/output.go
  4. 10
      internal/tests/sliceconv/input.go
  5. 66
      internal/tests/sliceconv/output.go
  6. 4
      main.go
  7. 56
      typeutil.go

@ -212,7 +212,7 @@ func (m *marshalMethod) marshalConversions(from, to Var, format string) (s []Sta
}
func (m *marshalMethod) convert(from, to Expression, fromtyp, totyp types.Type) (s []Statement) {
// Remove pointer introduced by ensurePointer during field building.
// Remove pointer introduced by ensureNilCheckable during field building.
if isPointer(fromtyp) && !isPointer(totyp) {
from = Star{Value: from}
fromtyp = fromtyp.(*types.Pointer).Elem()
@ -220,22 +220,20 @@ func (m *marshalMethod) convert(from, to Expression, fromtyp, totyp types.Type)
from = AddressOf{Value: from}
fromtyp = types.NewPointer(fromtyp)
}
// Generate the conversion.
qf := m.mtyp.scope.qualify
switch {
case types.ConvertibleTo(fromtyp, totyp):
s = append(s, Assign{Lhs: to, Rhs: simpleConv(from, fromtyp, totyp, qf)})
case isSlice(fromtyp):
fromElem := fromtyp.(*types.Slice).Elem()
toElem := totyp.(*types.Slice).Elem()
case underlyingSlice(fromtyp) != nil:
fromElem := underlyingSlice(fromtyp).Elem()
toElem := underlyingSlice(totyp).Elem()
s = append(s, Assign{Lhs: to, Rhs: makeExpr(totyp, from, qf)})
s = append(s, m.loopConv(from, to, intType, intType, fromElem, toElem))
case isMap(fromtyp):
fromKey, fromElem := fromtyp.(*types.Map).Key(), fromtyp.(*types.Map).Elem()
toKey, toElem := totyp.(*types.Map).Key(), totyp.(*types.Map).Elem()
case underlyingMap(fromtyp) != nil:
fromMap, toMap := underlyingMap(fromtyp), underlyingMap(totyp)
s = append(s, Assign{Lhs: to, Rhs: makeExpr(totyp, from, qf)})
s = append(s, m.loopConv(from, to, fromKey, toKey, fromElem, toElem))
s = append(s, m.loopConv(from, to, fromMap.Key(), toMap.Key(), fromMap.Elem(), toMap.Elem()))
default:
invalidConv(fromtyp, totyp, qf)
}
@ -243,14 +241,13 @@ func (m *marshalMethod) convert(from, to Expression, fromtyp, totyp types.Type)
}
func (m *marshalMethod) loopConv(from, to Expression, fromKey, toKey, fromElem, toElem types.Type) Statement {
qf := m.scope.parent.qualify
return Range{
Key: m.iterKey,
Value: m.iterVal,
RangeValue: from,
Body: []Statement{Assign{
Lhs: Index{Value: to, Index: simpleConv(m.iterKey, fromKey, toKey, qf)},
Rhs: simpleConv(m.iterVal, fromElem, toElem, qf),
Lhs: Index{Value: to, Index: simpleConv(m.iterKey, fromKey, toKey, m.scope.parent.qualify)},
Rhs: simpleConv(m.iterVal, fromElem, toElem, m.scope.parent.qualify),
}},
}
}

@ -10,10 +10,16 @@ type replacedString string
type replacedInt int
type namedMap map[string]int
type namedMap2 map[replacedString]replacedInt
type X struct {
M map[string]int
Map map[string]int
Named namedMap
}
type Xo struct {
M map[replacedString]replacedInt
Map map[replacedString]replacedInt
Named namedMap2
}

@ -9,31 +9,44 @@ import (
func (x *X) MarshalJSON() ([]byte, error) {
type XJSON struct {
M map[replacedString]replacedInt
Map map[replacedString]replacedInt
Named namedMap2
}
var enc XJSON
enc.M = make(map[replacedString]replacedInt, len(x.M))
for k, v := range x.M {
enc.M[replacedString(k)] = replacedInt(v)
enc.Map = make(map[replacedString]replacedInt, len(x.Map))
for k, v := range x.Map {
enc.Map[replacedString(k)] = replacedInt(v)
}
enc.Named = make(namedMap2, len(x.Named))
for k, v := range x.Named {
enc.Named[replacedString(k)] = replacedInt(v)
}
return json.Marshal(&enc)
}
func (x *X) UnmarshalJSON(input []byte) error {
type XJSON struct {
M map[replacedString]replacedInt
Map map[replacedString]replacedInt
Named namedMap2
}
var dec XJSON
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
var x0 X
if dec.M == nil {
return errors.New("missing required field m for X")
if dec.Map == nil {
return errors.New("missing required field map for X")
}
x0.Map = make(map[string]int, len(dec.Map))
for k, v := range dec.Map {
x0.Map[string(k)] = int(v)
}
if dec.Named == nil {
return errors.New("missing required field named for X")
}
x0.M = make(map[string]int, len(dec.M))
for k, v := range dec.M {
x0.M[string(k)] = int(v)
x0.Named = make(namedMap, len(dec.Named))
for k, v := range dec.Named {
x0.Named[string(k)] = int(v)
}
*x = x0
return nil
@ -41,31 +54,44 @@ func (x *X) UnmarshalJSON(input []byte) error {
func (x *X) MarshalYAML() (interface{}, error) {
type XYAML struct {
M map[replacedString]replacedInt
Map map[replacedString]replacedInt
Named namedMap2
}
var enc XYAML
enc.M = make(map[replacedString]replacedInt, len(x.M))
for k, v := range x.M {
enc.M[replacedString(k)] = replacedInt(v)
enc.Map = make(map[replacedString]replacedInt, len(x.Map))
for k, v := range x.Map {
enc.Map[replacedString(k)] = replacedInt(v)
}
enc.Named = make(namedMap2, len(x.Named))
for k, v := range x.Named {
enc.Named[replacedString(k)] = replacedInt(v)
}
return &enc, nil
}
func (x *X) UnmarshalYAML(unmarshal func(interface{}) error) error {
type XYAML struct {
M map[replacedString]replacedInt
Map map[replacedString]replacedInt
Named namedMap2
}
var dec XYAML
if err := unmarshal(&dec); err != nil {
return err
}
var x0 X
if dec.M == nil {
return errors.New("missing required field m for X")
if dec.Map == nil {
return errors.New("missing required field map for X")
}
x0.Map = make(map[string]int, len(dec.Map))
for k, v := range dec.Map {
x0.Map[string(k)] = int(v)
}
if dec.Named == nil {
return errors.New("missing required field named for X")
}
x0.M = make(map[string]int, len(dec.M))
for k, v := range dec.M {
x0.M[string(k)] = int(v)
x0.Named = make(namedMap, len(dec.Named))
for k, v := range dec.Named {
x0.Named[string(k)] = int(v)
}
*x = x0
return nil

@ -8,10 +8,16 @@ package sliceconv
type replacedInt int
type namedSlice []int
type namedSlice2 []replacedInt
type X struct {
S []int
Slice []int
Named namedSlice
}
type Xo struct {
S []replacedInt
Slice []replacedInt
Named namedSlice2
}

@ -9,31 +9,44 @@ import (
func (x *X) MarshalJSON() ([]byte, error) {
type XJSON struct {
S []replacedInt
Slice []replacedInt
Named namedSlice2
}
var enc XJSON
enc.S = make([]replacedInt, len(x.S))
for k, v := range x.S {
enc.S[k] = replacedInt(v)
enc.Slice = make([]replacedInt, len(x.Slice))
for k, v := range x.Slice {
enc.Slice[k] = replacedInt(v)
}
enc.Named = make(namedSlice2, len(x.Named))
for k, v := range x.Named {
enc.Named[k] = replacedInt(v)
}
return json.Marshal(&enc)
}
func (x *X) UnmarshalJSON(input []byte) error {
type XJSON struct {
S []replacedInt
Slice []replacedInt
Named namedSlice2
}
var dec XJSON
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
var x0 X
if dec.S == nil {
return errors.New("missing required field s for X")
if dec.Slice == nil {
return errors.New("missing required field slice for X")
}
x0.Slice = make([]int, len(dec.Slice))
for k, v := range dec.Slice {
x0.Slice[k] = int(v)
}
if dec.Named == nil {
return errors.New("missing required field named for X")
}
x0.S = make([]int, len(dec.S))
for k, v := range dec.S {
x0.S[k] = int(v)
x0.Named = make(namedSlice, len(dec.Named))
for k, v := range dec.Named {
x0.Named[k] = int(v)
}
*x = x0
return nil
@ -41,31 +54,44 @@ func (x *X) UnmarshalJSON(input []byte) error {
func (x *X) MarshalYAML() (interface{}, error) {
type XYAML struct {
S []replacedInt
Slice []replacedInt
Named namedSlice2
}
var enc XYAML
enc.S = make([]replacedInt, len(x.S))
for k, v := range x.S {
enc.S[k] = replacedInt(v)
enc.Slice = make([]replacedInt, len(x.Slice))
for k, v := range x.Slice {
enc.Slice[k] = replacedInt(v)
}
enc.Named = make(namedSlice2, len(x.Named))
for k, v := range x.Named {
enc.Named[k] = replacedInt(v)
}
return &enc, nil
}
func (x *X) UnmarshalYAML(unmarshal func(interface{}) error) error {
type XYAML struct {
S []replacedInt
Slice []replacedInt
Named namedSlice2
}
var dec XYAML
if err := unmarshal(&dec); err != nil {
return err
}
var x0 X
if dec.S == nil {
return errors.New("missing required field s for X")
if dec.Slice == nil {
return errors.New("missing required field slice for X")
}
x0.Slice = make([]int, len(dec.Slice))
for k, v := range dec.Slice {
x0.Slice[k] = int(v)
}
if dec.Named == nil {
return errors.New("missing required field named for X")
}
x0.S = make([]int, len(dec.S))
for k, v := range dec.S {
x0.S[k] = int(v)
x0.Named = make(namedSlice, len(dec.Named))
for k, v := range dec.Named {
x0.Named[k] = int(v)
}
*x = x0
return nil

@ -284,7 +284,7 @@ func newMarshalerType(fs *token.FileSet, imp types.Importer, typ *types.Named) *
}
mf := &marshalerField{
name: f.Name(),
typ: ensurePointer(f.Type()),
typ: ensureNilCheckable(f.Type()),
origTyp: f.Type(),
tag: styp.Tag(i),
}
@ -312,7 +312,7 @@ func (mtyp *marshalerType) loadOverrides(otypename string, otyp *types.Struct) e
if err := checkConvertible(of.Type(), f.origTyp); err != nil {
return fmt.Errorf("%v: invalid field override: %v", mtyp.fs.Position(of.Pos()), err)
}
f.typ = ensurePointer(of.Type())
f.typ = ensureNilCheckable(of.Type())
}
mtyp.scope.addReferences(otyp)
return nil

@ -66,42 +66,66 @@ func isPointer(typ types.Type) bool {
return ok
}
func isSlice(typ types.Type) bool {
_, ok := typ.(*types.Slice)
return ok
func underlyingSlice(typ types.Type) *types.Slice {
for {
switch typ.(type) {
case *types.Named:
typ = typ.Underlying()
case *types.Slice:
return typ.(*types.Slice)
default:
return nil
}
}
}
func isMap(typ types.Type) bool {
_, ok := typ.(*types.Map)
return ok
func underlyingMap(typ types.Type) *types.Map {
for {
switch typ.(type) {
case *types.Named:
typ = typ.Underlying()
case *types.Map:
return typ.(*types.Map)
default:
return nil
}
}
}
func ensurePointer(typ types.Type) types.Type {
if isPointer(typ) || isSlice(typ) || isMap(typ) {
return typ
func ensureNilCheckable(typ types.Type) types.Type {
orig := typ
for {
switch typ.(type) {
case *types.Named:
typ = typ.Underlying()
case *types.Map, *types.Slice, *types.Pointer:
return orig
default:
return types.NewPointer(orig)
}
}
return types.NewPointer(typ)
}
// checkConvertible determines whether values of type from can be converted to type to. It
// returns nil if convertible and a descriptive error otherwise.
// See package documentation for this definition of 'convertible'.
func checkConvertible(from, to types.Type) error {
if types.ConvertibleTo(from, to) {
return nil
}
// Slices.
sfrom, fromIsSlice := from.(*types.Slice)
sto, toIsSlice := to.(*types.Slice)
if fromIsSlice && toIsSlice {
sfrom := underlyingSlice(from)
sto := underlyingSlice(to)
if sfrom != nil && sto != nil {
if !types.ConvertibleTo(sfrom.Elem(), sto.Elem()) {
return fmt.Errorf("slice element type %s is not convertible to %s", sfrom.Elem(), sto.Elem())
}
return nil
}
// Maps.
mfrom, fromIsMap := from.(*types.Map)
mto, toIsMap := to.(*types.Map)
if fromIsMap && toIsMap {
mfrom := underlyingMap(from)
mto := underlyingMap(to)
if mfrom != nil && mto != nil {
if !types.ConvertibleTo(mfrom.Key(), mto.Key()) {
return fmt.Errorf("map key type %s is not convertible to %s", mfrom.Key(), mto.Key())
}

Loading…
Cancel
Save