Skip to content

Commit cc6fea6

Browse files
committed
Merge pull request scala#4927 from szeiger/issue/9572
SI-9572 Check for illegal tuple sizes in the parser
2 parents d796f2c + 0f08802 commit cc6fea6

File tree

5 files changed

+71
-35
lines changed

5 files changed

+71
-35
lines changed

src/compiler/scala/reflect/quasiquotes/Parsers.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ trait Parsers { self: Quasiquotes =>
5959

6060
override implicit lazy val fresh: FreshNameCreator = new FreshNameCreator(nme.QUASIQUOTE_PREFIX)
6161

62+
// Do not check for tuple arity. The placeholders can support arbitrary tuple sizes.
63+
override def makeSafeTupleTerm(trees: List[Tree], offset: Offset): Tree = treeBuilder.makeTupleTerm(trees)
64+
override def makeSafeTupleType(trees: List[Tree], offset: Offset): Tree = treeBuilder.makeTupleType(trees)
65+
6266
override val treeBuilder = new ParserTreeBuilder {
6367
override implicit def fresh: FreshNameCreator = parser.fresh
6468

src/compiler/scala/tools/nsc/ast/parser/Parsers.scala

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,58 @@ self =>
766766
@inline final def caseSeparated[T](part: => T): List[T] = tokenSeparated(CASE, sepFirst = true, part)
767767
def readAnnots(part: => Tree): List[Tree] = tokenSeparated(AT, sepFirst = true, part)
768768

769-
/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
769+
/** Create a tuple type Tree. If the arity is not supported, a syntax error is emitted. */
770+
def makeSafeTupleType(elems: List[Tree], offset: Offset) = {
771+
if (checkTupleSize(elems, offset)) makeTupleType(elems)
772+
else makeTupleType(Nil) // create a dummy node; makeTupleType(elems) would fail
773+
}
774+
775+
/** Create a tuple term Tree. If the arity is not supported, a syntax error is emitted. */
776+
def makeSafeTupleTerm(elems: List[Tree], offset: Offset) = {
777+
checkTupleSize(elems, offset)
778+
makeTupleTerm(elems)
779+
}
780+
781+
private[this] def checkTupleSize(elems: List[Tree], offset: Offset): Boolean =
782+
if (elems.lengthCompare(definitions.MaxTupleArity) > 0) {
783+
syntaxError(offset, "too many elements for tuple: "+elems.length+", allowed: "+definitions.MaxTupleArity, skipIt = false)
784+
false
785+
} else true
786+
787+
/** Strip the artifitial `Parens` node to create a tuple term Tree. */
788+
def stripParens(t: Tree) = t match {
789+
case Parens(ts) => atPos(t.pos) { makeSafeTupleTerm(ts, t.pos.point) }
790+
case _ => t
791+
}
792+
793+
/** Create tree representing (unencoded) binary operation expression or pattern. */
794+
def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position, targs: List[Tree] = Nil): Tree = {
795+
require(isExpr || targs.isEmpty || targs.exists(_.isErroneous), s"Incompatible args to makeBinop: !isExpr but targs=$targs")
796+
797+
def mkSelection(t: Tree) = {
798+
def sel = atPos(opPos union t.pos)(Select(stripParens(t), op.encode))
799+
if (targs.isEmpty) sel else atPos(left.pos)(TypeApply(sel, targs))
800+
}
801+
def mkNamed(args: List[Tree]) = if (isExpr) args map treeInfo.assignmentToMaybeNamedArg else args
802+
val arguments = right match {
803+
case Parens(args) => mkNamed(args)
804+
case _ => List(right)
805+
}
806+
if (isExpr) {
807+
if (treeInfo.isLeftAssoc(op)) {
808+
Apply(mkSelection(left), arguments)
809+
} else {
810+
val x = freshTermName()
811+
Block(
812+
List(ValDef(Modifiers(symtab.Flags.SYNTHETIC | symtab.Flags.ARTIFACT), x, TypeTree(), stripParens(left))),
813+
Apply(mkSelection(right), List(Ident(x))))
814+
}
815+
} else {
816+
Apply(Ident(op.encode), stripParens(left) :: arguments)
817+
}
818+
}
819+
820+
/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
770821

771822
/** Modes for infix types. */
772823
object InfixMode extends Enumeration {
@@ -870,7 +921,7 @@ self =>
870921
atPos(start, in.skipToken()) { makeFunctionTypeTree(ts, typ()) }
871922
else {
872923
ts foreach checkNotByNameOrVarargs
873-
val tuple = atPos(start) { makeTupleType(ts) }
924+
val tuple = atPos(start) { makeSafeTupleType(ts, start) }
874925
infixTypeRest(
875926
compoundTypeRest(
876927
annotTypeRest(
@@ -937,7 +988,7 @@ self =>
937988
def simpleType(): Tree = {
938989
val start = in.offset
939990
simpleTypeRest(in.token match {
940-
case LPAREN => atPos(start)(makeTupleType(inParens(types())))
991+
case LPAREN => atPos(start)(makeSafeTupleType(inParens(types()), start))
941992
case USCORE => wildcardType(in.skipToken())
942993
case _ =>
943994
path(thisOK = false, typeOK = true) match {

src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -43,44 +43,12 @@ abstract class TreeBuilder {
4343

4444
def makeTupleType(elems: List[Tree]) = gen.mkTupleType(elems)
4545

46-
def stripParens(t: Tree) = t match {
47-
case Parens(ts) => atPos(t.pos) { makeTupleTerm(ts) }
48-
case _ => t
49-
}
50-
5146
def makeAnnotated(t: Tree, annot: Tree): Tree =
5247
atPos(annot.pos union t.pos)(Annotated(annot, t))
5348

5449
def makeSelfDef(name: TermName, tpt: Tree): ValDef =
5550
ValDef(Modifiers(PRIVATE), name, tpt, EmptyTree)
5651

57-
/** Create tree representing (unencoded) binary operation expression or pattern. */
58-
def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position, targs: List[Tree] = Nil): Tree = {
59-
require(isExpr || targs.isEmpty || targs.exists(_.isErroneous), s"Incompatible args to makeBinop: !isExpr but targs=$targs")
60-
61-
def mkSelection(t: Tree) = {
62-
def sel = atPos(opPos union t.pos)(Select(stripParens(t), op.encode))
63-
if (targs.isEmpty) sel else atPos(left.pos)(TypeApply(sel, targs))
64-
}
65-
def mkNamed(args: List[Tree]) = if (isExpr) args map treeInfo.assignmentToMaybeNamedArg else args
66-
val arguments = right match {
67-
case Parens(args) => mkNamed(args)
68-
case _ => List(right)
69-
}
70-
if (isExpr) {
71-
if (treeInfo.isLeftAssoc(op)) {
72-
Apply(mkSelection(left), arguments)
73-
} else {
74-
val x = freshTermName()
75-
Block(
76-
List(ValDef(Modifiers(SYNTHETIC | ARTIFACT), x, TypeTree(), stripParens(left))),
77-
Apply(mkSelection(right), List(Ident(x))))
78-
}
79-
} else {
80-
Apply(Ident(op.encode), stripParens(left) :: arguments)
81-
}
82-
}
83-
8452
/** Tree for `od op`, start is start0 if od.pos is borked. */
8553
def makePostfixSelect(start0: Int, end: Int, od: Tree, op: Name): Tree = {
8654
val start = if (od.pos.isDefined) od.pos.start else start0

test/files/neg/t9572.check

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
t9572.scala:3: error: too many elements for tuple: 23, allowed: 22
2+
val term23 = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23)
3+
^
4+
t9572.scala:5: error: too many elements for tuple: 23, allowed: 22
5+
val type23: (Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int) = null
6+
^
7+
two errors found

test/files/neg/t9572.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class T9572 {
2+
val term22 = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)
3+
val term23 = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23)
4+
val type22: (Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int) = null
5+
val type23: (Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int) = null
6+
}

0 commit comments

Comments
 (0)