build: return ErrorList type

Adds a very similar interface to go/scanner package for returning a list
of errors.

Updates #34
This commit is contained in:
Michael McLoughlin
2019-01-06 13:32:09 -08:00
parent b6576feee6
commit 07901bb91b
4 changed files with 80 additions and 19 deletions

View File

@@ -14,6 +14,7 @@ import (
// Config contains options for an avo main function.
type Config struct {
ErrOut io.Writer
MaxErrors int // max errors to report; 0 means unlimited
CPUProfile io.WriteCloser
Passes []pass.Interface
}
@@ -33,11 +34,9 @@ func Main(cfg *Config, context *Context) int {
defer pprof.StopCPUProfile()
}
f, errs := context.Result()
if errs != nil {
for _, err := range errs {
diag.Println(err)
}
f, err := context.Result()
if err != nil {
LogError(diag, err, cfg.MaxErrors)
return 1
}
@@ -52,9 +51,10 @@ func Main(cfg *Config, context *Context) int {
// Flags represents CLI flags for an avo program.
type Flags struct {
errout *outputValue
cpuprof *outputValue
printers []*printerValue
errout *outputValue
allerrors bool
cpuprof *outputValue
printers []*printerValue
}
// NewFlags initializes avo flags for the given FlagSet.
@@ -64,6 +64,8 @@ func NewFlags(fs *flag.FlagSet) *Flags {
f.errout = newOutputValue(os.Stderr)
fs.Var(f.errout, "log", "diagnostics output")
fs.BoolVar(&f.allerrors, "e", false, "no limit on number of errors reported")
f.cpuprof = newOutputValue(nil)
fs.Var(f.cpuprof, "cpuprofile", "write cpu profile to `file`")
@@ -88,11 +90,19 @@ func (f *Flags) Config() *Config {
passes = append(passes, p)
}
}
return &Config{
cfg := &Config{
ErrOut: f.errout.w,
MaxErrors: 10,
CPUProfile: f.cpuprof.w,
Passes: passes,
}
if f.allerrors {
cfg.MaxErrors = 0
}
return cfg
}
type outputValue struct {

View File

@@ -19,7 +19,7 @@ type Context struct {
file *avo.File
function *avo.Function
global *avo.Global
errs []error
errs ErrorList
reg.Collection
}
@@ -176,8 +176,7 @@ func (c *Context) activeglobal() *avo.Global {
}
func (c *Context) adderror(err error) {
e := exterr(err)
c.errs = append(c.errs, e)
c.errs.addext(err)
}
func (c *Context) adderrormessage(msg string) {
@@ -185,6 +184,6 @@ func (c *Context) adderrormessage(msg string) {
}
// Result returns the built file and any accumulated errors.
func (c *Context) Result() (*avo.File, []error) {
return c.file, c.errs
func (c *Context) Result() (*avo.File, error) {
return c.file, c.errs.Err()
}

View File

@@ -1,6 +1,9 @@
package build
import (
"fmt"
"log"
"github.com/mmcloughlin/avo/internal/stack"
"github.com/mmcloughlin/avo/src"
)
@@ -28,3 +31,53 @@ func (e Error) Error() string {
}
return msg
}
// ErrorList is a collection of errors for a source file.
type ErrorList []Error
// Add appends an error to the list.
func (e *ErrorList) Add(err Error) {
*e = append(*e, err)
}
// addext appends an error to the list, tagged with the
func (e *ErrorList) addext(err error) {
e.Add(exterr(err))
}
// Err returns an error equivalent to this error list.
// If the list is empty, Err returns nil.
func (e ErrorList) Err() error {
if len(e) == 0 {
return nil
}
return e
}
// An ErrorList implements the error interface.
func (e ErrorList) Error() string {
switch len(e) {
case 0:
return "no errors"
case 1:
return e[0].Error()
}
return fmt.Sprintf("%s (and %d more errors)", e[0], len(e)-1)
}
// LogError logs a list of errors, one error per line, if the err parameter is
// an ErrorList. Otherwise it just logs the err string. Reports at most max
// errors, or unlimited if max is 0.
func LogError(l *log.Logger, err error, max int) {
if list, ok := err.(ErrorList); ok {
for i, e := range list {
l.Printf("%s\n", e)
if max > 0 && i == max {
l.Print("too many errors")
return
}
}
} else if err != nil {
l.Printf("%s\n", err)
}
}

View File

@@ -3,6 +3,7 @@ package pass_test
import (
"testing"
"github.com/mmcloughlin/avo/internal/test"
"github.com/mmcloughlin/avo/reg"
"github.com/mmcloughlin/avo/pass"
@@ -57,11 +58,9 @@ func AssertRegistersMatchSet(t *testing.T, rs []reg.Register, s reg.Set) {
}
func ConstructLiveness(t *testing.T, ctx *build.Context) *avo.Function {
f, errs := ctx.Result()
if errs != nil {
for _, err := range errs {
t.Error(err)
}
f, err := ctx.Result()
if err != nil {
build.LogError(test.Logger(t), err, 0)
t.FailNow()
}