diff --git a/sjsonnet/src/sjsonnet/Evaluator.scala b/sjsonnet/src/sjsonnet/Evaluator.scala index 31d1cb2d..cef32e9a 100644 --- a/sjsonnet/src/sjsonnet/Evaluator.scala +++ b/sjsonnet/src/sjsonnet/Evaluator.scala @@ -589,12 +589,16 @@ class Evaluator(resolver: CachedResolver, visitExpr(e.key)(s) match { case Val.Str(_, k) => + val prev_length = builder.size() builder.put(k, new Val.Obj.Member(false, Visibility.Normal) { def invoke(self: Val.Obj, sup: Val.Obj, fs: FileScope, ev: EvalScope): Val = visitExpr(e.value)( s.extend(newBindings, self, null) ) }) + if (prev_length == builder.size()) { + Error.fail(s"Duplicate key ${k} in evaluated object comprehension.", e.pos); + } case Val.Null(_) => // do nothing } } diff --git a/sjsonnet/test/src/sjsonnet/EvaluatorTests.scala b/sjsonnet/test/src/sjsonnet/EvaluatorTests.scala index 9ff12b46..77bdeb32 100644 --- a/sjsonnet/test/src/sjsonnet/EvaluatorTests.scala +++ b/sjsonnet/test/src/sjsonnet/EvaluatorTests.scala @@ -338,6 +338,19 @@ object EvaluatorTests extends TestSuite{ test("givenNoDuplicateFieldsInListComprehension2_expectSuccess") { eval("""{ ["bar_" + x]: x for x in [5,12]}""") ==> ujson.Obj("bar_5" -> 5, "bar_12" -> 12) } + test("givenDuplicateFieldsInListComprehension_expectFailure") { + evalErr("""{ [x]: x for x in ["A", "A"]}""") ==> + """sjsonnet.Error: Duplicate key A in evaluated object comprehension. + |at .(:1:3)""".stripMargin + } + test("givenDuplicateFieldsInIndirectListComprehension_expectFailure") { + evalErr( + """local y = { a: "A" }; + |local z = { a: "A" }; + |{ [x.a]: x for x in [y, z]}""".stripMargin) ==> + """sjsonnet.Error: Duplicate key A in evaluated object comprehension. + |at .(:3:3)""".stripMargin + } test("functionEqualsNull") { eval("""local f(x)=null; f == null""") ==> ujson.False eval("""local f=null; f == null""") ==> ujson.True