diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index f4bd2b27..a2dc268a 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -5,9 +5,13 @@ package reflect_test import ( + "io" "math" . "reflect" + "strings" "testing" + "time" + "unsafe" ) type Basic struct { @@ -184,5 +188,107 @@ func TestDeepEqualComplexStructInequality(t *testing.T) { } } +type T struct { + a int + b float64 + c string + d *int +} + +var _i = 7 + +func TestIsZero(t *testing.T) { + for i, tt := range []struct { + x interface{} + want bool + }{ + // Booleans + {true, false}, + {false, true}, + // Numeric types + {int(0), true}, + {int(1), false}, + {int8(0), true}, + {int8(1), false}, + {int16(0), true}, + {int16(1), false}, + {int32(0), true}, + {int32(1), false}, + {int64(0), true}, + {int64(1), false}, + {uint(0), true}, + {uint(1), false}, + {uint8(0), true}, + {uint8(1), false}, + {uint16(0), true}, + {uint16(1), false}, + {uint32(0), true}, + {uint32(1), false}, + {uint64(0), true}, + {uint64(1), false}, + {float32(0), true}, + {float32(1.2), false}, + {float64(0), true}, + {float64(1.2), false}, + {math.Copysign(0, -1), false}, + {complex64(0), true}, + {complex64(1.2), false}, + {complex128(0), true}, + {complex128(1.2), false}, + {complex(math.Copysign(0, -1), 0), false}, + {complex(0, math.Copysign(0, -1)), false}, + {complex(math.Copysign(0, -1), math.Copysign(0, -1)), false}, + {uintptr(0), true}, + {uintptr(128), false}, + // Array + {[5]string{"", "", "", "", ""}, true}, + {[5]string{}, true}, + {[5]string{"", "", "", "a", ""}, false}, + // Chan + {(chan string)(nil), true}, + {make(chan string), false}, + {time.After(1), false}, + // Func + {(func())(nil), true}, + {New, false}, + // Interface + {New(TypeOf(new(error)).Elem()).Elem(), true}, + {(io.Reader)(strings.NewReader("")), false}, + // Map + {(map[string]string)(nil), true}, + {map[string]string{}, false}, + {make(map[string]string), false}, + // Pointer + {(*func())(nil), true}, + {(*int)(nil), true}, + {new(int), false}, + // Slice + {[]string{}, false}, + {([]string)(nil), true}, + {make([]string, 0), false}, + // Strings + {"", true}, + {"not-zero", false}, + // Structs + {T{}, true}, + {T{123, 456.75, "hello", &_i}, false}, + // UnsafePointer + {(unsafe.Pointer)(nil), true}, + {(unsafe.Pointer)(new(int)), false}, + } { + var x Value + if v, ok := tt.x.(Value); ok { + x = v + } else { + x = ValueOf(tt.x) + } + + b := x.IsZero() + if b != tt.want { + t.Errorf("%d: IsZero((%s)(%+v)) = %t, want %t", i, x.Kind(), tt.x, b, tt.want) + } + } +} + type MyBytes []byte type MyByte byte diff --git a/src/reflect/value.go b/src/reflect/value.go index e00863d0..8d2000b0 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1,6 +1,7 @@ package reflect import ( + "math" "unsafe" ) @@ -91,6 +92,46 @@ func (v Value) Type() Type { return v.typecode } +// IsZero reports whether v is the zero value for its type. +// It panics if the argument is invalid. +func (v Value) IsZero() bool { + switch v.Kind() { + case Bool: + return !v.Bool() + case Int, Int8, Int16, Int32, Int64: + return v.Int() == 0 + case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + return v.Uint() == 0 + case Float32, Float64: + return math.Float64bits(v.Float()) == 0 + case Complex64, Complex128: + c := v.Complex() + return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0 + case Array: + for i := 0; i < v.Len(); i++ { + if !v.Index(i).IsZero() { + return false + } + } + return true + case Chan, Func, Interface, Map, Pointer, Slice, UnsafePointer: + return v.IsNil() + case String: + return v.Len() == 0 + case Struct: + for i := 0; i < v.NumField(); i++ { + if !v.Field(i).IsZero() { + return false + } + } + return true + default: + // This should never happens, but will act as a safeguard for + // later, as a default value doesn't makes sense here. + panic(&ValueError{"reflect.Value.IsZero", v.Kind()}) + } +} + // Internal function only, do not use. // // RawType returns the raw, underlying type code. It is used in the runtime @@ -107,7 +148,7 @@ func (v Value) Kind() Kind { // is not a channel, map, pointer, function, slice, or interface. func (v Value) IsNil() bool { switch v.Kind() { - case Chan, Map, Ptr: + case Chan, Map, Ptr, UnsafePointer: return v.pointer() == nil case Func: if v.value == nil {