summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2013-08-16 11:41:53 -0700
committerAdriaan Moors <adriaan.moors@typesafe.com>2013-08-16 11:41:53 -0700
commit63bce12e94eb4480823627d6328f74befa421bbf (patch)
tree9d4f6b762467e9af15c1d886e7f37caad9064911
parent01f2c2ac45a15312bd45193fd6302b7b01de9db7 (diff)
parent417718b7f68cb3a5c596a96cd351709a1ca18a7b (diff)
downloadscala-63bce12e94eb4480823627d6328f74befa421bbf.tar.gz
scala-63bce12e94eb4480823627d6328f74befa421bbf.tar.bz2
scala-63bce12e94eb4480823627d6328f74befa421bbf.zip
Merge pull request #2830 from densh/topic/stats-parsing-2
updated SI-7331, SI-6843, SI-7731, parser entry point refactoring, assertThrows utility function
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala76
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala2
-rw-r--r--src/compiler/scala/tools/reflect/ToolBoxFactory.scala10
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala94
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala21
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala2
-rw-r--r--src/reflect/scala/reflect/internal/Trees.scala10
-rw-r--r--src/repl/scala/tools/nsc/interpreter/ExprTyper.scala26
-rw-r--r--src/repl/scala/tools/nsc/interpreter/IMain.scala40
-rw-r--r--src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala4
-rw-r--r--src/repl/scala/tools/nsc/interpreter/Power.scala2
-rw-r--r--src/repl/scala/tools/nsc/interpreter/Results.scala2
-rw-r--r--src/repl/scala/tools/nsc/interpreter/package.scala4
-rw-r--r--test/files/neg/quasiquotes-syntax-error-position.check32
-rw-r--r--test/files/neg/quasiquotes-syntax-error-position.scala15
-rw-r--r--test/files/run/t7331a.check2
-rw-r--r--test/files/run/t7331a.scala10
-rw-r--r--test/files/run/t7331b.check3
-rw-r--r--test/files/run/t7331b.scala11
-rw-r--r--test/files/run/t7331c.check3
-rw-r--r--test/files/run/t7331c.scala11
-rw-r--r--test/files/scalacheck/quasiquotes/ErrorProps.scala6
-rw-r--r--test/junit/scala/tools/nsc/symtab/CannotHaveAttrsTest.scala67
-rw-r--r--test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala5
-rw-r--r--test/junit/scala/tools/testing/AssertThrowsTest.scala34
-rw-r--r--test/junit/scala/tools/testing/AssertUtil.scala19
26 files changed, 368 insertions, 143 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index f2d59fcce8..8479df512e 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -334,22 +334,27 @@ self =>
def parseStartRule: () => Tree
- /** This is the general parse entry point.
- */
- def parse(): Tree = {
- val t = parseStartRule()
+ def parseRule[T](rule: this.type => T): T = {
+ val t = rule(this)
accept(EOF)
t
}
+ /** This is the general parse entry point.
+ */
+ def parse(): Tree = parseRule(_.parseStartRule())
+
+ /** This is alternative entry point for repl, script runner, toolbox and quasiquotes.
+ */
+ def parseStats(): List[Tree] = parseRule(_.templateStats())
+
/** This is the parse entry point for code which is not self-contained, e.g.
* a script which is a series of template statements. They will be
* swaddled in Trees until the AST is equivalent to the one returned
* by compilationUnit().
*/
def scriptBody(): Tree = {
- val stmts = templateStats()
- accept(EOF)
+ val stmts = parseStats()
def mainModuleName = newTermName(settings.script.value)
/* If there is only a single object template in the file and it has a
@@ -563,8 +568,8 @@ self =>
and
}
- def expectedMsg(token: Int): String =
- token2string(token) + " expected but " +token2string(in.token) + " found."
+ def expectedMsgTemplate(exp: String, fnd: String) = s"$exp expected but $fnd found."
+ def expectedMsg(token: Int): String = expectedMsgTemplate(token2string(token), token2string(in.token))
/** Consume one token of the specified type, or signal an error if it is not there. */
def accept(token: Int): Int = {
@@ -627,6 +632,8 @@ self =>
def isAnnotation: Boolean = in.token == AT
+ def isCaseDefStart: Boolean = in.token == CASE
+
def isLocalModifier: Boolean = in.token match {
case ABSTRACT | FINAL | SEALED | IMPLICIT | LAZY => true
case _ => false
@@ -1325,7 +1332,7 @@ self =>
in.nextToken()
if (in.token != LBRACE) catchFromExpr()
else inBracesOrNil {
- if (in.token == CASE) caseClauses()
+ if (isCaseDefStart) caseClauses()
else catchFromExpr()
}
}
@@ -1634,7 +1641,7 @@ self =>
*/
def blockExpr(): Tree = atPos(in.offset) {
inBraces {
- if (in.token == CASE) Match(EmptyTree, caseClauses())
+ if (isCaseDefStart) Match(EmptyTree, caseClauses())
else block()
}
}
@@ -2626,7 +2633,7 @@ self =>
case EQUALS =>
in.nextToken()
TypeDef(mods, name, tparams, typ())
- case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE =>
+ case t if t == SUPERTYPE || t == SUBTYPE || t == COMMA || t == RBRACE || isStatSep(t) =>
TypeDef(mods | Flags.DEFERRED, name, tparams, typeBounds())
case _ =>
syntaxErrorOrIncompleteAnd("`=', `>:', or `<:' expected", skipIt = true)(EmptyTree)
@@ -2927,27 +2934,14 @@ self =>
stats.toList
}
- /** Informal - for the repl and other direct parser accessors.
- */
- def templateStats(): List[Tree] = templateStatSeq(isPre = false)._2 match {
- case Nil => EmptyTree.asList
- case stats => stats
- }
-
/** {{{
- * TemplateStatSeq ::= [id [`:' Type] `=>'] TemplateStat {semi TemplateStat}
- * TemplateStat ::= Import
- * | Annotations Modifiers Def
- * | Annotations Modifiers Dcl
- * | Expr1
- * | super ArgumentExprs {ArgumentExprs}
- * |
+ * TemplateStatSeq ::= [id [`:' Type] `=>'] TemplateStats
* }}}
* @param isPre specifies whether in early initializer (true) or not (false)
*/
def templateStatSeq(isPre : Boolean): (ValDef, List[Tree]) = checkNoEscapingPlaceholders {
var self: ValDef = emptyValDef
- val stats = new ListBuffer[Tree]
+ var firstOpt: Option[Tree] = None
if (isExprIntro) {
in.flushDoc
val first = expr(InTemplate) // @S: first statement is potentially converted so cannot be stubbed.
@@ -2964,10 +2958,25 @@ self =>
}
in.nextToken()
} else {
- stats += first
+ firstOpt = Some(first)
acceptStatSepOpt()
}
}
+ (self, firstOpt ++: templateStats())
+ }
+
+ /** {{{
+ * TemplateStats ::= TemplateStat {semi TemplateStat}
+ * TemplateStat ::= Import
+ * | Annotations Modifiers Def
+ * | Annotations Modifiers Dcl
+ * | Expr1
+ * | super ArgumentExprs {ArgumentExprs}
+ * |
+ * }}}
+ */
+ def templateStats(): List[Tree] = {
+ val stats = new ListBuffer[Tree]
while (!isStatSeqEnd) {
if (in.token == IMPORT) {
in.flushDoc
@@ -2982,7 +2991,14 @@ self =>
}
acceptStatSepOpt()
}
- (self, stats.toList)
+ stats.toList
+ }
+
+ /** Informal - for the repl and other direct parser accessors.
+ */
+ def templateStatsCompat(): List[Tree] = templateStats() match {
+ case Nil => EmptyTree.asList
+ case stats => stats
}
/** {{{
@@ -3047,14 +3063,14 @@ self =>
*/
def blockStatSeq(): List[Tree] = checkNoEscapingPlaceholders {
val stats = new ListBuffer[Tree]
- while (!isStatSeqEnd && in.token != CASE) {
+ while (!isStatSeqEnd && !isCaseDefStart) {
if (in.token == IMPORT) {
stats ++= importClause()
acceptStatSep()
}
else if (isExprIntro) {
stats += statement(InBlock)
- if (in.token != RBRACE && in.token != CASE) acceptStatSep()
+ if (in.token != RBRACE && !isCaseDefStart) acceptStatSep()
}
else if (isDefIntro || isLocalModifier || isAnnotation) {
if (in.token == IMPLICIT) {
diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
index 666f19851d..ed694023d7 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
@@ -39,7 +39,7 @@ abstract class TreeBuilder {
* x becomes x @ _
* x: T becomes x @ (_: T)
*/
- private object patvarTransformer extends Transformer {
+ object patvarTransformer extends Transformer {
override def transform(tree: Tree): Tree = tree match {
case Ident(name) if (treeInfo.isVarPattern(tree) && name != nme.WILDCARD) =>
atPos(tree.pos)(Bind(name, atPos(tree.pos.focus) (Ident(nme.WILDCARD))))
diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
index afaca3396c..8d2f200e99 100644
--- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
+++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
@@ -269,17 +269,13 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
}
def parse(code: String): Tree = {
- val run = new Run
reporter.reset()
- val wrappedCode = "object wrapper {" + EOL + code + EOL + "}"
- val file = new BatchSourceFile("<toolbox>", wrappedCode)
+ val file = new BatchSourceFile("<toolbox>", code)
val unit = new CompilationUnit(file)
- phase = run.parserPhase
- val parser = newUnitParser(unit)
- val wrappedTree = parser.parse()
+ val parsed = newUnitParser(unit).parseStats()
throwIfErrors()
- val PackageDef(_, List(ModuleDef(_, _, Template(_, _, _ :: parsed)))) = wrappedTree
parsed match {
+ case Nil => EmptyTree
case expr :: Nil => expr
case stats :+ expr => Block(stats, expr)
}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
index 9a6ba56c18..18a806e5ff 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
@@ -17,32 +17,38 @@ trait Parsers { self: Quasiquotes =>
abstract class Parser extends {
val global: self.global.type = self.global
} with ScalaParser {
- /** Wraps given code to obtain a desired parser mode.
- * This way we can just re-use standard parser entry point.
- */
- def wrapCode(code: String): String =
- s"object wrapper { self => $EOL $code $EOL }"
-
- def unwrapTree(wrappedTree: Tree): Tree = {
- val PackageDef(_, List(ModuleDef(_, _, Template(_, _, _ :: parsed)))) = wrappedTree
- parsed match {
- case tree :: Nil => tree
- case stats :+ tree => Block(stats, tree)
- }
- }
-
def parse(code: String): Tree = {
try {
- val wrapped = wrapCode(code)
- debug(s"wrapped code\n=${wrapped}\n")
- val file = new BatchSourceFile(nme.QUASIQUOTE_FILE, wrapped)
- val tree = new QuasiquoteParser(file).parse()
- unwrapTree(tree)
+ val file = new BatchSourceFile(nme.QUASIQUOTE_FILE, code)
+ new QuasiquoteParser(file).parseRule(entryPoint)
} catch {
- case mi: MalformedInput => c.abort(c.macroApplication.pos, s"syntax error: ${mi.msg}")
+ case mi: MalformedInput => c.abort(correspondingPosition(mi.offset), mi.msg)
+ }
+ }
+
+ def correspondingPosition(offset: Int): Position = {
+ val posMapList = posMap.toList
+ def containsOffset(start: Int, end: Int) = start <= offset && offset <= end
+ def fallbackPosition = posMapList match {
+ case (pos1, (start1, end1)) :: _ if start1 > offset => pos1
+ case _ :+ ((pos2, (start2, end2))) if offset > end2 => pos2.withPoint(pos2.point + (end2 - start2))
}
+ posMapList.sliding(2).collect {
+ case (pos1, (start1, end1)) :: _ if containsOffset(start1, end1) => (pos1, offset - start1)
+ case (pos1, (_, end1)) :: (_, (start2, _)) :: _ if containsOffset(end1, start2) => (pos1, end1)
+ case _ :: (pos2, (start2, end2)) :: _ if containsOffset(start2, end2) => (pos2, offset - start2)
+ }.map { case (pos, offset) =>
+ pos.withPoint(pos.point + offset)
+ }.toList.headOption.getOrElse(fallbackPosition)
}
+ override def token2string(token: Int): String = token match {
+ case EOF => "end of quote"
+ case _ => super.token2string(token)
+ }
+
+ def entryPoint: QuasiquoteParser => Tree
+
class QuasiquoteParser(source0: SourceFile) extends SourceFileParser(source0) {
override val treeBuilder = new ParserTreeBuilder {
// q"(..$xs)"
@@ -73,9 +79,11 @@ trait Parsers { self: Quasiquotes =>
} else
super.caseClause()
- def isHole = isIdent && holeMap.contains(in.name)
+ def isHole: Boolean = isIdent && holeMap.contains(in.name)
- override def isAnnotation: Boolean = super.isAnnotation || (isHole && lookingAhead { isAnnotation })
+ override def isAnnotation: Boolean = super.isAnnotation || (isHole && lookingAhead { isAnnotation })
+
+ override def isCaseDefStart: Boolean = super.isCaseDefStart || (in.token == EOF)
override def isModifier: Boolean = super.isModifier || (isHole && lookingAhead { isModifier })
@@ -85,6 +93,12 @@ trait Parsers { self: Quasiquotes =>
override def isDclIntro: Boolean = super.isDclIntro || (isHole && lookingAhead { isDclIntro })
+ override def isStatSep(token: Int) = token == EOF || super.isStatSep(token)
+
+ override def expectedMsg(token: Int): String =
+ if (isHole) expectedMsgTemplate(token2string(token), "splicee")
+ else super.expectedMsg(token)
+
// $mods def foo
// $mods T
override def readAnnots(annot: => Tree): List[Tree] = in.token match {
@@ -101,34 +115,26 @@ trait Parsers { self: Quasiquotes =>
}
}
- object TermParser extends Parser
-
- object CaseParser extends Parser {
- override def wrapCode(code: String) = super.wrapCode("something match { case " + code + " }")
-
- override def unwrapTree(wrappedTree: Tree): Tree = {
- val Match(_, head :: tail) = super.unwrapTree(wrappedTree)
- if (tail.nonEmpty)
- c.abort(c.macroApplication.pos, "Can't parse more than one casedef, consider generating a match tree instead")
- head
+ object TermParser extends Parser {
+ def entryPoint = _.templateStats() match {
+ case Nil => EmptyTree
+ case tree :: Nil => tree
+ case stats :+ tree => Block(stats, tree)
}
}
- object PatternParser extends Parser {
- override def wrapCode(code: String) = super.wrapCode("something match { case " + code + " => }")
-
- override def unwrapTree(wrappedTree: Tree): Tree = {
- val Match(_, List(CaseDef(pat, _, _))) = super.unwrapTree(wrappedTree)
- pat
- }
+ object TypeParser extends Parser {
+ def entryPoint = _.typ()
}
- object TypeParser extends Parser {
- override def wrapCode(code: String) = super.wrapCode("type T = " + code)
+ object CaseParser extends Parser {
+ def entryPoint = _.caseClause()
+ }
- override def unwrapTree(wrappedTree: Tree): Tree = {
- val TypeDef(_, _, _, rhs) = super.unwrapTree(wrappedTree)
- rhs
+ object PatternParser extends Parser {
+ def entryPoint = { parser =>
+ val pat = parser.noSeq.pattern1()
+ parser.treeBuilder.patvarTransformer.transform(pat)
}
}
} \ No newline at end of file
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
index b680c25f76..b3ac1e293a 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
@@ -17,18 +17,31 @@ trait Placeholders { self: Quasiquotes =>
// Step 1: Transform Scala source with holes into vanilla Scala source
lazy val holeMap = new HoleMap()
+ lazy val posMap = mutable.ListMap[Position, (Int, Int)]()
lazy val code = {
val sb = new StringBuilder()
val sessionSuffix = randomUUID().toString.replace("-", "").substring(0, 8) + "$"
- foreach2(args, parts.init) { (tree, p) =>
- val (part, cardinality) = parseDots(p)
+ def appendPart(value: String, pos: Position) = {
+ val start = sb.length
+ sb.append(value)
+ val end = sb.length
+ posMap += pos -> (start, end)
+ }
+
+ def appendHole(tree: Tree, cardinality: Cardinality) = {
val placeholderName = c.freshName(TermName(nme.QUASIQUOTE_PREFIX + sessionSuffix))
- sb.append(part)
sb.append(placeholderName)
holeMap(placeholderName) = Hole(tree, cardinality)
}
- sb.append(parts.last)
+
+ foreach2(args, parts.init) { case (tree, (p, pos)) =>
+ val (part, cardinality) = parseDots(p)
+ appendPart(part, pos)
+ appendHole(tree, cardinality)
+ }
+ val (p, pos) = parts.last
+ appendPart(p, pos)
sb.toString
}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala
index fe954e0bfd..ee99a5e280 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala
@@ -17,7 +17,7 @@ abstract class Quasiquotes extends Parsers
lazy val (universe: Tree, args, parts, parse, reify) = c.macroApplication match {
case Apply(Select(Select(Apply(Select(universe0, _), List(Apply(_, parts0))), interpolator0), method0), args0) =>
val parts1 = parts0.map {
- case Literal(Constant(s: String)) => s
+ case lit @ Literal(Constant(s: String)) => s -> lit.pos
case part => c.abort(part.pos, "Quasiquotes can only be used with literal strings")
}
val reify0 = method0 match {
diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala
index df1ba1e2ea..02bee5e369 100644
--- a/src/reflect/scala/reflect/internal/Trees.scala
+++ b/src/reflect/scala/reflect/internal/Trees.scala
@@ -1017,14 +1017,16 @@ trait Trees extends api.Trees { self: SymbolTable =>
trait CannotHaveAttrs extends Tree {
override def canHaveAttrs = false
- private def unsupported(what: String, args: Any*) =
- throw new UnsupportedOperationException(s"$what($args) inapplicable for "+self.toString)
+ private def requireLegal(value: Any, allowed: Any, what: String) =
+ require(value == allowed, s"can't set $what for $self to value other than $allowed")
super.setPos(NoPosition)
- override def setPos(pos: Position) = unsupported("setPos", pos)
+ override def setPos(pos: Position) = { requireLegal(pos, NoPosition, "pos"); this }
+ override def pos_=(pos: Position) = setPos(pos)
super.setType(NoType)
- override def tpe_=(t: Type) = if (t != NoType) unsupported("tpe_=", t)
+ override def setType(t: Type) = { requireLegal(t, NoType, "tpe"); this }
+ override def tpe_=(t: Type) = setType(t)
}
case object EmptyTree extends TermTree with CannotHaveAttrs { override def isEmpty = true; val asList = List(this) }
diff --git a/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala b/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala
index 9353215e1e..6406dacc24 100644
--- a/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala
+++ b/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala
@@ -16,32 +16,6 @@ trait ExprTyper {
import syntaxAnalyzer.UnitParser
import naming.freshInternalVarName
- object codeParser {
- val global: repl.global.type = repl.global
- def applyRule[T](code: String, rule: UnitParser => T): T = {
- reporter.reset()
- val scanner = newUnitParser(code)
- val result = rule(scanner)
-
- if (!reporter.hasErrors)
- scanner.accept(EOF)
-
- result
- }
- def stmts(code: String) = applyRule(code, _.templateStats())
- }
-
- /** Parse a line into a sequence of trees. Returns None if the input is incomplete. */
- def parse(line: String): Option[List[Tree]] = debugging(s"""parse("$line")""") {
- var isIncomplete = false
- reporter.withIncompleteHandler((_, _) => isIncomplete = true) {
- val trees = codeParser.stmts(line)
- if (reporter.hasErrors) Some(Nil)
- else if (isIncomplete) None
- else Some(trees)
- }
- }
-
def symbolOfLine(code: String): Symbol = {
def asExpr(): Symbol = {
val name = freshInternalVarName()
diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala
index 3a71930383..5b3f64afca 100644
--- a/src/repl/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala
@@ -446,9 +446,9 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
private def requestFromLine(line: String, synthetic: Boolean): Either[IR.Result, Request] = {
val content = indentCode(line)
val trees = parse(content) match {
- case None => return Left(IR.Incomplete)
- case Some(Nil) => return Left(IR.Error) // parse error or empty input
- case Some(trees) => trees
+ case parse.Incomplete => return Left(IR.Incomplete)
+ case parse.Error => return Left(IR.Error)
+ case parse.Success(trees) => trees
}
repltrace(
trees map (t => {
@@ -466,7 +466,8 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
// If the last tree is a bare expression, pinpoint where it begins using the
// AST node position and snap the line off there. Rewrite the code embodied
// by the last tree as a ValDef instead, so we can access the value.
- trees.last match {
+ val last = trees.lastOption.getOrElse(EmptyTree)
+ last match {
case _:Assign => // we don't want to include assignments
case _:TermTree | _:Ident | _:Select => // ... but do want other unnamed terms.
val varName = if (synthetic) freshInternalVarName() else freshUserVarName()
@@ -478,7 +479,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
if (trees.size == 1) "val " + varName + " =\n" + content
else {
// The position of the last tree
- val lastpos0 = earliestPosition(trees.last)
+ val lastpos0 = earliestPosition(last)
// Oh boy, the parser throws away parens so "(2+2)" is mispositioned,
// with increasingly hard to decipher positions as we move on to "() => 5",
// (x: Int) => x + 1, and more. So I abandon attempts to finesse and just
@@ -1096,7 +1097,24 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
val repl: IMain.this.type = imain
} with ExprTyper { }
- def parse(line: String): Option[List[Tree]] = exprTyper.parse(line)
+ /** Parse a line into and return parsing result (error, incomplete or success with list of trees) */
+ object parse {
+ abstract sealed class Result
+ case object Error extends Result
+ case object Incomplete extends Result
+ case class Success(trees: List[Tree]) extends Result
+
+ def apply(line: String): Result = debugging(s"""parse("$line")""") {
+ var isIncomplete = false
+ reporter.withIncompleteHandler((_, _) => isIncomplete = true) {
+ reporter.reset()
+ val trees = newUnitParser(line).parseStats()
+ if (reporter.hasErrors) Error
+ else if (isIncomplete) Incomplete
+ else Success(trees)
+ }
+ }
+ }
def symbolOfLine(code: String): Symbol =
exprTyper.symbolOfLine(code)
@@ -1155,10 +1173,12 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
*/
def isShow = code.lines exists (_.trim endsWith "// show")
if (isReplDebug || isShow) {
- beSilentDuring(parse(code)) foreach { ts =>
- ts foreach { t =>
- withoutUnwrapping(echo(asCompactString(t)))
- }
+ beSilentDuring(parse(code)) match {
+ case parse.Success(ts) =>
+ ts foreach { t =>
+ withoutUnwrapping(echo(asCompactString(t)))
+ }
+ case _ =>
}
}
}
diff --git a/src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala b/src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala
index 8b8b668c9f..61db8d1748 100644
--- a/src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala
+++ b/src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala
@@ -190,10 +190,10 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput
// literal Ints, Strings, etc.
object literals extends CompletionAware {
- def simpleParse(code: String): Tree = newUnitParser(code).templateStats().last
+ def simpleParse(code: String): Option[Tree] = newUnitParser(code).parseStats().lastOption
def completions(verbosity: Int) = Nil
- override def follow(id: String) = simpleParse(id) match {
+ override def follow(id: String) = simpleParse(id).flatMap {
case x: Literal => Some(new LiteralCompletion(x))
case _ => None
}
diff --git a/src/repl/scala/tools/nsc/interpreter/Power.scala b/src/repl/scala/tools/nsc/interpreter/Power.scala
index da6d271a68..f69a5b487d 100644
--- a/src/repl/scala/tools/nsc/interpreter/Power.scala
+++ b/src/repl/scala/tools/nsc/interpreter/Power.scala
@@ -316,7 +316,7 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re
lazy val phased: Phased = new { val global: intp.global.type = intp.global } with Phased { }
def unit(code: String) = newCompilationUnit(code)
- def trees(code: String) = parse(code) getOrElse Nil
+ def trees(code: String) = parse(code) match { case parse.Success(trees) => trees; case _ => Nil }
override def toString = s"""
|** Power mode status **
diff --git a/src/repl/scala/tools/nsc/interpreter/Results.scala b/src/repl/scala/tools/nsc/interpreter/Results.scala
index e400906a58..a4e1e25cbb 100644
--- a/src/repl/scala/tools/nsc/interpreter/Results.scala
+++ b/src/repl/scala/tools/nsc/interpreter/Results.scala
@@ -19,4 +19,4 @@ object Results {
/** The input was incomplete. The caller should request more input.
*/
case object Incomplete extends Result
-}
+} \ No newline at end of file
diff --git a/src/repl/scala/tools/nsc/interpreter/package.scala b/src/repl/scala/tools/nsc/interpreter/package.scala
index f82c38f5e7..5dc9b65436 100644
--- a/src/repl/scala/tools/nsc/interpreter/package.scala
+++ b/src/repl/scala/tools/nsc/interpreter/package.scala
@@ -145,8 +145,8 @@ package object interpreter extends ReplConfig with ReplStrings {
case sym: TypeSymbol => Some(sym)
case _ => None
}
- (typeFromTypeString orElse typeFromNameTreatedAsTerm orElse typeFromFullName orElse typeOfTerm) foreach { sym =>
- val (kind, tpe) = exitingTyper {
+ (typeFromTypeString orElse typeFromNameTreatedAsTerm orElse typeFromFullName orElse typeOfTerm) foreach { sym =>
+ val (kind, tpe) = exitingTyper {
val tpe = sym.tpeHK
(intp.global.inferKind(NoPrefix)(tpe, sym.owner), tpe)
}
diff --git a/test/files/neg/quasiquotes-syntax-error-position.check b/test/files/neg/quasiquotes-syntax-error-position.check
new file mode 100644
index 0000000000..3bd813b1bb
--- /dev/null
+++ b/test/files/neg/quasiquotes-syntax-error-position.check
@@ -0,0 +1,32 @@
+quasiquotes-syntax-error-position.scala:5: error: '=' expected but identifier found.
+ q"def $a f"
+ ^
+quasiquotes-syntax-error-position.scala:6: error: illegal start of simple expression
+ q"$a("
+ ^
+quasiquotes-syntax-error-position.scala:7: error: '}' expected but end of quote found.
+ q"class $t { def foo = $a"
+ ^
+quasiquotes-syntax-error-position.scala:8: error: '.' expected but splicee found.
+ q"import $t $t"
+ ^
+quasiquotes-syntax-error-position.scala:9: error: illegal start of definition
+ q"package p"
+ ^
+quasiquotes-syntax-error-position.scala:10: error: ';' expected but '@' found.
+ q"foo@$a"
+ ^
+quasiquotes-syntax-error-position.scala:11: error: case classes without a parameter list are not allowed;
+use either case objects or case classes with an explicit `()' as a parameter list.
+ q"case class A"
+ ^
+quasiquotes-syntax-error-position.scala:12: error: identifier expected but ']' found.
+ tq"$t => $t $t]"
+ ^
+quasiquotes-syntax-error-position.scala:13: error: end of quote expected but 'case' found.
+ cq"pattern => body ; case pattern2 =>"
+ ^
+quasiquotes-syntax-error-position.scala:14: error: ')' expected but end of quote found.
+ pq"$a(bar"
+ ^
+10 errors found
diff --git a/test/files/neg/quasiquotes-syntax-error-position.scala b/test/files/neg/quasiquotes-syntax-error-position.scala
new file mode 100644
index 0000000000..b97af52cfc
--- /dev/null
+++ b/test/files/neg/quasiquotes-syntax-error-position.scala
@@ -0,0 +1,15 @@
+import scala.reflect.runtime.universe._
+object test extends App {
+ val a = TermName("a")
+ val t = TypeName("t")
+ q"def $a f"
+ q"$a("
+ q"class $t { def foo = $a"
+ q"import $t $t"
+ q"package p"
+ q"foo@$a"
+ q"case class A"
+ tq"$t => $t $t]"
+ cq"pattern => body ; case pattern2 =>"
+ pq"$a(bar"
+} \ No newline at end of file
diff --git a/test/files/run/t7331a.check b/test/files/run/t7331a.check
new file mode 100644
index 0000000000..a59b400344
--- /dev/null
+++ b/test/files/run/t7331a.check
@@ -0,0 +1,2 @@
+source-<toolbox>,line-1,offset=0
+2 \ No newline at end of file
diff --git a/test/files/run/t7331a.scala b/test/files/run/t7331a.scala
new file mode 100644
index 0000000000..1851945e63
--- /dev/null
+++ b/test/files/run/t7331a.scala
@@ -0,0 +1,10 @@
+import scala.reflect.runtime.universe._
+import scala.reflect.runtime.{currentMirror => cm}
+import scala.tools.reflect.ToolBox
+
+object Test extends App {
+ val tb = cm.mkToolBox()
+ val tree = tb.parse("x")
+ println(tree.pos)
+ println(tree.pos.source.content.length)
+} \ No newline at end of file
diff --git a/test/files/run/t7331b.check b/test/files/run/t7331b.check
new file mode 100644
index 0000000000..7034a95a3f
--- /dev/null
+++ b/test/files/run/t7331b.check
@@ -0,0 +1,3 @@
+reflective compilation has failed:
+
+')' expected but eof found.
diff --git a/test/files/run/t7331b.scala b/test/files/run/t7331b.scala
new file mode 100644
index 0000000000..052656d11b
--- /dev/null
+++ b/test/files/run/t7331b.scala
@@ -0,0 +1,11 @@
+import scala.reflect.runtime.universe._
+import scala.reflect.runtime.{currentMirror => cm}
+import scala.tools.reflect.{ToolBox, ToolBoxError}
+
+object Test extends App {
+ val tb = cm.mkToolBox()
+ try tb.parse("f(x")
+ catch {
+ case ToolBoxError(msg, _) => println(msg)
+ }
+} \ No newline at end of file
diff --git a/test/files/run/t7331c.check b/test/files/run/t7331c.check
new file mode 100644
index 0000000000..fd3ac1d9ef
--- /dev/null
+++ b/test/files/run/t7331c.check
@@ -0,0 +1,3 @@
+ClassDef(Modifiers(), TypeName("C"), List(), Template(List(Select(Ident(scala), TypeName("AnyRef"))), emptyValDef, List(DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(())))))))
+source-<toolbox>,line-1,offset=6
+NoPosition
diff --git a/test/files/run/t7331c.scala b/test/files/run/t7331c.scala
new file mode 100644
index 0000000000..75873afcd0
--- /dev/null
+++ b/test/files/run/t7331c.scala
@@ -0,0 +1,11 @@
+import scala.reflect.runtime.universe._
+import scala.reflect.runtime.{currentMirror => cm}
+import scala.tools.reflect.ToolBox
+
+object Test extends App {
+ val tb = cm.mkToolBox()
+ val tree = tb.parse("class C").asInstanceOf[ClassDef]
+ println(showRaw(tree))
+ println(tree.pos)
+ println(tree.impl.self.pos)
+} \ No newline at end of file
diff --git a/test/files/scalacheck/quasiquotes/ErrorProps.scala b/test/files/scalacheck/quasiquotes/ErrorProps.scala
index 044a332a04..b9e69e0e88 100644
--- a/test/files/scalacheck/quasiquotes/ErrorProps.scala
+++ b/test/files/scalacheck/quasiquotes/ErrorProps.scala
@@ -188,12 +188,6 @@ object ErrorProps extends QuasiquoteProperties("errors") {
val q"$m1 $m2 def foo" = EmptyTree
""")
- property("can't parse more than one casedef") = fails(
- "Can't parse more than one casedef, consider generating a match tree instead",
- """
- cq"1 => 2 case 3 => 5"
- """)
-
// // Make sure a nice error is reported in this case
// { import Flag._; val mods = NoMods; q"lazy $mods val x: Int" }
} \ No newline at end of file
diff --git a/test/junit/scala/tools/nsc/symtab/CannotHaveAttrsTest.scala b/test/junit/scala/tools/nsc/symtab/CannotHaveAttrsTest.scala
new file mode 100644
index 0000000000..355771bf04
--- /dev/null
+++ b/test/junit/scala/tools/nsc/symtab/CannotHaveAttrsTest.scala
@@ -0,0 +1,67 @@
+package scala.tools.nsc
+package symtab
+
+import org.junit.Assert._
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+import scala.tools.testing.AssertUtil.assertThrows
+import scala.reflect.internal.util.OffsetPosition
+
+@RunWith(classOf[JUnit4])
+class CannotHaveAttrsTest {
+ object symbolTable extends SymbolTableForUnitTesting {
+ object CHA extends CannotHaveAttrs {
+ def canEqual(that: Any): Boolean = ???
+ def productArity: Int = ???
+ def productElement(n: Int): Any = ???
+ }
+ val attrlessTrees = List(CHA, EmptyTree, emptyValDef, pendingSuperCall)
+ }
+ import symbolTable._
+
+ @Test
+ def canHaveAttrsIsFalse =
+ attrlessTrees.foreach { t =>
+ assertFalse(t.canHaveAttrs)
+ }
+
+ @Test
+ def defaultPosAssignment =
+ attrlessTrees.foreach { t =>
+ assertEquals(t.pos, NoPosition)
+ t.pos = NoPosition
+ assertEquals(t.pos, NoPosition)
+ t.setPos(NoPosition)
+ assertEquals(t.pos, NoPosition)
+ }
+
+ @Test
+ def defaultTpeAssignment =
+ attrlessTrees.foreach { t =>
+ assertEquals(t.tpe, NoType)
+ t.tpe = NoType
+ assertEquals(t.tpe, NoType)
+ t.setType(NoType)
+ assertEquals(t.tpe, NoType)
+ }
+
+ @Test
+ def nonDefaultPosAssignmentFails = {
+ val pos = new OffsetPosition(null, 0)
+ attrlessTrees.foreach { t =>
+ assertThrows[IllegalArgumentException] { t.pos = pos }
+ assertThrows[IllegalArgumentException] { t.setPos(pos) }
+ }
+ }
+
+ @Test
+ def nonDefaultTpeAssignmentFails = {
+ val tpe = typeOf[Int]
+ attrlessTrees.foreach { t =>
+ assertThrows[IllegalArgumentException] { t.tpe = tpe }
+ assertThrows[IllegalArgumentException] { t.setType(tpe) }
+ }
+ }
+}
diff --git a/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala b/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala
index 537cb93ef3..11e955a4bb 100644
--- a/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala
+++ b/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala
@@ -9,17 +9,15 @@ import org.junit.runners.JUnit4
@RunWith(classOf[JUnit4])
class SymbolTableTest {
- private def createSymbolTable: SymbolTable = new SymbolTableForUnitTesting
+ object symbolTable extends SymbolTableForUnitTesting
@Test
def initDefinitions = {
- val symbolTable = createSymbolTable
symbolTable.definitions.init()
}
@Test
def basicSubTypeCheck = {
- val symbolTable = createSymbolTable
symbolTable.definitions.init()
val listClassTpe = symbolTable.definitions.ListClass.tpe
val seqClassTpe = symbolTable.definitions.SeqClass.tpe
@@ -32,7 +30,6 @@ class SymbolTableTest {
*/
@Test
def customClassesSubTypeCheck: Unit = {
- val symbolTable = createSymbolTable
import symbolTable._
symbolTable.definitions.init()
val rootClass = symbolTable.rootMirror.RootClass
diff --git a/test/junit/scala/tools/testing/AssertThrowsTest.scala b/test/junit/scala/tools/testing/AssertThrowsTest.scala
new file mode 100644
index 0000000000..a70519e63c
--- /dev/null
+++ b/test/junit/scala/tools/testing/AssertThrowsTest.scala
@@ -0,0 +1,34 @@
+package scala.tools
+package testing
+
+import org.junit.Assert._
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import AssertUtil.assertThrows
+
+@RunWith(classOf[JUnit4])
+class AssertThrowsTest {
+ class Foo extends Exception
+ class SubFoo extends Foo
+ class Bar extends Exception
+
+ @Test
+ def catchFoo = assertThrows[Foo] { throw new Foo }
+
+ @Test
+ def catchSubclass = assertThrows[Foo] { throw new SubFoo }
+
+ @Test
+ def rethrowBar =
+ assertTrue("exception wasn't rethrown", {
+ try {
+ assertThrows[Foo] { throw new Bar }
+ false
+ } catch {
+ case bar: Bar => true
+ case e: Throwable => fail(s"expected Bar but got $e"); false
+ }
+ })
+
+} \ No newline at end of file
diff --git a/test/junit/scala/tools/testing/AssertUtil.scala b/test/junit/scala/tools/testing/AssertUtil.scala
new file mode 100644
index 0000000000..9efac64a97
--- /dev/null
+++ b/test/junit/scala/tools/testing/AssertUtil.scala
@@ -0,0 +1,19 @@
+package scala.tools
+package testing
+
+/** This module contains additional higher-level assert statements
+ * that are ultimately based on junit.Assert primitives.
+ */
+object AssertUtil {
+ /** Check if exception T (or a subclass) was thrown during evaluation of f.
+ * If any other exception or throwable is found instead it will be re-thrown.
+ */
+ def assertThrows[T <: Exception](f: => Any)(implicit manifest: Manifest[T]): Unit =
+ try f
+ catch {
+ case e: Exception =>
+ val clazz = manifest.erasure.asInstanceOf[Class[T]]
+ if (!clazz.isAssignableFrom(e.getClass))
+ throw e
+ }
+} \ No newline at end of file