Home | History | Annotate | Download | only in scanner
      1 // Copyright 2009 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package scanner
      6 
      7 import (
      8 	"fmt"
      9 	"go/token"
     10 	"io"
     11 	"sort"
     12 )
     13 
     14 // In an ErrorList, an error is represented by an *Error.
     15 // The position Pos, if valid, points to the beginning of
     16 // the offending token, and the error condition is described
     17 // by Msg.
     18 //
     19 type Error struct {
     20 	Pos token.Position
     21 	Msg string
     22 }
     23 
     24 // Error implements the error interface.
     25 func (e Error) Error() string {
     26 	if e.Pos.Filename != "" || e.Pos.IsValid() {
     27 		// don't print "<unknown position>"
     28 		// TODO(gri) reconsider the semantics of Position.IsValid
     29 		return e.Pos.String() + ": " + e.Msg
     30 	}
     31 	return e.Msg
     32 }
     33 
     34 // ErrorList is a list of *Errors.
     35 // The zero value for an ErrorList is an empty ErrorList ready to use.
     36 //
     37 type ErrorList []*Error
     38 
     39 // Add adds an Error with given position and error message to an ErrorList.
     40 func (p *ErrorList) Add(pos token.Position, msg string) {
     41 	*p = append(*p, &Error{pos, msg})
     42 }
     43 
     44 // Reset resets an ErrorList to no errors.
     45 func (p *ErrorList) Reset() { *p = (*p)[0:0] }
     46 
     47 // ErrorList implements the sort Interface.
     48 func (p ErrorList) Len() int      { return len(p) }
     49 func (p ErrorList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
     50 
     51 func (p ErrorList) Less(i, j int) bool {
     52 	e := &p[i].Pos
     53 	f := &p[j].Pos
     54 	// Note that it is not sufficient to simply compare file offsets because
     55 	// the offsets do not reflect modified line information (through //line
     56 	// comments).
     57 	if e.Filename != f.Filename {
     58 		return e.Filename < f.Filename
     59 	}
     60 	if e.Line != f.Line {
     61 		return e.Line < f.Line
     62 	}
     63 	if e.Column != f.Column {
     64 		return e.Column < f.Column
     65 	}
     66 	return p[i].Msg < p[j].Msg
     67 }
     68 
     69 // Sort sorts an ErrorList. *Error entries are sorted by position,
     70 // other errors are sorted by error message, and before any *Error
     71 // entry.
     72 //
     73 func (p ErrorList) Sort() {
     74 	sort.Sort(p)
     75 }
     76 
     77 // RemoveMultiples sorts an ErrorList and removes all but the first error per line.
     78 func (p *ErrorList) RemoveMultiples() {
     79 	sort.Sort(p)
     80 	var last token.Position // initial last.Line is != any legal error line
     81 	i := 0
     82 	for _, e := range *p {
     83 		if e.Pos.Filename != last.Filename || e.Pos.Line != last.Line {
     84 			last = e.Pos
     85 			(*p)[i] = e
     86 			i++
     87 		}
     88 	}
     89 	(*p) = (*p)[0:i]
     90 }
     91 
     92 // An ErrorList implements the error interface.
     93 func (p ErrorList) Error() string {
     94 	switch len(p) {
     95 	case 0:
     96 		return "no errors"
     97 	case 1:
     98 		return p[0].Error()
     99 	}
    100 	return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1)
    101 }
    102 
    103 // Err returns an error equivalent to this error list.
    104 // If the list is empty, Err returns nil.
    105 func (p ErrorList) Err() error {
    106 	if len(p) == 0 {
    107 		return nil
    108 	}
    109 	return p
    110 }
    111 
    112 // PrintError is a utility function that prints a list of errors to w,
    113 // one error per line, if the err parameter is an ErrorList. Otherwise
    114 // it prints the err string.
    115 //
    116 func PrintError(w io.Writer, err error) {
    117 	if list, ok := err.(ErrorList); ok {
    118 		for _, e := range list {
    119 			fmt.Fprintf(w, "%s\n", e)
    120 		}
    121 	} else if err != nil {
    122 		fmt.Fprintf(w, "%s\n", err)
    123 	}
    124 }
    125