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 7557c8f
Show file tree
Hide file tree
Showing 3 changed files with 39 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.ErrWithPosition); ok {
issue.Location = ewp.Location()
issue.Msg = ewp.Message()
return issue
}

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

Expand Down
40 changes: 30 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 ErrWithPosition struct {

Check failure on line 140 in gnovm/pkg/gnolang/go2gno.go

View workflow job for this annotation

GitHub Actions / Run GnoVM suite / Go Lint / lint

the error type name `ErrWithPosition` should conform to the `XxxError` format (errname)
pos token.Position
msg string
}

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

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

func (ewp *ErrWithPosition) 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,7 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) {
}
}()
}
posn := fs.Position(gon.Pos())
switch gon := gon.(type) {
case *ast.ParenExpr:
return toExpr(fs, gon.X)
Expand Down Expand Up @@ -245,10 +263,10 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) {
Tag: toExpr(fs, gon.Tag),
}
} else {
panic(fmt.Sprintf(
panic(&ErrWithPosition{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 +348,8 @@ 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(&ErrWithPosition{posn,
"expected panic statement to have single exception value"})
}
return &PanicStmt{
Exception: toExpr(fs, cx.Args[0]),
Expand Down Expand Up @@ -412,9 +431,9 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) {
VarName: "",
}
default:
panic(fmt.Sprintf(
panic(&ErrWithPosition{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 +452,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(&ErrWithPosition{posn, "method has multiple receivers"})
}

if len(gon.Recv.List) == 0 {
panic("method has no receiver")
panic(&ErrWithPosition{posn, "method has no receiver"})
}
recv = *Go2Gno(fs, gon.Recv.List[0]).(*FieldTypeExpr)
}
Expand All @@ -454,7 +474,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(&ErrWithPosition{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 +493,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(&ErrWithPosition{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 7557c8f

Please sign in to comment.