diff --git a/spew/dump.go b/spew/dump.go index 02d4c9d..983d23f 100644 --- a/spew/dump.go +++ b/spew/dump.go @@ -367,6 +367,12 @@ func (d *dumpState) dump(v reflect.Value) { // been handled above. case reflect.Map: + // nil maps should be indicated as different than empty maps + if v.IsNil() { + d.w.Write(nilAngleBytes) + break + } + d.w.Write(openBraceNewlineBytes) d.depth++ if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { diff --git a/spew/dump_test.go b/spew/dump_test.go index f1a5644..9e0e65f 100644 --- a/spew/dump_test.go +++ b/spew/dump_test.go @@ -547,6 +547,7 @@ func addMapDumpTests() { klen := fmt.Sprintf("%d", len(k)) // not kLen to shut golint up kkLen := fmt.Sprintf("%d", len(kk)) mLen := fmt.Sprintf("%d", len(m)) + nilMap := map[string]int(nil) nm := (*map[string]int)(nil) pm := &m mAddr := fmt.Sprintf("%p", pm) @@ -566,6 +567,7 @@ func addMapDumpTests() { addDumpTest(&pm, "(**"+mt+")("+pmAddr+"->"+mAddr+")("+ms+")\n", "(**"+mt+")("+pmAddr+"->"+mAddr+")("+ms2+")\n") addDumpTest(nm, "(*"+mt+")()\n") + addDumpTest(nilMap, "("+mt+") \n") // Map with custom formatter type on pointer receiver only keys and vals. k2 := pstringer("one") @@ -574,6 +576,7 @@ func addMapDumpTests() { k2Len := fmt.Sprintf("%d", len(k2)) v2Len := fmt.Sprintf("%d", len(v2)) m2Len := fmt.Sprintf("%d", len(m2)) + nilMap2 := map[pstringer]pstringer(nil) nm2 := (*map[pstringer]pstringer)(nil) pm2 := &m2 m2Addr := fmt.Sprintf("%p", pm2) @@ -587,12 +590,14 @@ func addMapDumpTests() { addDumpTest(pm2, "(*"+m2t+")("+m2Addr+")("+m2s+")\n") addDumpTest(&pm2, "(**"+m2t+")("+pm2Addr+"->"+m2Addr+")("+m2s+")\n") addDumpTest(nm2, "(*"+m2t+")()\n") + addDumpTest(nilMap2, "("+m2t+") \n") // Map with interface keys and values. k3 := "one" k3Len := fmt.Sprintf("%d", len(k3)) m3 := map[interface{}]interface{}{k3: 1} m3Len := fmt.Sprintf("%d", len(m3)) + nilMap3 := map[interface{}]interface{}(nil) nm3 := (*map[interface{}]interface{})(nil) pm3 := &m3 m3Addr := fmt.Sprintf("%p", pm3) @@ -606,12 +611,14 @@ func addMapDumpTests() { addDumpTest(pm3, "(*"+m3t+")("+m3Addr+")("+m3s+")\n") addDumpTest(&pm3, "(**"+m3t+")("+pm3Addr+"->"+m3Addr+")("+m3s+")\n") addDumpTest(nm3, "(*"+m3t+")()\n") + addDumpTest(nilMap3, "("+m3t+") \n") // Map with nil interface value. k4 := "nil" k4Len := fmt.Sprintf("%d", len(k4)) m4 := map[string]interface{}{k4: nil} m4Len := fmt.Sprintf("%d", len(m4)) + nilMap4 := map[string]interface{}(nil) nm4 := (*map[string]interface{})(nil) pm4 := &m4 m4Addr := fmt.Sprintf("%p", pm4) @@ -625,6 +632,7 @@ func addMapDumpTests() { addDumpTest(pm4, "(*"+m4t+")("+m4Addr+")("+m4s+")\n") addDumpTest(&pm4, "(**"+m4t+")("+pm4Addr+"->"+m4Addr+")("+m4s+")\n") addDumpTest(nm4, "(*"+m4t+")()\n") + addDumpTest(nilMap4, "("+m4t+") \n") } func addStructDumpTests() { diff --git a/spew/format.go b/spew/format.go index b6b1fb0..cc152ae 100644 --- a/spew/format.go +++ b/spew/format.go @@ -296,6 +296,12 @@ func (f *formatState) format(v reflect.Value) { // been handled above. case reflect.Map: + // nil maps should be indicated as different than empty maps + if v.IsNil() { + f.fs.Write(nilAngleBytes) + break + } + f.fs.Write(openMapBytes) f.depth++ if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { diff --git a/spew/format_test.go b/spew/format_test.go index 80c5ef9..4dd0ac2 100644 --- a/spew/format_test.go +++ b/spew/format_test.go @@ -762,6 +762,7 @@ func addInterfaceFormatterTests() { func addMapFormatterTests() { // Map with string keys and int vals. v := map[string]int{"one": 1, "two": 2} + nilMap := map[string]int(nil) nv := (*map[string]int)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) @@ -772,21 +773,25 @@ func addMapFormatterTests() { addFormatterTest("%v", v, vs, vs2) addFormatterTest("%v", pv, "<*>"+vs, "<*>"+vs2) addFormatterTest("%v", &pv, "<**>"+vs, "<**>"+vs2) + addFormatterTest("%+v", nilMap, "") addFormatterTest("%+v", nv, "") addFormatterTest("%+v", v, vs, vs2) addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs, "<*>("+vAddr+")"+vs2) addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs, "<**>("+pvAddr+"->"+vAddr+")"+vs2) + addFormatterTest("%+v", nilMap, "") addFormatterTest("%+v", nv, "") addFormatterTest("%#v", v, "("+vt+")"+vs, "("+vt+")"+vs2) addFormatterTest("%#v", pv, "(*"+vt+")"+vs, "(*"+vt+")"+vs2) addFormatterTest("%#v", &pv, "(**"+vt+")"+vs, "(**"+vt+")"+vs2) + addFormatterTest("%#v", nilMap, "("+vt+")"+"") addFormatterTest("%#v", nv, "(*"+vt+")"+"") addFormatterTest("%#+v", v, "("+vt+")"+vs, "("+vt+")"+vs2) addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs, "(*"+vt+")("+vAddr+")"+vs2) addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs2) + addFormatterTest("%#+v", nilMap, "("+vt+")"+"") addFormatterTest("%#+v", nv, "(*"+vt+")"+"") // Map with custom formatter type on pointer receiver only keys and vals.