Repository: magefile/mage
Pull request #166 (2018-09-17 01:36:12)

Fix bug #165 - check returns val vs. nil before cast to error
Reflection is hard :) . When you call reflect.Value.Interface(), it might return a nil value, which can't convert to the error interface, even though it's supposed to be returning an error.   Annoying, but not too hard to fix.


Patch:
diff --git a/.travis.yml b/.travis.yml
index 94463a5..f7e0703 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,6 +8,7 @@ branches:
 # In theory, older versions would probably work fine, but since this isn't a 
 # library, I'm not going to worry about older versions for now.
 go:
+  - tip
   - 1.11.x
   - 1.10.x
   - 1.9.x
diff --git a/mg/deps.go b/mg/deps.go
index 3a8b9d4..3b72fe8 100644
--- a/mg/deps.go
+++ b/mg/deps.go
@@ -309,7 +309,11 @@ func funcTypeWrap(t funcType, fn interface{}) func(context.Context) error {
 		return func(context.Context) error {
 			v := reflect.ValueOf(fn)
 			ret := v.Call(args)
-			return ret[0].Interface().(error)
+			val := ret[0].Interface()
+			if val == nil {
+				return nil
+			}
+			return val.(error)
 		}
 	case namespaceContextVoidType:
 		return func(ctx context.Context) error {
@@ -321,7 +325,11 @@ func funcTypeWrap(t funcType, fn interface{}) func(context.Context) error {
 		return func(ctx context.Context) error {
 			v := reflect.ValueOf(fn)
 			ret := v.Call(append(args, reflect.ValueOf(ctx)))
-			return ret[0].Interface().(error)
+			val := ret[0].Interface()
+			if val == nil {
+				return nil
+			}
+			return val.(error)
 		}
 	default:
 		panic(fmt.Errorf("Don't know how to deal with dep of type %T", fn))


Test patch:
diff --git a/mage/main_test.go b/mage/main_test.go
index 13b0178..686ab25 100644
--- a/mage/main_test.go
+++ b/mage/main_test.go
@@ -781,15 +781,16 @@ func minorVer(t *testing.T, v string) int {
 
 func TestNamespaceDep(t *testing.T) {
 	stdout := &bytes.Buffer{}
+	stderr := &bytes.Buffer{}
 	inv := Invocation{
 		Dir:    "./testdata/namespaces",
-		Stderr: ioutil.Discard,
+		Stderr: stderr,
 		Stdout: stdout,
 		Args:   []string{"TestNamespaceDep"},
 	}
 	code := Invoke(inv)
 	if code != 0 {
-		t.Fatalf("expected 0, but got %v", code)
+		t.Fatalf("expected 0, but got %v, stderr:\n%s", code, stderr)
 	}
 	expected := "hi!\n"
 	if stdout.String() != expected {
@@ -803,7 +804,7 @@ func TestNamespace(t *testing.T) {
 		Dir:    "./testdata/namespaces",
 		Stderr: ioutil.Discard,
 		Stdout: stdout,
-		Args:   []string{"namespace:SayHi"},
+		Args:   []string{"ns:error"},
 	}
 	code := Invoke(inv)
 	if code != 0 {
diff --git a/mage/testdata/namespaces/magefile.go b/mage/testdata/namespaces/magefile.go
index d7b1b4b..a080d12 100644
--- a/mage/testdata/namespaces/magefile.go
+++ b/mage/testdata/namespaces/magefile.go
@@ -3,17 +3,28 @@
 package main
 
 import (
+	"context"
 	"fmt"
 
 	"github.com/magefile/mage/mg"
 )
 
 func TestNamespaceDep() {
-	mg.Deps(Namespace.SayHi)
+	mg.Deps(NS.Error, NS.Bare, NS.BareCtx, NS.CtxErr)
 }
 
-type Namespace mg.Namespace
+type NS mg.Namespace
 
-func (Namespace) SayHi() {
+func (NS) Error() error {
 	fmt.Println("hi!")
+	return nil
+}
+
+func (NS) Bare() {
+}
+
+func (NS) BareCtx(ctx context.Context) {
+}
+func (NS) CtxErr(ctx context.Context) error {
+	return nil
 }