Skip to content

Commit 29bcc9a

Browse files
Fix "Duplicate Local Variables in Object Scope" (#234)
Fixes #178
1 parent 5149b6e commit 29bcc9a

3 files changed

Lines changed: 28 additions & 2 deletions

File tree

sjsonnet/src/sjsonnet/Expr.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ trait Expr{
1818
val n = getClass.getName
1919
if(n.startsWith("sjsonnet.Expr$")) n.substring(14) else n
2020
}
21+
22+
override def toString: String = s"$exprErrorString@$pos"
2123
}
2224
object Expr{
2325
private final def arrStr(a: Array[_]): String = {
@@ -165,7 +167,9 @@ object Expr{
165167

166168
trait ObjBody extends Expr
167169
object ObjBody{
168-
case class MemberList(pos: Position, binds: Array[Bind], fields: Array[Member.Field], asserts: Array[Member.AssertStmt]) extends ObjBody
170+
case class MemberList(pos: Position, binds: Array[Bind], fields: Array[Member.Field], asserts: Array[Member.AssertStmt]) extends ObjBody {
171+
override def toString: String = s"MemberList($pos, ${arrStr(binds)}, ${arrStr(fields)}, ${arrStr(asserts)})"
172+
}
169173
case class ObjComp(pos: Position,
170174
preLocals: Array[Bind],
171175
key: Expr,

sjsonnet/src/sjsonnet/Parser.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,15 @@ class Parser(val currentFile: Path,
328328
case (pos, exprs, None) =>
329329
val binds = {
330330
val b = exprs.iterator.filter(_.isInstanceOf[Expr.Bind]).asInstanceOf[Iterator[Expr.Bind]].toArray
331+
val seen = collection.mutable.Set.empty[String]
332+
var overlap: String = null
333+
b.foreach {
334+
case Expr.Bind(_, n, _, _) =>
335+
if (seen(n)) overlap = n
336+
else seen.add(n)
337+
case _ =>
338+
}
339+
if (overlap != null) Fail.opaque("no duplicate local: " + overlap)
331340
if(b.isEmpty) null else b
332341
}
333342
val fields = exprs.iterator.filter(_.isInstanceOf[Expr.Member.Field]).asInstanceOf[Iterator[Expr.Member.Field]].toArray

sjsonnet/test/src/sjsonnet/ParserTests.scala

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import scala.collection.mutable
44
import utest._
55
import Expr._
66
import fastparse.Parsed
7-
import Val.{True, Num}
7+
import Val.{Num, True}
8+
import sjsonnet.Expr.FieldName.Fixed
89
object ParserTests extends TestSuite{
910
def parse(s: String, strictImportSyntax: Boolean = false) = fastparse.parse(s, new Parser(null, strictImportSyntax, mutable.HashMap.empty, mutable.HashMap.empty).document(_)).get.value._1
1011
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{
2627
test("duplicateFields") {
2728
parseErr("{ a: 1, a: 2 }") ==> """Expected no duplicate field: a:1:14, found "}""""
2829
}
30+
test("localInObj") {
31+
parse("""{
32+
|local x = 1,
33+
|a: x,
34+
|}""".stripMargin).toString ==> (ObjBody.MemberList(pos(2), Array(Bind(pos(8), "x", null, Num(pos(12), 1))),
35+
Array(Member.Field(pos(15), Fixed("a"), false, null, Member.Visibility.Normal, Id(pos(18), "x"))), null)).toString
36+
parseErr("""{
37+
|local x = 1,
38+
|local x = x + 1,
39+
|a: x,
40+
|}""".stripMargin) ==> """Expected no duplicate local: x:5:1, found "}""""
41+
}
2942
test("givenDuplicateFieldsInListComprehension_expectError") {
3043
parseErr("""{ ["bar"]: x for x in [1, 2]}""") ==> """Expected no duplicate field: "bar" :1:29, found "}""""
3144
}

0 commit comments

Comments
 (0)