Blob Blame History Raw
From 4ae2a9cd07f77baff2d8007186d334313e5fc01b Mon Sep 17 00:00:00 2001
From: Elliott Sales de Andrade <quantum.analyst@gmail.com>
Date: Mon, 18 Feb 2019 01:17:44 -0500
Subject: [PATCH] Fix build against Go 1.12.

Backport #183 and #193, effectively.

Signed-off-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
---
 stack.go      |  26 +++++++------
 stack_test.go | 104 ++++++++++++++++++++------------------------------
 2 files changed, 57 insertions(+), 73 deletions(-)

diff --git a/stack.go b/stack.go
index 2874a04..4db50c7 100644
--- a/stack.go
+++ b/stack.go
@@ -5,6 +5,7 @@ import (
 	"io"
 	"path"
 	"runtime"
+	"strconv"
 	"strings"
 )
 
@@ -37,6 +38,15 @@ func (f Frame) line() int {
 	return line
 }
 
+// name returns the name of this function, if known.
+func (f Frame) name() string {
+	fn := runtime.FuncForPC(f.pc())
+	if fn == nil {
+		return "unknown"
+	}
+	return fn.Name()
+}
+
 // Format formats the frame according to the fmt.Formatter interface.
 //
 //    %s    source file
@@ -54,22 +64,16 @@ func (f Frame) Format(s fmt.State, verb rune) {
 	case 's':
 		switch {
 		case s.Flag('+'):
-			pc := f.pc()
-			fn := runtime.FuncForPC(pc)
-			if fn == nil {
-				io.WriteString(s, "unknown")
-			} else {
-				file, _ := fn.FileLine(pc)
-				fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
-			}
+			io.WriteString(s, f.name())
+			io.WriteString(s, "\n\t")
+			io.WriteString(s, f.file())
 		default:
 			io.WriteString(s, path.Base(f.file()))
 		}
 	case 'd':
-		fmt.Fprintf(s, "%d", f.line())
+		io.WriteString(s, strconv.Itoa(f.line()))
 	case 'n':
-		name := runtime.FuncForPC(f.pc()).Name()
-		io.WriteString(s, funcname(name))
+		io.WriteString(s, funcname(f.name()))
 	case 'v':
 		f.Format(s, 's')
 		io.WriteString(s, ":")
diff --git a/stack_test.go b/stack_test.go
index 85fc419..bc1b3fb 100644
--- a/stack_test.go
+++ b/stack_test.go
@@ -6,51 +6,18 @@ import (
 	"testing"
 )
 
-var initpc, _, _, _ = runtime.Caller(0)
-
-func TestFrameLine(t *testing.T) {
-	var tests = []struct {
-		Frame
-		want int
-	}{{
-		Frame(initpc),
-		9,
-	}, {
-		func() Frame {
-			var pc, _, _, _ = runtime.Caller(0)
-			return Frame(pc)
-		}(),
-		20,
-	}, {
-		func() Frame {
-			var pc, _, _, _ = runtime.Caller(1)
-			return Frame(pc)
-		}(),
-		28,
-	}, {
-		Frame(0), // invalid PC
-		0,
-	}}
-
-	for _, tt := range tests {
-		got := tt.Frame.line()
-		want := tt.want
-		if want != got {
-			t.Errorf("Frame(%v): want: %v, got: %v", uintptr(tt.Frame), want, got)
-		}
-	}
-}
+var initpc = caller()
 
 type X struct{}
 
+// val returns a Frame pointing to itself.
 func (x X) val() Frame {
-	var pc, _, _, _ = runtime.Caller(0)
-	return Frame(pc)
+	return caller()
 }
 
+// ptr returns a Frame pointing to itself.
 func (x *X) ptr() Frame {
-	var pc, _, _, _ = runtime.Caller(0)
-	return Frame(pc)
+	return caller()
 }
 
 func TestFrameFormat(t *testing.T) {
@@ -59,32 +26,32 @@ func TestFrameFormat(t *testing.T) {
 		format string
 		want   string
 	}{{
-		Frame(initpc),
+		initpc,
 		"%s",
 		"stack_test.go",
 	}, {
-		Frame(initpc),
+		initpc,
 		"%+s",
 		"github.com/pkg/errors.init\n" +
 			"\t.+/github.com/pkg/errors/stack_test.go",
 	}, {
-		Frame(0),
+		0,
 		"%s",
 		"unknown",
 	}, {
-		Frame(0),
+		0,
 		"%+s",
 		"unknown",
 	}, {
-		Frame(initpc),
+		initpc,
 		"%d",
 		"9",
 	}, {
-		Frame(0),
+		0,
 		"%d",
 		"0",
 	}, {
-		Frame(initpc),
+		initpc,
 		"%n",
 		"init",
 	}, {
@@ -102,20 +69,20 @@ func TestFrameFormat(t *testing.T) {
 		"%n",
 		"X.val",
 	}, {
-		Frame(0),
+		0,
 		"%n",
 		"",
 	}, {
-		Frame(initpc),
+		initpc,
 		"%v",
 		"stack_test.go:9",
 	}, {
-		Frame(initpc),
+		initpc,
 		"%+v",
 		"github.com/pkg/errors.init\n" +
 			"\t.+/github.com/pkg/errors/stack_test.go:9",
 	}, {
-		Frame(0),
+		0,
 		"%v",
 		"unknown:0",
 	}}
@@ -153,24 +120,24 @@ func TestStackTrace(t *testing.T) {
 	}{{
 		New("ooh"), []string{
 			"github.com/pkg/errors.TestStackTrace\n" +
-				"\t.+/github.com/pkg/errors/stack_test.go:154",
+				"\t.+/github.com/pkg/errors/stack_test.go:121",
 		},
 	}, {
 		Wrap(New("ooh"), "ahh"), []string{
 			"github.com/pkg/errors.TestStackTrace\n" +
-				"\t.+/github.com/pkg/errors/stack_test.go:159", // this is the stack of Wrap, not New
+				"\t.+/github.com/pkg/errors/stack_test.go:126", // this is the stack of Wrap, not New
 		},
 	}, {
 		Cause(Wrap(New("ooh"), "ahh")), []string{
 			"github.com/pkg/errors.TestStackTrace\n" +
-				"\t.+/github.com/pkg/errors/stack_test.go:164", // this is the stack of New
+				"\t.+/github.com/pkg/errors/stack_test.go:131", // this is the stack of New
 		},
 	}, {
-		func() error { return New("ooh") }(), []string{
+		func() error { noinline(); return New("ooh") }(), []string{
 			`github.com/pkg/errors.(func·009|TestStackTrace.func1)` +
-				"\n\t.+/github.com/pkg/errors/stack_test.go:169", // this is the stack of New
+				"\n\t.+/github.com/pkg/errors/stack_test.go:136", // this is the stack of New
 			"github.com/pkg/errors.TestStackTrace\n" +
-				"\t.+/github.com/pkg/errors/stack_test.go:169", // this is the stack of New's caller
+				"\t.+/github.com/pkg/errors/stack_test.go:136", // this is the stack of New's caller
 		},
 	}, {
 		Cause(func() error {
@@ -179,11 +146,11 @@ func TestStackTrace(t *testing.T) {
 			}()
 		}()), []string{
 			`github.com/pkg/errors.(func·010|TestStackTrace.func2.1)` +
-				"\n\t.+/github.com/pkg/errors/stack_test.go:178", // this is the stack of Errorf
+				"\n\t.+/github.com/pkg/errors/stack_test.go:145", // this is the stack of Errorf
 			`github.com/pkg/errors.(func·011|TestStackTrace.func2)` +
-				"\n\t.+/github.com/pkg/errors/stack_test.go:179", // this is the stack of Errorf's caller
+				"\n\t.+/github.com/pkg/errors/stack_test.go:146", // this is the stack of Errorf's caller
 			"github.com/pkg/errors.TestStackTrace\n" +
-				"\t.+/github.com/pkg/errors/stack_test.go:180", // this is the stack of Errorf's caller's caller
+				"\t.+/github.com/pkg/errors/stack_test.go:147", // this is the stack of Errorf's caller's caller
 		},
 	}}
 	for i, tt := range tests {
@@ -253,22 +220,35 @@ func TestStackTraceFormat(t *testing.T) {
 	}, {
 		stackTrace()[:2],
 		"%v",
-		`\[stack_test.go:207 stack_test.go:254\]`,
+		`\[stack_test.go:174 stack_test.go:221\]`,
 	}, {
 		stackTrace()[:2],
 		"%+v",
 		"\n" +
 			"github.com/pkg/errors.stackTrace\n" +
-			"\t.+/github.com/pkg/errors/stack_test.go:207\n" +
+			"\t.+/github.com/pkg/errors/stack_test.go:174\n" +
 			"github.com/pkg/errors.TestStackTraceFormat\n" +
-			"\t.+/github.com/pkg/errors/stack_test.go:258",
+			"\t.+/github.com/pkg/errors/stack_test.go:225",
 	}, {
 		stackTrace()[:2],
 		"%#v",
-		`\[\]errors.Frame{stack_test.go:207, stack_test.go:266}`,
+		`\[\]errors.Frame{stack_test.go:174, stack_test.go:233}`,
 	}}
 
 	for i, tt := range tests {
 		testFormatRegexp(t, i, tt.StackTrace, tt.format, tt.want)
 	}
 }
+
+// a version of runtime.Caller that returns a Frame, not a uintptr.
+func caller() Frame {
+	var pcs [3]uintptr
+	n := runtime.Callers(2, pcs[:])
+	frames := runtime.CallersFrames(pcs[:n])
+	frame, _ := frames.Next()
+	return Frame(frame.PC)
+}
+
+//go:noinline
+// noinline prevents the caller being inlined
+func noinline() {}
-- 
2.20.1