diff --git a/src/jsonata.js b/src/jsonata.js index 184de40..693df56 100644 --- a/src/jsonata.js +++ b/src/jsonata.js @@ -459,8 +459,14 @@ var jsonata = (function() { try { return await evaluateBooleanExpression(lhs, evalrhs, op); } catch(err) { - err.position = expr.position; - err.token = op; + // The RHS is evaluated inside evaluateBooleanExpression so that it can be + // short-circuited, which means an error raised by the RHS expression itself + // surfaces here. If that error already names what raised it, keep that + // attribution instead of blaming this operator for it. + if (typeof err.token === 'undefined') { + err.position = expr.position; + err.token = op; + } throw err; } } diff --git a/test/implementation-tests.js b/test/implementation-tests.js index e213ed7..4f9f586 100644 --- a/test/implementation-tests.js +++ b/test/implementation-tests.js @@ -1204,4 +1204,74 @@ describe("Concurrency tests", () => { {"id": "c", "delayed": "done:c"} ]) }) -}) \ No newline at end of file +}) + +describe("Error reporting for the `and`/`or` operators", () => { + describe("error raised while evaluating the right hand side of `and`", function() { + it("should be reported against the failing function, not the operator", async function() { + var expr = jsonata("(1=1 and $contains([''], ''))"); + var error; + try { + await expr.evaluate({}); + } catch (err) { + error = err; + } + expect(error).to.not.equal(undefined); + expect(error.code).to.equal("T0410"); + expect(error.token).to.equal("contains"); + }); + }); + + describe("error raised while evaluating the right hand side of `or`", function() { + it("should be reported against the failing function, not the operator", async function() { + var expr = jsonata("(1=0 or $contains([''], ''))"); + var error; + try { + await expr.evaluate({}); + } catch (err) { + error = err; + } + expect(error).to.not.equal(undefined); + expect(error.code).to.equal("T0410"); + expect(error.token).to.equal("contains"); + }); + }); + + describe("the same expression inline and inside a user-defined function", function() { + it("should report the same error", async function() { + var inline = jsonata("(1=1 and $contains([''], ''))"); + var wrapped = jsonata("( $fn := function($a, $b) { $a and $b }; $fn(1=1, $contains([''], '')) )"); + var inlineError; + var wrappedError; + try { + await inline.evaluate({}); + } catch (err) { + inlineError = err; + } + try { + await wrapped.evaluate({}); + } catch (err) { + wrappedError = err; + } + expect(inlineError).to.not.equal(undefined); + expect(wrappedError).to.not.equal(undefined); + expect(inlineError.code).to.equal(wrappedError.code); + expect(inlineError.token).to.equal(wrappedError.token); + }); + }); + + describe("error that nothing else has attributed", function() { + it("should still be reported against the operator", async function() { + var expr = jsonata('(true and {"k": $.a, "k": $.b})'); + var error; + try { + await expr.evaluate({ a: 1, b: 2 }); + } catch (err) { + error = err; + } + expect(error).to.not.equal(undefined); + expect(error.code).to.equal("D1009"); + expect(error.token).to.equal("and"); + }); + }); +}); diff --git a/test/test-suite/groups/boolean-expresssions/case031.json b/test/test-suite/groups/boolean-expresssions/case031.json new file mode 100644 index 0000000..e14a4cd --- /dev/null +++ b/test/test-suite/groups/boolean-expresssions/case031.json @@ -0,0 +1,10 @@ +{ + "description": "error on the right of and is reported against the failing function, not the operator", + "expr": "(1=1 and $contains([''], ''))", + "dataset": null, + "bindings": {}, + "error": { + "code": "T0410", + "token": "contains" + } +} diff --git a/test/test-suite/groups/boolean-expresssions/case032.json b/test/test-suite/groups/boolean-expresssions/case032.json new file mode 100644 index 0000000..d057617 --- /dev/null +++ b/test/test-suite/groups/boolean-expresssions/case032.json @@ -0,0 +1,10 @@ +{ + "description": "error on the right of or is reported against the failing function, not the operator", + "expr": "(1=0 or $contains([''], ''))", + "dataset": null, + "bindings": {}, + "error": { + "code": "T0410", + "token": "contains" + } +} diff --git a/test/test-suite/groups/boolean-expresssions/case033.json b/test/test-suite/groups/boolean-expresssions/case033.json new file mode 100644 index 0000000..35635de --- /dev/null +++ b/test/test-suite/groups/boolean-expresssions/case033.json @@ -0,0 +1,13 @@ +{ + "description": "error that nothing else has attributed is still reported against the boolean operator", + "expr": "(true and {\"k\": $.a, \"k\": $.b})", + "data": { + "a": 1, + "b": 2 + }, + "bindings": {}, + "error": { + "code": "D1009", + "token": "and" + } +}