From 6140a2f75ace11284b72e63e584bb89fb89fdd9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Barzowski?= Date: Sun, 13 Dec 2020 18:02:53 +0100 Subject: [PATCH] Better stacktrace for manifestation, +: and object assertions. Fixes #282 --- internal/program/desugarer.go | 7 +- interpreter.go | 90 +++++++++++++------ linter/internal/types/stdlib.go | 2 +- linter/testdata/stdlib_return_types.jsonnet | 1 + .../stdlib_return_types.linter.golden | 0 testdata/error_in_object_local.golden | 3 + testdata/function_manifested.golden | 3 + testdata/missing_super.golden | 3 + testdata/object_comp_err_elem.golden | 3 + testdata/object_invariant10.golden | 7 +- testdata/object_invariant11.golden | 4 +- testdata/object_invariant13.golden | 3 + testdata/object_invariant14.golden | 7 +- testdata/object_invariant2.golden | 7 +- testdata/object_invariant7.golden | 3 + testdata/object_invariant8.golden | 7 +- testdata/object_invariant9.golden | 7 +- testdata/object_invariant_plus.golden | 7 +- testdata/object_invariant_plus2.golden | 7 +- testdata/object_invariant_plus6.golden | 7 +- testdata/stacktrace_assert.golden | 13 +++ testdata/stacktrace_assert.jsonnet | 1 + testdata/stacktrace_assert.linter.golden | 0 testdata/stacktrace_plussuper.golden | 13 +++ testdata/stacktrace_plussuper.jsonnet | 5 ++ testdata/stacktrace_plussuper.linter.golden | 0 testdata/supersugar8.golden | 7 +- thunks.go | 43 ++++++++- value.go | 2 + 29 files changed, 220 insertions(+), 42 deletions(-) create mode 100644 linter/testdata/stdlib_return_types.jsonnet create mode 100644 linter/testdata/stdlib_return_types.linter.golden create mode 100644 testdata/stacktrace_assert.golden create mode 100644 testdata/stacktrace_assert.jsonnet create mode 100644 testdata/stacktrace_assert.linter.golden create mode 100644 testdata/stacktrace_plussuper.golden create mode 100644 testdata/stacktrace_plussuper.jsonnet create mode 100644 testdata/stacktrace_plussuper.linter.golden diff --git a/internal/program/desugarer.go b/internal/program/desugarer.go index 98c83f7..71b0549 100644 --- a/internal/program/desugarer.go +++ b/internal/program/desugarer.go @@ -62,7 +62,12 @@ func desugarFields(nodeBase ast.NodeBase, fields *ast.ObjectFields, objLevel int if msg == nil { msg = buildLiteralString("Object assertion failed.") } - onFailure := &ast.Error{Expr: msg} + onFailure := &ast.Error{ + NodeBase: ast.NodeBase{ + LocRange: field.LocRange, + }, + Expr: msg, + } asserts = append(asserts, &ast.Conditional{ NodeBase: ast.NodeBase{ LocRange: field.LocRange, diff --git a/interpreter.go b/interpreter.go index 087223d..99a3993 100644 --- a/interpreter.go +++ b/interpreter.go @@ -35,9 +35,9 @@ type environment struct { // Bindings introduced in this frame. The way previous bindings are treated // depends on the type of a frame. - // If isCall == true then previous bindings are ignored (it's a clean + // If cleanEnv == true then previous bindings are ignored (it's a clean // environment with just the variables we have here). - // If isCall == false then if this frame doesn't contain a binding + // If cleanEnv == false then if this frame doesn't contain a binding // previous bindings will be used. upValues bindingFrame } @@ -52,7 +52,7 @@ func makeEnvironment(upValues bindingFrame, sb selfBinding) environment { func (i *interpreter) getCurrentStackTrace() []traceFrame { var result []traceFrame for _, f := range i.stack.stack { - if f.isCall { + if f.cleanEnv { result = append(result, traceElementToTraceFrame(f.trace)) } } @@ -65,8 +65,7 @@ func (i *interpreter) getCurrentStackTrace() []traceFrame { type callFrame struct { // True if it switches to a clean environment (function call or array element) // False otherwise, e.g. for local - // This makes callFrame a misnomer as it is technically not always a call... - isCall bool + cleanEnv bool // Tracing information about the place where it was called from. trace traceElement @@ -87,8 +86,8 @@ func dumpCallFrame(c *callFrame) string { } else { loc = *c.trace.loc } - return fmt.Sprintf("", - c.isCall, + return fmt.Sprintf("", + c.cleanEnv, loc, c.trimmable, ) @@ -121,7 +120,7 @@ func (s *callStack) top() *callFrame { // of the frame we want to pop. func (s *callStack) popIfExists(whichFrame int) { if len(s.stack) == whichFrame { - if s.top().isCall { + if s.top().cleanEnv { s.calls-- } s.setCurrentTrace(s.stack[len(s.stack)-1].trace) @@ -132,7 +131,7 @@ func (s *callStack) popIfExists(whichFrame int) { /** If there is a trimmable frame followed by some locals, pop them all. */ func (s *callStack) tailCallTrimStack() { for i := len(s.stack) - 1; i >= 0; i-- { - if s.stack[i].isCall { + if s.stack[i].cleanEnv { if !s.stack[i].trimmable { return } @@ -167,7 +166,7 @@ func (s *callStack) newCall(env environment, trimmable bool) { panic("Saving empty traceElement on stack") } s.stack = append(s.stack, &callFrame{ - isCall: true, + cleanEnv: true, trace: s.currentTrace, env: env, trimmable: trimmable, @@ -187,7 +186,7 @@ func (s *callStack) newLocal(vars bindingFrame) { // getSelfBinding resolves the self construct func (s *callStack) getSelfBinding() selfBinding { for i := len(s.stack) - 1; i >= 0; i-- { - if s.stack[i].isCall { + if s.stack[i].cleanEnv { return s.stack[i].env.selfBinding } } @@ -201,7 +200,7 @@ func (s *callStack) lookUpVar(id ast.Identifier) *cachedThunk { if present { return bind } - if s.stack[i].isCall { + if s.stack[i].cleanEnv { // Nothing beyond the captured environment of the thunk / closure. break } @@ -654,6 +653,15 @@ func (i *interpreter) manifestJSON(v value) (interface{}, error) { if i.stack.currentTrace == (traceElement{}) { panic("manifesting JSON with empty traceElement") } + + // Fresh frame for better stack traces + err := i.newCall(environment{}, false) + if err != nil { + return nil, err + } + stackSize := len(i.stack.stack) + defer i.stack.popIfExists(stackSize) + switch v := v.(type) { case *valueBoolean: @@ -673,16 +681,23 @@ func (i *interpreter) manifestJSON(v value) (interface{}, error) { case *valueArray: result := make([]interface{}, 0, len(v.elements)) - for _, th := range v.elements { + for index, th := range v.elements { + msg := ast.MakeLocationRangeMessage(fmt.Sprintf("Array element %d", index)) + i.stack.setCurrentTrace(traceElement{ + loc: &msg, + }) elVal, err := i.evaluatePV(th) if err != nil { + i.stack.clearCurrentTrace() return nil, err } elem, err := i.manifestJSON(elVal) if err != nil { + i.stack.clearCurrentTrace() return nil, err } result = append(result, elem) + i.stack.clearCurrentTrace() } return result, nil @@ -690,24 +705,37 @@ func (i *interpreter) manifestJSON(v value) (interface{}, error) { fieldNames := objectFields(v, withoutHidden) sort.Strings(fieldNames) + msg := ast.MakeLocationRangeMessage("Checking object assertions") + i.stack.setCurrentTrace(traceElement{ + loc: &msg, + }) err := checkAssertions(i, v) if err != nil { + i.stack.clearCurrentTrace() return nil, err } + i.stack.clearCurrentTrace() result := make(map[string]interface{}) for _, fieldName := range fieldNames { + msg := ast.MakeLocationRangeMessage(fmt.Sprintf("Field %#v", fieldName)) + i.stack.setCurrentTrace(traceElement{ + loc: &msg, + }) fieldVal, err := v.index(i, fieldName) if err != nil { + i.stack.clearCurrentTrace() return nil, err } field, err := i.manifestJSON(fieldVal) if err != nil { + i.stack.clearCurrentTrace() return nil, err } result[fieldName] = field + i.stack.clearCurrentTrace() } return result, nil @@ -944,10 +972,13 @@ func (i *interpreter) EvalInCleanEnv(env *environment, ast ast.Node, trimmable b stackSize := len(i.stack.stack) val, err := i.evaluate(ast, tailCall) + if err != nil { + return nil, err + } i.stack.popIfExists(stackSize) - return val, err + return val, nil } func (i *interpreter) evaluatePV(ph potentialValue) (value, error) { @@ -1211,7 +1242,14 @@ func makeInitialEnv(filename string, baseStd *valueObject) environment { ) } -func evaluateAux(i *interpreter, node ast.Node, tla vmExtMap) (value, traceElement, error) { +func manifestationTrace() traceElement { + manifestationLoc := ast.MakeLocationRangeMessage("During manifestation") + return traceElement{ + loc: &manifestationLoc, + } +} + +func evaluateAux(i *interpreter, node ast.Node, tla vmExtMap) (value, error) { evalLoc := ast.MakeLocationRangeMessage("During evaluation") evalTrace := traceElement{ loc: &evalLoc, @@ -1221,7 +1259,7 @@ func evaluateAux(i *interpreter, node ast.Node, tla vmExtMap) (value, traceEleme result, err := i.EvalInCleanEnv(&env, node, false) i.stack.clearCurrentTrace() if err != nil { - return nil, traceElement{}, err + return nil, err } // If it's not a function, ignore TLA if f, ok := result.(*valueFunction); ok { @@ -1238,14 +1276,10 @@ func evaluateAux(i *interpreter, node ast.Node, tla vmExtMap) (value, traceEleme result, err = f.call(i, args) i.stack.clearCurrentTrace() if err != nil { - return nil, traceElement{}, err + return nil, err } } - manifestationLoc := ast.MakeLocationRangeMessage("During manifestation") - manifestationTrace := traceElement{ - loc: &manifestationLoc, - } - return result, manifestationTrace, nil + return result, nil } // TODO(sbarzowski) this function takes far too many arguments - build interpreter in vm instead @@ -1257,13 +1291,13 @@ func evaluate(node ast.Node, ext vmExtMap, tla vmExtMap, nativeFuncs map[string] return "", err } - result, manifestationTrace, err := evaluateAux(i, node, tla) + result, err := evaluateAux(i, node, tla) if err != nil { return "", err } var buf bytes.Buffer - i.stack.setCurrentTrace(manifestationTrace) + i.stack.setCurrentTrace(manifestationTrace()) if stringOutputMode { err = i.manifestString(&buf, result) } else { @@ -1286,12 +1320,12 @@ func evaluateMulti(node ast.Node, ext vmExtMap, tla vmExtMap, nativeFuncs map[st return nil, err } - result, manifestationTrace, err := evaluateAux(i, node, tla) + result, err := evaluateAux(i, node, tla) if err != nil { return nil, err } - i.stack.setCurrentTrace(manifestationTrace) + i.stack.setCurrentTrace(manifestationTrace()) manifested, err := i.manifestAndSerializeMulti(result, stringOutputMode) i.stack.clearCurrentTrace() return manifested, err @@ -1306,12 +1340,12 @@ func evaluateStream(node ast.Node, ext vmExtMap, tla vmExtMap, nativeFuncs map[s return nil, err } - result, manifestationTrace, err := evaluateAux(i, node, tla) + result, err := evaluateAux(i, node, tla) if err != nil { return nil, err } - i.stack.setCurrentTrace(manifestationTrace) + i.stack.setCurrentTrace(manifestationTrace()) manifested, err := i.manifestAndSerializeYAMLStream(result) i.stack.clearCurrentTrace() return manifested, err diff --git a/linter/internal/types/stdlib.go b/linter/internal/types/stdlib.go index 0b78265..9abf264 100644 --- a/linter/internal/types/stdlib.go +++ b/linter/internal/types/stdlib.go @@ -124,7 +124,7 @@ func prepareStdlib(g *typeGraph) { "setInter": g.newFuncType(anyArrayType, []ast.Parameter{required("a"), required("b"), optional("keyF")}), "setUnion": g.newFuncType(anyArrayType, []ast.Parameter{required("a"), required("b"), optional("keyF")}), "setDiff": g.newFuncType(anyArrayType, []ast.Parameter{required("a"), required("b"), optional("keyF")}), - "setMember": g.newFuncType(anyArrayType, []ast.Parameter{required("x"), required("arr"), optional("keyF")}), + "setMember": g.newFuncType(boolType, []ast.Parameter{required("x"), required("arr"), optional("keyF")}), // Encoding diff --git a/linter/testdata/stdlib_return_types.jsonnet b/linter/testdata/stdlib_return_types.jsonnet new file mode 100644 index 0000000..9358d04 --- /dev/null +++ b/linter/testdata/stdlib_return_types.jsonnet @@ -0,0 +1 @@ +!std.setMember([1, 2, 3], 1) \ No newline at end of file diff --git a/linter/testdata/stdlib_return_types.linter.golden b/linter/testdata/stdlib_return_types.linter.golden new file mode 100644 index 0000000..e69de29 diff --git a/testdata/error_in_object_local.golden b/testdata/error_in_object_local.golden index 712abd3..d8a1252 100644 --- a/testdata/error_in_object_local.golden +++ b/testdata/error_in_object_local.golden @@ -9,6 +9,9 @@ RUNTIME ERROR: xxx { local foo(bar) = error bar, baz: foo("xxx") } +------------------------------------------------- + Field "baz" + ------------------------------------------------- During manifestation diff --git a/testdata/function_manifested.golden b/testdata/function_manifested.golden index a8f6e21..3f27b30 100644 --- a/testdata/function_manifested.golden +++ b/testdata/function_manifested.golden @@ -1,4 +1,7 @@ RUNTIME ERROR: couldn't manifest function as JSON +------------------------------------------------- + Field "f" + ------------------------------------------------- During manifestation diff --git a/testdata/missing_super.golden b/testdata/missing_super.golden index 376aa86..ac55f9a 100644 --- a/testdata/missing_super.golden +++ b/testdata/missing_super.golden @@ -4,6 +4,9 @@ RUNTIME ERROR: Attempt to use super when there is no super class. { x: super.x } +------------------------------------------------- + Field "x" + ------------------------------------------------- During manifestation diff --git a/testdata/object_comp_err_elem.golden b/testdata/object_comp_err_elem.golden index 764ac73..63c3658 100644 --- a/testdata/object_comp_err_elem.golden +++ b/testdata/object_comp_err_elem.golden @@ -4,6 +4,9 @@ RUNTIME ERROR: xxx { ["x"]: error "xxx" for x in [1] } +------------------------------------------------- + Field "x" + ------------------------------------------------- During manifestation diff --git a/testdata/object_invariant10.golden b/testdata/object_invariant10.golden index f263854..eda059b 100644 --- a/testdata/object_invariant10.golden +++ b/testdata/object_invariant10.golden @@ -1,6 +1,11 @@ RUNTIME ERROR: Object assertion failed. ------------------------------------------------- - + testdata/object_invariant10:1:16-28 + +{ assert true, assert false } + +------------------------------------------------- + Checking object assertions ------------------------------------------------- During manifestation diff --git a/testdata/object_invariant11.golden b/testdata/object_invariant11.golden index 7fcf45b..0221dae 100644 --- a/testdata/object_invariant11.golden +++ b/testdata/object_invariant11.golden @@ -1,6 +1,8 @@ RUNTIME ERROR: Object assertion failed. ------------------------------------------------- - + testdata/object_invariant11:1:3-15 + +{ assert false }.x ------------------------------------------------- testdata/object_invariant11:1:1-19 $ diff --git a/testdata/object_invariant13.golden b/testdata/object_invariant13.golden index bf082ee..c741707 100644 --- a/testdata/object_invariant13.golden +++ b/testdata/object_invariant13.golden @@ -4,6 +4,9 @@ RUNTIME ERROR: x { assert error "x" } +------------------------------------------------- + Checking object assertions + ------------------------------------------------- During manifestation diff --git a/testdata/object_invariant14.golden b/testdata/object_invariant14.golden index 012d66a..1f5ab45 100644 --- a/testdata/object_invariant14.golden +++ b/testdata/object_invariant14.golden @@ -1,6 +1,11 @@ RUNTIME ERROR: xxx ------------------------------------------------- - + testdata/object_invariant14:1:3-22 + +{ assert false: "xxx" } + +------------------------------------------------- + Checking object assertions ------------------------------------------------- During manifestation diff --git a/testdata/object_invariant2.golden b/testdata/object_invariant2.golden index f263854..3de0825 100644 --- a/testdata/object_invariant2.golden +++ b/testdata/object_invariant2.golden @@ -1,6 +1,11 @@ RUNTIME ERROR: Object assertion failed. ------------------------------------------------- - + testdata/object_invariant2:1:3-15 + +{ assert false } + +------------------------------------------------- + Checking object assertions ------------------------------------------------- During manifestation diff --git a/testdata/object_invariant7.golden b/testdata/object_invariant7.golden index c375c8a..971e1e0 100644 --- a/testdata/object_invariant7.golden +++ b/testdata/object_invariant7.golden @@ -4,6 +4,9 @@ RUNTIME ERROR: Attempt to use super when there is no super class. { x: 5, assert super.x == 5 } +------------------------------------------------- + Checking object assertions + ------------------------------------------------- During manifestation diff --git a/testdata/object_invariant8.golden b/testdata/object_invariant8.golden index f263854..611565f 100644 --- a/testdata/object_invariant8.golden +++ b/testdata/object_invariant8.golden @@ -1,6 +1,11 @@ RUNTIME ERROR: Object assertion failed. ------------------------------------------------- - + testdata/object_invariant8:1:9-27 + +{ x: 5, assert self.x == 4 } + +------------------------------------------------- + Checking object assertions ------------------------------------------------- During manifestation diff --git a/testdata/object_invariant9.golden b/testdata/object_invariant9.golden index f263854..889ee20 100644 --- a/testdata/object_invariant9.golden +++ b/testdata/object_invariant9.golden @@ -1,6 +1,11 @@ RUNTIME ERROR: Object assertion failed. ------------------------------------------------- - + testdata/object_invariant9:1:16-28 + +{ assert true, assert false } + +------------------------------------------------- + Checking object assertions ------------------------------------------------- During manifestation diff --git a/testdata/object_invariant_plus.golden b/testdata/object_invariant_plus.golden index f263854..eab48c9 100644 --- a/testdata/object_invariant_plus.golden +++ b/testdata/object_invariant_plus.golden @@ -1,6 +1,11 @@ RUNTIME ERROR: Object assertion failed. ------------------------------------------------- - + testdata/object_invariant_plus:1:2-14 + +{assert false} + {assert true} + +------------------------------------------------- + Checking object assertions ------------------------------------------------- During manifestation diff --git a/testdata/object_invariant_plus2.golden b/testdata/object_invariant_plus2.golden index f263854..0988abf 100644 --- a/testdata/object_invariant_plus2.golden +++ b/testdata/object_invariant_plus2.golden @@ -1,6 +1,11 @@ RUNTIME ERROR: Object assertion failed. ------------------------------------------------- - + testdata/object_invariant_plus2:1:18-30 + +{assert true} + {assert false} + +------------------------------------------------- + Checking object assertions ------------------------------------------------- During manifestation diff --git a/testdata/object_invariant_plus6.golden b/testdata/object_invariant_plus6.golden index 20e53d9..f1c459c 100644 --- a/testdata/object_invariant_plus6.golden +++ b/testdata/object_invariant_plus6.golden @@ -1,6 +1,11 @@ RUNTIME ERROR: yyy ------------------------------------------------- - + testdata/object_invariant_plus6:1:27-46 + +{ assert false: "xxx" } { assert false: "yyy" } + +------------------------------------------------- + Checking object assertions ------------------------------------------------- During manifestation diff --git a/testdata/stacktrace_assert.golden b/testdata/stacktrace_assert.golden new file mode 100644 index 0000000..09227ea --- /dev/null +++ b/testdata/stacktrace_assert.golden @@ -0,0 +1,13 @@ +RUNTIME ERROR: Object assertion failed. +------------------------------------------------- + testdata/stacktrace_assert:1:3-15 + +{ assert false } + +------------------------------------------------- + Checking object assertions + +------------------------------------------------- + During manifestation + + diff --git a/testdata/stacktrace_assert.jsonnet b/testdata/stacktrace_assert.jsonnet new file mode 100644 index 0000000..5a90dd9 --- /dev/null +++ b/testdata/stacktrace_assert.jsonnet @@ -0,0 +1 @@ +{ assert false } \ No newline at end of file diff --git a/testdata/stacktrace_assert.linter.golden b/testdata/stacktrace_assert.linter.golden new file mode 100644 index 0000000..e69de29 diff --git a/testdata/stacktrace_plussuper.golden b/testdata/stacktrace_plussuper.golden new file mode 100644 index 0000000..8d8db3c --- /dev/null +++ b/testdata/stacktrace_plussuper.golden @@ -0,0 +1,13 @@ +RUNTIME ERROR: Unexpected type null +------------------------------------------------- + testdata/stacktrace_plussuper:2:7-9 +: + + a+: {}, + +------------------------------------------------- + Field "a" + +------------------------------------------------- + During manifestation + + diff --git a/testdata/stacktrace_plussuper.jsonnet b/testdata/stacktrace_plussuper.jsonnet new file mode 100644 index 0000000..cd681b9 --- /dev/null +++ b/testdata/stacktrace_plussuper.jsonnet @@ -0,0 +1,5 @@ +local a(input) = input + { + a+: {}, +}; + +a({a: null}) \ No newline at end of file diff --git a/testdata/stacktrace_plussuper.linter.golden b/testdata/stacktrace_plussuper.linter.golden new file mode 100644 index 0000000..e69de29 diff --git a/testdata/supersugar8.golden b/testdata/supersugar8.golden index f263854..0abefb9 100644 --- a/testdata/supersugar8.golden +++ b/testdata/supersugar8.golden @@ -1,6 +1,11 @@ RUNTIME ERROR: Object assertion failed. ------------------------------------------------- - + testdata/supersugar8:1:3-16 + +{ assert self.x } { x +: false } + +------------------------------------------------- + Checking object assertions ------------------------------------------------- During manifestation diff --git a/thunks.go b/thunks.go index 1cd4175..d332d51 100644 --- a/thunks.go +++ b/thunks.go @@ -36,6 +36,10 @@ func (rv *readyValue) evaluate(i *interpreter, sb selfBinding, origBinding bindi return rv.content, nil } +func (rv *readyValue) loc() *ast.LocationRange { + return &ast.LocationRange{} +} + // potentialValues // ------------------------------------- @@ -89,7 +93,12 @@ type codeUnboundField struct { func (f *codeUnboundField) evaluate(i *interpreter, sb selfBinding, origBindings bindingFrame, fieldName string) (value, error) { env := makeEnvironment(origBindings, sb) - return i.EvalInCleanEnv(&env, f.body, false) + val, err := i.EvalInCleanEnv(&env, f.body, false) + return val, err +} + +func (f *codeUnboundField) loc() *ast.LocationRange { + return f.body.Loc() } // Provide additional bindings for a field. It shadows bindings from the object. @@ -110,24 +119,54 @@ func (f *bindingsUnboundField) evaluate(i *interpreter, sb selfBinding, origBind return f.inner.evaluate(i, sb, upValues, fieldName) } +func (f *bindingsUnboundField) loc() *ast.LocationRange { + return f.inner.loc() +} + // plusSuperUnboundField represents a `field+: ...` that hasn't been bound to an object. type plusSuperUnboundField struct { inner unboundField } func (f *plusSuperUnboundField) evaluate(i *interpreter, sb selfBinding, origBinding bindingFrame, fieldName string) (value, error) { + err := i.newCall(environment{}, false) + if err != nil { + return nil, err + } + stackSize := len(i.stack.stack) + defer i.stack.popIfExists(stackSize) + + context := "+:" + i.stack.setCurrentTrace(traceElement{ + loc: f.loc(), + context: &context, + }) + defer i.stack.clearCurrentTrace() + right, err := f.inner.evaluate(i, sb, origBinding, fieldName) if err != nil { return nil, err } + if !objectHasField(sb.super(), fieldName, withHidden) { return right, nil } + left, err := objectIndex(i, sb.super(), fieldName) if err != nil { return nil, err } - return builtinPlus(i, left, right) + + value, err := builtinPlus(i, left, right) + if err != nil { + return nil, err + } + + return value, nil +} + +func (f *plusSuperUnboundField) loc() *ast.LocationRange { + return f.inner.loc() } // evalCallables diff --git a/value.go b/value.go index 7598a7d..a35c3fd 100644 --- a/value.go +++ b/value.go @@ -572,6 +572,7 @@ func checkAssertionsHelper(i *interpreter, obj *valueObject, curr uncachedObject default: panic(fmt.Sprintf("Unknown object type %#v", curr)) } + } func checkAssertions(i *interpreter, obj *valueObject) error { @@ -611,6 +612,7 @@ type simpleObjectField struct { // unboundField is a field that doesn't know yet in which object it is. type unboundField interface { evaluate(i *interpreter, sb selfBinding, origBinding bindingFrame, fieldName string) (value, error) + loc() *ast.LocationRange } // extendedObject represents an object created through inheritance (left + right).