cue/errors: use Pos instead of Position in Error
Issue #52
This simplifies code and allows optimizations down
the road.
Change-Id: Ifd6c946a7f22f84c561a3ecae935e7e8f7560353
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2205
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/cue/build/instance.go b/cue/build/instance.go
index da3eb24..f0407c0 100644
--- a/cue/build/instance.go
+++ b/cue/build/instance.go
@@ -140,7 +140,7 @@
}
func (inst *Instance) errorf(pos token.Pos, format string, args ...interface{}) error {
- return inst.chkErr(errors.E(pos.Position(), fmt.Sprintf(format, args...)))
+ return inst.chkErr(errors.E(pos, fmt.Sprintf(format, args...)))
}
// Context defines the build context for this instance. All files defined
diff --git a/cue/errors.go b/cue/errors.go
index bc1e2ae..afd89ae 100644
--- a/cue/errors.go
+++ b/cue/errors.go
@@ -24,6 +24,8 @@
"golang.org/x/xerrors"
)
+var _ errors.Error = &nodeError{}
+
// A nodeError is an error associated with processing an AST node.
type nodeError struct {
path []string // optional
@@ -35,12 +37,12 @@
func nodeErrorf(n ast.Node, format string, args ...interface{}) *nodeError {
return &nodeError{
n: n,
- Message: errors.Message{Format: format, Args: args},
+ Message: errors.NewMessage(format, args),
}
}
-func (e *nodeError) Position() token.Position {
- return e.n.Pos().Position()
+func (e *nodeError) Position() token.Pos {
+ return e.n.Pos()
}
func (e *nodeError) Path() []string {
@@ -49,15 +51,14 @@
func (v Value) toErr(b *bottom) errors.Error {
return &valueError{
- Message: errors.Message{
- Format: b.msg,
- Args: nil,
- },
- v: v,
- err: b,
+ Message: errors.NewMessage(b.msg, nil),
+ v: v,
+ err: b,
}
}
+var _ errors.Error = &valueError{}
+
// A valueError is returned as a result of evaluating a value.
type valueError struct {
errors.Message
@@ -66,8 +67,8 @@
err *bottom
}
-func (e *valueError) Position() token.Position {
- return e.err.pos.Pos().Position()
+func (e *valueError) Position() token.Pos {
+ return e.err.pos.Pos()
}
func (e *valueError) Path() (a []string) {
diff --git a/cue/errors/errors.go b/cue/errors/errors.go
index 1531f51..019c71d 100644
--- a/cue/errors/errors.go
+++ b/cue/errors/errors.go
@@ -34,7 +34,7 @@
// A Handler is a generic error handler used throughout CUE packages.
//
// The position points to the beginning of the offending value.
-type Handler func(pos token.Position, msg string)
+type Handler func(pos token.Pos, msg string)
// A Message implements the error interface as well as Message to allow
// internationalized messages.
@@ -62,7 +62,7 @@
// Error is the common error message.
type Error interface {
- Position() token.Position
+ Position() token.Pos
// Error reports the error message without position information.
Error() string
@@ -71,11 +71,7 @@
// // TODO: make Error an interface that returns a list of positions.
// Newf creates an Error with the associated position and message.
-func Newf(pos token.Pos, format string, args ...interface{}) Error {
- var p token.Position
- if pos.IsValid() {
- p = pos.Position()
- }
+func Newf(p token.Pos, format string, args ...interface{}) Error {
return &posError{
pos: p,
Message: NewMessage(format, args),
@@ -84,11 +80,7 @@
// Wrapf creates an Error with the associated position and message. The provided
// error is added for inspection context.
-func Wrapf(err error, pos token.Pos, format string, args ...interface{}) Error {
- var p token.Position
- if pos.IsValid() {
- p = pos.Position()
- }
+func Wrapf(err error, p token.Pos, format string, args ...interface{}) Error {
return &posError{
pos: p,
Message: NewMessage(format, args),
@@ -96,12 +88,14 @@
}
}
+var _ Error = &posError{}
+
// In an List, an error is represented by an *posError.
// The position Pos, if valid, points to the beginning of
// the offending token, and the error condition is described
// by Msg.
type posError struct {
- pos token.Position
+ pos token.Pos
Message
// The underlying error that triggered this one, if any.
@@ -121,9 +115,9 @@
switch x := a.(type) {
case string:
e.Message = NewMessage("%s", []interface{}{x})
- case token.Position:
+ case token.Pos:
e.pos = x
- case []token.Position:
+ case []token.Pos:
// TODO: do something more clever
if len(x) > 0 {
e.pos = x[0]
@@ -157,7 +151,7 @@
return &posError{err: err}
}
-func (e *posError) Position() token.Position {
+func (e *posError) Position() token.Pos {
return e.pos
}
@@ -171,7 +165,7 @@
} else {
p.Printf(format, args...)
}
- if p.Detail() && e.pos.Filename != "" || e.pos.IsValid() {
+ if p.Detail() && e.pos.Filename() != "" || e.pos.IsValid() {
p.Printf("%s", e.pos.String())
}
return next
@@ -204,7 +198,7 @@
}
// AddNew adds an Error with given position and error message to an List.
-func (p *List) AddNew(pos token.Position, msg string) {
+func (p *List) AddNew(pos token.Pos, msg string) {
p.add(&posError{pos: pos, Message: Message{format: msg}})
}
@@ -226,14 +220,14 @@
// Note that it is not sufficient to simply compare file offsets because
// the offsets do not reflect modified line information (through //line
// comments).
- if e.Filename != f.Filename {
- return e.Filename < f.Filename
+ if e.Filename() != f.Filename() {
+ return e.Filename() < f.Filename()
}
- if e.Line != f.Line {
- return e.Line < f.Line
+ if e.Line() != f.Line() {
+ return e.Line() < f.Line()
}
- if e.Column != f.Column {
- return e.Column < f.Column
+ if e.Column() != f.Column() {
+ return e.Column() < f.Column()
}
return p[i].Error() < p[j].Error()
}
@@ -249,11 +243,11 @@
// RemoveMultiples sorts an List and removes all but the first error per line.
func (p *List) RemoveMultiples() {
sort.Sort(p)
- var last token.Position // initial last.Line is != any legal error line
+ var last token.Pos // initial last.Line is != any legal error line
i := 0
for _, e := range *p {
pos := e.Position()
- if pos.Filename != last.Filename || pos.Line != last.Line {
+ if pos.Filename() != last.Filename() || pos.Line() != last.Line() {
last = pos
(*p)[i] = e
i++
@@ -305,7 +299,7 @@
for e := err; e != nil; e = xerrors.Unwrap(e) {
switch x := e.(type) {
- case interface{ Position() token.Position }:
+ case interface{ Position() token.Pos }:
if pos := x.Position().String(); pos != "-" {
positions = append(positions, pos)
}
diff --git a/cue/errors/errors_test.go b/cue/errors/errors_test.go
index fb8e7ff..2f124a4 100644
--- a/cue/errors/errors_test.go
+++ b/cue/errors/errors_test.go
@@ -38,7 +38,7 @@
func TestErrorList_Add(t *testing.T) {
type args struct {
- pos token.Position
+ pos token.Pos
msg string
}
tests := []struct {
diff --git a/cue/load/errors.go b/cue/load/errors.go
index 6f77495..6196e2c 100644
--- a/cue/load/errors.go
+++ b/cue/load/errors.go
@@ -53,14 +53,14 @@
// A packageError describes an error loading information about a package.
type packageError struct {
- ImportStack []string // shortest path from package named on command line to this one
- Pos string // position of error
- Err string // the error itself
- IsImportCycle bool `json:"-"` // the error is an import cycle
- Hard bool `json:"-"` // whether the error is soft or hard; soft errors are ignored in some places
+ ImportStack []string // shortest path from package named on command line to this one
+ Pos token.Pos // position of error
+ Err string // the error itself
+ IsImportCycle bool `json:"-"` // the error is an import cycle
+ Hard bool `json:"-"` // whether the error is soft or hard; soft errors are ignored in some places
}
-func (l *loader) errPkgf(importPos []token.Position, format string, args ...interface{}) *packageError {
+func (l *loader) errPkgf(importPos []token.Pos, format string, args ...interface{}) *packageError {
err := &packageError{
ImportStack: l.stk.Copy(),
Err: fmt.Sprintf(format, args...),
@@ -69,11 +69,9 @@
return err
}
-func (p *packageError) fillPos(cwd string, positions []token.Position) {
- if len(positions) > 0 && p.Pos == "" {
- pos := positions[0]
- pos.Filename = shortPath(cwd, pos.Filename)
- p.Pos = pos.String()
+func (p *packageError) fillPos(cwd string, positions []token.Pos) {
+ if len(positions) > 0 && !p.Pos.IsValid() {
+ p.Pos = positions[0]
}
}
@@ -82,10 +80,10 @@
if p.IsImportCycle {
return fmt.Sprintf("%s\npackage %s\n", p.Err, strings.Join(p.ImportStack, "\n\timports "))
}
- if p.Pos != "" {
+ if p.Pos.IsValid() {
// Omit import stack. The full path to the file where the error
// is the most important thing.
- return p.Pos + ": " + p.Err
+ return p.Pos.String() + ": " + p.Err
}
if len(p.ImportStack) == 0 {
return p.Err
diff --git a/cue/load/loader.go b/cue/load/loader.go
index eb4597d..c9a3c78 100644
--- a/cue/load/loader.go
+++ b/cue/load/loader.go
@@ -89,13 +89,6 @@
getTestDeps
)
-func firstPos(p []token.Position) token.Position {
- if len(p) == 0 {
- return token.Position{}
- }
- return p[0]
-}
-
type loader struct {
cfg *Config
stk importStack
diff --git a/cue/parser/error_test.go b/cue/parser/error_test.go
index eb0cb1f..628c9c6 100644
--- a/cue/parser/error_test.go
+++ b/cue/parser/error_test.go
@@ -114,7 +114,7 @@
// a Pos so we can do a map lookup
ePos := error.Position()
eMsg := error.Error()
- pos := getPos(file, ePos.Offset).WithRel(0)
+ pos := getPos(file, ePos.Offset()).WithRel(0)
if msg, found := expected[pos]; found {
// we expect a message at pos; check if it matches
rx, err := regexp.Compile(msg)
diff --git a/cue/parser/parser.go b/cue/parser/parser.go
index 20e63c4..27b4e5b 100644
--- a/cue/parser/parser.go
+++ b/cue/parser/parser.go
@@ -70,7 +70,7 @@
if p.mode&parseCommentsMode != 0 {
m = scanner.ScanComments
}
- eh := func(pos token.Position, msg string) { p.errors.AddNew(pos, msg) }
+ eh := func(pos token.Pos, msg string) { p.errors.AddNew(pos, msg) }
p.scanner.Init(p.file, src, eh, m)
p.trace = p.mode&traceMode != 0 // for convenience (p.trace is used frequently)
@@ -343,14 +343,15 @@
}
func (p *parser) error(pos token.Pos, msg string) {
- ePos := p.file.Position(pos)
+ // ePos := p.file.Position(pos)
+ ePos := pos
// If AllErrors is not set, discard errors reported on the same line
// as the last recorded error and stop parsing if there are more than
// 10 errors.
if p.mode&allErrorsMode == 0 {
n := len(p.errors)
- if n > 0 && p.errors[n-1].Position().Line == ePos.Line {
+ if n > 0 && p.errors[n-1].Position().Line() == ePos.Line() {
return // discard - likely a spurious error
}
if n > 10 {
diff --git a/cue/scanner/scanner.go b/cue/scanner/scanner.go
index 03c9c19..c0617d4 100644
--- a/cue/scanner/scanner.go
+++ b/cue/scanner/scanner.go
@@ -147,7 +147,7 @@
func (s *Scanner) error(offs int, msg string) {
if s.err != nil {
- s.err(s.file.Position(s.file.Pos(offs, 0)), msg)
+ s.err(s.file.Pos(offs, 0), msg)
}
s.ErrorCount++
}
diff --git a/cue/scanner/scanner_test.go b/cue/scanner/scanner_test.go
index 9b0a8f7..18cbd3f 100644
--- a/cue/scanner/scanner_test.go
+++ b/cue/scanner/scanner_test.go
@@ -208,7 +208,7 @@
whitespace_linecount := newlineCount(whitespace)
// error handler
- eh := func(_ token.Position, msg string) {
+ eh := func(_ token.Pos, msg string) {
t.Errorf("error handler called (msg = %s)", msg)
}
@@ -591,7 +591,7 @@
func TestScanInterpolation(t *testing.T) {
// error handler
- eh := func(pos token.Position, msg string) {
+ eh := func(pos token.Pos, msg string) {
t.Errorf("error handler called (pos = %v, msg = %s)", pos, msg)
}
trim := func(s string) string { return strings.Trim(s, `#"\\()`) }
@@ -646,7 +646,7 @@
"~ ~ ~" // original file, line 1 again
var list errors.List
- eh := func(pos token.Position, msg string) { list.AddNew(pos, msg) }
+ eh := func(pos token.Pos, msg string) { list.AddNew(pos, msg) }
var s Scanner
s.Init(token.NewFile("File1", 1, len(src)), []byte(src), eh, dontInsertCommas)
@@ -679,16 +679,16 @@
}
type errorCollector struct {
- cnt int // number of errors encountered
- msg string // last error message encountered
- pos token.Position // last error position encountered
+ cnt int // number of errors encountered
+ msg string // last error message encountered
+ pos token.Pos // last error position encountered
}
func checkError(t *testing.T, src string, tok token.Token, pos int, lit, err string) {
t.Helper()
var s Scanner
var h errorCollector
- eh := func(pos token.Position, msg string) {
+ eh := func(pos token.Pos, msg string) {
h.cnt++
h.msg = msg
h.pos = pos
@@ -711,8 +711,8 @@
if h.msg != err {
t.Errorf("%q: got msg %q, expected %q", src, h.msg, err)
}
- if h.pos.Offset != pos {
- t.Errorf("%q: got offset %d, expected %d", src, h.pos.Offset, pos)
+ if h.pos.Offset() != pos {
+ t.Errorf("%q: got offset %d, expected %d", src, h.pos.Offset(), pos)
}
}
diff --git a/cue/token/position.go b/cue/token/position.go
index a3bded3..9371083 100644
--- a/cue/token/position.go
+++ b/cue/token/position.go
@@ -84,6 +84,20 @@
return p.Position().Line
}
+func (p Pos) Column() int {
+ if p.file == nil {
+ return 0
+ }
+ return p.Position().Column
+}
+
+func (p Pos) Filename() string {
+ if p.file == nil {
+ return ""
+ }
+ return p.Position().Filename
+}
+
func (p Pos) Position() Position {
if p.file == nil {
return Position{}
@@ -150,7 +164,7 @@
// Offset reports the byte offset relative to the file.
func (p Pos) Offset() int {
- return p.offset >> relShift
+ return p.Position().Offset
}
// Add creates a new position relative to the p offset by n.
diff --git a/cue/types.go b/cue/types.go
index 810753c..f168f35 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -796,12 +796,12 @@
}
// Pos returns position information.
-func (v Value) Pos() token.Position {
+func (v Value) Pos() token.Pos {
if v.path == nil || v.Source() == nil {
- return token.Position{}
+ return token.NoPos
}
pos := v.Source().Pos()
- return pos.Position()
+ return pos
}
// IsConcrete reports whether the current value is a concrete scalar value,