xerrors: check for nil pointers in As's target

As already checks for a nil or non-pointer target; check for nil pointers
as well. Produces a better error message and catches incorrect target
parameters even when the target doesn't match the error.

Change-Id: I06efb4b78f640b17076f002519d275b328b42557
Reviewed-on: https://go-review.googlesource.com/c/161657
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/wrap.go b/wrap.go
index 2462d3d..3e71058 100644
--- a/wrap.go
+++ b/wrap.go
@@ -79,14 +79,15 @@
 	if target == nil {
 		panic("errors: target cannot be nil")
 	}
-	typ := reflect.TypeOf(target)
-	if typ.Kind() != reflect.Ptr {
-		panic("errors: target must be a pointer")
+	val := reflect.ValueOf(target)
+	typ := val.Type()
+	if typ.Kind() != reflect.Ptr || val.IsNil() {
+		panic("errors: target must be a non-nil pointer")
 	}
 	targetType := typ.Elem()
 	for {
 		if reflect.TypeOf(err).AssignableTo(targetType) {
-			reflect.ValueOf(target).Elem().Set(reflect.ValueOf(err))
+			val.Elem().Set(reflect.ValueOf(err))
 			return true
 		}
 		if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) {
diff --git a/wrap_test.go b/wrap_test.go
index 2380190..ef6a9ed 100644
--- a/wrap_test.go
+++ b/wrap_test.go
@@ -150,6 +150,27 @@
 	}
 }
 
+func TestAsValidation(t *testing.T) {
+	testCases := []interface{}{
+		nil,
+		(*int)(nil),
+		"error",
+	}
+	err := xerrors.New("error")
+	for _, tc := range testCases {
+		t.Run(fmt.Sprintf("%T(%v)", tc, tc), func(t *testing.T) {
+			defer func() {
+				recover()
+			}()
+			if xerrors.As(err, tc) {
+				t.Errorf("As(err, %T(%v)) = true, want false", tc, tc)
+				return
+			}
+			t.Errorf("As(err, %T(%v)) did not panic", tc, tc)
+		})
+	}
+}
+
 func TestUnwrap(t *testing.T) {
 	err1 := xerrors.New("1")
 	erra := xerrors.Errorf("wrap 2: %w", err1)