diff options
131 files changed, 1383 insertions, 815 deletions
@@ -1440,9 +1440,13 @@ TODO: <stopwatch name="test.junit.timer"/> <mkdir dir="${test.junit.classes}"/> <echo message="Note: details of failed tests will be output to ${build-junit.dir}"/> + + <if><isset property="test.method" /><then><property name="test.methods" value="${test.method}" /></then></if> <junit fork="yes" haltonfailure="yes" printsummary="on"> <classpath refid="test.junit.compiler.build.path"/> - <batchtest fork="yes" todir="${build-junit.dir}"> + <test fork="yes" todir="${build-junit.dir}" if="test.class" unless="test.methods" name="${test.class}" /> + <test fork="yes" todir="${build-junit.dir}" if="test.methods" name="${test.class}" methods="${test.methods}" /> + <batchtest fork="yes" todir="${build-junit.dir}" unless="test.class"> <fileset dir="${test.junit.classes}"> <include name="**/*Test.class"/> </fileset> diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index f23bca77cd..0a356ed7b6 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -135,11 +135,8 @@ trait CompilationUnits { global: Global => @deprecated("Call global.currentRun.reporting.uncheckedWarning directly instead.", "2.11.2") final def uncheckedWarning(pos: Position, msg: String): Unit = currentRun.reporting.uncheckedWarning(pos, msg) - // called by ScalaDocAnalyzer, overridden by the IDE (in Reporter) - // TODO: don't use reporter to communicate comments from parser to IDE! - @deprecated("This method will be removed.", "2.11.2") - final def comment(pos: Position, msg: String): Unit = reporter.comment(pos, msg) - + @deprecated("This method will be removed. It does nothing.", "2.11.2") + final def comment(pos: Position, msg: String): Unit = {} /** Is this about a .java source file? */ lazy val isJava = source.file.name.endsWith(".java") diff --git a/src/compiler/scala/tools/nsc/CompileServer.scala b/src/compiler/scala/tools/nsc/CompileServer.scala index 1f3a4237eb..029e1c4629 100644 --- a/src/compiler/scala/tools/nsc/CompileServer.scala +++ b/src/compiler/scala/tools/nsc/CompileServer.scala @@ -152,6 +152,7 @@ class StandardCompileServer extends SocketServer { clearCompiler() case ex: Throwable => warn("Compile server encountered fatal condition: " + ex) + reporter.error(null, "Compile server encountered fatal condition: " + ex.getMessage) shutdown = true throw ex } diff --git a/src/compiler/scala/tools/nsc/CompileSocket.scala b/src/compiler/scala/tools/nsc/CompileSocket.scala index c4f06b59ec..c693fbe8e2 100644 --- a/src/compiler/scala/tools/nsc/CompileSocket.scala +++ b/src/compiler/scala/tools/nsc/CompileSocket.scala @@ -32,7 +32,8 @@ trait HasCompileSocket { if (isErrorMessage(line)) noErrors = false - compileSocket.echo(line) + // be consistent with scalac: everything goes to stderr + compileSocket.warn(line) loop() } try loop() diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 5bf0d8d9f7..3e72dc5e4a 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -45,7 +45,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) with Printers with DocComments with Positions - with Reporting { self => + with Reporting + with Parsing { self => // the mirror -------------------------------------------------- @@ -218,6 +219,14 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** Called from parser, which signals hereby that a method definition has been parsed. */ def signalParseProgress(pos: Position) {} + /** Called by ScalaDocAnalyzer when a doc comment has been parsed. */ + def signalParsedDocComment(comment: String, pos: Position) = { + // TODO: this is all very borken (only works for scaladoc comments, not regular ones) + // --> add hooks to parser and refactor Interactive global to handle comments directly + // in any case don't use reporter for parser hooks + reporter.comment(pos, comment) + } + /** Register new context; called for every created context */ def registerContext(c: analyzer.Context) { @@ -968,7 +977,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** A Run is a single execution of the compiler on a set of units. */ - class Run extends RunContextApi with RunReporting { + class Run extends RunContextApi with RunReporting with RunParsing { /** Have been running into too many init order issues with Run * during erroneous conditions. Moved all these vals up to the * top of the file so at least they're not trivially null. diff --git a/src/compiler/scala/tools/nsc/Parsing.scala b/src/compiler/scala/tools/nsc/Parsing.scala new file mode 100644 index 0000000000..4dd3c3f378 --- /dev/null +++ b/src/compiler/scala/tools/nsc/Parsing.scala @@ -0,0 +1,36 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2014 LAMP/EPFL, Typesafe Inc. + * @author Adriaan Moors + */ + +package scala +package tools.nsc + +import scala.reflect.internal.Positions +import scala.tools.nsc.reporters.Reporter + +/** Similar to Reporting: gather global functionality specific to parsing. + */ +trait Parsing { self : Positions with Reporting => + def currentRun: RunParsing + + trait RunParsing { + val parsing: PerRunParsing = new PerRunParsing + } + + class PerRunParsing { + // for repl + private[this] var incompleteHandler: (Position, String) => Unit = null + def withIncompleteHandler[T](handler: (Position, String) => Unit)(thunk: => T) = { + val saved = incompleteHandler + incompleteHandler = handler + try thunk + finally incompleteHandler = saved + } + + def incompleteHandled = incompleteHandler != null + def incompleteInputError(pos: Position, msg: String): Unit = + if (incompleteHandled) incompleteHandler(pos, msg) + else reporter.error(pos, msg) + } +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/Properties.scala b/src/compiler/scala/tools/nsc/Properties.scala index ed5fda9c3f..59fefba954 100644 --- a/src/compiler/scala/tools/nsc/Properties.scala +++ b/src/compiler/scala/tools/nsc/Properties.scala @@ -14,6 +14,7 @@ object Properties extends scala.util.PropertiesTrait { // settings based on jar properties def residentPromptString = scalaPropOrElse("resident.prompt", "\nnsc> ") def shellPromptString = scalaPropOrElse("shell.prompt", "\nscala> ") + def shellInterruptedString = scalaPropOrElse("shell.interrupted", ":quit\n") // derived values def isEmacsShell = propOrEmpty("env.emacs") != "" diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala index 0263586418..b164f395fe 100644 --- a/src/compiler/scala/tools/nsc/Reporting.scala +++ b/src/compiler/scala/tools/nsc/Reporting.scala @@ -104,20 +104,5 @@ trait Reporting extends scala.reflect.internal.Reporting { self: ast.Positions w if (settings.fatalWarnings && reporter.hasWarnings) reporter.error(NoPosition, "No warnings can be incurred under -Xfatal-warnings.") } - - // for repl - private[this] var incompleteHandler: (Position, String) => Unit = null - def withIncompleteHandler[T](handler: (Position, String) => Unit)(thunk: => T) = { - val saved = incompleteHandler - incompleteHandler = handler - try thunk - finally incompleteHandler = saved - } - - def incompleteHandled = incompleteHandler != null - def incompleteInputError(pos: Position, msg: String): Unit = - if (incompleteHandled) incompleteHandler(pos, msg) - else reporter.error(pos, msg) - } }
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 883fd31dbc..8d810d456e 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -154,8 +154,8 @@ self => def unit = global.currentUnit // suppress warnings; silent abort on errors - def warning(offset: Offset, msg: String) {} - def deprecationWarning(offset: Offset, msg: String) {} + def warning(offset: Offset, msg: String): Unit = () + def deprecationWarning(offset: Offset, msg: String): Unit = () def syntaxError(offset: Offset, msg: String): Unit = throw new MalformedInput(offset, msg) def incompleteInputError(msg: String): Unit = throw new MalformedInput(source.content.length - 1, msg) @@ -204,13 +204,11 @@ self => override def newScanner() = new UnitScanner(unit, patches) - override def warning(offset: Offset, msg: String) { + override def warning(offset: Offset, msg: String): Unit = reporter.warning(o2p(offset), msg) - } - override def deprecationWarning(offset: Offset, msg: String) { + override def deprecationWarning(offset: Offset, msg: String): Unit = currentRun.reporting.deprecationWarning(o2p(offset), msg) - } private var smartParsing = false @inline private def withSmartParsing[T](body: => T): T = { @@ -226,15 +224,15 @@ self => for ((offset, msg) <- syntaxErrors) reporter.error(o2p(offset), msg) - override def syntaxError(offset: Offset, msg: String) { + override def syntaxError(offset: Offset, msg: String): Unit = { if (smartParsing) syntaxErrors += ((offset, msg)) else reporter.error(o2p(offset), msg) } - override def incompleteInputError(msg: String) { + override def incompleteInputError(msg: String): Unit = { val offset = source.content.length - 1 if (smartParsing) syntaxErrors += ((offset, msg)) - else currentRun.reporting.incompleteInputError(o2p(offset), msg) + else currentRun.parsing.incompleteInputError(o2p(offset), msg) } /** parse unit. If there are inbalanced braces, @@ -335,7 +333,7 @@ self => */ private var inScalaPackage = false private var currentPackage = "" - def resetPackage() { + def resetPackage(): Unit = { inScalaPackage = false currentPackage = "" } @@ -514,7 +512,7 @@ self => finally inFunReturnType = saved } - protected def skip(targetToken: Token) { + protected def skip(targetToken: Token): Unit = { var nparens = 0 var nbraces = 0 while (true) { @@ -544,27 +542,25 @@ self => } def warning(offset: Offset, msg: String): Unit def incompleteInputError(msg: String): Unit - private def syntaxError(pos: Position, msg: String, skipIt: Boolean) { - syntaxError(pos pointOrElse in.offset, msg, skipIt) - } def syntaxError(offset: Offset, msg: String): Unit - def syntaxError(msg: String, skipIt: Boolean) { + + private def syntaxError(pos: Position, msg: String, skipIt: Boolean): Unit = + syntaxError(pos pointOrElse in.offset, msg, skipIt) + def syntaxError(msg: String, skipIt: Boolean): Unit = syntaxError(in.offset, msg, skipIt) - } - def syntaxError(offset: Offset, msg: String, skipIt: Boolean) { + def syntaxError(offset: Offset, msg: String, skipIt: Boolean): Unit = { if (offset > lastErrorOffset) { syntaxError(offset, msg) - // no more errors on this token. - lastErrorOffset = in.offset + lastErrorOffset = in.offset // no more errors on this token. } if (skipIt) skip(UNDEF) } - def warning(msg: String) { warning(in.offset, msg) } + def warning(msg: String): Unit = warning(in.offset, msg) - def syntaxErrorOrIncomplete(msg: String, skipIt: Boolean) { + def syntaxErrorOrIncomplete(msg: String, skipIt: Boolean): Unit = { if (in.token == EOF) incompleteInputError(msg) else @@ -719,7 +715,7 @@ self => /** Convert tree to formal parameter. */ def convertToParam(tree: Tree): ValDef = atPos(tree.pos) { - def removeAsPlaceholder(name: Name) { + def removeAsPlaceholder(name: Name): Unit = { placeholderParams = placeholderParams filter (_.name != name) } def errorParam = makeParam(nme.ERROR, errorTypeTree setPos o2p(tree.pos.end)) @@ -1233,15 +1229,15 @@ self => skipIt = true)(EmptyTree) // Like Swiss cheese, with holes def stringCheese: Tree = atPos(in.offset) { - val start = in.offset + val start = in.offset val interpolator = in.name.encoded // ident() for INTERPOLATIONID val partsBuf = new ListBuffer[Tree] - val exprBuf = new ListBuffer[Tree] + val exprsBuf = new ListBuffer[Tree] in.nextToken() while (in.token == STRINGPART) { partsBuf += literal() - exprBuf += ( + exprsBuf += ( if (inPattern) dropAnyBraces(pattern()) else in.token match { case IDENTIFIER => atPos(in.offset)(Ident(ident())) @@ -1254,11 +1250,13 @@ self => } if (in.token == STRINGLIT) partsBuf += literal() + // Documenting that it is intentional that the ident is not rooted for purposes of virtualization + //val t1 = atPos(o2p(start)) { Select(Select (Ident(nme.ROOTPKG), nme.scala_), nme.StringContext) } val t1 = atPos(o2p(start)) { Ident(nme.StringContext) } val t2 = atPos(start) { Apply(t1, partsBuf.toList) } t2 setPos t2.pos.makeTransparent val t3 = Select(t2, interpolator) setPos t2.pos - atPos(start) { Apply(t3, exprBuf.toList) } + atPos(start) { Apply(t3, exprsBuf.toList) } } if (inPattern) stringCheese else withPlaceholders(stringCheese, isAny = true) // strinterpolator params are Any* by definition @@ -1266,21 +1264,21 @@ self => /* ------------- NEW LINES ------------------------------------------------- */ - def newLineOpt() { + def newLineOpt(): Unit = { if (in.token == NEWLINE) in.nextToken() } - def newLinesOpt() { + def newLinesOpt(): Unit = { if (in.token == NEWLINE || in.token == NEWLINES) in.nextToken() } - def newLineOptWhenFollowedBy(token: Offset) { + def newLineOptWhenFollowedBy(token: Offset): Unit = { // note: next is defined here because current == NEWLINE if (in.token == NEWLINE && in.next.token == token) newLineOpt() } - def newLineOptWhenFollowing(p: Token => Boolean) { + def newLineOptWhenFollowing(p: Token => Boolean): Unit = { // note: next is defined here because current == NEWLINE if (in.token == NEWLINE && p(in.next.token)) newLineOpt() } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 572497ac90..9ebc94b5fc 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -113,7 +113,7 @@ trait Scanners extends ScannersCommon { case SU | CR | LF => case _ => nextChar() ; skipLineComment() } - private def maybeOpen() { + private def maybeOpen(): Unit = { putCommentChar() if (ch == '*') { putCommentChar() @@ -137,7 +137,7 @@ trait Scanners extends ScannersCommon { def skipDocComment(): Unit = skipNestedComments() def skipBlockComment(): Unit = skipNestedComments() - private def skipToCommentEnd(isLineComment: Boolean) { + private def skipToCommentEnd(isLineComment: Boolean): Unit = { nextChar() if (isLineComment) skipLineComment() else { @@ -185,7 +185,7 @@ trait Scanners extends ScannersCommon { /** append Unicode character to "cbuf" buffer */ - protected def putChar(c: Char) { + protected def putChar(c: Char): Unit = { // assert(cbuf.size < 10000, cbuf) cbuf.append(c) } @@ -196,7 +196,7 @@ trait Scanners extends ScannersCommon { protected def emitIdentifierDeprecationWarnings = true /** Clear buffer and set name and token */ - private def finishNamed(idtoken: Token = IDENTIFIER) { + private def finishNamed(idtoken: Token = IDENTIFIER): Unit = { name = newTermName(cbuf.toString) cbuf.clear() token = idtoken @@ -215,7 +215,7 @@ trait Scanners extends ScannersCommon { } /** Clear buffer and set string */ - private def setStrVal() { + private def setStrVal(): Unit = { strVal = cbuf.toString cbuf.clear() } @@ -270,7 +270,7 @@ trait Scanners extends ScannersCommon { /** Produce next token, filling TokenData fields of Scanner. */ - def nextToken() { + def nextToken(): Unit = { val lastToken = token // Adapt sepRegions according to last token (lastToken: @switch) match { @@ -341,7 +341,7 @@ trait Scanners extends ScannersCommon { prev copyFrom this val nextLastOffset = charOffset - 1 fetchToken() - def resetOffset() { + def resetOffset(): Unit = { offset = prev.offset lastOffset = prev.lastOffset } @@ -399,7 +399,7 @@ trait Scanners extends ScannersCommon { /** read next token, filling TokenData fields of Scanner. */ - protected final def fetchToken() { + protected final def fetchToken(): Unit = { offset = charOffset - 1 (ch: @switch) match { @@ -604,7 +604,7 @@ trait Scanners extends ScannersCommon { // Identifiers --------------------------------------------------------------- - private def getBackquotedIdent() { + private def getBackquotedIdent(): Unit = { nextChar() getLitChars('`') if (ch == '`') { @@ -664,7 +664,7 @@ trait Scanners extends ScannersCommon { else finishNamed() } - private def getIdentOrOperatorRest() { + private def getIdentOrOperatorRest(): Unit = { if (isIdentifierPart(ch)) getIdentRest() else ch match { @@ -688,9 +688,11 @@ trait Scanners extends ScannersCommon { setStrVal() nextChar() token = STRINGLIT - } else syntaxError("unclosed string literal") + } else unclosedStringLit() } + private def unclosedStringLit(): Unit = syntaxError("unclosed string literal") + private def getRawStringLit(): Unit = { if (ch == '\"') { nextRawChar() @@ -764,7 +766,7 @@ trait Scanners extends ScannersCommon { if (multiLine) incompleteInputError("unclosed multi-line string literal") else - syntaxError("unclosed string literal") + unclosedStringLit() } else { putChar(ch) @@ -857,7 +859,7 @@ trait Scanners extends ScannersCommon { /** read fractional part and exponent of floating point number * if one is present. */ - protected def getFraction() { + protected def getFraction(): Unit = { token = DOUBLELIT while ('0' <= ch && ch <= '9') { putChar(ch) @@ -966,14 +968,13 @@ trait Scanners extends ScannersCommon { def floatVal: Double = floatVal(negated = false) - def checkNoLetter() { + def checkNoLetter(): Unit = { if (isIdentifierPart(ch) && ch >= ' ') syntaxError("Invalid literal number") } - /** Read a number into strVal and set base - */ - protected def getNumber() { + /** Read a number into strVal and set base */ + protected def getNumber(): Unit = { val base1 = if (base < 10) 10 else base // Read 8,9's even if format is octal, produce a malformed number error afterwards. // At this point, we have already read the first digit, so to tell an innocent 0 apart @@ -1052,7 +1053,7 @@ trait Scanners extends ScannersCommon { /** Parse character literal if current character is followed by \', * or follow with given op and return a symbol literal token */ - def charLitOr(op: () => Unit) { + def charLitOr(op: () => Unit): Unit = { putChar(ch) nextChar() if (ch == '\'') { @@ -1068,21 +1069,19 @@ trait Scanners extends ScannersCommon { // Errors ----------------------------------------------------------------- - /** generate an error at the given offset - */ - def syntaxError(off: Offset, msg: String) { + /** generate an error at the given offset */ + def syntaxError(off: Offset, msg: String): Unit = { error(off, msg) token = ERROR } - /** generate an error at the current token offset - */ + /** generate an error at the current token offset */ def syntaxError(msg: String): Unit = syntaxError(offset, msg) def deprecationWarning(msg: String): Unit = deprecationWarning(offset, msg) /** signal an error where the input ended in the middle of a token */ - def incompleteInputError(msg: String) { + def incompleteInputError(msg: String): Unit = { incompleteInputError(offset, msg) token = EOF } @@ -1134,7 +1133,7 @@ trait Scanners extends ScannersCommon { /** Initialization method: read first char, then first token */ - def init() { + def init(): Unit = { nextChar() nextToken() } @@ -1261,7 +1260,7 @@ trait Scanners extends ScannersCommon { override def deprecationWarning(off: Offset, msg: String) = currentRun.reporting.deprecationWarning(unit.position(off), msg) override def error (off: Offset, msg: String) = reporter.error(unit.position(off), msg) - override def incompleteInputError(off: Offset, msg: String) = currentRun.reporting.incompleteInputError(unit.position(off), msg) + override def incompleteInputError(off: Offset, msg: String) = currentRun.parsing.incompleteInputError(unit.position(off), msg) private var bracePatches: List[BracePatch] = patches @@ -1490,6 +1489,6 @@ trait Scanners extends ScannersCommon { // when skimming through the source file trying to heal braces override def emitIdentifierDeprecationWarnings = false - override def error(offset: Offset, msg: String) {} + override def error(offset: Offset, msg: String): Unit = () } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala index 64b762696e..df2073785b 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala @@ -83,7 +83,7 @@ abstract class SyntaxAnalyzer extends SubComponent with Parsers with MarkupParse private def initialUnitBody(unit: CompilationUnit): Tree = { if (unit.isJava) new JavaUnitParser(unit).parse() - else if (currentRun.reporting.incompleteHandled) newUnitParser(unit).parse() + else if (currentRun.parsing.incompleteHandled) newUnitParser(unit).parse() else newUnitParser(unit).smartParse() } diff --git a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala index bddcf6567c..ac86dfd665 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala @@ -861,7 +861,7 @@ trait JavaScanners extends ast.parser.ScannersCommon { in = new JavaCharArrayReader(unit.source.content, !settings.nouescape.value, syntaxError) init() def error (pos: Int, msg: String) = reporter.error(pos, msg) - def incompleteInputError(pos: Int, msg: String) = currentRun.reporting.incompleteInputError(pos, msg) + def incompleteInputError(pos: Int, msg: String) = currentRun.parsing.incompleteInputError(pos, msg) def deprecationWarning(pos: Int, msg: String) = currentRun.reporting.deprecationWarning(pos, msg) implicit def g2p(pos: Int): Position = Position.offset(unit.source, pos) } diff --git a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala index 6c592ead0d..5e4914fa83 100644 --- a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala @@ -30,6 +30,7 @@ abstract class AbstractReporter extends Reporter { private def isVerbose = settings.verbose.value private def noWarnings = settings.nowarnings.value private def isPromptSet = settings.prompt.value + private def isDebug = settings.debug protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean) { if (severity == INFO) { @@ -46,7 +47,7 @@ abstract class AbstractReporter extends Reporter { severity.count += 1 display(pos, msg, severity) } - else if (settings.debug) { + else if (isDebug) { severity.count += 1 display(pos, "[ suppressed ] " + msg, severity) } @@ -57,6 +58,7 @@ abstract class AbstractReporter extends Reporter { } } + /** Logs a position and returns true if it was already logged. * @note Two positions are considered identical for logging if they have the same point. */ diff --git a/src/compiler/scala/tools/nsc/reporters/Reporter.scala b/src/compiler/scala/tools/nsc/reporters/Reporter.scala index 5b576a547d..3d688efae1 100644 --- a/src/compiler/scala/tools/nsc/reporters/Reporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/Reporter.scala @@ -13,8 +13,8 @@ import scala.reflect.internal.util._ * This describes the internal interface for issuing information, warnings and errors. * The only abstract method in this class must be info0. * - * TODO: Move external clients (sbt/ide/partest) to reflect.internal.Reporter - * This interface should be considered private to the compiler. + * TODO: Move external clients (sbt/ide/partest) to reflect.internal.Reporter, + * and remove this class. */ abstract class Reporter extends scala.reflect.internal.Reporter { /** Informational messages. If `!force`, they may be suppressed. */ diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index ec4deb6be0..54bcc9e93e 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -1133,7 +1133,7 @@ abstract class Erasure extends AddInterfaces val tree2 = mixinTransformer.transform(tree1) // debuglog("tree after addinterfaces: \n" + tree2) - newTyper(rootContext(unit, tree, erasedTypes = true)).typed(tree2) + newTyper(rootContextPostTyper(unit, tree)).typed(tree2) } } } diff --git a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala index 3feadcd9b2..dc3313e2e4 100644 --- a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala +++ b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala @@ -17,9 +17,9 @@ trait TypingTransformers { abstract class TypingTransformer(unit: CompilationUnit) extends Transformer { var localTyper: analyzer.Typer = if (phase.erasedTypes) - erasure.newTyper(erasure.rootContext(unit, EmptyTree, erasedTypes = true)).asInstanceOf[analyzer.Typer] - else - analyzer.newTyper(analyzer.rootContext(unit, EmptyTree, true)) + erasure.newTyper(erasure.rootContextPostTyper(unit, EmptyTree)).asInstanceOf[analyzer.Typer] + else // TODO: AM: should some phases use a regular rootContext instead of a post-typer one?? + analyzer.newTyper(analyzer.rootContextPostTyper(unit, EmptyTree)) protected var curTree: Tree = _ override final def atOwner[A](owner: Symbol)(trans: => A): A = atOwner(curTree, owner)(trans) diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 9715fdaf00..20e462bbce 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -27,6 +27,16 @@ trait ContextErrors { override def toString() = "[Type error at:" + errPos + "] " + errMsg } + abstract class AbsAmbiguousTypeError extends AbsTypeError + + case class AmbiguousTypeError(errPos: Position, errMsg: String) + extends AbsAmbiguousTypeError + + case class AmbiguousImplicitTypeError(underlyingTree: Tree, errMsg: String) + extends AbsAmbiguousTypeError { + def errPos = underlyingTree.pos + } + sealed abstract class TreeTypeError extends AbsTypeError { def underlyingTree: Tree def errPos = underlyingTree.pos @@ -38,9 +48,6 @@ trait ContextErrors { case class AccessTypeError(underlyingTree: Tree, errMsg: String) extends TreeTypeError - case class AmbiguousTypeError(errPos: Position, errMsg: String) - extends AbsTypeError - case class SymbolTypeError(underlyingSym: Symbol, errMsg: String) extends AbsTypeError { @@ -75,8 +82,6 @@ trait ContextErrors { s"diverging implicit expansion for type ${pt}\nstarting with ${sym.fullLocationString}" } - case class AmbiguousImplicitTypeError(underlyingTree: Tree, errMsg: String) - extends TreeTypeError case class PosAndMsgTypeError(errPos: Position, errMsg: String) extends AbsTypeError @@ -90,10 +95,6 @@ trait ContextErrors { issueTypeError(SymbolTypeError(sym, msg)) } - def issueAmbiguousTypeError(pre: Type, sym1: Symbol, sym2: Symbol, err: AmbiguousTypeError)(implicit context: Context) { - context.issueAmbiguousError(pre, sym1, sym2, err) - } - def issueTypeError(err: AbsTypeError)(implicit context: Context) { context.issue(err) } def typeErrorMsg(found: Type, req: Type) = "type mismatch" + foundReqMsg(found, req) @@ -123,6 +124,36 @@ trait ContextErrors { import ErrorUtils._ + private def MacroIncompatibleEngineError(friendlyMessage: String, internalMessage: String) = { + def debugDiagnostic = s"(internal diagnostic: $internalMessage)" + val message = if (macroDebugLite || macroDebugVerbose) s"$friendlyMessage $debugDiagnostic" else friendlyMessage + // TODO: clean this up! (This is a more explicit version of what the code use to do, to reveal the issue.) + throw new TypeError(analyzer.lastTreeToTyper.pos, message) + } + + def MacroCantExpand210xMacrosError(internalMessage: String) = + MacroIncompatibleEngineError("can't expand macros compiled by previous versions of Scala", internalMessage) + + def MacroCantExpandIncompatibleMacrosError(internalMessage: String) = + MacroIncompatibleEngineError("macro cannot be expanded, because it was compiled by an incompatible macro engine", internalMessage) + + def NoImplicitFoundError(tree: Tree, param: Symbol)(implicit context: Context): Unit = { + def errMsg = { + val paramName = param.name + val paramTp = param.tpe + def evOrParam = ( + if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX) + "evidence parameter of type" + else + s"parameter $paramName:") + paramTp.typeSymbolDirect match { + case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp) + case _ => s"could not find implicit value for $evOrParam $paramTp" + } + } + issueNormalTypeError(tree, errMsg) + } + trait TyperContextErrors { self: Typer => @@ -141,24 +172,6 @@ trait ContextErrors { setError(tree) } - def NoImplicitFoundError(tree: Tree, param: Symbol) = { - def errMsg = { - val paramName = param.name - val paramTp = param.tpe - def evOrParam = ( - if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX) - "evidence parameter of type" - else - s"parameter $paramName:" - ) - paramTp.typeSymbolDirect match { - case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp) - case _ => s"could not find implicit value for $evOrParam $paramTp" - } - } - issueNormalTypeError(tree, errMsg) - } - def AdaptTypeError(tree: Tree, found: Type, req: Type) = { // SI-3971 unwrapping to the outermost Apply helps prevent confusion with the // error message point. @@ -733,17 +746,6 @@ trait ContextErrors { NormalTypeError(expandee, "too many argument lists for " + fun) } - private def MacroIncompatibleEngineError(friendlyMessage: String, internalMessage: String) = { - def debugDiagnostic = s"(internal diagnostic: $internalMessage)" - val message = if (macroDebugLite || macroDebugVerbose) s"$friendlyMessage $debugDiagnostic" else friendlyMessage - issueNormalTypeError(lastTreeToTyper, message) - } - - def MacroCantExpand210xMacrosError(internalMessage: String) = - MacroIncompatibleEngineError("can't expand macros compiled by previous versions of Scala", internalMessage) - - def MacroCantExpandIncompatibleMacrosError(internalMessage: String) = - MacroIncompatibleEngineError("macro cannot be expanded, because it was compiled by an incompatible macro engine", internalMessage) case object MacroExpansionException extends Exception with scala.util.control.ControlThrowable @@ -883,19 +885,21 @@ trait ContextErrors { val WrongNumber, NoParams, ArgsDoNotConform = Value } - private def ambiguousErrorMsgPos(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String) = - if (sym1.hasDefault && sym2.hasDefault && sym1.enclClass == sym2.enclClass) { - val methodName = nme.defaultGetterToMethod(sym1.name) - (sym1.enclClass.pos, - "in "+ sym1.enclClass +", multiple overloaded alternatives of " + methodName + - " define default arguments") - } else { - (pos, - ("ambiguous reference to overloaded definition,\n" + - "both " + sym1 + sym1.locationString + " of type " + pre.memberType(sym1) + - "\nand " + sym2 + sym2.locationString + " of type " + pre.memberType(sym2) + - "\nmatch " + rest) - ) + private def issueAmbiguousTypeErrorUnlessErroneous(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String): Unit = + if (!(pre.isErroneous || sym1.isErroneous || sym2.isErroneous)) { + if (sym1.hasDefault && sym2.hasDefault && sym1.enclClass == sym2.enclClass) { + val methodName = nme.defaultGetterToMethod(sym1.name) + context.issueAmbiguousError(AmbiguousTypeError(sym1.enclClass.pos, + "in "+ sym1.enclClass +", multiple overloaded alternatives of " + methodName + + " define default arguments")) + } else { + context.issueAmbiguousError(AmbiguousTypeError(pos, + ("ambiguous reference to overloaded definition,\n" + + "both " + sym1 + sym1.locationString + " of type " + pre.memberType(sym1) + + "\nand " + sym2 + sym2.locationString + " of type " + pre.memberType(sym2) + + "\nmatch " + rest) + )) + } } def AccessError(tree: Tree, sym: Symbol, ctx: Context, explanation: String): AbsTypeError = @@ -952,8 +956,7 @@ trait ContextErrors { val msg0 = "argument types " + argtpes.mkString("(", ",", ")") + (if (pt == WildcardType) "" else " and expected result type " + pt) - val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, msg0) - issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(pos, msg)) + issueAmbiguousTypeErrorUnlessErroneous(tree.pos, pre, best, firstCompeting, msg0) setErrorOnLastTry(lastTry, tree) } else setError(tree) // do not even try further attempts because they should all fail // even if this is not the last attempt (because of the SO's possibility on the horizon) @@ -966,8 +969,7 @@ trait ContextErrors { } def AmbiguousExprAlternativeError(tree: Tree, pre: Type, best: Symbol, firstCompeting: Symbol, pt: Type, lastTry: Boolean) = { - val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, "expected type " + pt) - issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(pos, msg)) + issueAmbiguousTypeErrorUnlessErroneous(tree.pos, pre, best, firstCompeting, "expected type " + pt) setErrorOnLastTry(lastTry, tree) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 72ca9b879a..a79f162140 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -9,6 +9,7 @@ package typechecker import scala.collection.{ immutable, mutable } import scala.annotation.tailrec import scala.reflect.internal.util.shortClassOfInstance +import scala.tools.nsc.reporters.Reporter /** * @author Martin Odersky @@ -98,7 +99,7 @@ trait Contexts { self: Analyzer => } - def rootContext(unit: CompilationUnit, tree: Tree = EmptyTree, erasedTypes: Boolean = false): Context = { + def rootContext(unit: CompilationUnit, tree: Tree = EmptyTree, throwing: Boolean = false, checking: Boolean = false): Context = { val rootImportsContext = (startContext /: rootImports(unit))((c, sym) => c.make(gen.mkWildcardImport(sym))) // there must be a scala.xml package when xml literals were parsed in this unit @@ -113,18 +114,21 @@ trait Contexts { self: Analyzer => else rootImportsContext.make(gen.mkImport(ScalaXmlPackage, nme.TopScope, nme.dollarScope)) val c = contextWithXML.make(tree, unit = unit) - if (erasedTypes) c.setThrowErrors() else c.setReportErrors() - c(EnrichmentEnabled | ImplicitsEnabled) = !erasedTypes + + c.initRootContext(throwing, checking) c } + def rootContextPostTyper(unit: CompilationUnit, tree: Tree = EmptyTree): Context = + rootContext(unit, tree, throwing = true) + def resetContexts() { startContext.enclosingContextChain foreach { context => context.tree match { case Import(qual, _) => qual setType singleType(qual.symbol.owner.thisType, qual.symbol) case _ => } - context.reportBuffer.clearAll() + context.reporter.clearAll() } } @@ -178,7 +182,8 @@ trait Contexts { self: Analyzer => * @param _outer The next outer context. */ class Context private[typechecker](val tree: Tree, val owner: Symbol, val scope: Scope, - val unit: CompilationUnit, _outer: Context) { + val unit: CompilationUnit, _outer: Context, + private[this] var _reporter: ContextReporter = new ThrowingReporter) { private def outerIsNoContext = _outer eq null final def outer: Context = if (outerIsNoContext) NoContext else _outer @@ -254,8 +259,6 @@ trait Contexts { self: Analyzer => def macrosEnabled = this(MacrosEnabled) def enrichmentEnabled_=(value: Boolean) = this(EnrichmentEnabled) = value def enrichmentEnabled = this(EnrichmentEnabled) - def checking_=(value: Boolean) = this(Checking) = value - def checking = this(Checking) def retyping_=(value: Boolean) = this(ReTyping) = value def retyping = this(ReTyping) def inSecondTry = this(SecondTry) @@ -265,8 +268,9 @@ trait Contexts { self: Analyzer => def defaultModeForTyped: Mode = if (inTypeConstructorAllowed) Mode.NOmode else Mode.EXPRmode - /** These messages are printed when issuing an error */ - var diagnostic: List[String] = Nil + /** To enrich error messages involving default arguments. + When extending the notion, group diagnostics in an object. */ + var diagUsedDefaults: Boolean = false /** Saved type bounds for type parameters which are narrowed in a GADT. */ var savedTypeBounds: List[(Symbol, Type)] = List() @@ -310,7 +314,7 @@ trait Contexts { self: Analyzer => */ def savingUndeterminedTypeParams[A](reportAmbiguous: Boolean = ambiguousErrors)(body: => A): A = { withMode() { - this(AmbiguousErrors) = reportAmbiguous + setAmbiguousErrors(reportAmbiguous) val saved = extractUndetparams() try body finally undetparams = saved @@ -321,54 +325,59 @@ trait Contexts { self: Analyzer => // Error reporting policies and buffer. // - private var _reportBuffer: ReportBuffer = new ReportBuffer - /** A buffer for errors and warnings, used with `this.bufferErrors == true` */ - def reportBuffer = _reportBuffer - /** Discard the current report buffer, and replace with an empty one */ - def useFreshReportBuffer() = _reportBuffer = new ReportBuffer - /** Discard the current report buffer, and replace with `other` */ - def restoreReportBuffer(other: ReportBuffer) = _reportBuffer = other - - /** The first error, if any, in the report buffer */ - def firstError: Option[AbsTypeError] = reportBuffer.firstError - def errors: Seq[AbsTypeError] = reportBuffer.errors - /** Does the report buffer contain any errors? */ - def hasErrors = reportBuffer.hasErrors - - def reportErrors = this(ReportErrors) - def bufferErrors = this(BufferErrors) + // the reporter for this context + def reporter: ContextReporter = _reporter + + // if set, errors will not be reporter/thrown + def bufferErrors = reporter.isBuffering + def reportErrors = !bufferErrors + + // whether to *report* (which is separate from buffering/throwing) ambiguity errors def ambiguousErrors = this(AmbiguousErrors) - def throwErrors = contextMode.inNone(ReportErrors | BufferErrors) - - def setReportErrors(): Unit = set(enable = ReportErrors | AmbiguousErrors, disable = BufferErrors) - def setBufferErrors(): Unit = set(enable = BufferErrors, disable = ReportErrors | AmbiguousErrors) - def setThrowErrors(): Unit = this(ReportErrors | AmbiguousErrors | BufferErrors) = false - def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report - - /** Append the given errors to the report buffer */ - def updateBuffer(errors: Traversable[AbsTypeError]) = reportBuffer ++= errors - /** Clear all errors from the report buffer */ - def flushBuffer() { reportBuffer.clearAllErrors() } - /** Return and clear all errors from the report buffer */ - def flushAndReturnBuffer(): immutable.Seq[AbsTypeError] = { - val current = reportBuffer.errors - reportBuffer.clearAllErrors() - current - } - /** Issue and clear all warnings from the report buffer */ - def flushAndIssueWarnings() { - reportBuffer.warnings foreach { - case (pos, msg) => reporter.warning(pos, msg) + private def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report + + /** + * Try inference twice: once without views and once with views, + * unless views are already disabled. + */ + abstract class TryTwice { + def tryOnce(isLastTry: Boolean): Unit + + final def apply(): Unit = { + val doLastTry = + // do first try if implicits are enabled + if (implicitsEnabled) { + // We create a new BufferingReporter to + // distinguish errors that occurred before entering tryTwice + // and our first attempt in 'withImplicitsDisabled'. If the + // first attempt fails, we try with implicits on + // and the original reporter. + // immediate reporting of ambiguous errors is suppressed, so that they are buffered + inSilentMode { + try { + set(disable = ImplicitsEnabled | EnrichmentEnabled) // restored by inSilentMode + tryOnce(false) + reporter.hasErrors + } catch { + case ex: CyclicReference => throw ex + case ex: TypeError => true // recoverable cyclic references? + } + } + } else true + + // do last try if try with implicits enabled failed + // (or if it was not attempted because they were disabled) + if (doLastTry) + tryOnce(true) } - reportBuffer.clearAllWarnings() } // // Temporary mode adjustment // - @inline def withMode[T](enabled: ContextMode = NOmode, disabled: ContextMode = NOmode)(op: => T): T = { + @inline final def withMode[T](enabled: ContextMode = NOmode, disabled: ContextMode = NOmode)(op: => T): T = { val saved = contextMode set(enabled, disabled) try op @@ -402,12 +411,18 @@ trait Contexts { self: Analyzer => // See comment on FormerNonStickyModes. @inline final def withOnlyStickyModes[T](op: => T): T = withMode(disabled = FormerNonStickyModes)(op) - /** @return true if the `expr` evaluates to true within a silent Context that incurs no errors */ + // inliner note: this has to be a simple method for inlining to work -- moved the `&& !reporter.hasErrors` out @inline final def inSilentMode(expr: => Boolean): Boolean = { - withMode() { // withMode with no arguments to restore the mode mutated by `setBufferErrors`. - setBufferErrors() - try expr && !hasErrors - finally reportBuffer.clearAll() + val savedContextMode = contextMode + val savedReporter = reporter + + setAmbiguousErrors(false) + _reporter = new BufferingReporter + + try expr + finally { + contextMode = savedContextMode + _reporter = savedReporter } } @@ -423,7 +438,8 @@ trait Contexts { self: Analyzer => * `Context#imports`. */ def make(tree: Tree = tree, owner: Symbol = owner, - scope: Scope = scope, unit: CompilationUnit = unit): Context = { + scope: Scope = scope, unit: CompilationUnit = unit, + reporter: ContextReporter = this.reporter): Context = { val isTemplateOrPackage = tree match { case _: Template | _: PackageDef => true case _ => false @@ -446,16 +462,15 @@ trait Contexts { self: Analyzer => // The blank canvas val c = if (isImport) - new Context(tree, owner, scope, unit, this) with ImportContext + new Context(tree, owner, scope, unit, this, reporter) with ImportContext else - new Context(tree, owner, scope, unit, this) + new Context(tree, owner, scope, unit, this, reporter) // Fields that are directly propagated c.variance = variance - c.diagnostic = diagnostic + c.diagUsedDefaults = diagUsedDefaults c.openImplicits = openImplicits c.contextMode = contextMode // note: ConstructorSuffix, a bit within `mode`, is conditionally overwritten below. - c._reportBuffer = reportBuffer // Fields that may take on a different value in the child c.prefix = prefixInChild @@ -470,22 +485,38 @@ trait Contexts { self: Analyzer => c } + /** Use reporter (possibly buffered) for errors/warnings and enable implicit conversion **/ + def initRootContext(throwing: Boolean = false, checking: Boolean = false): Unit = { + _reporter = + if (checking) new CheckingReporter + else if (throwing) new ThrowingReporter + else new ImmediateReporter + + setAmbiguousErrors(!throwing) + this(EnrichmentEnabled | ImplicitsEnabled) = !throwing + } + def make(tree: Tree, owner: Symbol, scope: Scope): Context = // TODO SI-7345 Moving this optimization into the main overload of `make` causes all tests to fail. - // even if it is extened to check that `unit == this.unit`. Why is this? + // even if it is extended to check that `unit == this.unit`. Why is this? if (tree == this.tree && owner == this.owner && scope == this.scope) this else make(tree, owner, scope, unit) /** Make a child context that represents a new nested scope */ - def makeNewScope(tree: Tree, owner: Symbol): Context = - make(tree, owner, newNestedScope(scope)) + def makeNewScope(tree: Tree, owner: Symbol, reporter: ContextReporter = this.reporter): Context = + make(tree, owner, newNestedScope(scope), reporter = reporter) /** Make a child context that buffers errors and warnings into a fresh report buffer. */ def makeSilent(reportAmbiguousErrors: Boolean = ambiguousErrors, newtree: Tree = tree): Context = { - val c = make(newtree) - c.setBufferErrors() + // A fresh buffer so as not to leak errors/warnings into `this`. + val c = make(newtree, reporter = new BufferingReporter) c.setAmbiguousErrors(reportAmbiguousErrors) - c._reportBuffer = new ReportBuffer // A fresh buffer so as not to leak errors/warnings into `this`. + c + } + + def makeNonSilent(newtree: Tree): Context = { + val c = make(newtree, reporter = reporter.makeImmediate) + c.setAmbiguousErrors(true) c } @@ -508,7 +539,9 @@ trait Contexts { self: Analyzer => */ def makeConstructorContext = { val baseContext = enclClass.outer.nextEnclosing(!_.tree.isInstanceOf[Template]) - val argContext = baseContext.makeNewScope(tree, owner) + // must propagate reporter! + // (caught by neg/t3649 when refactoring reporting to be specified only by this.reporter and not also by this.contextMode) + val argContext = baseContext.makeNewScope(tree, owner, reporter = this.reporter) argContext.contextMode = contextMode argContext.inSelfSuperCall = true def enterElems(c: Context) { @@ -533,65 +566,16 @@ trait Contexts { self: Analyzer => // Error and warning issuance // - private def addDiagString(msg: String) = { - val ds = - if (diagnostic.isEmpty) "" - else diagnostic.mkString("\n","\n", "") - if (msg endsWith ds) msg else msg + ds - } - - private def unitError(pos: Position, msg: String): Unit = - if (checking) onTreeCheckerError(pos, msg) else reporter.error(pos, msg) - - @inline private def issueCommon(err: AbsTypeError)(pf: PartialFunction[AbsTypeError, Unit]) { - // TODO: are errors allowed to have pos == NoPosition?? - // if not, Jason suggests doing: val pos = err.errPos.orElse( { devWarning("Que?"); context.tree.pos }) - if (settings.Yissuedebug) { - log("issue error: " + err.errMsg) - (new Exception).printStackTrace() - } - if (pf isDefinedAt err) pf(err) - else if (bufferErrors) { reportBuffer += err } - else throw new TypeError(err.errPos, err.errMsg) - } - /** Issue/buffer/throw the given type error according to the current mode for error reporting. */ - def issue(err: AbsTypeError) { - issueCommon(err) { case _ if reportErrors => - unitError(err.errPos, addDiagString(err.errMsg)) - } - } - - /** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */ - def issueAmbiguousError(pre: Type, sym1: Symbol, sym2: Symbol, err: AbsTypeError) { - issueCommon(err) { case _ if ambiguousErrors => - if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous) - unitError(err.errPos, err.errMsg) - } - } - + private[typechecker] def issue(err: AbsTypeError) = reporter.issue(err)(this) /** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */ - def issueAmbiguousError(err: AbsTypeError) { - issueCommon(err) { case _ if ambiguousErrors => unitError(err.errPos, addDiagString(err.errMsg)) } - } - - /** Issue/throw the given `err` according to the current mode for error reporting. */ - def error(pos: Position, err: Throwable) = - if (reportErrors) unitError(pos, addDiagString(err.getMessage())) - else throw err - + private[typechecker] def issueAmbiguousError(err: AbsAmbiguousTypeError) = reporter.issueAmbiguousError(err)(this) /** Issue/throw the given error message according to the current mode for error reporting. */ - def error(pos: Position, msg: String) = { - val msg1 = addDiagString(msg) - if (reportErrors) unitError(pos, msg1) - else throw new TypeError(pos, msg1) - } - + def error(pos: Position, msg: String) = reporter.error(pos, msg) /** Issue/throw the given error message according to the current mode for error reporting. */ - def warning(pos: Position, msg: String, force: Boolean = false) { - if (reportErrors || force) reporter.warning(pos, msg) - else if (bufferErrors) reportBuffer += (pos -> msg) - } + def warning(pos: Position, msg: String) = reporter.warning(pos, msg) + def echo(pos: Position, msg: String) = reporter.echo(pos, msg) + def deprecationWarning(pos: Position, sym: Symbol, msg: String): Unit = currentRun.reporting.deprecationWarning(pos, sym, msg) @@ -601,7 +585,6 @@ trait Contexts { self: Analyzer => def featureWarning(pos: Position, featureName: String, featureDesc: String, featureTrait: Symbol, construct: => String = "", required: Boolean): Unit = currentRun.reporting.featureWarning(pos, featureName, featureDesc, featureTrait, construct, required) - def echo(pos: Position, msg: String): Unit = reporter.echo(pos, msg) // nextOuter determines which context is searched next for implicits // (after `this`, which contributes `newImplicits` below.) In @@ -1238,61 +1221,176 @@ trait Contexts { self: Analyzer => override final def toString = super.toString + " with " + s"ImportContext { $impInfo; outer.owner = ${outer.owner} }" } - /** A buffer for warnings and errors that are accumulated during speculative type checking. */ - final class ReportBuffer { + /** A reporter for use during type checking. It has multiple modes for handling errors. + * + * The default (immediate mode) is to send the error to the global reporter. + * When switched into buffering mode via makeBuffering, errors and warnings are buffered and not be reported + * (there's a special case for ambiguity errors for some reason: those are force to the reporter when context.ambiguousErrors, + * or else they are buffered -- TODO: can we simplify this?) + * + * When using the type checker after typers, an error results in a TypeError being thrown. TODO: get rid of this mode. + * + * To handle nested contexts, reporters share buffers. TODO: only buffer in BufferingReporter, emit immediately in ImmediateReporter + */ + abstract class ContextReporter(private[this] var _errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, private[this] var _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends Reporter { type Error = AbsTypeError type Warning = (Position, String) - private def newBuffer[A] = mutable.LinkedHashSet.empty[A] // Important to use LinkedHS for stable results. + def issue(err: AbsTypeError)(implicit context: Context): Unit = handleError(err.errPos, addDiagString(err.errMsg)) - // [JZ] Contexts, pre- the SI-7345 refactor, avoided allocating the buffers until needed. This - // is replicated here out of conservatism. - private var _errorBuffer: mutable.LinkedHashSet[Error] = _ - private def errorBuffer = {if (_errorBuffer == null) _errorBuffer = newBuffer; _errorBuffer} - def errors: immutable.Seq[Error] = errorBuffer.toVector + protected def handleError(pos: Position, msg: String): Unit + protected def handleSuppressedAmbiguous(err: AbsAmbiguousTypeError): Unit = () + protected def handleWarning(pos: Position, msg: String): Unit = reporter.warning(pos, msg) - private var _warningBuffer: mutable.LinkedHashSet[Warning] = _ - private def warningBuffer = {if (_warningBuffer == null) _warningBuffer = newBuffer; _warningBuffer} - def warnings: immutable.Seq[Warning] = warningBuffer.toVector + def makeImmediate: ContextReporter = this + def makeBuffering: ContextReporter = this + def isBuffering: Boolean = false - def +=(error: AbsTypeError): this.type = { - errorBuffer += error - this - } - def ++=(errors: Traversable[AbsTypeError]): this.type = { - errorBuffer ++= errors - this - } - def +=(warning: Warning): this.type = { - warningBuffer += warning - this + /** Emit an ambiguous error according to context.ambiguousErrors + * + * - when true, use global.reporter regardless of whether we're buffering (TODO: can we change this?) + * - else, let this context reporter decide + */ + final def issueAmbiguousError(err: AbsAmbiguousTypeError)(implicit context: Context): Unit = + if (context.ambiguousErrors) reporter.error(err.errPos, addDiagString(err.errMsg)) // force reporting... see TODO above + else handleSuppressedAmbiguous(err) + + @inline final def withFreshErrorBuffer[T](expr: => T): T = { + val previousBuffer = _errorBuffer + _errorBuffer = newBuffer + val res = expr // expr will read _errorBuffer + _errorBuffer = previousBuffer + res } - def clearAll(): this.type = { - clearAllErrors(); clearAllWarnings(); + @inline final def propagatingErrorsTo[T](target: ContextReporter)(expr: => T): T = { + val res = expr // TODO: make sure we're okay skipping the try/finally overhead + if ((this ne target) && hasErrors) { // `this eq target` in e.g., test/files/neg/divergent-implicit.scala + // assert(target.errorBuffer ne _errorBuffer) + target ++= errors + // TODO: is clearAllErrors necessary? (no tests failed when dropping it) + // NOTE: even though `this ne target`, it may still be that `target.errorBuffer eq _errorBuffer`, + // so don't clear the buffer, but null out the reference so that a new one will be created when necessary (should be never??) + // (we should refactor error buffering to avoid mutation on shared buffers) + clearAllErrors() + } + res } - def clearAllErrors(): this.type = { - errorBuffer.clear() - this - } - def clearErrors(removeF: PartialFunction[AbsTypeError, Boolean]): this.type = { - errorBuffer.retain(!PartialFunction.cond(_)(removeF)) - this + protected final def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = + severity match { + case ERROR => handleError(pos, msg) + case WARNING => handleWarning(pos, msg) + case INFO => reporter.echo(pos, msg) + } + + final override def hasErrors = super.hasErrors || errorBuffer.nonEmpty + + // TODO: everything below should be pushed down to BufferingReporter (related to buffering) + // Implicit relies on this most heavily, but there you know reporter.isInstanceOf[BufferingReporter] + // can we encode this statically? + + // have to pass in context because multiple contexts may share the same ReportBuffer + def reportFirstDivergentError(fun: Tree, param: Symbol, paramTp: Type)(implicit context: Context): Unit = + errors.collectFirst { + case dte: DivergentImplicitTypeError => dte + } match { + case Some(divergent) => + // DivergentImplicit error has higher priority than "no implicit found" + // no need to issue the problem again if we are still in silent mode + if (context.reportErrors) { + context.issue(divergent.withPt(paramTp)) + errorBuffer.retain { + case dte: DivergentImplicitTypeError => false + case _ => true + } + } + case _ => + NoImplicitFoundError(fun, param)(context) + } + + def retainDivergentErrorsExcept(saved: DivergentImplicitTypeError) = + errorBuffer.retain { + case err: DivergentImplicitTypeError => err ne saved + case _ => false + } + + def propagateImplicitTypeErrorsTo(target: ContextReporter) = { + errors foreach { + case err@(_: DivergentImplicitTypeError | _: AmbiguousImplicitTypeError) => + target.errorBuffer += err + case _ => + } + // debuglog("propagateImplicitTypeErrorsTo: " + errors) } - def retainErrors(leaveF: PartialFunction[AbsTypeError, Boolean]): this.type = { - errorBuffer.retain(PartialFunction.cond(_)(leaveF)) - this + + protected def addDiagString(msg: String)(implicit context: Context): String = { + val diagUsedDefaultsMsg = "Error occurred in an application involving default arguments." + if (context.diagUsedDefaults && !(msg endsWith diagUsedDefaultsMsg)) msg + "\n" + diagUsedDefaultsMsg + else msg } - def clearAllWarnings(): this.type = { - warningBuffer.clear() - this + + final def emitWarnings() = if (_warningBuffer != null) { + _warningBuffer foreach { + case (pos, msg) => reporter.warning(pos, msg) + } + _warningBuffer = null } - def hasErrors = errorBuffer.nonEmpty - def firstError = errorBuffer.headOption + // [JZ] Contexts, pre- the SI-7345 refactor, avoided allocating the buffers until needed. This + // is replicated here out of conservatism. + private def newBuffer[A] = mutable.LinkedHashSet.empty[A] // Important to use LinkedHS for stable results. + final protected def errorBuffer = { if (_errorBuffer == null) _errorBuffer = newBuffer; _errorBuffer } + final protected def warningBuffer = { if (_warningBuffer == null) _warningBuffer = newBuffer; _warningBuffer } + + final def errors: immutable.Seq[Error] = errorBuffer.toVector + final def warnings: immutable.Seq[Warning] = warningBuffer.toVector + final def firstError: Option[AbsTypeError] = errorBuffer.headOption + + // TODO: remove ++= and clearAll* entirely in favor of more high-level combinators like withFreshErrorBuffer + final private[typechecker] def ++=(errors: Traversable[AbsTypeError]): Unit = errorBuffer ++= errors + + // null references to buffers instead of clearing them, + // as the buffers may be shared between different reporters + final def clearAll(): Unit = { _errorBuffer = null; _warningBuffer = null } + final def clearAllErrors(): Unit = { _errorBuffer = null } + } + + private[typechecker] class ImmediateReporter(_errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends ContextReporter(_errorBuffer, _warningBuffer) { + override def makeBuffering: ContextReporter = new BufferingReporter(errorBuffer, warningBuffer) + protected def handleError(pos: Position, msg: String): Unit = reporter.error(pos, msg) + } + + + private[typechecker] class BufferingReporter(_errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends ContextReporter(_errorBuffer, _warningBuffer) { + override def isBuffering = true + + override def issue(err: AbsTypeError)(implicit context: Context): Unit = errorBuffer += err + + // this used to throw new TypeError(pos, msg) -- buffering lets us report more errors (test/files/neg/macro-basic-mamdmi) + // the old throwing behavior was relied on by diagnostics in manifestOfType + protected def handleError(pos: Position, msg: String): Unit = errorBuffer += TypeErrorWrapper(new TypeError(pos, msg)) + override protected def handleSuppressedAmbiguous(err: AbsAmbiguousTypeError): Unit = errorBuffer += err + override protected def handleWarning(pos: Position, msg: String): Unit = warningBuffer += ((pos, msg)) + + // TODO: emit all buffered errors, warnings + override def makeImmediate: ContextReporter = new ImmediateReporter(errorBuffer, warningBuffer) } + /** Used after typer (specialization relies on TypeError being thrown, among other post-typer phases). + * + * TODO: get rid of it, use ImmediateReporter and a check for reporter.hasErrors where necessary + */ + private[typechecker] class ThrowingReporter extends ContextReporter { + protected def handleError(pos: Position, msg: String): Unit = throw new TypeError(pos, msg) + } + + /** Used during a run of [[scala.tools.nsc.typechecker.TreeCheckers]]? */ + private[typechecker] class CheckingReporter extends ContextReporter { + protected def handleError(pos: Position, msg: String): Unit = onTreeCheckerError(pos, msg) + } + + class ImportInfo(val tree: Import, val depth: Int) { def pos = tree.pos def posOf(sel: ImportSelector) = tree.pos withPoint sel.namePos @@ -1385,8 +1483,6 @@ object ContextMode { def apply(bits: Int): ContextMode = new ContextMode(bits) final val NOmode: ContextMode = 0 - final val ReportErrors: ContextMode = 1 << 0 - final val BufferErrors: ContextMode = 1 << 1 final val AmbiguousErrors: ContextMode = 1 << 2 /** Are we in a secondary constructor after the this constructor call? */ @@ -1409,8 +1505,6 @@ object ContextMode { /** To selectively allow enrichment in patterns, where other kinds of implicit conversions are not allowed */ final val EnrichmentEnabled: ContextMode = 1 << 8 - /** Are we in a run of [[scala.tools.nsc.typechecker.TreeCheckers]]? */ - final val Checking: ContextMode = 1 << 9 /** Are we retypechecking arguments independently from the function applied to them? See `Typer.tryTypedApply` * TODO - iron out distinction/overlap with SecondTry. @@ -1447,17 +1541,14 @@ object ContextMode { PatternAlternative | StarPatterns | SuperInit | SecondTry | ReturnExpr | TypeConstructorAllowed ) - final val DefaultMode: ContextMode = MacrosEnabled + final val DefaultMode: ContextMode = MacrosEnabled private val contextModeNameMap = Map( - ReportErrors -> "ReportErrors", - BufferErrors -> "BufferErrors", AmbiguousErrors -> "AmbiguousErrors", ConstructorSuffix -> "ConstructorSuffix", SelfSuperCall -> "SelfSuperCall", ImplicitsEnabled -> "ImplicitsEnabled", MacrosEnabled -> "MacrosEnabled", - Checking -> "Checking", ReTyping -> "ReTyping", PatternAlternative -> "PatternAlternative", StarPatterns -> "StarPatterns", diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 73c3e6f016..b85c8e6d42 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -71,13 +71,10 @@ trait Implicits { typingStack.printTyping(tree, "typing implicit: %s %s".format(tree, context.undetparamsString)) val implicitSearchContext = context.makeImplicit(reportAmbiguous) val result = new ImplicitSearch(tree, pt, isView, implicitSearchContext, pos).bestImplicit - if (result.isFailure && saveAmbiguousDivergent && implicitSearchContext.hasErrors) { - context.updateBuffer(implicitSearchContext.reportBuffer.errors.collect { - case dte: DivergentImplicitTypeError => dte - case ate: AmbiguousImplicitTypeError => ate - }) - debuglog("update buffer: " + implicitSearchContext.reportBuffer.errors) - } + + if (result.isFailure && saveAmbiguousDivergent && implicitSearchContext.reporter.hasErrors) + implicitSearchContext.reporter.propagateImplicitTypeErrorsTo(context.reporter) + // SI-7944 undetermined type parameters that result from inference within typedImplicit land in // `implicitSearchContext.undetparams`, *not* in `context.undetparams` // Here, we copy them up to parent context (analogously to the way the errors are copied above), @@ -99,7 +96,7 @@ trait Implicits { def wrapper(inference: => SearchResult) = wrapper1(inference) val result = wrapper(inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos)) if (result.isFailure && !silent) { - val err = context.firstError + val err = context.reporter.firstError val errPos = err.map(_.errPos).getOrElse(pos) val errMsg = err.map(_.errMsg).getOrElse("implicit search has failed. to find out the reason, turn on -Xlog-implicits") onError(errPos, errMsg) @@ -635,7 +632,7 @@ trait Implicits { } case _ => fallback } - context.firstError match { // using match rather than foreach to avoid non local return. + context.reporter.firstError match { // using match rather than foreach to avoid non local return. case Some(err) => log("implicit adapt failed: " + err.errMsg) return fail(err.errMsg) @@ -658,8 +655,8 @@ trait Implicits { } } - if (context.hasErrors) - fail("hasMatchingSymbol reported error: " + context.firstError.get.errMsg) + if (context.reporter.hasErrors) + fail("hasMatchingSymbol reported error: " + context.reporter.firstError.get.errMsg) else if (itree3.isErroneous) fail("error typechecking implicit candidate") else if (isLocalToCallsite && !hasMatchingSymbol(itree2)) @@ -677,7 +674,7 @@ trait Implicits { // #2421: check that we correctly instantiated type parameters outside of the implicit tree: checkBounds(itree3, NoPrefix, NoSymbol, undetParams, targs, "inferred ") - context.firstError match { + context.reporter.firstError match { case Some(err) => return fail("type parameters weren't correctly instantiated outside of the implicit tree: " + err.errMsg) case None => @@ -716,7 +713,7 @@ trait Implicits { case t => t } - context.firstError match { + context.reporter.firstError match { case Some(err) => fail("typing TypeApply reported errors for the implicit tree: " + err.errMsg) case None => @@ -857,13 +854,11 @@ trait Implicits { SearchFailure } else { if (search.isFailure) { - // We don't want errors that occur during checking implicit info + // Discard the divergentError we saved (if any), as well as all errors that are not of type DivergentImplicitTypeError + // We don't want errors that occur while checking the implicit info // to influence the check of further infos, but we should retain divergent implicit errors // (except for the one we already squirreled away) - val saved = divergentError.getOrElse(null) - context.reportBuffer.retainErrors { - case err: DivergentImplicitTypeError => err ne saved - } + context.reporter.retainDivergentErrorsExcept(divergentError.getOrElse(null)) } search } @@ -909,7 +904,7 @@ trait Implicits { // the first `DivergentImplicitTypeError` that is being propagated // from a nested implicit search; this one will be // re-issued if this level of the search fails. - DivergentImplicitRecovery(typedFirstPending, firstPending, context.errors) match { + DivergentImplicitRecovery(typedFirstPending, firstPending, context.reporter.errors) match { case sr if sr.isDivergent => Nil case sr if sr.isFailure => rankImplicits(otherPending, acc) case newBest => @@ -1146,7 +1141,7 @@ trait Implicits { try { val tree1 = typedPos(pos.focus)(arg) - context.firstError match { + context.reporter.firstError match { case Some(err) => processMacroExpansionError(err.errPos, err.errMsg) case None => new SearchResult(tree1, EmptyTreeTypeSubstituter, Nil) } @@ -1278,19 +1273,20 @@ trait Implicits { if (tagInScope.isEmpty) mot(tp, Nil, Nil) else { if (ReflectRuntimeUniverse == NoSymbol) { - // todo. write a test for this - context.error(pos, + // TODO: write a test for this (the next error message is already checked by neg/interop_typetags_without_classtags_arenot_manifests.scala) + // TODO: this was using context.error, and implicit search always runs in silent mode, thus it was actually throwing a TypeError + // with the new strategy-based reporting, a BufferingReporter buffers instead of throwing + // it would be good to rework this logic to fit into the regular context.error mechanism + throw new TypeError(pos, sm"""to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. |however typetag -> manifest conversion requires Scala reflection, which is not present on the classpath. |to proceed put scala-reflect.jar on your compilation classpath and recompile.""") - return SearchFailure } if (resolveClassTag(pos, tp, allowMaterialization = true) == EmptyTree) { - context.error(pos, + throw new TypeError(pos, sm"""to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. |however typetag -> manifest conversion requires a class tag for the corresponding type to be present. |to proceed add a class tag to the type `$tp` (e.g. by introducing a context bound) and recompile.""") - return SearchFailure } val cm = typed(Ident(ReflectRuntimeCurrentMirror)) val internal = gen.mkAttributedSelect(gen.mkAttributedRef(ReflectRuntimeUniverse), UniverseInternal) @@ -1346,52 +1342,66 @@ trait Implicits { * If all fails return SearchFailure */ def bestImplicit: SearchResult = { - val failstart = if (Statistics.canEnable) Statistics.startTimer(inscopeFailNanos) else null - val succstart = if (Statistics.canEnable) Statistics.startTimer(inscopeSucceedNanos) else null + val stats = Statistics.canEnable + val failstart = if (stats) Statistics.startTimer(inscopeFailNanos) else null + val succstart = if (stats) Statistics.startTimer(inscopeSucceedNanos) else null var result = searchImplicit(context.implicitss, isLocalToCallsite = true) - if (result.isFailure) { - if (Statistics.canEnable) Statistics.stopTimer(inscopeFailNanos, failstart) - } else { - if (Statistics.canEnable) Statistics.stopTimer(inscopeSucceedNanos, succstart) - if (Statistics.canEnable) Statistics.incCounter(inscopeImplicitHits) + if (stats) { + if (result.isFailure) Statistics.stopTimer(inscopeFailNanos, failstart) + else { + Statistics.stopTimer(inscopeSucceedNanos, succstart) + Statistics.incCounter(inscopeImplicitHits) + } } + if (result.isFailure) { - val previousErrs = context.flushAndReturnBuffer() - val failstart = if (Statistics.canEnable) Statistics.startTimer(oftypeFailNanos) else null - val succstart = if (Statistics.canEnable) Statistics.startTimer(oftypeSucceedNanos) else null + val failstart = if (stats) Statistics.startTimer(oftypeFailNanos) else null + val succstart = if (stats) Statistics.startTimer(oftypeSucceedNanos) else null + + // SI-6667, never search companions after an ambiguous error in in-scope implicits + val wasAmbigious = result.isAmbiguousFailure + + // TODO: encapsulate + val previousErrs = context.reporter.errors + context.reporter.clearAllErrors() - val wasAmbigious = result.isAmbiguousFailure // SI-6667, never search companions after an ambiguous error in in-scope implicits result = materializeImplicit(pt) + // `materializeImplicit` does some preprocessing for `pt` // is it only meant for manifests/tags or we need to do the same for `implicitsOfExpectedType`? if (result.isFailure && !wasAmbigious) result = searchImplicit(implicitsOfExpectedType, isLocalToCallsite = false) - if (result.isFailure) { - context.updateBuffer(previousErrs) - if (Statistics.canEnable) Statistics.stopTimer(oftypeFailNanos, failstart) - } else { - if (Statistics.canEnable) Statistics.stopTimer(oftypeSucceedNanos, succstart) - if (Statistics.canEnable) Statistics.incCounter(oftypeImplicitHits) + if (result.isFailure) + context.reporter ++= previousErrs + + if (stats) { + if (result.isFailure) Statistics.stopTimer(oftypeFailNanos, failstart) + else { + Statistics.stopTimer(oftypeSucceedNanos, succstart) + Statistics.incCounter(oftypeImplicitHits) + } } } if (result.isSuccess && isView) { def maybeInvalidConversionError(msg: String) { // We have to check context.ambiguousErrors even though we are calling "issueAmbiguousError" // which ostensibly does exactly that before issuing the error. Why? I have no idea. Test is pos/t7690. + // AM: I would guess it's because ambiguous errors will be buffered in silent mode if they are not reported if (context.ambiguousErrors) context.issueAmbiguousError(AmbiguousImplicitTypeError(tree, msg)) } pt match { case Function1(_, out) => - def prohibit(sym: Symbol) = if (sym.tpe <:< out) { - maybeInvalidConversionError(s"the result type of an implicit conversion must be more specific than ${sym.name}") - result = SearchFailure + // must inline to avoid capturing result + def prohibit(sym: Symbol) = (sym.tpe <:< out) && { + maybeInvalidConversionError(s"the result type of an implicit conversion must be more specific than ${sym.name}") + true } - prohibit(AnyRefClass) - if (settings.isScala211) prohibit(AnyValClass) + if (prohibit(AnyRefClass) || (settings.isScala211 && prohibit(AnyValClass))) + result = SearchFailure case _ => false } if (settings.isScala211 && isInvalidConversionSource(pt)) { @@ -1399,8 +1409,9 @@ trait Implicits { result = SearchFailure } } - if (result.isFailure) - debuglog("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType) + + if (result.isFailure && settings.debug) // debuglog is not inlined for some reason + log("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType) result } @@ -1422,20 +1433,19 @@ trait Implicits { val eligible = new ImplicitComputation(iss, isLocalToCallsite).eligible eligible.toList.flatMap { (ii: ImplicitInfo) => - // each ImplicitInfo contributes a distinct set of constraints (generated indirectly by typedImplicit) - // thus, start each type var off with a fresh for every typedImplicit - resetTVars() - // any previous errors should not affect us now - context.flushBuffer() - - val res = typedImplicit(ii, ptChecked = false, isLocalToCallsite) - if (res.tree ne EmptyTree) List((res, tvars map (_.constr))) - else Nil + // each ImplicitInfo contributes a distinct set of constraints (generated indirectly by typedImplicit) + // thus, start each type var off with a fresh for every typedImplicit + resetTVars() + // any previous errors should not affect us now + context.reporter.clearAllErrors() + val res = typedImplicit(ii, ptChecked = false, isLocalToCallsite) + if (res.tree ne EmptyTree) List((res, tvars map (_.constr))) + else Nil + } } - } eligibleInfos(context.implicitss, isLocalToCallsite = true) ++ eligibleInfos(implicitsOfExpectedType, isLocalToCallsite = false) - } + } } object ImplicitNotFoundMsg { diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index a3f1da60ce..fb7651ffd6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -199,8 +199,6 @@ trait Infer extends Checkable { def getContext = context - def issue(err: AbsTypeError): Unit = context.issue(err) - def explainTypes(tp1: Type, tp2: Type) = { if (context.reportErrors) withDisambiguation(List(), tp1, tp2)(global.explainTypes(tp1, tp2)) @@ -781,7 +779,7 @@ trait Infer extends Checkable { def applicableExpectingPt(pt: Type): Boolean = { val silent = context.makeSilent(reportAmbiguousErrors = false) val result = newTyper(silent).infer.isApplicable(undetparams, ftpe, argtpes0, pt) - if (silent.hasErrors && !pt.isWildcard) + if (silent.reporter.hasErrors && !pt.isWildcard) applicableExpectingPt(WildcardType) // second try else result @@ -1266,33 +1264,36 @@ trait Infer extends Checkable { * If no alternative matches `pt`, take the parameterless one anyway. */ def inferExprAlternative(tree: Tree, pt: Type): Tree = { - def tryOurBests(pre: Type, alts: List[Symbol], isSecondTry: Boolean): Unit = { - val alts0 = alts filter (alt => isWeaklyCompatible(pre memberType alt, pt)) - val alts1 = if (alts0.isEmpty) alts else alts0 - val bests = bestAlternatives(alts1) { (sym1, sym2) => - val tp1 = pre memberType sym1 - val tp2 = pre memberType sym2 - - ( (tp2 eq ErrorType) - || isWeaklyCompatible(tp1, pt) && !isWeaklyCompatible(tp2, pt) - || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2) - ) - } - // todo: missing test case for bests.isEmpty - bests match { - case best :: Nil => tree setSymbol best setType (pre memberType best) - case best :: competing :: _ if alts0.nonEmpty => - // SI-6912 Don't give up and leave an OverloadedType on the tree. - // Originally I wrote this as `if (secondTry) ... `, but `tryTwice` won't attempt the second try - // unless an error is issued. We're not issuing an error, in the assumption that it would be - // spurious in light of the erroneous expected type - if (pt.isErroneous) setError(tree) - else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry) - case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry) + val c = context + class InferTwice(pre: Type, alts: List[Symbol]) extends c.TryTwice { + def tryOnce(isSecondTry: Boolean): Unit = { + val alts0 = alts filter (alt => isWeaklyCompatible(pre memberType alt, pt)) + val alts1 = if (alts0.isEmpty) alts else alts0 + val bests = bestAlternatives(alts1) { (sym1, sym2) => + val tp1 = pre memberType sym1 + val tp2 = pre memberType sym2 + + ( (tp2 eq ErrorType) + || isWeaklyCompatible(tp1, pt) && !isWeaklyCompatible(tp2, pt) + || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2) + ) + } + // todo: missing test case for bests.isEmpty + bests match { + case best :: Nil => tree setSymbol best setType (pre memberType best) + case best :: competing :: _ if alts0.nonEmpty => + // SI-6912 Don't give up and leave an OverloadedType on the tree. + // Originally I wrote this as `if (secondTry) ... `, but `tryTwice` won't attempt the second try + // unless an error is issued. We're not issuing an error, in the assumption that it would be + // spurious in light of the erroneous expected type + if (pt.isErroneous) setError(tree) + else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry) + case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry) + } } } tree.tpe match { - case OverloadedType(pre, alts) => tryTwice(tryOurBests(pre, alts, _)) ; tree + case OverloadedType(pre, alts) => (new InferTwice(pre, alts)).apply() ; tree case _ => tree } } @@ -1370,70 +1371,41 @@ trait Infer extends Checkable { * @pre tree.tpe is an OverloadedType. */ def inferMethodAlternative(tree: Tree, undetparams: List[Symbol], argtpes0: List[Type], pt0: Type): Unit = { - val OverloadedType(pre, alts) = tree.tpe - var varargsStar = false - val argtpes = argtpes0 mapConserve { - case RepeatedType(tp) => varargsStar = true ; tp - case tp => tp - } - def followType(sym: Symbol) = followApply(pre memberType sym) - def bestForExpectedType(pt: Type, isLastTry: Boolean): Unit = { - val applicable0 = alts filter (alt => context inSilentMode isApplicable(undetparams, followType(alt), argtpes, pt)) - val applicable = overloadsToConsiderBySpecificity(applicable0, argtpes, varargsStar) - val ranked = bestAlternatives(applicable)((sym1, sym2) => - isStrictlyMoreSpecific(followType(sym1), followType(sym2), sym1, sym2) - ) - ranked match { - case best :: competing :: _ => AmbiguousMethodAlternativeError(tree, pre, best, competing, argtpes, pt, isLastTry) // ambiguous - case best :: Nil => tree setSymbol best setType (pre memberType best) // success - case Nil if pt.isWildcard => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed - case Nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType - } - } - // This potentially makes up to four attempts: tryTwice may execute + // This potentially makes up to four attempts: tryOnce may execute // with and without views enabled, and bestForExpectedType will try again // with pt = WildcardType if it fails with pt != WildcardType. - tryTwice { isLastTry => - val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 - debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt") - bestForExpectedType(pt, isLastTry) - } - } + val c = context + class InferMethodAlternativeTwice extends c.TryTwice { + private[this] val OverloadedType(pre, alts) = tree.tpe + private[this] var varargsStar = false + private[this] val argtpes = argtpes0 mapConserve { + case RepeatedType(tp) => varargsStar = true ; tp + case tp => tp + } - /** Try inference twice, once without views and once with views, - * unless views are already disabled. - */ - def tryTwice(infer: Boolean => Unit): Unit = { - if (context.implicitsEnabled) { - val savedContextMode = context.contextMode - var fallback = false - context.setBufferErrors() - // We cache the current buffer because it is impossible to - // distinguish errors that occurred before entering tryTwice - // and our first attempt in 'withImplicitsDisabled'. If the - // first attempt fails we try with implicits on *and* clean - // buffer but that would also flush any pre-tryTwice valid - // errors, hence some manual buffer tweaking is necessary. - val errorsToRestore = context.flushAndReturnBuffer() - try { - context.withImplicitsDisabled(infer(false)) - if (context.hasErrors) { - fallback = true - context.contextMode = savedContextMode - context.flushBuffer() - infer(true) + private def followType(sym: Symbol) = followApply(pre memberType sym) + // separate method to help the inliner + private def isAltApplicable(pt: Type)(alt: Symbol) = context inSilentMode { isApplicable(undetparams, followType(alt), argtpes, pt) && !context.reporter.hasErrors } + private def rankAlternatives(sym1: Symbol, sym2: Symbol) = isStrictlyMoreSpecific(followType(sym1), followType(sym2), sym1, sym2) + private def bestForExpectedType(pt: Type, isLastTry: Boolean): Unit = { + val applicable = overloadsToConsiderBySpecificity(alts filter isAltApplicable(pt), argtpes, varargsStar) + val ranked = bestAlternatives(applicable)(rankAlternatives) + ranked match { + case best :: competing :: _ => AmbiguousMethodAlternativeError(tree, pre, best, competing, argtpes, pt, isLastTry) // ambiguous + case best :: Nil => tree setSymbol best setType (pre memberType best) // success + case Nil if pt.isWildcard => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed + case Nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType } - } catch { - case ex: CyclicReference => throw ex - case ex: TypeError => // recoverable cyclic references - context.contextMode = savedContextMode - if (!fallback) infer(true) else () - } finally { - context.contextMode = savedContextMode - context.updateBuffer(errorsToRestore) + } + + private[this] val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 + def tryOnce(isLastTry: Boolean): Unit = { + debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt") + bestForExpectedType(pt, isLastTry) } } - else infer(true) + + (new InferMethodAlternativeTwice).apply() } /** Assign `tree` the type of all polymorphic alternatives diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 33d3432ae2..da7b8b09aa 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -226,7 +226,8 @@ trait Macros extends MacroRuntimes with Traces with Helpers { val Apply(_, pickledPayload) = wrapped val payload = pickledPayload.map{ case Assign(k, v) => (unpickleAtom(k), unpickleAtom(v)) }.toMap - import typer.TyperErrorGen._ + // TODO: refactor error handling: fail always throws a TypeError, + // and uses global state (analyzer.lastTreeToTyper) to determine the position for the error def fail(msg: String) = MacroCantExpandIncompatibleMacrosError(msg) def unpickle[T](field: String, clazz: Class[T]): T = { def failField(msg: String) = fail(s"$field $msg") @@ -624,7 +625,7 @@ trait Macros extends MacroRuntimes with Traces with Helpers { // `macroExpandApply` is called from `adapt`, where implicit conversions are disabled // therefore we need to re-enable the conversions back temporarily val result = typer.context.withImplicitsEnabled(typer.typed(tree, mode, pt)) - if (result.isErrorTyped && macroDebugVerbose) println(s"$label has failed: ${typer.context.reportBuffer.errors}") + if (result.isErrorTyped && macroDebugVerbose) println(s"$label has failed: ${typer.context.reporter.errors}") result } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 7bbd81118a..fdff2f3076 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1494,8 +1494,7 @@ trait Namers extends MethodSynthesis { case defn: MemberDef => val ainfos = defn.mods.annotations filterNot (_ eq null) map { ann => val ctx = typer.context - val annCtx = ctx.make(ann) - annCtx.setReportErrors() + val annCtx = ctx.makeNonSilent(ann) // need to be lazy, #1782. beforeTyper to allow inferView in annotation args, SI-5892. AnnotationInfo lazily { enteringTyper(newTyper(annCtx) typedAnnotation ann) diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala index cf3f265f0c..da0e67a2a5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala @@ -261,7 +261,7 @@ trait PatternTypers { def doTypedUnapply(tree: Tree, fun0: Tree, fun: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = { def duplErrTree = setError(treeCopy.Apply(tree, fun0, args)) - def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree } + def duplErrorTree(err: AbsTypeError) = { context.issue(err); duplErrTree } if (args.length > MaxTupleArity) return duplErrorTree(TooManyArgsPatternError(fun)) diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index 399a4ca8d5..743bbe53bd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -208,8 +208,7 @@ abstract class TreeCheckers extends Analyzer { } def check(unit: CompilationUnit) { informProgress("checking "+unit) - val context = rootContext(unit) - context.checking = true + val context = rootContext(unit, checking = true) tpeOfTree.clear() SymbolTracker.check(phase, unit) val checker = new TreeChecker(context) diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 7440f69e93..89b064e7c1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -572,11 +572,11 @@ trait TypeDiagnostics { } else f } def apply(tree: Tree): Tree = { - // Error suppression will squash some of these warnings unless we circumvent it. + // Error suppression (in context.warning) would squash some of these warnings. // It is presumed if you are using a -Y option you would really like to hear - // the warnings you've requested. + // the warnings you've requested; thus, use reporter.warning. if (settings.warnDeadCode && context.unit.exists && treeOK(tree) && exprOK) - context.warning(tree.pos, "dead code following this construct", force = true) + reporter.warning(tree.pos, "dead code following this construct") tree } @@ -629,7 +629,7 @@ trait TypeDiagnostics { throw new FatalError("cannot redefine root "+sym) } case _ => - context0.error(ex.pos, ex) + context0.error(ex.pos, ex.msg) } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 70f44c4fc6..0621fbefe4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -155,21 +155,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } else { mkArg = gen.mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args if (!param.hasDefault && !paramFailed) { - context.reportBuffer.errors.collectFirst { - case dte: DivergentImplicitTypeError => dte - } match { - case Some(divergent) => - // DivergentImplicit error has higher priority than "no implicit found" - // no need to issue the problem again if we are still in silent mode - if (context.reportErrors) { - context.issue(divergent.withPt(paramTp)) - context.reportBuffer.clearErrors { - case dte: DivergentImplicitTypeError => true - } - } - case _ => - NoImplicitFoundError(fun, param) - } + context.reporter.reportFirstDivergentError(fun, param, paramTp)(context) paramFailed = true } /* else { @@ -478,20 +464,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (cond) typerWithLocalContext(c)(f) else f(this) @inline - final def typerWithLocalContext[T](c: Context)(f: Typer => T): T = { - val res = f(newTyper(c)) - if (c.hasErrors) - context.updateBuffer(c.flushAndReturnBuffer()) - res - } - - @inline - final def withSavedContext[T](c: Context)(f: => T) = { - val savedErrors = c.flushAndReturnBuffer() - val res = f - c.updateBuffer(savedErrors) - res - } + final def typerWithLocalContext[T](c: Context)(f: Typer => T): T = + c.reporter.propagatingErrorsTo(context.reporter)(f(newTyper(c))) /** The typer for a label definition. If this is part of a template we * first have to enter the label definition. @@ -684,6 +658,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (Statistics.canEnable) Statistics.stopCounter(subtypeFailed, subtypeStart) if (Statistics.canEnable) Statistics.stopTimer(failedSilentNanos, failedSilentStart) } + @inline def wrapResult(reporter: ContextReporter, result: T) = + if (reporter.hasErrors) { + stopStats() + SilentTypeError(reporter.errors: _*) + } else SilentResultValue(result) + try { if (context.reportErrors || reportAmbiguousErrors != context.ambiguousErrors || @@ -697,20 +677,17 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper context.undetparams = context1.undetparams context.savedTypeBounds = context1.savedTypeBounds context.namedApplyBlockInfo = context1.namedApplyBlockInfo - if (context1.hasErrors) { - stopStats() - SilentTypeError(context1.errors: _*) - } else { - // If we have a successful result, emit any warnings it created. - context1.flushAndIssueWarnings() - SilentResultValue(result) - } + + // If we have a successful result, emit any warnings it created. + if (!context1.reporter.hasErrors) + context1.reporter.emitWarnings() + + wrapResult(context1.reporter, result) } else { assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer") - withSavedContext(context){ - val res = op(this) - val errorsToReport = context.flushAndReturnBuffer() - if (errorsToReport.isEmpty) SilentResultValue(res) else SilentTypeError(errorsToReport.head) + + context.reporter.withFreshErrorBuffer { + wrapResult(context.reporter, op(this)) } } } catch { @@ -816,14 +793,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } // avoid throwing spurious DivergentImplicit errors - if (context.hasErrors) + if (context.reporter.hasErrors) setError(tree) else withCondConstrTyper(treeInfo.isSelfOrSuperConstrCall(tree))(typer1 => if (original != EmptyTree && pt != WildcardType) ( typer1 silent { tpr => val withImplicitArgs = tpr.applyImplicitArgs(tree) - if (tpr.context.hasErrors) tree // silent will wrap it in SilentTypeError anyway + if (tpr.context.reporter.hasErrors) tree // silent will wrap it in SilentTypeError anyway else tpr.typed(withImplicitArgs, mode, pt) } orElse { _ => @@ -1057,7 +1034,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val silentContext = context.makeImplicit(context.ambiguousErrors) val res = newTyper(silentContext).typed( new ApplyImplicitView(coercion, List(tree)) setPos tree.pos, mode, pt) - silentContext.firstError match { + silentContext.reporter.firstError match { case Some(err) => context.issue(err) case None => return res } @@ -2990,7 +2967,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper ConstructorsOrderError(stat) } - if (treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos, + if (!isPastTyper && treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos, "a pure expression does nothing in statement position; " + "you may be omitting necessary parentheses" ) @@ -3149,7 +3126,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = { // TODO_NMT: check the assumption that args nonEmpty def duplErrTree = setError(treeCopy.Apply(tree, fun0, args)) - def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree } + def duplErrorTree(err: AbsTypeError) = { context.issue(err); duplErrTree } def preSelectOverloaded(fun: Tree): Tree = { if (fun.hasSymbolField && fun.symbol.isOverloaded) { @@ -3229,7 +3206,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper (arg1, arg1.tpe.deconst) }.unzip } - if (context.hasErrors) + if (context.reporter.hasErrors) setError(tree) else { inferMethodAlternative(fun, undetparams, argTpes, pt) @@ -3357,8 +3334,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper duplErrTree } else if (lencmp2 == 0) { // useful when a default doesn't match parameter type, e.g. def f[T](x:T="a"); f[Int]() - val note = "Error occurred in an application involving default arguments." - if (!(context.diagnostic contains note)) context.diagnostic = note :: context.diagnostic + context.diagUsedDefaults = true doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt) } else { rollbackNamesDefaultsOwnerChanges() @@ -4331,7 +4307,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper c.retyping = true try { val res = newTyper(c).typedArgs(args, mode) - if (c.hasErrors) None else Some(res) + if (c.reporter.hasErrors) None else Some(res) } catch { case ex: CyclicReference => throw ex @@ -4395,7 +4371,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case _ => () } } - typeErrors foreach issue + typeErrors foreach context.issue setError(treeCopy.Apply(tree, fun, args)) } @@ -4449,7 +4425,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper doTypedApply(tree, fun2, args, mode, pt) case err: SilentTypeError => onError({ - err.reportableErrors foreach issue + err.reportableErrors foreach context.issue args foreach (arg => typed(arg, mode, ErrorType)) setError(tree) }) @@ -4686,7 +4662,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper else // before failing due to access, try a dynamic call. asDynamicCall getOrElse { - issue(accessibleError.get) + context.issue(accessibleError.get) setError(tree) } case _ => diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 923297bafb..1643e0061f 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -142,7 +142,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled globalPhase = run.typerPhase // amazing... looks like phase and globalPhase are different things, so we need to set them separately - currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions + currentTyper.context.initRootContext() // need to manually set context mode, otherwise typer.silent will throw exceptions reporter.reset() val expr3 = withContext(transform(currentTyper, expr2)) diff --git a/src/eclipse/repl/.classpath b/src/eclipse/repl/.classpath index 601a231aeb..8ff9aabfbf 100644 --- a/src/eclipse/repl/.classpath +++ b/src/eclipse/repl/.classpath @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> - <classpathentry kind="src" path="repl"/> - <classpathentry combineaccessrules="false" kind="src" path="/asm"/> - <classpathentry kind="var" path="M2_REPO/jline/jline/2.11/jline-2.11.jar"/> - <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/> - <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_COMPILER_CONTAINER"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/repl/jline-2.11.jar"/> - <classpathentry kind="output" path="build-quick-repl"/> + <classpathentry kind="src" path="repl"/> + <classpathentry combineaccessrules="false" kind="src" path="/asm"/> + <classpathentry kind="var" path="M2_REPO/jline/jline/2.12/jline-2.12.jar"/> + <!-- <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/repl/jline-2.12.jar"/> --> + <classpathentry combineaccessrules="false" kind="src" path="/scala-compiler"/> + <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="output" path="build-quick-repl"/> </classpath> diff --git a/src/library/scala/StringContext.scala b/src/library/scala/StringContext.scala index ed3b305d59..2632994a34 100644 --- a/src/library/scala/StringContext.scala +++ b/src/library/scala/StringContext.scala @@ -163,7 +163,7 @@ case class StringContext(parts: String*) { */ // The implementation is hardwired to `scala.tools.reflect.MacroImplementations.macro_StringInterpolation_f` // Using the mechanism implemented in `scala.tools.reflect.FastTrack` - def f(args: Any*): String = macro ??? + def f[A >: Any](args: A*): String = macro ??? } object StringContext { @@ -173,8 +173,13 @@ object StringContext { * @param str The offending string * @param idx The index of the offending backslash character in `str`. */ - class InvalidEscapeException(str: String, @deprecatedName('idx) val index: Int) - extends IllegalArgumentException("invalid escape character at index "+index+" in \""+str+"\"") + class InvalidEscapeException(str: String, @deprecatedName('idx) val index: Int) extends IllegalArgumentException( + s"""invalid escape ${ + require(index >= 0 && index < str.length) + val ok = """[\b, \t, \n, \f, \r, \\, \", \']""" + if (index == str.length - 1) "at terminal" else s"'\\${str(index + 1)}' not one of $ok at" + } index $index in "$str". Use \\\\ for literal \\.""" + ) /** Expands standard Scala escape sequences in a string. * Escape sequences are: diff --git a/src/library/scala/collection/IterableProxy.scala b/src/library/scala/collection/IterableProxy.scala index 3a0e2ab115..97aa830c5a 100644 --- a/src/library/scala/collection/IterableProxy.scala +++ b/src/library/scala/collection/IterableProxy.scala @@ -16,4 +16,5 @@ package collection * @version 2.8 * @since 2.8 */ +@deprecated("Proxying is deprecated due to lack of use and compiler-level support.", "2.11.3") trait IterableProxy[+A] extends Iterable[A] with IterableProxyLike[A, Iterable[A]] diff --git a/src/library/scala/collection/MapProxy.scala b/src/library/scala/collection/MapProxy.scala index 941c1f5a4a..26a7c710ee 100644 --- a/src/library/scala/collection/MapProxy.scala +++ b/src/library/scala/collection/MapProxy.scala @@ -17,4 +17,5 @@ package collection * @version 1.0, 21/07/2003 * @since 1 */ +@deprecated("Proxying is deprecated due to lack of use and compiler-level support.", "2.11.3") trait MapProxy[A, +B] extends Map[A, B] with MapProxyLike[A, B, Map[A, B]] diff --git a/src/library/scala/collection/SetProxy.scala b/src/library/scala/collection/SetProxy.scala index f9f38f148a..e17fb215b9 100644 --- a/src/library/scala/collection/SetProxy.scala +++ b/src/library/scala/collection/SetProxy.scala @@ -17,4 +17,5 @@ package collection * @author Martin Odersky * @version 2.0, 01/01/2007 */ +@deprecated("Proxying is deprecated due to lack of use and compiler-level support.", "2.11.3") trait SetProxy[A] extends Set[A] with SetProxyLike[A, Set[A]] diff --git a/src/library/scala/collection/TraversableProxy.scala b/src/library/scala/collection/TraversableProxy.scala index 65936da0e4..9eec685d10 100644 --- a/src/library/scala/collection/TraversableProxy.scala +++ b/src/library/scala/collection/TraversableProxy.scala @@ -21,4 +21,5 @@ package collection * @version 2.8 * @since 2.8 */ +@deprecated("Proxying is deprecated due to lack of use and compiler-level support.", "2.11.3") trait TraversableProxy[+A] extends Traversable[A] with TraversableProxyLike[A, Traversable[A]] diff --git a/src/library/scala/util/matching/Regex.scala b/src/library/scala/util/matching/Regex.scala index 2e1bfb02e4..5c4e706dc1 100644 --- a/src/library/scala/util/matching/Regex.scala +++ b/src/library/scala/util/matching/Regex.scala @@ -6,7 +6,6 @@ ** |/ ** \* */ - /** * This package is concerned with regular expression (regex) matching against strings, * with the main goal of pulling out information from those matches, or replacing @@ -28,117 +27,127 @@ * into a [[java.lang.String]]. * */ -package scala -package util.matching +package scala.util.matching import scala.collection.AbstractIterator import java.util.regex.{ Pattern, Matcher } -/** This class provides methods for creating and using regular expressions. - * It is based on the regular expressions of the JDK since 1.4. +/** A regular expression is used to determine whether a string matches a pattern + * and, if it does, to extract or transform the parts that match. * - * Its main goal is to extract strings that match a pattern, or the subgroups - * that make it up. For that reason, it is usually used with for comprehensions - * and matching (see methods for examples). + * This class delegates to the [[java.util.regex]] package of the Java Platform. + * See the documentation for [[java.util.regex.Pattern]] for details about + * the regular expression syntax for pattern strings. * - * A Regex is created from a [[java.lang.String]] representation of the - * regular expression pattern^1^. That pattern is compiled - * during construction, so frequently used patterns should be declared outside - * loops if performance is of concern. Possibly, they might be declared on a - * companion object, so that they need only to be initialized once. + * An instance of `Regex` represents a compiled regular expression pattern. + * Since compilation is expensive, frequently used `Regex`es should be constructed + * once, outside of loops and perhaps in a companion object. * - * The canonical way of creating regex patterns is by using the method `r`, provided - * on [[java.lang.String]] through an implicit conversion into - * [[scala.collection.immutable.WrappedString]]. Using triple quotes to write these - * strings avoids having to quote the backslash character (`\`). + * The canonical way to create a `Regex` is by using the method `r`, provided + * implicitly for strings: * - * Using the constructor directly, on the other hand, makes - * it possible to declare names for subgroups in the pattern. + * {{{ + * val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r + * }}} * - * For example, both declarations below generate the same regex, but the second - * one associate names with the subgroups. + * Since escapes are not processed in multi-line string literals, using triple quotes + * avoids having to escape the backslash character, so that `"\\d"` can be written `"""\d"""`. + * + * To extract the capturing groups when a `Regex` is matched, use it as + * an extractor in a pattern match: * * {{{ - * val dateP1 = """(\d\d\d\d)-(\d\d)-(\d\d)""".r - * val dateP2 = new scala.util.matching.Regex("""(\d\d\d\d)-(\d\d)-(\d\d)""", "year", "month", "day") + * "2004-01-20" match { + * case date(year, month, day) => s"$year was a good year for PLs." + * } * }}} * - * There are two ways of using a `Regex` to find a pattern: calling methods on - * Regex, such as `findFirstIn` or `findAllIn`, or using it as an extractor in a - * pattern match. + * To check only whether the `Regex` matches, ignoring any groups, + * use a sequence wildcard: + * + * {{{ + * "2004-01-20" match { + * case date(_*) => "It's a date!" + * } + * }}} * - * Note that, when calling `findAllIn`, the resulting [[scala.util.matching.Regex.MatchIterator]] - * needs to be initialized (by calling `hasNext` or `next()`, or causing these to be - * called) before information about a match can be retrieved: + * That works because a `Regex` extractor produces a sequence of strings. + * Extracting only the year from a date could also be expressed with + * a sequence wildcard: * * {{{ - * val msg = "I love Scala" + * "2004-01-20" match { + * case date(year, _*) => s"$year was a good year for PLs." + * } + * }}} * - * // val start = " ".r.findAllIn(msg).start // throws an IllegalStateException + * In a pattern match, `Regex` normally matches the entire input. + * However, an unanchored `Regex` finds the pattern anywhere + * in the input. * - * val matches = " ".r.findAllIn(msg) - * matches.hasNext // initializes the matcher - * val start = matches.start + * {{{ + * val embeddedDate = date.unanchored + * "Date: 2004-01-20 17:25:18 GMT (10 years, 28 weeks, 5 days, 17 hours and 51 minutes ago)" match { + * case embeddedDate("2004", "01", "20") => "A Scala is born." + * } * }}} * - * When Regex is used as an extractor in a pattern match, note that it - * only succeeds if the whole text can be matched. For this reason, one usually - * calls a method to find the matching substrings, and then use it as an extractor - * to break match into subgroups. + * To find or replace matches of the pattern, use the various find and replace methods. + * There is a flavor of each method that produces matched strings and + * another that produces `Match` objects. * - * As an example, the above patterns can be used like this: + * For example, pattern matching with an unanchored `Regex`, as in the previous example, + * is the same as using `findFirstMatchIn`, except that the findFirst methods return an `Option`, + * or `None` for no match: * * {{{ - * val dateP1(year, month, day) = "2011-07-15" + * val dates = "Important dates in history: 2004-01-20, 1958-09-05, 2010-10-06, 2011-07-15" + * val firstDate = date findFirstIn dates getOrElse "No date found." + * val firstYear = for (m <- date findFirstMatchIn dates) yield m group 1 + * }}} * - * // val dateP1(year, month, day) = "Date 2011-07-15" // throws an exception at runtime + * To find all matches: * - * val copyright: String = dateP1 findFirstIn "Date of this document: 2011-07-15" match { - * case Some(dateP1(year, month, day)) => "Copyright "+year - * case None => "No copyright" - * } + * {{{ + * val allYears = for (m <- date findAllMatchIn dates) yield m group 1 + * }}} * - * val copyright: Option[String] = for { - * dateP1(year, month, day) <- dateP1 findFirstIn "Last modified 2011-07-15" - * } yield year - - * def getYears(text: String): Iterator[String] = for (dateP1(year, _, _) <- dateP1 findAllIn text) yield year - * def getFirstDay(text: String): Option[String] = for (m <- dateP2 findFirstMatchIn text) yield m group "day" + * But `findAllIn` returns a special iterator of strings that can be queried for the `MatchData` + * of the last match: + * + * {{{ + * val mi = date findAllIn dates + * val oldies = mi filter (_ => (mi group 1).toInt < 1960) map (s => s"$s: An oldie but goodie.") * }}} * - * Regex does not provide a method that returns a [[scala.Boolean]]. One can - * use [[java.lang.String]] `matches` method, or, if `Regex` is preferred, - * either ignore the return value or test the `Option` for emptyness. For example: + * Note that `findAllIn` finds matches that don't overlap. (See [[findAllIn]] for more examples.) * * {{{ - * def hasDate(text: String): Boolean = (dateP1 findFirstIn text).nonEmpty - * def printLinesWithDates(lines: Traversable[String]) { - * lines foreach { line => - * dateP1 findFirstIn line foreach { _ => println(line) } - * } - * } + * val num = """(\d+)""".r + * val all = (num findAllIn "123").toList // List("123"), not List("123", "23", "3") * }}} * - * There are also methods that can be used to replace the patterns - * on a text. The substitutions can be simple replacements, or more - * complex functions. For example: + * Text replacement can be performed unconditionally or as a function of the current match: * * {{{ - * val months = Map( 1 -> "Jan", 2 -> "Feb", 3 -> "Mar", - * 4 -> "Apr", 5 -> "May", 6 -> "Jun", - * 7 -> "Jul", 8 -> "Aug", 9 -> "Sep", - * 10 -> "Oct", 11 -> "Nov", 12 -> "Dec") - * - * import scala.util.matching.Regex.Match - * def reformatDate(text: String) = dateP2 replaceAllIn ( text, (m: Match) => - * "%s %s, %s" format (months(m group "month" toInt), m group "day", m group "year") - * ) + * val redacted = date replaceAllIn (dates, "XXXX-XX-XX") + * val yearsOnly = date replaceAllIn (dates, m => m group 1) + * val months = (0 to 11) map { i => val c = Calendar.getInstance; c.set(2014, i, 1); f"$c%tb" } + * val reformatted = date replaceAllIn (dates, _ match { case date(y,m,d) => f"${months(m.toInt - 1)} $d, $y" }) * }}} * - * You can use special pattern syntax constructs like `(?idmsux-idmsux)`¹ to switch - * various regex compilation options like `CASE_INSENSITIVE` or `UNICODE_CASE`. + * Pattern matching the `Match` against the `Regex` that created it does not reapply the `Regex`. + * In the expression for `reformatted`, each `date` match is computed once. But it is possible to apply a + * `Regex` to a `Match` resulting from a different pattern: + * + * {{{ + * val docSpree = """2011(?:-\d{2}){2}""".r + * val docView = date replaceAllIn (dates, _ match { + * case docSpree() => "Historic doc spree!" + * case _ => "Something else happened" + * }) + * }}} * - * @note ¹ A detailed description is available in [[java.util.regex.Pattern]]. * @see [[java.util.regex.Pattern]] * * @author Thibaud Hottelier @@ -154,9 +163,8 @@ import java.util.regex.{ Pattern, Matcher } * interpreted as a reference to a group in the matched pattern, with numbers * 1 through 9 corresponding to the first nine groups, and 0 standing for the * whole match. Any other character is an error. The backslash (`\`) character - * will be interpreted as an escape character, and can be used to escape the - * dollar sign. One can use [[scala.util.matching.Regex]]'s `quoteReplacement` - * to automatically escape these characters. + * will be interpreted as an escape character and can be used to escape the + * dollar sign. Use `Regex.quoteReplacement` to escape these characters. */ @SerialVersionUID(-2094783597747625537L) class Regex private[matching](val pattern: Pattern, groupNames: String*) extends Serializable { @@ -164,51 +172,84 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends import Regex._ - /** - * @param regex A string representing a regular expression - * @param groupNames A mapping from names to indices in capture groups - */ + /** Compile a regular expression, supplied as a string, into a pattern that + * can be matched against inputs. + * + * If group names are supplied, they can be used this way: + * + * {{{ + * val namedDate = new Regex("""(\d\d\d\d)-(\d\d)-(\d\d)""", "year", "month", "day") + * val namedYears = for (m <- namedDate findAllMatchIn dates) yield m group "year" + * }}} + * + * This constructor does not support options as flags, which must be + * supplied as inline flags in the pattern string: `(?idmsux-idmsux)`. + * + * @param regex The regular expression to compile. + * @param groupNames Names of capturing groups. + */ def this(regex: String, groupNames: String*) = this(Pattern.compile(regex), groupNames: _*) /** Tries to match a [[java.lang.CharSequence]]. + * * If the match succeeds, the result is a list of the matching * groups (or a `null` element if a group did not match any input). * If the pattern specifies no groups, then the result will be an empty list * on a successful match. * * This method attempts to match the entire input by default; to find the next - * matching subsequence, use an unanchored Regex. - + * matching subsequence, use an unanchored `Regex`. + * * For example: * * {{{ * val p1 = "ab*c".r * val p1Matches = "abbbc" match { - * case p1() => true + * case p1() => true // no groups * case _ => false * } * val p2 = "a(b*)c".r + * val p2Matches = "abbbc" match { + * case p2(_*) => true // any groups + * case _ => false + * } * val numberOfB = "abbbc" match { - * case p2(b) => Some(b.length) + * case p2(b) => Some(b.length) // one group * case _ => None * } * val p3 = "b*".r.unanchored * val p3Matches = "abbbc" match { - * case p3() => true + * case p3() => true // find the b's * case _ => false * } + * val p4 = "a(b*)(c+)".r + * val p4Matches = "abbbcc" match { + * case p4(_*) => true // multiple groups + * case _ => false + * } + * val allGroups = "abbbcc" match { + * case p4(all @ _*) => all mkString "/" // "bbb/cc" + * case _ => "" + * } + * val cGroup = "abbbcc" match { + * case p4(_, c) => c + * case _ => "" + * } * }}} * * @param s The string to match * @return The matches */ - def unapplySeq(s: CharSequence): Option[List[String]] = { - val m = pattern matcher s - if (runMatcher(m)) Some((1 to m.groupCount).toList map m.group) - else None + def unapplySeq(s: CharSequence): Option[List[String]] = s match { + case null => None + case _ => + val m = pattern matcher s + if (runMatcher(m)) Some((1 to m.groupCount).toList map m.group) + else None } /** Tries to match the String representation of a [[scala.Char]]. + * * If the match succeeds, the result is the first matching * group if any groups are defined, or an empty Sequence otherwise. * @@ -247,13 +288,16 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends } /** Tries to match on a [[scala.util.matching.Regex.Match]]. + * * A previously failed match results in None. + * * If a successful match was made against the current pattern, then that result is used. + * * Otherwise, this Regex is applied to the previously matched input, * and the result of that match is used. */ def unapplySeq(m: Match): Option[List[String]] = - if (m.matched == null) None + if (m == null || m.matched == null) None else if (m.matcher.pattern == this.pattern) Some((1 to m.groupCount).toList map m.group) else unapplySeq(m.matched) @@ -274,30 +318,47 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends // @see UnanchoredRegex protected def runMatcher(m: Matcher) = m.matches() - /** Return all non-overlapping matches of this regexp in given character + /** Return all non-overlapping matches of this `Regex` in the given character * sequence as a [[scala.util.matching.Regex.MatchIterator]], * which is a special [[scala.collection.Iterator]] that returns the - * matched strings, but can also be converted into a normal iterator - * that returns objects of type [[scala.util.matching.Regex.Match]] - * that can be queried for data such as the text that precedes the - * match, subgroups, etc. + * matched strings but can also be queried for more data about the last match, + * such as capturing groups and start position. + * + * A `MatchIterator` can also be converted into an iterator + * that returns objects of type [[scala.util.matching.Regex.Match]], + * such as is normally returned by `findAllMatchIn`. * * Where potential matches overlap, the first possible match is returned, - * followed by the next match that is completely after the first. For - * instance, `"hat[^a]+".r` will match `hath` and `hattth` in the string - * `"hathatthattthatttt"`. + * followed by the next match that follows the input consumed by the + * first match: + * + * {{{ + * val hat = "hat[^a]+".r + * val hathaway = "hathatthattthatttt" + * val hats = (hat findAllIn hathaway).toList // List(hath, hattth) + * val pos = (hat findAllMatchIn hathaway map (_.start)).toList // List(0, 7) + * }}} + * + * To return overlapping matches, it is possible to formulate a regular expression + * with lookahead (`?=`) that does not consume the overlapping region. + * + * {{{ + * val madhatter = "(h)(?=(at[^a]+))".r + * val madhats = (madhatter findAllMatchIn hathaway map { + * case madhatter(x,y) => s"$x$y" + * }).toList // List(hath, hatth, hattth, hatttt) + * }}} * - * Attempting to retrieve information about a match before initializing - * the iterator can result in [[java.lang.IllegalStateException]]s. See - * [[scala.util.matching.Regex.MatchIterator]] for details. + * Attempting to retrieve match information before performing the first match + * or after exhausting the iterator results in [[java.lang.IllegalStateException]]. + * See [[scala.util.matching.Regex.MatchIterator]] for details. * * @param source The text to match against. - * @return A [[scala.util.matching.Regex.MatchIterator]] of all matches. + * @return A [[scala.util.matching.Regex.MatchIterator]] of matched substrings. * @example {{{for (words <- """\w+""".r findAllIn "A simple example.") yield words}}} */ def findAllIn(source: CharSequence) = new Regex.MatchIterator(source, this, groupNames) - /** Return all non-overlapping matches of this regexp in given character sequence as a * [[scala.collection.Iterator]] of [[scala.util.matching.Regex.Match]]. * @@ -316,8 +377,8 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends } } - /** Return optionally first matching string of this regexp in given character sequence, - * or None if it does not exist. + /** Return an optional first matching string of this `Regex` in the given character sequence, + * or None if there is no match. * * @param source The text to match against. * @return An [[scala.Option]] of the first matching string in the text. @@ -328,13 +389,11 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends if (m.find) Some(m.group) else None } - /** Return optionally first match of this regexp in given character sequence, + /** Return an optional first match of this `Regex` in the given character sequence, * or None if it does not exist. * - * The main difference between this method and `findFirstIn` is that the (optional) return - * type for this is [[scala.util.matching.Regex.Match]], through which more - * data can be obtained about the match, such as the strings that precede and follow it, - * or subgroups. + * If the match is successful, the [[scala.util.matching.Regex.Match]] can be queried for + * more data. * * @param source The text to match against. * @return A [[scala.Option]] of [[scala.util.matching.Regex.Match]] of the first matching string in the text. @@ -345,30 +404,28 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends if (m.find) Some(new Match(source, m, groupNames)) else None } - /** Return optionally match of this regexp at the beginning of the - * given character sequence, or None if regexp matches no prefix + /** Return an optional match of this `Regex` at the beginning of the + * given character sequence, or None if it matches no prefix * of the character sequence. * - * The main difference from this method to `findFirstIn` is that this - * method will not return any matches that do not begin at the start - * of the text being matched against. + * Unlike `findFirstIn`, this method will only return a match at + * the beginning of the input. * * @param source The text to match against. * @return A [[scala.Option]] of the matched prefix. - * @example {{{"""[a-z]""".r findPrefixOf "A simple example." // returns None, since the text does not begin with a lowercase letter}}} + * @example {{{"""\p{Lower}""".r findPrefixOf "A simple example." // returns None, since the text does not begin with a lowercase letter}}} */ def findPrefixOf(source: CharSequence): Option[String] = { val m = pattern.matcher(source) if (m.lookingAt) Some(m.group) else None } - /** Return optionally match of this regexp at the beginning of the - * given character sequence, or None if regexp matches no prefix + /** Return an optional match of this `Regex` at the beginning of the + * given character sequence, or None if it matches no prefix * of the character sequence. * - * The main difference from this method to `findFirstMatchIn` is that - * this method will not return any matches that do not begin at the - * start of the text being matched against. + * Unlike `findFirstMatchIn`, this method will only return a match at + * the beginning of the input. * * @param source The text to match against. * @return A [[scala.Option]] of the [[scala.util.matching.Regex.Match]] of the matched string. @@ -402,7 +459,7 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * import scala.util.matching.Regex * val datePattern = new Regex("""(\d\d\d\d)-(\d\d)-(\d\d)""", "year", "month", "day") * val text = "From 2011-07-15 to 2011-07-17" - * val repl = datePattern replaceAllIn (text, m => m.group("month")+"/"+m.group("day")) + * val repl = datePattern replaceAllIn (text, m => s"${m group "month"}/${m group "day"}") * }}} * * $replacementString @@ -425,10 +482,10 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * {{{ * import scala.util.matching.Regex._ * - * val map = Map("x" -> "a var", "y" -> """some $ and \ signs""") + * val vars = Map("x" -> "a var", "y" -> """some $ and \ signs""") * val text = "A text with variables %x, %y and %z." * val varPattern = """%(\w+)""".r - * val mapper = (m: Match) => map get (m group 1) map (quoteReplacement(_)) + * val mapper = (m: Match) => vars get (m group 1) map (quoteReplacement(_)) * val repl = varPattern replaceSomeIn (text, mapper) * }}} * @@ -469,17 +526,25 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends pattern.split(toSplit) /** Create a new Regex with the same pattern, but no requirement that - * the entire String matches in extractor patterns. For instance, the strings - * shown below lead to successful matches, where they would not otherwise. + * the entire String matches in extractor patterns. + * + * Normally, matching on `date` behaves as though the pattern were + * enclosed in anchors, `"^pattern$"`. + * + * The unanchored `Regex` behaves as though those anchors were removed. + * + * Note that this method does not actually strip any matchers from the pattern. + * + * Calling `anchored` returns the original `Regex`. * * {{{ - * val dateP1 = """(\d\d\d\d)-(\d\d)-(\d\d)""".r.unanchored + * val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r.unanchored * - * val dateP1(year, month, day) = "Date 2011-07-15" + * val date(year, month, day) = "Date 2011-07-15" // OK * * val copyright: String = "Date of this document: 2011-07-15" match { - * case dateP1(year, month, day) => "Copyright "+year - * case _ => "No copyright" + * case date(year, month, day) => s"Copyright $year" // OK + * case _ => "No copyright" * } * }}} * @@ -494,6 +559,10 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends override def toString = regex } +/** A [[Regex]] that finds the first match when used in a pattern match. + * + * @see [[Regex#unanchored]] + */ trait UnanchoredRegex extends Regex { override protected def runMatcher(m: Matcher) = m.find() override def unanchored = this @@ -509,70 +578,79 @@ object Regex { */ trait MatchData { - /** The source from where the match originated */ + /** The source from which the match originated */ val source: CharSequence - /** The names of the groups, or some empty sequence if one defined */ + /** The names of the groups, or an empty sequence if none defined */ val groupNames: Seq[String] - /** The number of subgroups in the pattern (not all of these need to match!) */ + /** The number of capturing groups in the pattern. + * (For a given successful match, some of those groups may not have matched any input.) + */ def groupCount: Int /** The index of the first matched character, or -1 if nothing was matched */ def start: Int /** The index of the first matched character in group `i`, - * or -1 if nothing was matched for that group */ + * or -1 if nothing was matched for that group. + */ def start(i: Int): Int - /** The index of the last matched character, or -1 if nothing was matched */ + /** The index following the last matched character, or -1 if nothing was matched. */ def end: Int /** The index following the last matched character in group `i`, - * or -1 if nothing was matched for that group */ + * or -1 if nothing was matched for that group. + */ def end(i: Int): Int - /** The matched string, or `null` if nothing was matched */ + /** The matched string, or `null` if nothing was matched. */ def matched: String = if (start >= 0) source.subSequence(start, end).toString else null /** The matched string in group `i`, - * or `null` if nothing was matched */ + * or `null` if nothing was matched. + */ def group(i: Int): String = if (start(i) >= 0) source.subSequence(start(i), end(i)).toString else null - /** All matched subgroups, i.e., not including group(0) */ + /** All capturing groups, i.e., not including group(0). */ def subgroups: List[String] = (1 to groupCount).toList map group /** The char sequence before first character of match, - * or `null` if nothing was matched */ + * or `null` if nothing was matched. + */ def before: CharSequence = if (start >= 0) source.subSequence(0, start) else null /** The char sequence before first character of match in group `i`, - * or `null` if nothing was matched for that group */ + * or `null` if nothing was matched for that group. + */ def before(i: Int): CharSequence = if (start(i) >= 0) source.subSequence(0, start(i)) else null /** Returns char sequence after last character of match, - * or `null` if nothing was matched */ + * or `null` if nothing was matched. + */ def after: CharSequence = if (end >= 0) source.subSequence(end, source.length) else null /** The char sequence after last character of match in group `i`, - * or `null` if nothing was matched for that group */ + * or `null` if nothing was matched for that group. + */ def after(i: Int): CharSequence = if (end(i) >= 0) source.subSequence(end(i), source.length) else null private lazy val nameToIndex: Map[String, Int] = Map[String, Int]() ++ ("" :: groupNames.toList).zipWithIndex - /** Returns the group with given name + /** Returns the group with given name. * * @param id The group name * @return The requested group @@ -583,24 +661,22 @@ object Regex { case Some(index) => group(index) } - /** The matched string; equivalent to `matched.toString` */ + /** The matched string; equivalent to `matched.toString`. */ override def toString = matched - } - /** Provides information about a succesful match. - */ + /** Provides information about a successful match. */ class Match(val source: CharSequence, private[matching] val matcher: Matcher, val groupNames: Seq[String]) extends MatchData { - /** The index of the first matched character */ + /** The index of the first matched character. */ val start = matcher.start - /** The index following the last matched character */ + /** The index following the last matched character. */ val end = matcher.end - /** The number of subgroups */ + /** The number of subgroups. */ def groupCount = matcher.groupCount private lazy val starts: Array[Int] = @@ -608,19 +684,19 @@ object Regex { private lazy val ends: Array[Int] = ((0 to groupCount) map matcher.end).toArray - /** The index of the first matched character in group `i` */ + /** The index of the first matched character in group `i`. */ def start(i: Int) = starts(i) - /** The index following the last matched character in group `i` */ + /** The index following the last matched character in group `i`. */ def end(i: Int) = ends(i) /** The match itself with matcher-dependent lazy vals forced, - * so that match is valid even once matcher is advanced + * so that match is valid even once matcher is advanced. */ def force: this.type = { starts; ends; this } } - /** An extractor object for Matches, yielding the matched string + /** An extractor object for Matches, yielding the matched string. * * This can be used to help writing replacer functions when you * are not interested in match data. For example: @@ -635,15 +711,15 @@ object Regex { def unapply(m: Match): Some[String] = Some(m.matched) } - /** An extractor object that yields the groups in the match. Using an extractor - * rather than the original regex avoids recomputing the match. + /** An extractor object that yields the groups in the match. Using this extractor + * rather than the original `Regex` ensures that the match is not recomputed. * * {{{ * import scala.util.matching.Regex.Groups * - * val datePattern = """(\d\d\d\d)-(\d\d)-(\d\d)""".r + * val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r * val text = "The doc spree happened on 2011-07-15." - * val day = datePattern replaceAllIn(text, _ match { case Groups(year, month, day) => month+"/"+day }) + * val day = date replaceAllIn(text, _ match { case Groups(_, month, day) => s"$month/$day" }) * }}} */ object Groups { @@ -672,7 +748,7 @@ object Regex { nextSeen } - /** The next matched substring of `source` */ + /** The next matched substring of `source`. */ def next(): String = { if (!hasNext) throw new NoSuchElementException nextSeen = false @@ -681,28 +757,28 @@ object Regex { override def toString = super[AbstractIterator].toString - /** The index of the first matched character */ + /** The index of the first matched character. */ def start: Int = matcher.start - /** The index of the first matched character in group `i` */ + /** The index of the first matched character in group `i`. */ def start(i: Int): Int = matcher.start(i) - /** The index of the last matched character */ + /** The index of the last matched character. */ def end: Int = matcher.end - /** The index following the last matched character in group `i` */ + /** The index following the last matched character in group `i`. */ def end(i: Int): Int = matcher.end(i) - /** The number of subgroups */ + /** The number of subgroups. */ def groupCount = matcher.groupCount - /** Convert to an iterator that yields MatchData elements instead of Strings */ + /** Convert to an iterator that yields MatchData elements instead of Strings. */ def matchData: Iterator[Match] = new AbstractIterator[Match] { def hasNext = self.hasNext def next = { self.next(); new Match(source, matcher, groupNames).force } } - /** Convert to an iterator that yields MatchData elements instead of Strings and has replacement support */ + /** Convert to an iterator that yields MatchData elements instead of Strings and has replacement support. */ private[matching] def replacementData = new AbstractIterator[Match] with Replacement { def matcher = self.matcher def hasNext = self.hasNext diff --git a/src/reflect/scala/reflect/api/Quasiquotes.scala b/src/reflect/scala/reflect/api/Quasiquotes.scala index e905aa4153..eaae05bed5 100644 --- a/src/reflect/scala/reflect/api/Quasiquotes.scala +++ b/src/reflect/scala/reflect/api/Quasiquotes.scala @@ -13,7 +13,7 @@ trait Quasiquotes { self: Universe => protected trait api { // implementation is hardwired to `dispatch` method of `scala.tools.reflect.quasiquotes.Quasiquotes` // using the mechanism implemented in `scala.tools.reflect.FastTrack` - def apply[T](args: T*): Tree = macro ??? + def apply[A >: Any](args: A*): Tree = macro ??? def unapply(scrutinee: Any): Any = macro ??? } object q extends api diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index bf560a21e5..02578e2038 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -836,12 +836,18 @@ trait Definitions extends api.StandardDefinitions { def typeOfMemberNamedHead(tp: Type) = typeArgOfBaseTypeOr(tp, SeqClass)(resultOfMatchingMethod(tp, nme.head)()) def typeOfMemberNamedApply(tp: Type) = typeArgOfBaseTypeOr(tp, SeqClass)(resultOfMatchingMethod(tp, nme.apply)(IntTpe)) def typeOfMemberNamedDrop(tp: Type) = typeArgOfBaseTypeOr(tp, SeqClass)(resultOfMatchingMethod(tp, nme.drop)(IntTpe)) - def typesOfSelectors(tp: Type) = getterMemberTypes(tp, productSelectors(tp)) + def typesOfSelectors(tp: Type) = + if (isTupleType(tp)) tp.typeArgs + else getterMemberTypes(tp, productSelectors(tp)) + // SI-8128 Still using the type argument of the base type at Seq/Option if this is an old-style (2.10 compatible) // extractor to limit exposure to regressions like the reported problem with existentials. // TODO fix the existential problem in the general case, see test/pending/pos/t8128.scala private def typeArgOfBaseTypeOr(tp: Type, baseClass: Symbol)(or: => Type): Type = (tp baseType baseClass).typeArgs match { - case x :: Nil => x + case x :: Nil => + val x1 = x + val x2 = repackExistential(x1) + x2 case _ => or } diff --git a/src/reflect/scala/reflect/internal/Reporting.scala b/src/reflect/scala/reflect/internal/Reporting.scala index 423127803e..f2de83bc5d 100644 --- a/src/reflect/scala/reflect/internal/Reporting.scala +++ b/src/reflect/scala/reflect/internal/Reporting.scala @@ -89,6 +89,9 @@ abstract class Reporter { def count(severity: Severity): Int def resetCount(severity: Severity): Unit + def errorCount: Int = count(ERROR) + def warningCount: Int = count(WARNING) + def hasErrors: Boolean = count(ERROR) > 0 def hasWarnings: Boolean = count(WARNING) > 0 diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala index ce0eadc04f..50c89f7442 100644 --- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala @@ -430,7 +430,14 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) import scala.concurrent.duration._ Await.ready(globalFuture, 60.seconds) - (line ne null) && (command(line) match { + if (line eq null) { + // SI-4563: this means the console was properly interrupted (Ctrl+D usually) + // so we display the output message (which by default ends with + // a newline so as not to break the user's terminal) + if (in.interactive) out.print(Properties.shellInterruptedString) + + false + } else (command(line) match { case Result(false, _) => false case Result(_, Some(line)) => addReplay(line) ; true case _ => true diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index 8ea8759ee5..6e30b73e0e 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -1121,7 +1121,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set def apply(line: String): Result = debugging(s"""parse("$line")""") { var isIncomplete = false - currentRun.reporting.withIncompleteHandler((_, _) => isIncomplete = true) { + currentRun.parsing.withIncompleteHandler((_, _) => isIncomplete = true) { reporter.reset() val trees = newUnitParser(line).parseStats() if (reporter.hasErrors) Error diff --git a/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala b/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala index ccf18b76de..cbf8ff22ba 100644 --- a/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala +++ b/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala @@ -208,7 +208,7 @@ abstract class ScaladocSyntaxAnalyzer[G <: Global](val global: G) extends Syntax super.skipDocComment() } override def skipBlockComment(): Unit = { - inDocComment = false + inDocComment = false // ??? this means docBuffer won't receive contents of this comment??? docBuffer = new StringBuilder("/*") super.skipBlockComment() } @@ -217,9 +217,10 @@ abstract class ScaladocSyntaxAnalyzer[G <: Global](val global: G) extends Syntax def foundStarComment(start: Int, end: Int) = try { val str = docBuffer.toString val pos = Position.range(unit.source, start, start, end) - unit.comment(pos, str) - if (inDocComment) + if (inDocComment) { + signalParsedDocComment(str, pos) lastDoc = DocComment(str, pos) + } true } finally { docBuffer = null diff --git a/test/files/jvm/innerClassAttribute/Classes_1.scala b/test/files/jvm/innerClassAttribute/Classes_1.scala new file mode 100644 index 0000000000..0875d9160c --- /dev/null +++ b/test/files/jvm/innerClassAttribute/Classes_1.scala @@ -0,0 +1,99 @@ +class A1 { + class B +} + +class A2 { + object B +} + +object A3 { + class B1 + object B2 +} + +class A4 { + def f(l: List[Int]): List[Int] = { + l map (_ + 1) + } +} + +class A5 { + def f(): Object = { + object B + B + } +} + +trait A6 { + def hui = -6 + trait TT +} + +class A7 extends A6 + +abstract class A8 extends A6 { + def fish: TT +} + +class A9 { + class brick extends annotation.StaticAnnotation +} + +class A10 { + val a9 = new A9() + // there's no reference to brick in the bytecode (only in the pickle), so there's no InnerClass attribute for it. + @a9.brick def f = -7 +} + +class A11 { + @JavaAnnot_1.Ann def f = -8 +} + +object A12 { + object B { + class C + } +} + +class A13 { + def oak: A12.B.C = new A12.B.C +} + +class A14 { + def f = { + val x: Object = { + class K + new K + } + x + } + def g = { + val x: Object = new A6 { } + } +} + +object A15 { + def f = { + class B { // static (does not have an outer pointer) + class C // non-static + } + } +} + +class A16 { + val x: A6 = { + class U extends A6 + new A6 { } + } + + { + class V extends A6 + new A6 { } + } +} + +class A17 { + object B { + class C // not static, has an outer pointer. + } +} diff --git a/test/files/jvm/innerClassAttribute/JavaAnnot_1.java b/test/files/jvm/innerClassAttribute/JavaAnnot_1.java new file mode 100644 index 0000000000..27c4d4e5d3 --- /dev/null +++ b/test/files/jvm/innerClassAttribute/JavaAnnot_1.java @@ -0,0 +1,3 @@ +public class JavaAnnot_1 { + public static @interface Ann {} +} diff --git a/test/files/jvm/innerClassAttribute/Test.scala b/test/files/jvm/innerClassAttribute/Test.scala new file mode 100644 index 0000000000..6cf60ab92d --- /dev/null +++ b/test/files/jvm/innerClassAttribute/Test.scala @@ -0,0 +1,208 @@ +import scala.tools.partest.BytecodeTest +import scala.tools.asm +import asm.tree.{ClassNode, InnerClassNode} +import asm.{Opcodes => Flags} +import scala.collection.JavaConverters._ + +object Test extends BytecodeTest { + def assertSame(a: Any, b: Any) = { + assert(a == b, s"\na: $a\nb: $b") + } + + val publicStatic = Flags.ACC_PUBLIC | Flags.ACC_STATIC + val publicAbstractInterface = Flags.ACC_PUBLIC | Flags.ACC_ABSTRACT | Flags.ACC_INTERFACE + + def innerClassNodes(className: String): List[InnerClassNode] = { + loadClassNode(className).innerClasses.asScala.toList.sortBy(_.name) + } + + final case class EnclosingMethod(name: String, descriptor: String, outerClass: String) + def enclosingMethod(className: String) = { + val n = loadClassNode(className) + EnclosingMethod(n.outerMethod, n.outerMethodDesc, n.outerClass) + } + + def assertMember(node: InnerClassNode, outer: String, inner: String, name: Option[String] = None, flags: Int = Flags.ACC_PUBLIC) = { + assertSame(node.name, name.getOrElse(s"$outer$$$inner")) + assertSame(node.outerName, outer) + assertSame(node.innerName, inner) + assertSame(node.access, flags) + } + + def assertAnonymous(node: InnerClassNode, name: String, flags: Int = Flags.ACC_PUBLIC | Flags.ACC_FINAL) = { + assertSame(node.name, name) + assertSame(node.outerName, null) + assertSame(node.innerName, null) + assertSame(node.access, flags) + } + + def assertLocal(node: InnerClassNode, name: String, inner: String, flags: Int = Flags.ACC_PUBLIC) = { + assertSame(node.name, name) + assertSame(node.outerName, null) + assertSame(node.innerName, inner) + assertSame(node.access, flags) + } + + def assertEnclosingMethod(enclosingMethod: EnclosingMethod, outerClass: String, name: String, descriptor: String) = { + assertSame(enclosingMethod.outerClass, outerClass) + assertSame(enclosingMethod.name, name) + assertSame(enclosingMethod.descriptor, descriptor) + } + + def testA1() = { + val List(b1) = innerClassNodes("A1") + assertMember(b1, "A1", "B") + val List(b2) = innerClassNodes("A1$B") + assertMember(b2, "A1", "B") + } + + def testA2() = { + val List(b1) = innerClassNodes("A2") + assertMember(b1, "A2", "B$") + val List(b2) = innerClassNodes("A2$B$") + assertMember(b2, "A2", "B$") + } + + def testA3() = { + def t(c: String) = { + val List(b1, b2) = innerClassNodes(c) + // the outer class for classes nested inside top-level modules is not the module class, but the mirror class. + // this is a hack for java interop, handled in the backend. see BTypes.scala, comment on "Java Compatibility". + assertMember(b1, "A3", "B1", flags = publicStatic) + assertMember(b2, "A3", "B2$", flags = publicStatic) + } + t("A3$") + // the mirror class has the same inner class attributes as the module + // class (added when the mirror is created in the backend) + t("A3") + } + + def testA4() = { + val List(an1) = innerClassNodes("A4") + assertAnonymous(an1, "A4$$anonfun$f$1") + val List(an2) = innerClassNodes("A4$$anonfun$f$1") + assertAnonymous(an2, "A4$$anonfun$f$1") + assertEnclosingMethod( + enclosingMethod("A4$$anonfun$f$1"), + "A4", "f", "(Lscala/collection/immutable/List;)Lscala/collection/immutable/List;") + } + + def testA5() = { + val List(b1) = innerClassNodes("A5") + assertLocal(b1, "A5$B$2$", "B$2$") + val List(b2) = innerClassNodes("A5$B$2$") + assertLocal(b2, "A5$B$2$", "B$2$") + assertEnclosingMethod( + enclosingMethod("A5$B$2$"), + "A5", "f", "()Ljava/lang/Object;") + } + + def testA6() = { + val List(tt1) = innerClassNodes("A6") + assertMember(tt1, "A6", "TT", flags = publicAbstractInterface) + val List() = innerClassNodes("A6$class") + val List(tt2) = innerClassNodes("A6$TT") + assertMember(tt2, "A6", "TT", flags = publicAbstractInterface) + } + + def testA7() = { + val List() = innerClassNodes("A7") + } + + def testA8() = { + val List(tt) = innerClassNodes("A8") + assertMember(tt, "A6", "TT", flags = publicAbstractInterface) + } + + def testA10() = { + val List() = innerClassNodes("A10") + } + + def testA11() = { + val List(ann) = innerClassNodes("A11") + // in the java class file, the INNERCLASS attribute has more flags (public | static | abstract | interface | annotation) + // the scala compiler has its own interpretation of java annotations ant their flags.. it only emits publicStatic. + assertMember(ann, "JavaAnnot_1", "Ann", flags = publicStatic) + } + + def testA13() = { + val List(b, c) = innerClassNodes("A13") + assertMember(b, "A12", "B$", flags = publicStatic) + assertMember(c, "A12$B$", "C", name = Some("A12$B$C"), flags = publicStatic) + } + + def testA14() = { + val List(anon, k) = innerClassNodes("A14") + + assertLocal(k, "A14$K$1", "K$1") + assertEnclosingMethod( + enclosingMethod("A14$K$1"), + "A14", "f", "()Ljava/lang/Object;") + + assertAnonymous(anon, "A14$$anon$1") + assertEnclosingMethod( + enclosingMethod("A14$$anon$1"), + "A14", "g", "()V") + } + + def testA15() = { + val List(b) = innerClassNodes("A15") + assertLocal(b, "A15$B$3", "B$3", flags = publicStatic) + + val List(_, c) = innerClassNodes("A15$B$3") + // TODO this is a bug in the backend, C should be a member. Instead, its outerClass is null + // assertMember(c, "A15$B$3", "C") + assertLocal(c, "A15$B$3$C", "C") + } + + def testA16() = { + val List(anon1, anon2, u, v) = innerClassNodes("A16") + // TODO there's a bug in the backend: anon$2 has outerClass A16, but anonymous classes should have outerClass null + // assertAnonymous(anon1, "A16$$anon$2") + assertMember(anon1, "A16", null, name = Some("A16$$anon$2"), flags = Flags.ACC_PUBLIC | Flags.ACC_FINAL) + assertAnonymous(anon2, "A16$$anon$3") + // TODO this is a bug in the backend, U should not be a member, its outerClass should be null + // assertLocal(u, "A16$U$1", "U$1") + assertMember(u, "A16", "U$1") + assertLocal(v, "A16$V$1", "V$1") + + assertEnclosingMethod( + enclosingMethod("A16$$anon$2"), + "A16", "<init>", "()V") + assertEnclosingMethod( + enclosingMethod("A16$$anon$3"), + "A16", "<init>", "()V") + // TODO this is a bug, there should be an enclosingMethod attribute in U + // assertEnclosingMethod( + // enclosingMethod("A16$U$1"), + // "A16", "<init>", "()V") + assertEnclosingMethod( + enclosingMethod("A16$V$1"), + "A16", "<init>", "()V") + } + + def testA17() = { + val List(b, c) = innerClassNodes("A17$B$") + assertMember(b, "A17", "B$") + // TODO this is a bug, should not be static. + assertMember(c, "A17$B$", "C", name = Some("A17$B$C"), flags = publicStatic) // (should be) not static, has an outer pointer. + } + + def show(): Unit = { + testA1() + testA2() + testA3() + testA4() + testA5() + testA6() + testA7() + testA8() + testA10() + testA11() + testA13() + testA14() + testA15() + testA16() + testA17() + } +} diff --git a/test/files/jvm/interpreter.check b/test/files/jvm/interpreter.check index d124794e72..d03edb638c 100644 --- a/test/files/jvm/interpreter.check +++ b/test/files/jvm/interpreter.check @@ -361,7 +361,7 @@ It would fail on the following inputs: Exp(), Term() ^ f: (e: Exp)Int -scala> +scala> :quit plusOne: (x: Int)Int res0: Int = 6 res0: String = after reset diff --git a/test/files/jvm/throws-annot-from-java.check b/test/files/jvm/throws-annot-from-java.check index be3ba412f8..c541b26fcc 100644 --- a/test/files/jvm/throws-annot-from-java.check +++ b/test/files/jvm/throws-annot-from-java.check @@ -44,4 +44,4 @@ bar tp.typeParams.isEmpty: true throws[test.PolymorphicException[_]](classOf[test.PolymorphicException]) -scala> +scala> :quit diff --git a/test/files/jvm/xml05.check b/test/files/jvm/xml05.check index 92ea995350..cad907525d 100644 --- a/test/files/jvm/xml05.check +++ b/test/files/jvm/xml05.check @@ -4,4 +4,4 @@ Type :help for more information. scala> <city name="San José"/> res0: scala.xml.Elem = <city name="San José"/> -scala> +scala> :quit diff --git a/test/files/neg/macro-basic-mamdmi.check b/test/files/neg/macro-basic-mamdmi.check index 61df5131cc..54743d4936 100644 --- a/test/files/neg/macro-basic-mamdmi.check +++ b/test/files/neg/macro-basic-mamdmi.check @@ -1,5 +1,13 @@ +Impls_Macros_Test_1.scala:33: error: macro implementation not found: foo +(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them) + println(foo(2) + Macros.bar(2) * new Macros().quux(4)) + ^ +Impls_Macros_Test_1.scala:33: error: macro implementation not found: bar +(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them) + println(foo(2) + Macros.bar(2) * new Macros().quux(4)) + ^ Impls_Macros_Test_1.scala:33: error: macro implementation not found: quux (the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them) println(foo(2) + Macros.bar(2) * new Macros().quux(4)) ^ -one error found +three errors found diff --git a/test/files/neg/t3909.check b/test/files/neg/t3909.check index 7da0195607..052b49f855 100644 --- a/test/files/neg/t3909.check +++ b/test/files/neg/t3909.check @@ -1,4 +1,5 @@ t3909.scala:1: error: in object DO, multiple overloaded alternatives of m1 define default arguments +Error occurred in an application involving default arguments. object DO { ^ one error found diff --git a/test/files/neg/t7848-interp-warn.flags b/test/files/neg/t7848-interp-warn.flags index 7949c2afa2..b0d7bc25cb 100644 --- a/test/files/neg/t7848-interp-warn.flags +++ b/test/files/neg/t7848-interp-warn.flags @@ -1 +1 @@ --Xlint -Xfatal-warnings +-Xlint:missing-interpolator -Xfatal-warnings diff --git a/test/files/neg/t8266-invalid-interp.check b/test/files/neg/t8266-invalid-interp.check index 70dd4081b0..bb2d44a80c 100644 --- a/test/files/neg/t8266-invalid-interp.check +++ b/test/files/neg/t8266-invalid-interp.check @@ -1,10 +1,10 @@ t8266-invalid-interp.scala:4: error: Trailing '\' escapes nothing. f"a\", ^ -t8266-invalid-interp.scala:5: error: invalid escape character at index 1 in "a\xc" +t8266-invalid-interp.scala:5: error: invalid escape '\x' not one of [\b, \t, \n, \f, \r, \\, \", \'] at index 1 in "a\xc". Use \\ for literal \. f"a\xc", ^ -t8266-invalid-interp.scala:7: error: invalid escape character at index 1 in "a\vc" +t8266-invalid-interp.scala:7: error: invalid escape '\v' not one of [\b, \t, \n, \f, \r, \\, \", \'] at index 1 in "a\vc". Use \\ for literal \. f"a\vc" ^ three errors found diff --git a/test/files/pos/t8013.flags b/test/files/pos/t8013.flags index 954eaba352..3955bb6710 100644 --- a/test/files/pos/t8013.flags +++ b/test/files/pos/t8013.flags @@ -1 +1 @@ --Xfatal-warnings -Xlint +-Xfatal-warnings -Xlint:-infer-any,_ diff --git a/test/files/pos/t8793.scala b/test/files/pos/t8793.scala new file mode 100644 index 0000000000..1276155675 --- /dev/null +++ b/test/files/pos/t8793.scala @@ -0,0 +1,15 @@ +package regr + +trait F[A] + +class G(val a: F[_], val b: F[_]) + +object G { + def unapply(g: G) = Option((g.a, g.b)) +} + +object H { + def unapply(g: G) = g match { + case G(a, _) => Option(a) + } +} diff --git a/test/files/run/class-symbol-contravariant.check b/test/files/run/class-symbol-contravariant.check index 987f215bca..cbb90b52c2 100644 --- a/test/files/run/class-symbol-contravariant.check +++ b/test/files/run/class-symbol-contravariant.check @@ -33,4 +33,4 @@ res2: Boolean = true scala> sym.isContravariant // was true res3: Boolean = false -scala> +scala> :quit diff --git a/test/files/run/constant-type.check b/test/files/run/constant-type.check index 77bdf618e6..a7ba5a46c2 100644 --- a/test/files/run/constant-type.check +++ b/test/files/run/constant-type.check @@ -23,4 +23,4 @@ Class[String](classOf[java.lang.String]) scala> { ConstantType(Constant(s)); exitingPhase(currentRun.erasurePhase)(println(ConstantType(Constant(s)))); } Class(classOf[java.lang.String]) -scala> +scala> :quit diff --git a/test/files/run/constrained-types.check b/test/files/run/constrained-types.check index a3cd59b9fb..89a08d5ccb 100644 --- a/test/files/run/constrained-types.check +++ b/test/files/run/constrained-types.check @@ -148,4 +148,4 @@ scala> val x = 3 : Int @Annot(e+f+g+h) // should have a graceful error message val x = 3 : Int @Annot(e+f+g+h) // should have a graceful error message ^ -scala> +scala> :quit diff --git a/test/files/run/kind-repl-command.check b/test/files/run/kind-repl-command.check index 1c292572e6..586b2710e1 100644 --- a/test/files/run/kind-repl-command.check +++ b/test/files/run/kind-repl-command.check @@ -25,4 +25,4 @@ scala> :k Nonexisting Nonexisting ^ -scala> +scala> :quit diff --git a/test/files/run/lub-visibility.check b/test/files/run/lub-visibility.check index f76579412e..70734966f0 100644 --- a/test/files/run/lub-visibility.check +++ b/test/files/run/lub-visibility.check @@ -8,4 +8,4 @@ scala> // but reverted that for SI-5534. scala> val x = List(List(), Vector()) x: List[scala.collection.immutable.Seq[Nothing] with scala.collection.AbstractSeq[Nothing] with java.io.Serializable] = List(List(), Vector()) -scala> +scala> :quit diff --git a/test/files/run/macro-bundle-repl.check b/test/files/run/macro-bundle-repl.check index 4a0b421606..75c5c2adda 100644 --- a/test/files/run/macro-bundle-repl.check +++ b/test/files/run/macro-bundle-repl.check @@ -21,4 +21,4 @@ defined term macro foo: Unit scala> foo -scala> +scala> :quit diff --git a/test/files/run/macro-repl-basic.check b/test/files/run/macro-repl-basic.check index 86b4d472ed..fab03d1558 100644 --- a/test/files/run/macro-repl-basic.check +++ b/test/files/run/macro-repl-basic.check @@ -49,4 +49,4 @@ import Macros.Shmacros._ scala> println(foo(2) + Macros.bar(2) * new Macros().quux(4)) 31 -scala> +scala> :quit diff --git a/test/files/run/macro-repl-dontexpand.check b/test/files/run/macro-repl-dontexpand.check index 20d3b2d702..6ecc9245fa 100644 --- a/test/files/run/macro-repl-dontexpand.check +++ b/test/files/run/macro-repl-dontexpand.check @@ -13,4 +13,4 @@ bar2: (c: scala.reflect.macros.whitebox.Context)Nothing scala> def foo2 = macro bar2 defined term macro foo2: Nothing -scala> +scala> :quit diff --git a/test/files/run/macro-system-properties.check b/test/files/run/macro-system-properties.check index ffbd5a8aa8..e2e2bd32b9 100644 --- a/test/files/run/macro-system-properties.check +++ b/test/files/run/macro-system-properties.check @@ -19,4 +19,4 @@ defined object Test scala> object Test { class C(implicit a: Any) { GrabContext.grab } } defined object Test -scala> +scala> :quit diff --git a/test/files/run/reflection-equality.check b/test/files/run/reflection-equality.check index 682326bc18..d60d861a90 100644 --- a/test/files/run/reflection-equality.check +++ b/test/files/run/reflection-equality.check @@ -48,4 +48,4 @@ res2: Boolean = true scala> t2 <:< t1 res3: Boolean = true -scala> +scala> :quit diff --git a/test/files/run/reflection-magicsymbols-repl.check b/test/files/run/reflection-magicsymbols-repl.check index 72aef1d3be..ca8857ada4 100644 --- a/test/files/run/reflection-magicsymbols-repl.check +++ b/test/files/run/reflection-magicsymbols-repl.check @@ -34,4 +34,4 @@ scala.Null scala.Nothing scala.Singleton -scala> +scala> :quit diff --git a/test/files/run/reflection-repl-classes.check b/test/files/run/reflection-repl-classes.check index 03a6aef2b5..5ebf993a87 100644 --- a/test/files/run/reflection-repl-classes.check +++ b/test/files/run/reflection-repl-classes.check @@ -30,4 +30,4 @@ scala> scala> mm(new A) res0: Any = 1 -scala> +scala> :quit diff --git a/test/files/run/reflection-repl-elementary.check b/test/files/run/reflection-repl-elementary.check index 4a223e8a24..e948c9fd61 100644 --- a/test/files/run/reflection-repl-elementary.check +++ b/test/files/run/reflection-repl-elementary.check @@ -4,4 +4,4 @@ Type :help for more information. scala> scala.reflect.runtime.universe.typeOf[List[Nothing]] res0: reflect.runtime.universe.Type = scala.List[Nothing] -scala> +scala> :quit diff --git a/test/files/run/reify-repl-fail-gracefully.check b/test/files/run/reify-repl-fail-gracefully.check index 29ccee3cc6..c9e69744d6 100644 --- a/test/files/run/reify-repl-fail-gracefully.check +++ b/test/files/run/reify-repl-fail-gracefully.check @@ -14,4 +14,4 @@ scala> reify reify ^ -scala> +scala> :quit diff --git a/test/files/run/reify_newimpl_22.check b/test/files/run/reify_newimpl_22.check index 1432d10127..952f384a1c 100644 --- a/test/files/run/reify_newimpl_22.check +++ b/test/files/run/reify_newimpl_22.check @@ -22,4 +22,4 @@ scala> { ^ 2 -scala> +scala> :quit diff --git a/test/files/run/reify_newimpl_23.check b/test/files/run/reify_newimpl_23.check index 217f0a98c7..b7e9bfdfbc 100644 --- a/test/files/run/reify_newimpl_23.check +++ b/test/files/run/reify_newimpl_23.check @@ -21,4 +21,4 @@ scala> def foo[T]{ ^ foo: [T]=> Unit -scala> +scala> :quit diff --git a/test/files/run/reify_newimpl_25.check b/test/files/run/reify_newimpl_25.check index 93ad69defa..4f36ba10ee 100644 --- a/test/files/run/reify_newimpl_25.check +++ b/test/files/run/reify_newimpl_25.check @@ -12,4 +12,4 @@ scala> { ^ TypeTag[x.type] -scala> +scala> :quit diff --git a/test/files/run/reify_newimpl_26.check b/test/files/run/reify_newimpl_26.check index 8e0ad87bf2..681b862795 100644 --- a/test/files/run/reify_newimpl_26.check +++ b/test/files/run/reify_newimpl_26.check @@ -14,4 +14,4 @@ foo: [T]=> Unit scala> foo[Int] WeakTypeTag[scala.List[T]] -scala> +scala> :quit diff --git a/test/files/run/reify_newimpl_35.check b/test/files/run/reify_newimpl_35.check index f884d2c0d0..bd9b3a2fb1 100644 --- a/test/files/run/reify_newimpl_35.check +++ b/test/files/run/reify_newimpl_35.check @@ -10,4 +10,4 @@ foo: [T](implicit evidence$1: reflect.runtime.universe.TypeTag[T])reflect.runtim scala> println(foo) Expr[List[Nothing]](Nil) -scala> +scala> :quit diff --git a/test/files/run/repl-assign.check b/test/files/run/repl-assign.check index bdc7793c37..faa8a93244 100644 --- a/test/files/run/repl-assign.check +++ b/test/files/run/repl-assign.check @@ -13,4 +13,4 @@ x: Int = 12 scala> y = 13 y: Int = 13 -scala> +scala> :quit diff --git a/test/files/run/repl-bare-expr.check b/test/files/run/repl-bare-expr.check index 97ae208ff4..07cf23412f 100644 --- a/test/files/run/repl-bare-expr.check +++ b/test/files/run/repl-bare-expr.check @@ -47,4 +47,4 @@ Bovine.x: List[Any] = List(Ruminant(5), Cow, Moooooo) scala> Bovine.x res4: List[Any] = List(Ruminant(5), Cow, Moooooo) -scala> +scala> :quit diff --git a/test/files/run/repl-colon-type.check b/test/files/run/repl-colon-type.check index 1f6d3e2b39..9898027c1d 100644 --- a/test/files/run/repl-colon-type.check +++ b/test/files/run/repl-colon-type.check @@ -218,4 +218,4 @@ Unit scala> :type println("side effect!") Unit -scala> +scala> :quit diff --git a/test/files/run/repl-empty-package.check b/test/files/run/repl-empty-package.check index ecf79c2c6d..d3b75f685e 100644 --- a/test/files/run/repl-empty-package.check +++ b/test/files/run/repl-empty-package.check @@ -4,4 +4,4 @@ Type :help for more information. scala> println(Bippy.bippy) bippy! -scala> +scala> :quit diff --git a/test/files/run/repl-javap-app.check b/test/files/run/repl-javap-app.check index 1136b415d7..eb3718f44b 100644 --- a/test/files/run/repl-javap-app.check +++ b/test/files/run/repl-javap-app.check @@ -15,7 +15,7 @@ public final void delayedEndpoint$MyApp$1(); Start Length Slot Name Signature 0 9 0 this LMyApp$; -scala> +scala> :quit #partest java7 Welcome to Scala Type in expressions to have them evaluated. @@ -37,7 +37,7 @@ scala> :javap -app MyApp$ line 5: 0 } -scala> +scala> :quit #partest java8 Welcome to Scala Type in expressions to have them evaluated. @@ -60,4 +60,4 @@ scala> :javap -app MyApp$ line 5: 0 } -scala> +scala> :quit diff --git a/test/files/run/repl-out-dir.check b/test/files/run/repl-out-dir.check index 3e51c63155..c354492898 100644 --- a/test/files/run/repl-out-dir.check +++ b/test/files/run/repl-out-dir.check @@ -46,4 +46,4 @@ repl-out-dir-run.obj Test$.class Test.class -scala> +scala> :quit diff --git a/test/files/run/repl-parens.check b/test/files/run/repl-parens.check index 15f4b4524a..74d15ff93c 100644 --- a/test/files/run/repl-parens.check +++ b/test/files/run/repl-parens.check @@ -81,4 +81,4 @@ scala> scala> List(1) ++ List('a') res16: List[AnyVal] = List(1, a) -scala> +scala> :quit diff --git a/test/files/run/repl-paste-2.check b/test/files/run/repl-paste-2.check index ab3809a2e0..6ea8e2f419 100644 --- a/test/files/run/repl-paste-2.check +++ b/test/files/run/repl-paste-2.check @@ -58,4 +58,4 @@ scala> x.length + res5 res3: Int = 129 -scala> +scala> :quit diff --git a/test/files/run/repl-paste-3.check b/test/files/run/repl-paste-3.check index 8fae61792e..23e402852f 100644 --- a/test/files/run/repl-paste-3.check +++ b/test/files/run/repl-paste-3.check @@ -7,4 +7,4 @@ scala> println(3) scala> List(1,2) res1: List[Int] = List(1, 2) -scala> +scala> :quit diff --git a/test/files/run/repl-paste-4.scala b/test/files/run/repl-paste-4.scala index 0060dc1ff6..cb0a6aa768 100644 --- a/test/files/run/repl-paste-4.scala +++ b/test/files/run/repl-paste-4.scala @@ -14,7 +14,7 @@ s"""|Type in expressions to have them evaluated. |scala> Foo(new Foo) |res0: Int = 7 | - |scala> """ + |scala> :quit""" def pastie = testPath changeExtension "pastie" } diff --git a/test/files/run/repl-paste-raw.scala b/test/files/run/repl-paste-raw.scala index 2953796f99..3b41254e96 100644 --- a/test/files/run/repl-paste-raw.scala +++ b/test/files/run/repl-paste-raw.scala @@ -15,6 +15,6 @@ s"""|Type in expressions to have them evaluated. |scala> favoriteThing.hasString |res0: Boolean = true | - |scala> """ + |scala> :quit""" def pastie = testPath changeExtension "pastie" } diff --git a/test/files/run/repl-paste.check b/test/files/run/repl-paste.check index 97f177ddc4..171447214f 100644 --- a/test/files/run/repl-paste.check +++ b/test/files/run/repl-paste.check @@ -23,4 +23,4 @@ defined class Dingus defined object Dingus x: Int = 110 -scala> +scala> :quit diff --git a/test/files/run/repl-power.check b/test/files/run/repl-power.check index 8a8ca46012..e2318c93f2 100644 --- a/test/files/run/repl-power.check +++ b/test/files/run/repl-power.check @@ -27,4 +27,4 @@ m: $r.treedsl.global.Literal = 10 scala> typed(m).tpe // typed is in scope res2: $r.treedsl.global.Type = Int(10) -scala> +scala> :quit diff --git a/test/files/run/repl-reset.check b/test/files/run/repl-reset.check index ed95c7b8ff..cd7893bbc3 100644 --- a/test/files/run/repl-reset.check +++ b/test/files/run/repl-reset.check @@ -54,4 +54,4 @@ defined class BippyBungus scala> { new BippyBungus ; x1 } res2: Int = 4 -scala> +scala> :quit diff --git a/test/files/run/repl-save.scala b/test/files/run/repl-save.scala index 4539790b1a..c98e6aebc3 100644 --- a/test/files/run/repl-save.scala +++ b/test/files/run/repl-save.scala @@ -16,7 +16,7 @@ s"""|Type in expressions to have them evaluated. | |scala> :save $saveto | - |scala> """ + |scala> :quit""" def saveto = testOutput / "session.repl" override def show() = { super.show() diff --git a/test/files/run/repl-term-macros.check b/test/files/run/repl-term-macros.check index 3580bfe1f1..2cd0b93cd0 100644 --- a/test/files/run/repl-term-macros.check +++ b/test/files/run/repl-term-macros.check @@ -37,4 +37,4 @@ defined term macro foo3: (x: Int)(y: Int)Unit scala> foo3(2)(3) -scala> +scala> :quit diff --git a/test/files/run/repl-transcript.check b/test/files/run/repl-transcript.check index 49891af900..b0f106387b 100644 --- a/test/files/run/repl-transcript.check +++ b/test/files/run/repl-transcript.check @@ -35,4 +35,4 @@ scala> res6.sum + res5 res0: Int = 5273 -scala> +scala> :quit diff --git a/test/files/run/repl-trim-stack-trace.scala b/test/files/run/repl-trim-stack-trace.scala index 483659146a..a53ce3b3e4 100644 --- a/test/files/run/repl-trim-stack-trace.scala +++ b/test/files/run/repl-trim-stack-trace.scala @@ -32,7 +32,7 @@ java.lang.Exception at .f(<console>:7) ... 69 elided -scala> """ +scala> :quit""" // normalize the "elided" lines because the frame count depends on test context lazy val elided = """(\s+\.{3} )\d+( elided)""".r diff --git a/test/files/run/repl-type-verbose.check b/test/files/run/repl-type-verbose.check index e37754a060..6f6b47b86d 100644 --- a/test/files/run/repl-type-verbose.check +++ b/test/files/run/repl-type-verbose.check @@ -187,4 +187,4 @@ PolyType( ) ) -scala> +scala> :quit diff --git a/test/files/run/t3376.check b/test/files/run/t3376.check index cc6949d326..b8fd2843f6 100644 --- a/test/files/run/t3376.check +++ b/test/files/run/t3376.check @@ -13,4 +13,4 @@ m2: M[Float] = mmm scala> val m3 = new M[String]() m3: M[String] = mmm -scala> +scala> :quit diff --git a/test/files/run/t4025.check b/test/files/run/t4025.check index 2d4f644c5a..e8c6851236 100644 --- a/test/files/run/t4025.check +++ b/test/files/run/t4025.check @@ -14,4 +14,4 @@ scala> scala> def f(c: Any) = c match { case Red(_) => () } f: (c: Any)Unit -scala> +scala> :quit diff --git a/test/files/run/t4172.check b/test/files/run/t4172.check index a748430e2e..315c1c9dbd 100644 --- a/test/files/run/t4172.check +++ b/test/files/run/t4172.check @@ -5,4 +5,4 @@ scala> val c = { class C { override def toString = "C" }; ((new C, new C { def f warning: there was one feature warning; re-run with -feature for details c: (C, C{def f: Int}) forSome { type C <: AnyRef } = (C,C) -scala> +scala> :quit diff --git a/test/files/run/t4216.check b/test/files/run/t4216.check index 091e55a0c7..e4610e87d3 100644 --- a/test/files/run/t4216.check +++ b/test/files/run/t4216.check @@ -34,4 +34,4 @@ res4: java.util.List[V] = [V@0] scala> o(new V(0)) res5: java.util.List[Any] = [V@0] -scala> +scala> :quit diff --git a/test/files/run/t4285.check b/test/files/run/t4285.check index 314c8e5a35..b952cb8e1b 100644 --- a/test/files/run/t4285.check +++ b/test/files/run/t4285.check @@ -10,4 +10,4 @@ y: scala.collection.mutable.WrappedArray[Int] = WrappedArray(2, 4, 6, 8, 10, 12, scala> println(y.sum) 56 -scala> +scala> :quit diff --git a/test/files/run/t4542.check b/test/files/run/t4542.check index a53f31a3c7..f7716dc2f0 100644 --- a/test/files/run/t4542.check +++ b/test/files/run/t4542.check @@ -12,4 +12,4 @@ scala> val f = new Foo ^ f: Foo = Bippy -scala> +scala> :quit diff --git a/test/files/run/t4594-repl-settings.scala b/test/files/run/t4594-repl-settings.scala index 8b8b2e3746..4202991607 100644 --- a/test/files/run/t4594-repl-settings.scala +++ b/test/files/run/t4594-repl-settings.scala @@ -22,5 +22,5 @@ object Test extends SessionTest { | ^ |b: String | - |scala> """ + |scala> :quit""" } diff --git a/test/files/run/t4671.check b/test/files/run/t4671.check index 0c36083759..1640dac8e4 100644 --- a/test/files/run/t4671.check +++ b/test/files/run/t4671.check @@ -43,4 +43,4 @@ println(s.mkString("")) } -scala> +scala> :quit diff --git a/test/files/run/t4710.check b/test/files/run/t4710.check index 6ee7198b4b..0dd49dfbd3 100644 --- a/test/files/run/t4710.check +++ b/test/files/run/t4710.check @@ -5,4 +5,4 @@ scala> def method : String = { implicit def f(s: Symbol) = "" ; 'symbol } warning: there was one feature warning; re-run with -feature for details method: String -scala> +scala> :quit diff --git a/test/files/run/t5072.check b/test/files/run/t5072.check index ddd49c71cb..ab34e49869 100644 --- a/test/files/run/t5072.check +++ b/test/files/run/t5072.check @@ -7,4 +7,4 @@ defined class C scala> Thread.currentThread.getContextClassLoader.loadClass(classOf[C].getName) res0: Class[_] = class C -scala> +scala> :quit diff --git a/test/files/run/t5256d.check b/test/files/run/t5256d.check index d42d234386..c2b49989ab 100644 --- a/test/files/run/t5256d.check +++ b/test/files/run/t5256d.check @@ -25,4 +25,4 @@ scala.AnyRef { def foo: scala.Nothing } -scala> +scala> :quit diff --git a/test/files/run/t5535.check b/test/files/run/t5535.check index a0c87a47f4..84097ccea9 100644 --- a/test/files/run/t5535.check +++ b/test/files/run/t5535.check @@ -13,4 +13,4 @@ f: Int => Int = <function1> scala> println(f(10)) 11 -scala> +scala> :quit diff --git a/test/files/run/t5537.check b/test/files/run/t5537.check index b9d521f301..98265ccc92 100644 --- a/test/files/run/t5537.check +++ b/test/files/run/t5537.check @@ -13,4 +13,4 @@ res2: List[scala.collection.immutable.List.type] = List() scala> List[Set.type]() res3: List[Set.type] = List() -scala> +scala> :quit diff --git a/test/files/run/t5583.check b/test/files/run/t5583.check index af96405bdd..32d285cbb3 100644 --- a/test/files/run/t5583.check +++ b/test/files/run/t5583.check @@ -13,4 +13,4 @@ scala> for (i <- 1 to 10) {s += i} scala> println(s) 165 -scala> +scala> :quit diff --git a/test/files/run/t5655.check b/test/files/run/t5655.check index 06c6b32599..4bbc54b641 100644 --- a/test/files/run/t5655.check +++ b/test/files/run/t5655.check @@ -23,4 +23,4 @@ and import x x ^ -scala> +scala> :quit diff --git a/test/files/run/t5789.check b/test/files/run/t5789.check index bcb2382559..193abfaff0 100644 --- a/test/files/run/t5789.check +++ b/test/files/run/t5789.check @@ -7,4 +7,4 @@ n: Int = 2 scala> () => n res0: () => Int = <function0> -scala> +scala> :quit diff --git a/test/files/run/t6086-repl.check b/test/files/run/t6086-repl.check index 115eff5f85..b904f118e8 100644 --- a/test/files/run/t6086-repl.check +++ b/test/files/run/t6086-repl.check @@ -7,4 +7,4 @@ defined class X scala> scala.reflect.runtime.universe.typeOf[X] res0: reflect.runtime.universe.Type = X -scala> +scala> :quit diff --git a/test/files/run/t6146b.check b/test/files/run/t6146b.check index a3b09efcd9..6998873fb7 100644 --- a/test/files/run/t6146b.check +++ b/test/files/run/t6146b.check @@ -60,4 +60,4 @@ res2: u.Type = O.S3 scala> memType(S4, fTpe) res3: u.Type = S4 -scala> +scala> :quit diff --git a/test/files/run/t6187.check b/test/files/run/t6187.check index 0180125809..9a9e266ec6 100644 --- a/test/files/run/t6187.check +++ b/test/files/run/t6187.check @@ -29,4 +29,4 @@ res1: List[Int] = List(1) scala> List("") collect { case x => x } res2: List[String] = List("") -scala> +scala> :quit diff --git a/test/files/run/t6273.check b/test/files/run/t6273.check index bef0b227d2..3b682800df 100644 --- a/test/files/run/t6273.check +++ b/test/files/run/t6273.check @@ -12,4 +12,4 @@ x: String = y = 55 " -scala> +scala> :quit diff --git a/test/files/run/t6320.check b/test/files/run/t6320.check index 013acc1c54..af7c865690 100644 --- a/test/files/run/t6320.check +++ b/test/files/run/t6320.check @@ -10,4 +10,4 @@ defined class Dyn scala> new Dyn(Map("foo" -> 10)).foo[Int] res0: Int = 10 -scala> +scala> :quit diff --git a/test/files/run/t6329_repl.check b/test/files/run/t6329_repl.check index ad0bb46e5b..ebb1aace7c 100644 --- a/test/files/run/t6329_repl.check +++ b/test/files/run/t6329_repl.check @@ -32,4 +32,4 @@ res6: scala.reflect.ClassTag[scala.collection.immutable.Set[_]] = scala.collecti scala> classTag[scala.collection.immutable.Set[_]] res7: scala.reflect.ClassTag[scala.collection.immutable.Set[_]] = scala.collection.immutable.Set -scala> +scala> :quit diff --git a/test/files/run/t6329_repl_bug.check b/test/files/run/t6329_repl_bug.check index 38a8de5606..84297a629f 100644 --- a/test/files/run/t6329_repl_bug.check +++ b/test/files/run/t6329_repl_bug.check @@ -14,4 +14,4 @@ res0: scala.reflect.ClassTag[List[_]] = scala.collection.immutable.List[<?>] scala> scala.reflect.classTag[List[_]] res1: scala.reflect.ClassTag[List[_]] = scala.collection.immutable.List -scala> +scala> :quit diff --git a/test/files/run/t6381.check b/test/files/run/t6381.check index 4ed11d59ff..49c6a784ad 100644 --- a/test/files/run/t6381.check +++ b/test/files/run/t6381.check @@ -16,4 +16,4 @@ defined term macro pos: String scala> pos res0: String = class scala.reflect.internal.util.RangePosition -scala> +scala> :quit diff --git a/test/files/run/t6434.check b/test/files/run/t6434.check index f898b6b781..0a75ae2bd5 100644 --- a/test/files/run/t6434.check +++ b/test/files/run/t6434.check @@ -7,4 +7,4 @@ f: (x: => Int)Int scala> f _ res0: (=> Int) => Int = <function1> -scala> +scala> :quit diff --git a/test/files/run/t6439.check b/test/files/run/t6439.check index f8d5b3a8cd..c4b7591069 100644 --- a/test/files/run/t6439.check +++ b/test/files/run/t6439.check @@ -70,4 +70,4 @@ defined object lookup scala> lookup("F") // this now works as a result of changing .typeSymbol to .typeSymbolDirect in IMain#Request#definedSymbols res0: $r.intp.global.Symbol = type F -scala> +scala> :quit diff --git a/test/files/run/t6507.check b/test/files/run/t6507.check index 3536c42381..5da4aa3a24 100644 --- a/test/files/run/t6507.check +++ b/test/files/run/t6507.check @@ -21,4 +21,4 @@ scala> res0 ! res1: A = A -scala> +scala> :quit diff --git a/test/files/run/t6549.check b/test/files/run/t6549.check index d5dfc5ebe8..be3445927e 100644 --- a/test/files/run/t6549.check +++ b/test/files/run/t6549.check @@ -25,4 +25,4 @@ m(scala.Symbol("s")).xxx: Any = 's scala> val `"` = 0 ": Int = 0 -scala> +scala> :quit diff --git a/test/files/run/t6631.scala b/test/files/run/t6631.scala deleted file mode 100644 index e472b83d50..0000000000 --- a/test/files/run/t6631.scala +++ /dev/null @@ -1,18 +0,0 @@ -import reflect.ClassTag - -object Test extends App { - def intercept[T <: Throwable : ClassTag](act: => Any) = try { - act - } catch { - case x: Throwable => - val cls = implicitly[ClassTag[T]].runtimeClass - assert(cls.isInstance(x), (x.getClass, x, cls).toString) - } - assert(s"""\f\r\n\t""" == "\f\r\n\t") - - import StringContext.InvalidEscapeException - intercept[InvalidEscapeException](s"""\""") - intercept[InvalidEscapeException](s"""\x""") - intercept[InvalidEscapeException](s"\") - -} diff --git a/test/files/run/t6937.check b/test/files/run/t6937.check index 4729dc7006..5c5d4485b6 100644 --- a/test/files/run/t6937.check +++ b/test/files/run/t6937.check @@ -19,4 +19,4 @@ apiru: scala.reflect.api.Universe = <lazy> scala> apiru.typeTag[A].in(cm) res0: reflect.runtime.universe.TypeTag[A] = TypeTag[A] -scala> +scala> :quit diff --git a/test/files/run/t7185.check b/test/files/run/t7185.check index ebf85b731f..e4f80a8ff9 100644 --- a/test/files/run/t7185.check +++ b/test/files/run/t7185.check @@ -29,4 +29,4 @@ res0: Any = } } -scala> +scala> :quit diff --git a/test/files/run/t7319.check b/test/files/run/t7319.check index 2ac4142098..e35cfc90c0 100644 --- a/test/files/run/t7319.check +++ b/test/files/run/t7319.check @@ -40,4 +40,4 @@ scala> Range(1,2).toArray: Seq[_] scala> 0 res2: Int = 0 -scala> +scala> :quit diff --git a/test/files/run/t7482a.check b/test/files/run/t7482a.check index 943538f352..a21ef7b68f 100644 --- a/test/files/run/t7482a.check +++ b/test/files/run/t7482a.check @@ -7,4 +7,4 @@ v: java.util.ArrayList[String] = [] scala> val v: java.util.ArrayList[String] = new java.util.ArrayList[String](5) v: java.util.ArrayList[String] = [] -scala> +scala> :quit diff --git a/test/files/run/t7634.check b/test/files/run/t7634.check index aea3b94da5..9c6b8b47dd 100644 --- a/test/files/run/t7634.check +++ b/test/files/run/t7634.check @@ -5,4 +5,4 @@ Type :help for more information. scala> .lines res1: List[String] = List(shello, world.) -scala> +scala> :quit diff --git a/test/files/run/t7747-repl.check b/test/files/run/t7747-repl.check index ad924f482c..105b238d01 100644 --- a/test/files/run/t7747-repl.check +++ b/test/files/run/t7747-repl.check @@ -283,4 +283,4 @@ object $read extends $read { } res3: List[Product with Serializable] = List(BippyBups(), PuppyPups(), Bingo()) -scala> +scala> :quit diff --git a/test/files/run/t7801.check b/test/files/run/t7801.check index d72060c684..e0b656b784 100644 --- a/test/files/run/t7801.check +++ b/test/files/run/t7801.check @@ -8,4 +8,4 @@ import g.abort scala> class C(val a: Any) extends AnyVal defined class C -scala> +scala> :quit diff --git a/test/files/run/t7805-repl-i.check b/test/files/run/t7805-repl-i.check index eecfff079a..7f66c06a11 100644 --- a/test/files/run/t7805-repl-i.check +++ b/test/files/run/t7805-repl-i.check @@ -8,4 +8,4 @@ Type :help for more information. scala> Console println Try(8) Success(8) -scala> +scala> :quit diff --git a/test/files/run/tpeCache-tyconCache.check b/test/files/run/tpeCache-tyconCache.check index a892f5477a..ff604819e0 100644 --- a/test/files/run/tpeCache-tyconCache.check +++ b/test/files/run/tpeCache-tyconCache.check @@ -16,4 +16,4 @@ res0: Boolean = true scala> AnyRefClass.tpe eq AnyRefClass.typeConstructor res1: Boolean = true -scala> +scala> :quit diff --git a/test/junit/scala/StringContextTest.scala b/test/junit/scala/StringContextTest.scala index 5abfe90cd1..608b82bd96 100644 --- a/test/junit/scala/StringContextTest.scala +++ b/test/junit/scala/StringContextTest.scala @@ -48,4 +48,31 @@ class StringContextTest { val res = processEscapes(s) assertEquals("Scala", res) } + + @Test def t6631_baseline() = assertEquals("\f\r\n\t", s"""\f\r\n\t""") + + @Test def t6631_badEscape() = assertThrows[InvalidEscapeException] { + s"""\x""" + } + + // verifying that the standard interpolators can be supplanted + @Test def antiHijack_?() = { + object AllYourStringsAreBelongToMe { case class StringContext(args: Any*) { def s(args: Any) = "!!!!" } } + import AllYourStringsAreBelongToMe._ + //assertEquals("????", s"????") + assertEquals("!!!!", s"????") // OK to hijack core interpolator ids + } + + @Test def fIf() = { + val res = f"${if (true) 2.5 else 2.5}%.2f" + assertEquals("2.50", res) + } + @Test def fIfNot() = { + val res = f"${if (false) 2.5 else 3.5}%.2f" + assertEquals("3.50", res) + } + @Test def fHeteroArgs() = { + val res = f"${3.14}%.2f rounds to ${3}%d" + assertEquals("3.14 rounds to 3", res) + } } diff --git a/test/junit/scala/reflect/QTest.scala b/test/junit/scala/reflect/QTest.scala new file mode 100644 index 0000000000..24c35dc401 --- /dev/null +++ b/test/junit/scala/reflect/QTest.scala @@ -0,0 +1,23 @@ + +package scala.reflect + +import org.junit.Test +import org.junit.Assert._ +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.tools.testing.AssertUtil._ + +@RunWith(classOf[JUnit4]) +class QTest { + + import reflect.runtime._ + import universe._ + @Test def qConstantsNotHomogenized() = { + //Apply(Select(Literal(Constant(1.0)), TermName("$plus")), List(Literal(Constant(1.0)))) + val t = q"${1} + ${1.0}" + val Apply(Select(Literal(Constant(i)), TermName("$plus")), List(Literal(Constant(j)))) = t + assertEquals(1, i) + assertEquals(1.0, j) + } +} diff --git a/test/junit/scala/util/matching/RegexTest.scala b/test/junit/scala/util/matching/RegexTest.scala index d25842cc57..5b13397d6a 100644 --- a/test/junit/scala/util/matching/RegexTest.scala +++ b/test/junit/scala/util/matching/RegexTest.scala @@ -27,4 +27,21 @@ class RegexTest { assertEquals("1", x) assertEquals("1", y) } + + @Test def t8787nullMatch() = { + val r = """\d+""".r + val s: String = null + val x = s match { case r() => 1 ; case _ => 2 } + assertEquals(2, x) + } + + @Test def t8787nullMatcher() = { + val r = """(\d+):(\d+)""".r + val s = "1:2 3:4 5:6" + val z = ((r findAllMatchIn s).toList :+ null) flatMap { + case r(x, y) => Some((x.toInt, y.toInt)) + case _ => None + } + assertEquals(List((1,2),(3,4),(5,6)), z) + } } |