Skip to content

Commit

Permalink
cue/errors: better vetting of error formats
Browse files Browse the repository at this point in the history
Currently there's no way for `go vet` to know that the arguments passed
to `errors.NewMessage` are intended for use by `fmt.Printf`.

This CL uses a never-called invocation of `fmt.Sprintf` to do that.
It also renames `NewMessage` to `NewMessagef` (deprecating the
original function) because `go vet` does not recognize printf-like
functions unless they have variadic arguments.

Also fix the various erroneous calls which were exposed by this change.
This includes changing `%s` to `%v` whereever the error argument transformation
code in `adt.OpContext.NewPosf` changes the type. This should not
affect behavior because `%v` acts the same as `%s` for `string`.

Signed-off-by: Roger Peppe <rogpeppe@gmail.com>
Change-Id: I0f4e8751db862ef822c4b5b16c8084d7a0cf66c6
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/551766
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Reviewed-by: Marcel van Lohuizen <mpvl@gmail.com>
Unity-Result: CUEcueckoo <cueckoo@cuelang.org>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
  • Loading branch information
rogpeppe committed Mar 29, 2023
1 parent be0601b commit c630554
Show file tree
Hide file tree
Showing 15 changed files with 48 additions and 39 deletions.
18 changes: 14 additions & 4 deletions cue/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,23 @@ type Message struct {
args []interface{}
}

