blob: 6f1b29dc85181a79a593f358738c6e89af467e2e [file] [log] [blame]
// Copyright 2020 CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package internal
import (
"encoding/json"
"fmt"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal"
"cuelang.org/go/internal/core/adt"
)
type bottomer interface {
error
Bottom() *adt.Bottom
}
type callError struct {
b *adt.Bottom
}
func (e *callError) Error() string {
return fmt.Sprint(e.b)
}
func (c *CallCtxt) errf(src adt.Node, underlying error, format string, args ...interface{}) {
var errs errors.Error
var code adt.ErrorCode
if err, ok := underlying.(bottomer); ok {
b := err.Bottom()
errs = b.Err
code = b.Code
}
errs = errors.Wrapf(errs, c.ctx.Pos(), format, args...)
c.Err = &callError{&adt.Bottom{Code: code, Err: errs}}
}
func (c *CallCtxt) errcf(src adt.Node, code adt.ErrorCode, format string, args ...interface{}) {
err := c.ctx.NewErrf(format, args...)
err.Code = code
c.Err = &callError{err}
}
func wrapCallErr(c *CallCtxt, b *adt.Bottom) *adt.Bottom {
pos := token.NoPos
if c.src != nil {
if src := c.src.Source(); src != nil {
pos = src.Pos()
}
}
var err errors.Error
for _, e := range errors.Errors(b.Err) {
const msg = "error in call to %s"
err = errors.Append(err,
errors.Wrapf(e, pos, msg, c.builtin.name(c.ctx)))
}
return &adt.Bottom{Code: b.Code, Err: err}
}
func (c *CallCtxt) convertError(x interface{}, name string) *adt.Bottom {
var err errors.Error
switch v := x.(type) {
case nil:
return nil
case *adt.Bottom:
return v
case *json.MarshalerError:
err = errors.Promote(v, "marshal error")
case errors.Error:
err = v
case error:
if name != "" {
err = errors.Newf(c.Pos(), "%s: %v", name, v)
} else {
err = errors.Newf(c.Pos(), "error in call to %s: %v", c.Name(), v)
}
default:
err = errors.Newf(token.NoPos, "%s", name)
}
if err != internal.ErrIncomplete {
return &adt.Bottom{
// Wrap to preserve position information.
Err: errors.Wrapf(err, c.Pos(), "error in call to %s", c.Name()),
}
}
return &adt.Bottom{
Code: adt.IncompleteError,
Err: errors.Newf(c.Pos(), "incomplete values in call to %s", c.Name()),
}
}
func (c *CallCtxt) invalidArgType(arg adt.Expr, i int, typ string, err error) {
if ve, ok := err.(bottomer); ok && ve.Bottom().IsIncomplete() {
c.Err = ve
return
}
v, ok := arg.(adt.Value)
// TODO: make these permanent errors if the value did not originate from
// a reference.
if !ok {
c.errf(c.src, nil,
"cannot use incomplete value %s as %s in argument %d to %s: %v",
c.ctx.Str(arg), typ, i, c.Name(), err)
}
if err != nil {
c.errf(c.src, err,
"cannot use %s (type %s) as %s in argument %d to %s: %v",
c.ctx.Str(arg), v.Kind(), typ, i, c.Name(), err)
} else {
c.errf(c.src, err,
"cannot use %s (type %s) as %s in argument %d to %s",
c.ctx.Str(arg), v.Kind(), typ, i, c.Name())
}
}