build: include position information in errors

Updates #5
This commit is contained in:
Michael McLoughlin
2019-01-04 15:01:29 -08:00
parent 80c427d051
commit 05cfa809f2
3 changed files with 65 additions and 2 deletions

View File

@@ -153,7 +153,8 @@ func (c *Context) activeglobal() *avo.Global {
}
func (c *Context) AddError(err error) {
c.errs = append(c.errs, err)
e := exterr(err)
c.errs = append(c.errs, e)
}
func (c *Context) AddErrorMessage(msg string) {

30
build/error.go Normal file
View File

@@ -0,0 +1,30 @@
package build
import (
"github.com/mmcloughlin/avo/internal/stack"
"github.com/mmcloughlin/avo/src"
)
// Error represents an error during building, optionally tagged with the position at which it happened.
type Error struct {
Position src.Position
Err error
}
// exterr constructs an Error with position derived from the first frame in the
// call stack outside this package.
func exterr(err error) Error {
e := Error{Err: err}
if f := stack.ExternalCaller(); f != nil {
e.Position = src.FramePosition(*f).Relwd()
}
return e
}
func (e Error) Error() string {
msg := e.Err.Error()
if e.Position.IsValid() {
return e.Position.String() + ": " + msg
}
return msg
}

View File

@@ -1,6 +1,11 @@
package src
import "strconv"
import (
"os"
"path/filepath"
"runtime"
"strconv"
)
// Position represents a position in a source file.
type Position struct {
@@ -8,6 +13,13 @@ type Position struct {
Line int // 1-up
}
func FramePosition(f runtime.Frame) Position {
return Position{
Filename: f.File,
Line: f.Line,
}
}
// IsValid reports whether the position is valid: Line must be positive, but
// Filename may be empty.
func (p Position) IsValid() bool {
@@ -26,3 +38,23 @@ func (p Position) String() string {
s += strconv.Itoa(p.Line)
return s
}
// Rel returns Position relative to basepath. If the given filename cannot be
// expressed relative to basepath the position will be returned unchanged.
func (p Position) Rel(basepath string) Position {
q := p
if rel, err := filepath.Rel(basepath, q.Filename); err == nil {
q.Filename = rel
}
return q
}
// Relwd returns Position relative to the current working directory. Returns p
// unchanged if the working directory cannot be determined, or the filename
// cannot be expressed relative to the working directory.
func (p Position) Relwd() Position {
if wd, err := os.Getwd(); err == nil {
return p.Rel(wd)
}
return p
}