diff options
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 |