diff --git a/sjsonnet/src/sjsonnet/Expr.scala b/sjsonnet/src/sjsonnet/Expr.scala index 8c32a1eb..1ee29c97 100644 --- a/sjsonnet/src/sjsonnet/Expr.scala +++ b/sjsonnet/src/sjsonnet/Expr.scala @@ -18,6 +18,8 @@ trait Expr{ val n = getClass.getName if(n.startsWith("sjsonnet.Expr$")) n.substring(14) else n } + + override def toString: String = s"$exprErrorString@$pos" } object Expr{ private final def arrStr(a: Array[_]): String = { @@ -165,7 +167,9 @@ object Expr{ trait ObjBody extends Expr object ObjBody{ - case class MemberList(pos: Position, binds: Array[Bind], fields: Array[Member.Field], asserts: Array[Member.AssertStmt]) extends ObjBody + case class MemberList(pos: Position, binds: Array[Bind], fields: Array[Member.Field], asserts: Array[Member.AssertStmt]) extends ObjBody { + override def toString: String = s"MemberList($pos, ${arrStr(binds)}, ${arrStr(fields)}, ${arrStr(asserts)})" + } case class ObjComp(pos: Position, preLocals: Array[Bind], key: Expr, diff --git a/sjsonnet/src/sjsonnet/Parser.scala b/sjsonnet/src/sjsonnet/Parser.scala index 3db4fb85..e1555b27 100644 --- a/sjsonnet/src/sjsonnet/Parser.scala +++ b/sjsonnet/src/sjsonnet/Parser.scala @@ -328,6 +328,15 @@ class Parser(val currentFile: Path, case (pos, exprs, None) => val binds = { val b = exprs.iterator.filter(_.isInstanceOf[Expr.Bind]).asInstanceOf[Iterator[Expr.Bind]].toArray + val seen = collection.mutable.Set.empty[String] + var overlap: String = null + b.foreach { + case Expr.Bind(_, n, _, _) => + if (seen(n)) overlap = n + else seen.add(n) + case _ => + } + if (overlap != null) Fail.opaque("no duplicate local: " + overlap) if(b.isEmpty) null else b } val fields = exprs.iterator.filter(_.isInstanceOf[Expr.Member.Field]).asInstanceOf[Iterator[Expr.Member.Field]].toArray diff --git a/sjsonnet/test/src/sjsonnet/ParserTests.scala b/sjsonnet/test/src/sjsonnet/ParserTests.scala index 37da8c5b..5fb5668b 100644 --- a/sjsonnet/test/src/sjsonnet/ParserTests.scala +++ b/sjsonnet/test/src/sjsonnet/ParserTests.scala @@ -4,7 +4,8 @@ import scala.collection.mutable import utest._ import Expr._ import fastparse.Parsed -import Val.{True, Num} +import Val.{Num, True} +import sjsonnet.Expr.FieldName.Fixed object ParserTests extends TestSuite{ def parse(s: String, strictImportSyntax: Boolean = false) = fastparse.parse(s, new Parser(null, strictImportSyntax, mutable.HashMap.empty, mutable.HashMap.empty).document(_)).get.value._1 def parseErr(s: String, strictImportSyntax: Boolean = false) = fastparse.parse(s, new Parser(null, strictImportSyntax, mutable.HashMap.empty, mutable.HashMap.empty).document(_), verboseFailures = true).asInstanceOf[Parsed.Failure].msg @@ -26,6 +27,18 @@ object ParserTests extends TestSuite{ test("duplicateFields") { parseErr("{ a: 1, a: 2 }") ==> """Expected no duplicate field: a:1:14, found "}"""" } + test("localInObj") { + parse("""{ + |local x = 1, + |a: x, + |}""".stripMargin).toString ==> (ObjBody.MemberList(pos(2), Array(Bind(pos(8), "x", null, Num(pos(12), 1))), + Array(Member.Field(pos(15), Fixed("a"), false, null, Member.Visibility.Normal, Id(pos(18), "x"))), null)).toString + parseErr("""{ + |local x = 1, + |local x = x + 1, + |a: x, + |}""".stripMargin) ==> """Expected no duplicate local: x:5:1, found "}"""" + } test("givenDuplicateFieldsInListComprehension_expectError") { parseErr("""{ ["bar"]: x for x in [1, 2]}""") ==> """Expected no duplicate field: "bar" :1:29, found "}"""" }