Skip to content

Commit

Permalink
feat(gnolang): make panics from Go2Gno produce lineno:column information
Browse files Browse the repository at this point in the history
With this change, panics from Go2Gno now have the line-number and column
information attached which will greatly aid in debugging.

Fixes #3747
  • Loading branch information
odeke-em committed Feb 13, 2025
1 parent 2e49141 commit 9154835
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 12 deletions.
9 changes: 8 additions & 1 deletion gnovm/cmd/gno/tool_lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,11 +264,12 @@ var reParseRecover = regexp.MustCompile(`^([^:]+)((?::(?:\d+)){1,2}):? *(.*)$`)

func catchRuntimeError(pkgPath string, stderr goio.WriteCloser, action func()) (hasError bool) {
defer func() {
// Errors catched here mostly come from: gnovm/pkg/gnolang/preprocess.go
// Errors caught here mostly come from: gnovm/pkg/gnolang/preprocess.go
r := recover()
if r == nil {
return
}

hasError = true
switch verr := r.(type) {
case *gno.PreprocessError:
Expand Down Expand Up @@ -302,6 +303,12 @@ func issueFromError(pkgPath string, err error) lintIssue {
issue.Confidence = 1
issue.Code = lintGnoError

if ewp, ok := err.(*gno.LocationPlusError); ok {
issue.Location = ewp.Location()
issue.Msg = ewp.Message()
return issue
}

parsedError := strings.TrimSpace(err.Error())
parsedError = strings.TrimPrefix(parsedError, pkgPath+"/")

Expand Down
44 changes: 34 additions & 10 deletions gnovm/pkg/gnolang/go2gno.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,23 @@ func setLoc(fs *token.FileSet, pos token.Pos, n Node) Node {
return n
}

type LocationPlusError struct {
pos token.Position
msg string
}

func (ewp *LocationPlusError) Error() string {
return fmt.Sprintf("%s: %s", ewp.Location(), ewp.msg)
}

func (ewp *LocationPlusError) Location() string {
return fmt.Sprintf("%s:%d:%d", ewp.pos.Filename, ewp.pos.Line, ewp.pos.Column)
}

func (ewp *LocationPlusError) Message() string {
return ewp.msg
}

// If gon is a *ast.File, the name must be filled later.
func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) {
if gon == nil {
Expand All @@ -149,6 +166,9 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) {
}
}()
}

posn := func() token.Position { return fs.Position(gon.Pos()) }

switch gon := gon.(type) {
case *ast.ParenExpr:
return toExpr(fs, gon.X)
Expand Down Expand Up @@ -245,10 +265,10 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) {
Tag: toExpr(fs, gon.Tag),
}
} else {
panic(fmt.Sprintf(
panic(&LocationPlusError{posn(), fmt.Sprintf(
"expected a Go Field with 1 name but got %v.\n"+
"maybe call toFields",
gon.Names))
gon.Names)})
}
case *ast.ArrayType:
if _, ok := gon.Len.(*ast.Ellipsis); ok {
Expand Down Expand Up @@ -330,7 +350,10 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) {
if cx, ok := gon.X.(*ast.CallExpr); ok {
if ix, ok := cx.Fun.(*ast.Ident); ok && ix.Name == "panic" {
if len(cx.Args) != 1 {
panic("expected panic statement to have single exception value")
panic(&LocationPlusError{
posn(),
"expected panic statement to have single exception value",
})
}
return &PanicStmt{
Exception: toExpr(fs, cx.Args[0]),
Expand Down Expand Up @@ -412,9 +435,9 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) {
VarName: "",
}
default:
panic(fmt.Sprintf(
panic(&LocationPlusError{posn(), fmt.Sprintf(
"unexpected *ast.TypeSwitchStmt.Assign type %s",
reflect.TypeOf(gon.Assign).String()))
reflect.TypeOf(gon.Assign).String())})
}
case *ast.SwitchStmt:
x := toExpr(fs, gon.Tag)
Expand All @@ -433,10 +456,11 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) {
recv := FieldTypeExpr{}
if isMethod {
if len(gon.Recv.List) > 1 {
panic("method has multiple receivers")
panic(&LocationPlusError{posn(), "method has multiple receivers"})
}

if len(gon.Recv.List) == 0 {
panic("method has no receiver")
panic(&LocationPlusError{posn(), "method has no receiver"})
}
recv = *Go2Gno(fs, gon.Recv.List[0]).(*FieldTypeExpr)
}
Expand All @@ -454,7 +478,7 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) {
Body: body,
}
case *ast.GenDecl:
panic("unexpected *ast.GenDecl; use toDecls(fs,) instead")
panic(&LocationPlusError{posn(), "unexpected *ast.GenDecl; use toDecls(fs,) instead"})
case *ast.File:
pkgName := Name(gon.Name.Name)
decls := make([]Decl, 0, len(gon.Decls))
Expand All @@ -473,10 +497,10 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) {
case *ast.EmptyStmt:
return &EmptyStmt{}
default:
panic(fmt.Sprintf("unknown Go type %v: %s\n",
panic(&LocationPlusError{posn(), fmt.Sprintf("unknown Go type %v: %s\n",
reflect.TypeOf(gon),
spew.Sdump(gon),
))
)})
}
}

Expand Down
2 changes: 1 addition & 1 deletion gnovm/tests/files/parse_err0.gno
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ func () A()
func main() {}

// Error:
// method has no receiver
// :5:1: method has no receiver

0 comments on commit 9154835

Please sign in to comment.