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:
28
build/cli.go
28
build/cli.go
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user