From 0f088022aac31f8ce9f174490c45f481db2faae9 Mon Sep 17 00:00:00 2001 From: Stefan Zeiger Date: Thu, 28 Jan 2016 18:49:47 +0100 Subject: SI-9572 Check for illegal tuple sizes in the parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds explicit checks with syntax errors for tuple literals and types of more than 22 elements. An alternative approach to fixing SI-9572 would be to revert to the old failure mode of Scala 2.10 where references to arbitrary `scala.TupleXY` would be generated in the parser, which then leads to “type/object not found” errors in the typechecker. This fix here is more intrusive but arguably provides a better user experience. Methods `stripParens` and `makeBinop` are moved from `TreeBuilder` to `Parsers` because they can now generate syntax errors. New methods `makeSafeTupleType` and `makeSafeTupleTerm` implement the error checking on top of `makeTupleType` and `makeTupleTerm`. They are overridden with no-op versions in the quasiquotes parser because it also overrides `makeTupleType` and `makeTupleTerm` in a way that supports arbitrary tuple sizes. --- .../scala/tools/nsc/ast/parser/Parsers.scala | 57 ++++++++++++++++++++-- 1 file changed, 54 insertions(+), 3 deletions(-) (limited to 'src/compiler/scala/tools/nsc/ast/parser/Parsers.scala') diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 4494a8ac8d..c04d305f9e 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -766,7 +766,58 @@ self => @inline final def caseSeparated[T](part: => T): List[T] = tokenSeparated(CASE, sepFirst = true, part) def readAnnots(part: => Tree): List[Tree] = tokenSeparated(AT, sepFirst = true, part) -/* --------- OPERAND/OPERATOR STACK --------------------------------------- */ + /** Create a tuple type Tree. If the arity is not supported, a syntax error is emitted. */ + def makeSafeTupleType(elems: List[Tree], offset: Offset) = { + if (checkTupleSize(elems, offset)) makeTupleType(elems) + else makeTupleType(Nil) // create a dummy node; makeTupleType(elems) would fail + } + + /** Create a tuple term Tree. If the arity is not supported, a syntax error is emitted. */ + def makeSafeTupleTerm(elems: List[Tree], offset: Offset) = { + checkTupleSize(elems, offset) + makeTupleTerm(elems) + } + + private[this] def checkTupleSize(elems: List[Tree], offset: Offset): Boolean = + if (elems.lengthCompare(definitions.MaxTupleArity) > 0) { + syntaxError(offset, "too many elements for tuple: "+elems.length+", allowed: "+definitions.MaxTupleArity, skipIt = false) + false + } else true + + /** Strip the artifitial `Parens` node to create a tuple term Tree. */ + def stripParens(t: Tree) = t match { + case Parens(ts) => atPos(t.pos) { makeSafeTupleTerm(ts, t.pos.point) } + case _ => t + } + + /** Create tree representing (unencoded) binary operation expression or pattern. */ + def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position, targs: List[Tree] = Nil): Tree = { + require(isExpr || targs.isEmpty || targs.exists(_.isErroneous), s"Incompatible args to makeBinop: !isExpr but targs=$targs") + + def mkSelection(t: Tree) = { + def sel = atPos(opPos union t.pos)(Select(stripParens(t), op.encode)) + if (targs.isEmpty) sel else atPos(left.pos)(TypeApply(sel, targs)) + } + def mkNamed(args: List[Tree]) = if (isExpr) args map treeInfo.assignmentToMaybeNamedArg else args + val arguments = right match { + case Parens(args) => mkNamed(args) + case _ => List(right) + } + if (isExpr) { + if (treeInfo.isLeftAssoc(op)) { + Apply(mkSelection(left), arguments) + } else { + val x = freshTermName() + Block( + List(ValDef(Modifiers(symtab.Flags.SYNTHETIC | symtab.Flags.ARTIFACT), x, TypeTree(), stripParens(left))), + Apply(mkSelection(right), List(Ident(x)))) + } + } else { + Apply(Ident(op.encode), stripParens(left) :: arguments) + } + } + + /* --------- OPERAND/OPERATOR STACK --------------------------------------- */ /** Modes for infix types. */ object InfixMode extends Enumeration { @@ -870,7 +921,7 @@ self => atPos(start, in.skipToken()) { makeFunctionTypeTree(ts, typ()) } else { ts foreach checkNotByNameOrVarargs - val tuple = atPos(start) { makeTupleType(ts) } + val tuple = atPos(start) { makeSafeTupleType(ts, start) } infixTypeRest( compoundTypeRest( annotTypeRest( @@ -937,7 +988,7 @@ self => def simpleType(): Tree = { val start = in.offset simpleTypeRest(in.token match { - case LPAREN => atPos(start)(makeTupleType(inParens(types()))) + case LPAREN => atPos(start)(makeSafeTupleType(inParens(types()), start)) case USCORE => wildcardType(in.skipToken()) case _ => path(thisOK = false, typeOK = true) match { -- cgit v1.2.3