// NewMessage creates an error message for human consumption. The arguments
// NewMessagef creates an error message for human consumption. The arguments
// are for later consumption, allowing the message to be localized at a later
// time. The passed argument list should not be modified.
func NewMessage(format string, args []interface{}) Message {
func NewMessagef(format string, args ...interface{}) Message {
if false {
// Let go vet know that we're expecting printf-like arguments.
_ = fmt.Sprintf(format, args...)
}
return Message{format: format, args: args}
}

// NewMessage creates an error message for human consumption.
// Deprecated: Use NewMessagef instead.
func NewMessage(format string, args []interface{}) Message {
return NewMessagef(format, args...)
}

// Msg returns a printf-style format string and its arguments for human
// consumption.
func (m *Message) Msg() (format string, args []interface{}) {
Expand Down Expand Up @@ -162,7 +172,7 @@ func Path(err error) []string {
func Newf(p token.Pos, format string, args ...interface{}) Error {
return &posError{
pos: p,
Message: NewMessage(format, args),
Message: NewMessagef(format, args...),
}
}

Expand All @@ -171,7 +181,7 @@ func Newf(p token.Pos, format string, args ...interface{}) Error {
func Wrapf(err error, p token.Pos, format string, args ...interface{}) Error {
pErr := &posError{
pos: p,
Message: NewMessage(format, args),
Message: NewMessagef(format, args...),
}
return Wrap(pErr, err)
}
Expand Down
3 changes: 1 addition & 2 deletions cue/load/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,7 @@ func (l *loader) importPkg(pos token.Pos, p *build.Instance) []*build.Instance {
if !found {
return retErr(
&PackageError{
Message: errors.NewMessage("cannot find package %q",
[]interface{}{p.DisplayPath}),
Message: errors.NewMessagef("cannot find package %q", p.DisplayPath),
})
}

Expand Down
2 changes: 1 addition & 1 deletion cue/load/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (l *loader) abs(filename string) string {
func (l *loader) errPkgf(importPos []token.Pos, format string, args ...interface{}) *PackageError {
err := &PackageError{
ImportStack: l.stk.Copy(),
Message: errors.NewMessage(format, args),
Message: errors.NewMessagef(format, args...),
}
err.fillPos(l.cfg.Dir, importPos)
return err
Expand Down
6 changes: 3 additions & 3 deletions encoding/jsonschema/constraints.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ var constraints = []*constraint{

p1("examples", func(n cue.Value, s *state) {
if n.Kind() != cue.ListKind {
s.errf(n, `value of "examples" must be an array, found %v`, n.Kind)
s.errf(n, `value of "examples" must be an array, found %v`, n.Kind())
}
// TODO: implement examples properly.
// for _, n := range s.listItems("examples", n, true) {
Expand Down Expand Up @@ -488,7 +488,7 @@ var constraints = []*constraint{

p2("required", func(n cue.Value, s *state) {
if n.Kind() != cue.ListKind {
s.errf(n, `value of "required" must be list of strings, found %v`, n.Kind)
s.errf(n, `value of "required" must be list of strings, found %v`, n.Kind())
return
}

Expand Down Expand Up @@ -573,7 +573,7 @@ var constraints = []*constraint{
p2("patternProperties", func(n cue.Value, s *state) {
s.usedTypes |= cue.StructKind
if n.Kind() != cue.StructKind {
s.errf(n, `value of "patternProperties" must be an an object, found %v`, n.Kind)
s.errf(n, `value of "patternProperties" must be an an object, found %v`, n.Kind())
}
obj := s.object(n)
existing := excludeFields(s.obj.Elts)
Expand Down
2 changes: 1 addition & 1 deletion encoding/openapi/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ func (c *buildContext) isInternal(sel cue.Selector) bool {

func (b *builder) failf(v cue.Value, format string, args ...interface{}) {
panic(&openapiError{
errors.NewMessage(format, args),
errors.NewMessagef(format, args...),
cue.MakePath(b.ctx.path...),
v.Pos(),
})
Expand Down
4 changes: 2 additions & 2 deletions internal/core/adt/composite.go
Original file line number Diff line number Diff line change
Expand Up @@ -837,10 +837,10 @@ func (v *Vertex) GetArc(c *OpContext, f Feature, t ArcType) (arc *Vertex, isNew
// TODO(errors): add positions.
if f.IsInt() {
c.addErrf(EvalError, token.NoPos,
"element at index %s not allowed by earlier comprehension or reference cycle", f)
"element at index %v not allowed by earlier comprehension or reference cycle", f)
} else {
c.addErrf(EvalError, token.NoPos,
"field %s not allowed by earlier comprehension or reference cycle", f)
"field %v not allowed by earlier comprehension or reference cycle", f)
}
}
arc = &Vertex{Parent: v, Label: f, ArcType: t}
Expand Down
14 changes: 7 additions & 7 deletions internal/core/adt/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ func (c *OpContext) getDefault(v Value) (result Value, ok bool) {

if d.NumDefaults != 1 {
c.addErrf(IncompleteError, c.pos(),
"unresolved disjunction %s (type %s)", d, d.Kind())
"unresolved disjunction %v (type %s)", d, d.Kind())
return nil, false
}
return c.getDefault(d.Values[0])
Expand Down Expand Up @@ -805,7 +805,7 @@ func (c *OpContext) lookup(x *Vertex, pos token.Pos, l Feature, state vertexStat
switch x.BaseValue.(type) {
case *StructMarker:
if l.Typ() == IntLabel {
c.addErrf(0, pos, "invalid struct selector %s (type int)", l)
c.addErrf(0, pos, "invalid struct selector %v (type int)", l)
return nil
}

Expand All @@ -814,17 +814,17 @@ func (c *OpContext) lookup(x *Vertex, pos token.Pos, l Feature, state vertexStat
case l.Typ() == IntLabel:
switch {
case l.Index() < 0:
c.addErrf(0, pos, "invalid list index %s (index must be non-negative)", l)
c.addErrf(0, pos, "invalid list index %v (index must be non-negative)", l)
return nil
case l.Index() > len(x.Arcs):
c.addErrf(0, pos, "invalid list index %s (out of bounds)", l)
c.addErrf(0, pos, "invalid list index %v (out of bounds)", l)
return nil
}

case l.IsDef(), l.IsHidden(), l.IsLet():

default:
c.addErrf(0, pos, "invalid list index %s (type string)", l)
c.addErrf(0, pos, "invalid list index %v (type string)", l)
return nil
}

Expand All @@ -843,7 +843,7 @@ func (c *OpContext) lookup(x *Vertex, pos token.Pos, l Feature, state vertexStat
// return nil
} else if !l.IsDef() && !l.IsHidden() && !l.IsLet() {
c.addErrf(0, pos,
"invalid selector %s for value of type %s", l, kind)
"invalid selector %v for value of type %s", l, kind)
return nil
}
}
Expand Down Expand Up @@ -1121,7 +1121,7 @@ func (c *OpContext) uint64(v Value, as string) uint64 {
}
if !x.X.Coeff.IsUint64() {
// TODO: improve message
c.AddErrf("cannot convert number %s to uint64", x.X)
c.AddErrf("cannot convert number %s to uint64", &x.X)
return 0
}
return x.X.Coeff.Uint64()
Expand Down
2 changes: 1 addition & 1 deletion internal/core/adt/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ func (c *OpContext) NewPosf(p token.Pos, format string, args ...interface{}) *Va
v: c.errNode(),
pos: p,
auxpos: a,
Message: errors.NewMessage(format, args),
Message: errors.NewMessagef(format, args...),
}
}

Expand Down
10 changes: 5 additions & 5 deletions internal/core/adt/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -1215,7 +1215,7 @@ func (x *UnaryExpr) evaluate(c *OpContext, state vertexStatus) Value {
"operand %s of '%s' not concrete (was %s)", x.X, op, k)
return nil
}
return c.NewErrf("invalid operation %s (%s %s)", x, op, k)
return c.NewErrf("invalid operation %v (%s %s)", x, op, k)
}

// BinaryExpr is a binary expression.
Expand Down Expand Up @@ -1566,15 +1566,15 @@ func (x *Builtin) call(c *OpContext, p token.Pos, validate bool, args []Value) E
fun := x // right now always x.
if len(args) > len(x.Params) {
c.addErrf(0, p,
"too many arguments in call to %s (have %d, want %d)",
"too many arguments in call to %v (have %d, want %d)",
fun, len(args), len(x.Params))
return nil
}
for i := len(args); i < len(x.Params); i++ {
v := x.Params[i].Default()
if v == nil {
c.addErrf(0, p,
"not enough arguments in call to %s (have %d, want %d)",
"not enough arguments in call to %v (have %d, want %d)",
fun, len(args), len(x.Params))
return nil
}
Expand All @@ -1594,7 +1594,7 @@ func (x *Builtin) call(c *OpContext, p token.Pos, validate bool, args []Value) E
code = b.Code
}
c.addErrf(code, pos(a),
"cannot use %s (type %s) as %s in argument %d to %s",
"cannot use %s (type %s) as %s in argument %d to %v",
a, k, x.Params[i].Kind(), i+1, fun)
return nil
}
Expand All @@ -1609,7 +1609,7 @@ func (x *Builtin) call(c *OpContext, p token.Pos, validate bool, args []Value) E
n.Finalize(c)
if _, ok := n.BaseValue.(*Bottom); ok {
c.addErrf(0, pos(a),
"cannot use %s as %s in argument %d to %s",
"cannot use %s as %s in argument %d to %v",
a, v, i+1, fun)
return nil
}
Expand Down
4 changes: 2 additions & 2 deletions internal/core/adt/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,10 @@ func labelFromValue(c *OpContext, src Expr, v Value) Feature {
case nil, *Num, *UnaryExpr:
// If the value is a constant, we know it is always an error.
// UnaryExpr is an approximation for a constant value here.
c.AddErrf("invalid index %s (index must be non-negative)", x)
c.AddErrf("invalid index %v (index must be non-negative)", x)
default:
// Use a different message is it is the result of evaluation.
c.AddErrf("index %s out of range [%s]", src, x)
c.AddErrf("index %v out of range [%v]", src, x)
}
return InvalidLabel
}
Expand Down
2 changes: 1 addition & 1 deletion internal/core/compile/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func (c *compiler) errf(n ast.Node, format string, args ...interface{}) *adt.Bot
err := &compilerError{
n: n,
path: c.path(),
Message: errors.NewMessage(format, args),
Message: errors.NewMessagef(format, args...),
}
c.errs = errors.Append(c.errs, err)
return &adt.Bottom{Err: err}
Expand Down
2 changes: 1 addition & 1 deletion internal/core/runtime/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (n *nodeError) Error() string {
func nodeErrorf(n ast.Node, format string, args ...interface{}) *nodeError {
return &nodeError{
n: n,
Message: errors.NewMessage(format, args),
Message: errors.NewMessagef(format, args...),
}
}

Expand Down
8 changes: 4 additions & 4 deletions internal/core/subsume/vertex.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (s *subsumer) vertices(x, y *adt.Vertex) bool {

case *adt.ListMarker:
if !y.IsList() {
s.errf("list does not subsume %s (type %s)", y, y.Kind())
s.errf("list does not subsume %v (type %s)", y, y.Kind())
return false
}
if !s.listVertices(x, y) {
Expand Down Expand Up @@ -145,7 +145,7 @@ func (s *subsumer) vertices(x, y *adt.Vertex) bool {
if b == nil {
// y.f is optional
if !aOpt {
s.errf("required field is optional in subsumed value: %s", f)
s.errf("required field is optional in subsumed value: %v", f)
return false
}

Expand All @@ -170,7 +170,7 @@ func (s *subsumer) vertices(x, y *adt.Vertex) bool {
s.gt = a
s.lt = y

s.errf("field %s not present in %s", f, y)
s.errf("field %v not present in %v", f, y)
return false
}

Expand Down Expand Up @@ -211,7 +211,7 @@ outer:
if s.Profile.IgnoreClosedness {
continue
}
s.errf("field not allowed in closed struct: %s", f)
s.errf("field not allowed in closed struct: %v", f)
return false
}

Expand Down
2 changes: 1 addition & 1 deletion internal/task/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (c *Context) addErr(v cue.Value, wrap error, format string, args ...interfa
err := &taskError{
task: c.Obj,
v: v,
Message: errors.NewMessage(format, args),
Message: errors.NewMessagef(format, args...),
}
c.Err = errors.Append(c.Err, errors.Wrap(err, wrap))
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/internal/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,14 +295,14 @@ func (c *CallCtxt) DecimalList(i int) (a []*apd.Decimal) {
default:
if k := w.Kind(); k&adt.NumKind == 0 {
err := c.ctx.NewErrf(
"invalid list element %d in argument %d to call: cannot use value %s (%s) as number",
"invalid list element %d in argument %d to call: cannot use value %v (%s) as number",
j, i, w, k)
c.Err = &callError{err}
return a
}

err := c.ctx.NewErrf(
"non-concrete value %s for element %d of number list argument %d",
"non-concrete value %v for element %d of number list argument %d",
w, j, i)
err.Code = adt.IncompleteError
c.Err = &callError{err}
Expand Down Expand Up @@ -333,14 +333,14 @@ func (c *CallCtxt) StringList(i int) (a []string) {
default:
if k := w.Kind(); k&adt.StringKind == 0 {
err := c.ctx.NewErrf(
"invalid list element %d in argument %d to call: cannot use value %s (%s) as string",
"invalid list element %d in argument %d to call: cannot use value %v (%s) as string",
j, i, w, k)
c.Err = &callError{err}
return a
}

err := c.ctx.NewErrf(
"non-concrete value %s for element %d of string list argument %d",
"non-concrete value %v for element %d of string list argument %d",
w, j, i)
err.Code = adt.IncompleteError
c.Err = &callError{err}
Expand Down

0 comments on commit c630554

Please sign in to comment.