interp: Record function names in panic
Currently, yaegi only records source positions when writing panic trace to stderr. This change adds function names to the panic output. Unfortunately, yaegi walks the trace in reverse order, comparing to Go runtime. This can be improved in the future.
This commit is contained in:
20
_test/panic0.go
Normal file
20
_test/panic0.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
foo()
|
||||
}
|
||||
|
||||
func foo() {
|
||||
bar()
|
||||
}
|
||||
|
||||
func bar() {
|
||||
baz()
|
||||
}
|
||||
|
||||
func baz() {
|
||||
panic("stop!")
|
||||
}
|
||||
|
||||
// Error:
|
||||
// stop!
|
||||
@@ -90,6 +90,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "redeclaration-global5.go" || // expect error
|
||||
file.Name() == "redeclaration-global6.go" || // expect error
|
||||
file.Name() == "redeclaration-global7.go" || // expect error
|
||||
file.Name() == "panic0.go" || // expect error
|
||||
file.Name() == "pkgname0.go" || // has deps
|
||||
file.Name() == "pkgname1.go" || // expect error
|
||||
file.Name() == "pkgname2.go" || // has deps
|
||||
@@ -188,6 +189,7 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
testCases := []struct {
|
||||
fileName string
|
||||
expectedInterp string
|
||||
expectedStderr string
|
||||
expectedExec string
|
||||
}{
|
||||
{
|
||||
@@ -285,6 +287,16 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
expectedInterp: "37:2: duplicate case Bir in type switch",
|
||||
expectedExec: "37:7: duplicate case Bir in type switch",
|
||||
},
|
||||
{
|
||||
fileName: "panic0.go",
|
||||
expectedInterp: "stop!",
|
||||
expectedStderr: `
|
||||
../_test/panic0.go:16:2: panic: main.baz(...)
|
||||
../_test/panic0.go:12:2: panic: main.bar(...)
|
||||
../_test/panic0.go:8:2: panic: main.foo(...)
|
||||
../_test/panic0.go:4:2: panic: main.main(...)
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
@@ -295,7 +307,8 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
|
||||
filePath := filepath.Join("..", "_test", test.fileName)
|
||||
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
|
||||
var stderr bytes.Buffer
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, Stderr: &stderr})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -308,6 +321,12 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
if !strings.Contains(errEval.Error(), test.expectedInterp) {
|
||||
t.Errorf("got %q, want: %q", errEval.Error(), test.expectedInterp)
|
||||
}
|
||||
if test.expectedStderr != "" {
|
||||
exp, got := strings.TrimSpace(test.expectedStderr), strings.TrimSpace(stderr.String())
|
||||
if exp != got {
|
||||
t.Errorf("got %q, want: %q", got, exp)
|
||||
}
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "run", filePath)
|
||||
outRun, errExec := cmd.CombinedOutput()
|
||||
|
||||
@@ -180,6 +180,27 @@ var errAbortHandler = errors.New("net/http: abort Handler")
|
||||
|
||||
// Functions set to run during execution of CFG.
|
||||
|
||||
func panicFunc(s *scope) string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
def := s.def
|
||||
if def == nil {
|
||||
return s.pkgID
|
||||
}
|
||||
switch def.kind {
|
||||
case funcDecl:
|
||||
if c := def.child[1]; c.kind == identExpr {
|
||||
return s.pkgID + "." + c.ident
|
||||
}
|
||||
case funcLit:
|
||||
if def.anc != nil {
|
||||
return panicFunc(def.anc.scope) + ".func"
|
||||
}
|
||||
}
|
||||
return s.pkgID
|
||||
}
|
||||
|
||||
// runCfg executes a node AST by walking its CFG and running node builtin at each step.
|
||||
func runCfg(n *node, f *frame, funcNode, callNode *node) {
|
||||
var exec bltn
|
||||
@@ -199,7 +220,7 @@ func runCfg(n *node, f *frame, funcNode, callNode *node) {
|
||||
// suppress the logging here accordingly, to get a similar and consistent
|
||||
// behavior.
|
||||
if !ok || errorer.Error() != errAbortHandler.Error() {
|
||||
fmt.Fprintln(n.interp.stderr, oNode.cfgErrorf("panic"))
|
||||
fmt.Fprintln(n.interp.stderr, oNode.cfgErrorf("panic: %s(...)", panicFunc(oNode.scope)))
|
||||
}
|
||||
f.mutex.Unlock()
|
||||
panic(f.recovered)
|
||||
|
||||
Reference in New Issue
Block a user