From 8f25a5125ea0974e42e6c365a3331163d0a1e154 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 26 Jun 2014 11:24:37 +0200 Subject: Eclipse project: repl depends on compiler/lib projects --- src/eclipse/repl/.classpath | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/eclipse/repl/.classpath b/src/eclipse/repl/.classpath index 601a231aeb..8fed2a3f61 100644 --- a/src/eclipse/repl/.classpath +++ b/src/eclipse/repl/.classpath @@ -1,11 +1,11 @@ - - - - - - - - + + + + + + + + -- cgit v1.2.3 From 4b333fbf7a38be11e568bd71e2dc5dca00e3bce6 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 23 Jun 2014 17:49:23 +0200 Subject: Minor cleanup in AbstractReporter. --- src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') 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. */ -- cgit v1.2.3 From 638b4c3f5b5721b68a6af97e237b4f056c2da7ed Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 26 Jun 2014 11:22:04 +0200 Subject: Regularize `comment` hook method This is currently pretty borken, but let's at least not clutter innocent interfaces with this functionality. Moved `comment` (as `signalParsedDocComment`) next to the other hook methods in `Global`. For now, it calls the old `reporter.comment` hook method. As soon as the IDE is refactored to receive comments properly, the deprecated `Reporter#comment` method can be removed. --- src/compiler/scala/tools/nsc/CompilationUnits.scala | 7 ++----- src/compiler/scala/tools/nsc/Global.scala | 8 ++++++++ src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala | 7 ++++--- 3 files changed, 14 insertions(+), 8 deletions(-) (limited to 'src') 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/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 572e579aca..72464cea8a 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -218,6 +218,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) { 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 -- cgit v1.2.3 From 64ebac245d58221814f9c9375927e3f2e7a2d4f0 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 26 Jun 2014 11:21:15 +0200 Subject: Move more parsing hooks out of reporting. Create a trait Parsing, which, like Reporting, factors our functionality from Global (aka. "the cake"), that is related to global aspects of configuring parsing. --- src/compiler/scala/tools/nsc/Global.scala | 5 +-- src/compiler/scala/tools/nsc/Parsing.scala | 36 ++++++++++++++++++++++ src/compiler/scala/tools/nsc/Reporting.scala | 15 --------- .../scala/tools/nsc/ast/parser/Parsers.scala | 2 +- .../scala/tools/nsc/ast/parser/Scanners.scala | 2 +- .../tools/nsc/ast/parser/SyntaxAnalyzer.scala | 2 +- .../scala/tools/nsc/javac/JavaScanners.scala | 2 +- .../scala/tools/nsc/reporters/Reporter.scala | 4 +-- src/repl/scala/tools/nsc/interpreter/IMain.scala | 2 +- 9 files changed, 46 insertions(+), 24 deletions(-) create mode 100644 src/compiler/scala/tools/nsc/Parsing.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 72464cea8a..0ee7f4e21c 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 -------------------------------------------------- @@ -1163,7 +1164,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/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..97cb543e34 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -234,7 +234,7 @@ self => override def incompleteInputError(msg: String) { 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, diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 572497ac90..d01b3abe4f 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -1261,7 +1261,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 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/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/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 -- cgit v1.2.3 From 2aa1f1e249596b6d629be35170bdbea7ea14cbe2 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 7 Jul 2014 16:08:57 +0200 Subject: Remove another redundant forwarder In this case, `infer.issue -> context.issue`. Forwarders are dead weight that cause bit rot. They tend to acquire functionality, clutter their defining interface and dilute the purpose of the method they forward to. --- src/compiler/scala/tools/nsc/typechecker/Infer.scala | 2 -- src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala | 2 +- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 8 ++++---- 3 files changed, 5 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index a3f1da60ce..04657c908b 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)) 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/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 70f44c4fc6..d35319bb8c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3149,7 +3149,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) { @@ -4395,7 +4395,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 +4449,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 +4686,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 _ => -- cgit v1.2.3 From 67651e220a6a4d1d1ee1004766d5b1e33fd46531 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 7 Jul 2014 14:53:22 +0200 Subject: Simplify (ambiguous) error issuing. The two functional differences are: - always add the diagnostics string - check erroneousness in `issueAmbiguousTypeErrorUnlessErroneous`, before even constructing the error message. Consider this nugget: ``` - def issueAmbiguousError(pre: Type, sym1: Symbol, sym2: Symbol, err: AbsTypeError) { - issueCommon(err) { case _ if ambiguousErrors => - if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous) ``` I'd like to state for the record that the if-erroneous in the case of the partial function looked super-dodgy: it meant that, when `ambiguousErrors`, `issueCommon` would not get to the `else` branches that buffer or throw, and if the erroneous condition was met, nothing would be issued/buffered/thrown. This refactoring checks this condition up front. --- .../tools/nsc/typechecker/ContextErrors.scala | 38 ++++++++++------------ .../scala/tools/nsc/typechecker/Contexts.scala | 22 +++---------- .../scala/tools/nsc/typechecker/Implicits.scala | 1 + test/files/neg/t3909.check | 1 + 4 files changed, 23 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 9715fdaf00..5c31685bcd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -90,10 +90,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) @@ -883,19 +879,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 +950,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 +963,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..aa2394897c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -543,37 +543,23 @@ trait Contexts { self: Analyzer => 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]) { + @inline private def issueCommon(err: AbsTypeError, reportError: Boolean) { // 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) + if (reportError) unitError(err.errPos, addDiagString(err.errMsg)) 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)) - } - } + private[typechecker] def issue(err: AbsTypeError) = issueCommon(err, reportErrors) /** 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) - } - } - - /** 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)) } - } + private[typechecker] def issueAmbiguousError(err: AbsTypeError) = issueCommon(err, ambiguousErrors) /** Issue/throw the given `err` according to the current mode for error reporting. */ def error(pos: Position, err: Throwable) = diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 73c3e6f016..6b5a70bffb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1381,6 +1381,7 @@ trait Implicits { 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)) } 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 -- cgit v1.2.3 From 5895e10adb3ec3e711cd48a9d0f0f9095c7f8ab9 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 8 Jul 2014 08:27:27 +0200 Subject: Concretize diagnostics: one boolean suffices for now. Once we get the next diagnostic, lets encapsulate them in an object, with a boolean flag for each one when it needs to trigger, and a nice message that should be presented to our delighted user. A list of Strings that is searched by contents is a bit fragile, and can't be very fast either. --- .../scala/tools/nsc/typechecker/Contexts.scala | 18 +++++++++--------- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 3 +-- 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index aa2394897c..7cb2ad7ac5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -265,8 +265,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() @@ -452,7 +453,7 @@ trait Contexts { self: Analyzer => // 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 @@ -532,16 +533,15 @@ 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 + val diagUsedDefaultsMsg = "Error occurred in an application involving default arguments." + if (diagUsedDefaults && !(msg endsWith diagUsedDefaultsMsg)) msg + "\n" + diagUsedDefaultsMsg + else msg } private def unitError(pos: Position, msg: String): Unit = - if (checking) onTreeCheckerError(pos, msg) else reporter.error(pos, msg) + if (checking) onTreeCheckerError(pos, msg) + else reporter.error(pos, msg) @inline private def issueCommon(err: AbsTypeError, reportError: Boolean) { // TODO: are errors allowed to have pos == NoPosition?? diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index d35319bb8c..e0af229d3a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3357,8 +3357,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() -- cgit v1.2.3 From 3fd4f47d7609e8818ea97e29e86af8b5ef09be9e Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 8 Jul 2014 12:24:03 +0200 Subject: Add errorCount to wean partest off Reporter$Severity Once a release with this method is out, move partest to use errorCount and cut ties with the Severity nested class, so we can move it to the right enclosing class. --- src/reflect/scala/reflect/internal/Reporting.scala | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') 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 -- cgit v1.2.3 From a7407468be46799619f1b7ebe1f0975f3a6c2dbb Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Wed, 9 Jul 2014 11:46:32 +0200 Subject: Clarify divergentError comment --- src/compiler/scala/tools/nsc/typechecker/Implicits.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 6b5a70bffb..98cf9c7830 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -857,7 +857,8 @@ 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) -- cgit v1.2.3 From ac6dcad89a0121d5c5c78e373fe691a56f1103d4 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 8 Jul 2014 16:43:39 +0200 Subject: Extract the `makeNonSilent` method. Setting the scene of removing the reporting mode bits from `contextMode`. --- src/compiler/scala/tools/nsc/typechecker/Contexts.scala | 6 ++++++ src/compiler/scala/tools/nsc/typechecker/Namers.scala | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 7cb2ad7ac5..695932c870 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -490,6 +490,12 @@ trait Contexts { self: Analyzer => c } + def makeNonSilent(newtree: Tree): Context = { + val c = make(newtree) + c.setReportErrors() + c + } + /** Make a silent child context does not allow implicits. Used to prevent chaining of implicit views. */ def makeImplicit(reportAmbiguousErrors: Boolean) = { val c = makeSilent(reportAmbiguousErrors) 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) -- cgit v1.2.3 From 543bb3e0b60cd1e1d4a3e3be19ec90256cc13151 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 8 Jul 2014 16:44:23 +0200 Subject: Encapsulate `TryTwice` as a class, move to `Context`. All functionality that's closely tied to the error buffer should be in `Context`'s reporting infrastructure. (called `ContextReporter`, soon to follow.) --- .../scala/tools/nsc/typechecker/Contexts.scala | 40 ++++++ .../scala/tools/nsc/typechecker/Infer.scala | 138 +++++++++------------ 2 files changed, 96 insertions(+), 82 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 695932c870..199209c5fa 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -365,6 +365,46 @@ trait Contexts { self: Analyzer => reportBuffer.clearAllWarnings() } + + /** Try inference twice, once without views and once with views, + * unless views are already disabled. + */ + abstract class TryTwice { + def tryOnce(isLastTry: Boolean): Unit + + def apply(): Unit = + if (implicitsEnabled) { + val savedContextMode = contextMode + var fallback = false + 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 = flushAndReturnBuffer() + try { + withImplicitsDisabled(tryOnce(false)) + if (hasErrors) { + fallback = true + contextMode = savedContextMode + flushBuffer() + tryOnce(true) + } + } catch { + case ex: CyclicReference => throw ex + case ex: TypeError => // recoverable cyclic references + contextMode = savedContextMode + if (!fallback) tryOnce(true) else () + } finally { + contextMode = savedContextMode + updateBuffer(errorsToRestore) + } + } + else tryOnce(true) + } + // // Temporary mode adjustment // diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 04657c908b..cb985ddaf6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -1264,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 } } @@ -1368,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 InferTwice 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) + private 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 } - } 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 InferTwice).apply() } /** Assign `tree` the type of all polymorphic alternatives -- cgit v1.2.3 From b8503f6cf937b739341a6a387800c4542e81ae77 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Wed, 9 Jul 2014 11:32:58 +0200 Subject: Restrict `contextMode` fiddling to `Context` Introduce `initRootContext` to set the relevant bits. --- .../scala/tools/nsc/transform/Erasure.scala | 2 +- .../tools/nsc/transform/TypingTransformers.scala | 6 ++--- .../scala/tools/nsc/typechecker/Contexts.scala | 26 +++++++++++++++++++--- .../scala/tools/reflect/ToolBoxFactory.scala | 2 +- 4 files changed, 28 insertions(+), 8 deletions(-) (limited to 'src') 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/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 199209c5fa..6951ff2f0b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -98,7 +98,7 @@ trait Contexts { self: Analyzer => } - def rootContext(unit: CompilationUnit, tree: Tree = EmptyTree, erasedTypes: Boolean = false): Context = { + def rootContext(unit: CompilationUnit, tree: Tree = EmptyTree): 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,11 +113,17 @@ 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() c } + def rootContextPostTyper(unit: CompilationUnit, tree: Tree = EmptyTree): Context = { + val c = rootContext(unit, tree) + c.initRootContextPostTyper() + c + } + + def resetContexts() { startContext.enclosingContextChain foreach { context => context.tree match { @@ -511,6 +517,20 @@ trait Contexts { self: Analyzer => c } + /** Use reporter (possibly buffered) for errors/warnings and enable implicit conversion **/ + def initRootContext(): Unit = { + setReportErrors() + this(EnrichmentEnabled | ImplicitsEnabled) = true + } + + /** Disable implicit conversion/enrichment, throw TypeError on error. + * TODO: can we phase out TypeError and uniformly rely on reporter? + */ + def initRootContextPostTyper(): Unit = { + setThrowErrors() + this(EnrichmentEnabled | ImplicitsEnabled) = false + } + 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? 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)) -- cgit v1.2.3 From dda3f241e7c47d13710be6a7e1ea9d5ef69709b2 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Wed, 9 Jul 2014 18:14:06 +0200 Subject: Rely less on intricacies of `contextMode`-based reporting. - when warning must not be suppressed, use `reporter.warning` - don't (implicitly) rely on `reporter.warning` being silent after typer --> don't do pure expression check after typer --- src/compiler/scala/tools/nsc/typechecker/Contexts.scala | 4 ++-- src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala | 6 +++--- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 6951ff2f0b..1010ceafe3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -640,8 +640,8 @@ trait Contexts { self: Analyzer => } /** 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) + def warning(pos: Position, msg: String) { + if (reportErrors) reporter.warning(pos, msg) else if (bufferErrors) reportBuffer += (pos -> msg) } diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 7440f69e93..fbea69db5a 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 } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index e0af229d3a..ce031e9cd7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2990,7 +2990,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" ) -- cgit v1.2.3 From ecda1010416305e5d5cbf9ba0c7f90ddee8a9737 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 10 Jul 2014 15:30:28 +0200 Subject: Make more explicit that TypeError is being thrown. `typer.TyperErrorGen.MacroCantExpandIncompatibleMacrosError` throws because the type checker it uses is at `NoContext`, which throws by default... This default is bad and is going to change, so make this code independent of that future sanity. TODO: don't use mutable state to determine position for the error --- .../tools/nsc/typechecker/ContextErrors.scala | 24 ++++++++++++---------- .../scala/tools/nsc/typechecker/Macros.scala | 3 ++- 2 files changed, 15 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 5c31685bcd..028ac46058 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -119,6 +119,19 @@ 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) + trait TyperContextErrors { self: Typer => @@ -729,17 +742,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 diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 9c22688581..66152a6ac2 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") -- cgit v1.2.3 From 338cfff69ea5cbd91f0cbb8f29d690f2069c0a00 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 17 Jul 2014 11:34:16 +0200 Subject: Reduce Context iface: inline trivial forwarders. The overarching goal is to route all contextual reporting through a single entry point: `context.reporter`. The following commits take baby steps towards this goal. --- .../scala/tools/nsc/typechecker/Implicits.scala | 20 ++++++++++---------- src/compiler/scala/tools/nsc/typechecker/Infer.scala | 2 +- .../scala/tools/nsc/typechecker/Typers.scala | 14 +++++++------- 3 files changed, 18 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 98cf9c7830..965547d835 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -71,8 +71,8 @@ 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 { + if (result.isFailure && saveAmbiguousDivergent && implicitSearchContext.reportBuffer.hasErrors) { + context.reportBuffer ++= (implicitSearchContext.reportBuffer.errors.collect { case dte: DivergentImplicitTypeError => dte case ate: AmbiguousImplicitTypeError => ate }) @@ -99,7 +99,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.reportBuffer.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 +635,7 @@ trait Implicits { } case _ => fallback } - context.firstError match { // using match rather than foreach to avoid non local return. + context.reportBuffer.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 +658,8 @@ trait Implicits { } } - if (context.hasErrors) - fail("hasMatchingSymbol reported error: " + context.firstError.get.errMsg) + if (context.reportBuffer.hasErrors) + fail("hasMatchingSymbol reported error: " + context.reportBuffer.firstError.get.errMsg) else if (itree3.isErroneous) fail("error typechecking implicit candidate") else if (isLocalToCallsite && !hasMatchingSymbol(itree2)) @@ -677,7 +677,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.reportBuffer.firstError match { case Some(err) => return fail("type parameters weren't correctly instantiated outside of the implicit tree: " + err.errMsg) case None => @@ -716,7 +716,7 @@ trait Implicits { case t => t } - context.firstError match { + context.reportBuffer.firstError match { case Some(err) => fail("typing TypeApply reported errors for the implicit tree: " + err.errMsg) case None => @@ -910,7 +910,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.reportBuffer.errors) match { case sr if sr.isDivergent => Nil case sr if sr.isFailure => rankImplicits(otherPending, acc) case newBest => @@ -1147,7 +1147,7 @@ trait Implicits { try { val tree1 = typedPos(pos.focus)(arg) - context.firstError match { + context.reportBuffer.firstError match { case Some(err) => processMacroExpansionError(err.errPos, err.errMsg) case None => new SearchResult(tree1, EmptyTreeTypeSubstituter, Nil) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index cb985ddaf6..69ac9ea61e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -779,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.reportBuffer.hasErrors && !pt.isWildcard) applicableExpectingPt(WildcardType) // second try else result diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ce031e9cd7..0bedaca4ee 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -697,9 +697,9 @@ 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) { + if (context1.reportBuffer.hasErrors) { stopStats() - SilentTypeError(context1.errors: _*) + SilentTypeError(context1.reportBuffer.errors: _*) } else { // If we have a successful result, emit any warnings it created. context1.flushAndIssueWarnings() @@ -816,14 +816,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } // avoid throwing spurious DivergentImplicit errors - if (context.hasErrors) + if (context.reportBuffer.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.reportBuffer.hasErrors) tree // silent will wrap it in SilentTypeError anyway else tpr.typed(withImplicitArgs, mode, pt) } orElse { _ => @@ -1057,7 +1057,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.reportBuffer.firstError match { case Some(err) => context.issue(err) case None => return res } @@ -3229,7 +3229,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper (arg1, arg1.tpe.deconst) }.unzip } - if (context.hasErrors) + if (context.reportBuffer.hasErrors) setError(tree) else { inferMethodAlternative(fun, undetparams, argTpes, pt) @@ -4330,7 +4330,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.reportBuffer.hasErrors) None else Some(res) } catch { case ex: CyclicReference => throw ex -- cgit v1.2.3 From 474141f4271d97d082cc8d26c64273eccb5a9ff6 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 17 Jul 2014 11:35:40 +0200 Subject: Reduce Context iface: inline complex forwarders. --- .../scala/tools/nsc/typechecker/Implicits.scala | 10 +++++----- .../scala/tools/nsc/typechecker/Typers.scala | 20 ++++++++++++++------ 2 files changed, 19 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 965547d835..e4925b939e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1359,7 +1359,8 @@ trait Implicits { if (Statistics.canEnable) Statistics.incCounter(inscopeImplicitHits) } if (result.isFailure) { - val previousErrs = context.flushAndReturnBuffer() + val previousErrs = context.reportBuffer.errors + context.reportBuffer.clearAllErrors() val failstart = if (Statistics.canEnable) Statistics.startTimer(oftypeFailNanos) else null val succstart = if (Statistics.canEnable) Statistics.startTimer(oftypeSucceedNanos) else null @@ -1371,7 +1372,7 @@ trait Implicits { result = searchImplicit(implicitsOfExpectedType, isLocalToCallsite = false) if (result.isFailure) { - context.updateBuffer(previousErrs) + context.reportBuffer ++= previousErrs if (Statistics.canEnable) Statistics.stopTimer(oftypeFailNanos, failstart) } else { if (Statistics.canEnable) Statistics.stopTimer(oftypeSucceedNanos, succstart) @@ -1428,9 +1429,8 @@ trait Implicits { // 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) + context.reportBuffer.clearAllErrors() + val res = typedImplicit(ii, ptChecked = false, isLocalToCallsite) if (res.tree ne EmptyTree) List((res, tvars map (_.constr))) else Nil } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 0bedaca4ee..44755c650c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -480,16 +480,20 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper @inline final def typerWithLocalContext[T](c: Context)(f: Typer => T): T = { val res = f(newTyper(c)) - if (c.hasErrors) - context.updateBuffer(c.flushAndReturnBuffer()) + val errors = c.reportBuffer.errors + if (errors.nonEmpty) { + c.reportBuffer.clearAllErrors() + context.reportBuffer ++= errors + } res } @inline final def withSavedContext[T](c: Context)(f: => T) = { - val savedErrors = c.flushAndReturnBuffer() + val savedErrors = c.reportBuffer.errors + c.reportBuffer.clearAllErrors() val res = f - c.updateBuffer(savedErrors) + c.reportBuffer ++= savedErrors res } @@ -702,14 +706,18 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper SilentTypeError(context1.reportBuffer.errors: _*) } else { // If we have a successful result, emit any warnings it created. - context1.flushAndIssueWarnings() + context1.reportBuffer.warnings foreach { + case (pos, msg) => reporter.warning(pos, msg) + } + context1.reportBuffer.clearAllWarnings() SilentResultValue(result) } } else { assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer") withSavedContext(context){ val res = op(this) - val errorsToReport = context.flushAndReturnBuffer() + val errorsToReport = context.reportBuffer.errors + context.reportBuffer.clearAllErrors() if (errorsToReport.isEmpty) SilentResultValue(res) else SilentTypeError(errorsToReport.head) } } -- cgit v1.2.3 From dc955c9c51d22e0dd3264130712e865aba5f47fb Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 17 Jul 2014 11:37:18 +0200 Subject: Reduce Context iface: encapsulate buffer manipulation. --- .../tools/nsc/typechecker/ContextErrors.scala | 35 +++++++++++----------- .../scala/tools/nsc/typechecker/Contexts.scala | 25 ++++++++++++++++ .../scala/tools/nsc/typechecker/Implicits.scala | 5 +--- .../scala/tools/nsc/typechecker/Typers.scala | 16 +--------- 4 files changed, 44 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 028ac46058..99cd027df0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -132,6 +132,23 @@ trait ContextErrors { 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 => @@ -150,24 +167,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. diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 1010ceafe3..56f945623b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1343,6 +1343,31 @@ trait Contexts { self: Analyzer => def hasErrors = errorBuffer.nonEmpty def firstError = errorBuffer.headOption + + // 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 + } } class ImportInfo(val tree: Import, val depth: Int) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index e4925b939e..ed773f0178 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -861,10 +861,7 @@ trait Implicits { // 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.reportBuffer.retainDivergentErrorsExcept(divergentError.getOrElse(null)) } search } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 44755c650c..65dc5148ad 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.reportBuffer.reportFirstDivergentError(fun, param, paramTp)(context) paramFailed = true } /* else { -- cgit v1.2.3 From 1f1c131bb3ba1f63a90aca89153353cba2693fba Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 17 Jul 2014 11:40:27 +0200 Subject: Reduce Context iface: inline internally. --- src/compiler/scala/tools/nsc/typechecker/Contexts.scala | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 56f945623b..f3c337f633 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -389,13 +389,14 @@ trait Contexts { self: Analyzer => // 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 = flushAndReturnBuffer() + val errorsToRestore = reportBuffer.errors + reportBuffer.clearAllErrors() try { withImplicitsDisabled(tryOnce(false)) - if (hasErrors) { + if (reportBuffer.hasErrors) { fallback = true contextMode = savedContextMode - flushBuffer() + reportBuffer.clearAllErrors() tryOnce(true) } } catch { @@ -405,7 +406,7 @@ trait Contexts { self: Analyzer => if (!fallback) tryOnce(true) else () } finally { contextMode = savedContextMode - updateBuffer(errorsToRestore) + reportBuffer ++= errorsToRestore } } else tryOnce(true) @@ -453,7 +454,7 @@ trait Contexts { self: Analyzer => @inline final def inSilentMode(expr: => Boolean): Boolean = { withMode() { // withMode with no arguments to restore the mode mutated by `setBufferErrors`. setBufferErrors() - try expr && !hasErrors + try expr && !reportBuffer.hasErrors finally reportBuffer.clearAll() } } -- cgit v1.2.3 From 03baff77059df0632e9acb9fedac3702b2687924 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 17 Jul 2014 11:42:36 +0200 Subject: Reduce Context iface: make contextMode mutators private. This allows local reasoning about these bits, giving some more confidence for the refactoring that's underway. --- src/compiler/scala/tools/nsc/typechecker/Contexts.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index f3c337f633..6adf7ba5cc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -345,12 +345,12 @@ trait Contexts { self: Analyzer => def reportErrors = this(ReportErrors) def bufferErrors = this(BufferErrors) def ambiguousErrors = this(AmbiguousErrors) - def throwErrors = contextMode.inNone(ReportErrors | BufferErrors) + private 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 + private def setReportErrors(): Unit = set(enable = ReportErrors | AmbiguousErrors, disable = BufferErrors) + private def setBufferErrors(): Unit = set(enable = BufferErrors, disable = ReportErrors | AmbiguousErrors) + private def setThrowErrors(): Unit = this(ReportErrors | AmbiguousErrors | BufferErrors) = false + private def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report /** Append the given errors to the report buffer */ def updateBuffer(errors: Traversable[AbsTypeError]) = reportBuffer ++= errors -- cgit v1.2.3 From 5e62c59aad9dabb3b07e2330d2f8b937f48b93e8 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 17 Jul 2014 11:44:43 +0200 Subject: Reduce Context iface: remove dead code. --- .../scala/tools/nsc/typechecker/Contexts.scala | 37 ---------------------- 1 file changed, 37 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 6adf7ba5cc..8b53e8eeac 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -331,16 +331,6 @@ trait Contexts { self: Analyzer => 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) @@ -352,25 +342,6 @@ trait Contexts { self: Analyzer => private def setThrowErrors(): Unit = this(ReportErrors | AmbiguousErrors | BufferErrors) = false private 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) - } - reportBuffer.clearAllWarnings() - } - /** Try inference twice, once without views and once with views, * unless views are already disabled. @@ -1329,14 +1300,6 @@ trait Contexts { self: Analyzer => errorBuffer.clear() this } - def clearErrors(removeF: PartialFunction[AbsTypeError, Boolean]): this.type = { - errorBuffer.retain(!PartialFunction.cond(_)(removeF)) - this - } - def retainErrors(leaveF: PartialFunction[AbsTypeError, Boolean]): this.type = { - errorBuffer.retain(PartialFunction.cond(_)(leaveF)) - this - } def clearAllWarnings(): this.type = { warningBuffer.clear() this -- cgit v1.2.3 From c6bee6437a626c93be3951ee0437adce8c88e96c Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Wed, 9 Jul 2014 17:15:40 +0200 Subject: Untangle reporting of ambiguous errors. Now that all reporting mode manipulators are private to Context, let's untangle this logic: - every `setReportErrors` gets a corresponding `setAmbiguousErrors(true)` - every `setBufferErrors` gets a corresponding `setAmbiguousErrors(false)` - every `setThrowErrors` gets a corresponding `setAmbiguousErrors(false)` `ambiguousErrors` means that ambiguity errors *must* be reported, even when in silent mode. When it's false, they are *not* reported, but they are buffered when the context reporter is buffering. TODO: this seems a bit dubious, but this is what happens now. Let's see if we can simplify this once the refactoring is complete. Again, the end goal is a strategy-based approach to reporting, where the reporting mode is captured in the reporter being used, with as little mutation as possible to capture more invariants (would like to stop throwing TypeError eventually and only have two reporters: buffering reporter, regular reporter) --- src/compiler/scala/tools/nsc/typechecker/Contexts.scala | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 8b53e8eeac..f5f9494562 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -317,7 +317,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 @@ -337,9 +337,9 @@ trait Contexts { self: Analyzer => def ambiguousErrors = this(AmbiguousErrors) private def throwErrors = contextMode.inNone(ReportErrors | BufferErrors) - private def setReportErrors(): Unit = set(enable = ReportErrors | AmbiguousErrors, disable = BufferErrors) - private def setBufferErrors(): Unit = set(enable = BufferErrors, disable = ReportErrors | AmbiguousErrors) - private def setThrowErrors(): Unit = this(ReportErrors | AmbiguousErrors | BufferErrors) = false + private def setReportErrors(): Unit = set(enable = ReportErrors, disable = BufferErrors) + private def setBufferErrors(): Unit = set(enable = BufferErrors, disable = ReportErrors) + private def setThrowErrors(): Unit = this(ReportErrors | BufferErrors) = false private def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report @@ -354,6 +354,7 @@ trait Contexts { self: Analyzer => val savedContextMode = contextMode var fallback = false setBufferErrors() + setAmbiguousErrors(false) // 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 @@ -425,6 +426,7 @@ trait Contexts { self: Analyzer => @inline final def inSilentMode(expr: => Boolean): Boolean = { withMode() { // withMode with no arguments to restore the mode mutated by `setBufferErrors`. setBufferErrors() + setAmbiguousErrors(false) try expr && !reportBuffer.hasErrors finally reportBuffer.clearAll() } @@ -492,6 +494,7 @@ trait Contexts { self: Analyzer => /** Use reporter (possibly buffered) for errors/warnings and enable implicit conversion **/ def initRootContext(): Unit = { setReportErrors() + setAmbiguousErrors(true) this(EnrichmentEnabled | ImplicitsEnabled) = true } @@ -500,6 +503,7 @@ trait Contexts { self: Analyzer => */ def initRootContextPostTyper(): Unit = { setThrowErrors() + setAmbiguousErrors(false) this(EnrichmentEnabled | ImplicitsEnabled) = false } @@ -525,6 +529,7 @@ trait Contexts { self: Analyzer => def makeNonSilent(newtree: Tree): Context = { val c = make(newtree) c.setReportErrors() + c.setAmbiguousErrors(true) c } -- cgit v1.2.3 From 43da1dbbe99e8bdb98d9e08b8e3a255dd4af6902 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 17 Jul 2014 11:48:49 +0200 Subject: Introduce `AbsAmbiguousTypeError`. It's the superclass for type errors that should be issued with `issueAmbiguousError`. TODO: enforce this in `issues` itself using a type test, remove the static overload. --- .../scala/tools/nsc/typechecker/ContextErrors.scala | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 99cd027df0..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 -- cgit v1.2.3 From 064bc5eb25094f0d6de2aa8990466fed29d3c095 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 17 Jul 2014 11:49:37 +0200 Subject: Remove `def error(pos: Position, err: Throwable)` The overload was used only once. --- src/compiler/scala/tools/nsc/typechecker/Contexts.scala | 5 ----- src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index f5f9494562..e7ab9dc94e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -604,11 +604,6 @@ trait Contexts { self: Analyzer => /** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */ private[typechecker] def issueAmbiguousError(err: AbsTypeError) = issueCommon(err, ambiguousErrors) - /** 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 - /** Issue/throw the given error message according to the current mode for error reporting. */ def error(pos: Position, msg: String) = { val msg1 = addDiagString(msg) diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index fbea69db5a..89b064e7c1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -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) } } } -- cgit v1.2.3 From cdee8350288edb1054bfc841f81bd21b9e6a0323 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 17 Jul 2014 12:14:46 +0200 Subject: Configure `checking` mode in `rootContext`. This is used by the tree checkers. --- .../scala/tools/nsc/typechecker/Contexts.scala | 29 ++++++++-------------- .../scala/tools/nsc/typechecker/TreeCheckers.scala | 3 +-- 2 files changed, 11 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index e7ab9dc94e..7464837409 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -98,7 +98,7 @@ trait Contexts { self: Analyzer => } - def rootContext(unit: CompilationUnit, tree: Tree = EmptyTree): 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,16 +113,13 @@ trait Contexts { self: Analyzer => else rootImportsContext.make(gen.mkImport(ScalaXmlPackage, nme.TopScope, nme.dollarScope)) val c = contextWithXML.make(tree, unit = unit) - c.initRootContext() - c - } - def rootContextPostTyper(unit: CompilationUnit, tree: Tree = EmptyTree): Context = { - val c = rootContext(unit, tree) - c.initRootContextPostTyper() + c.initRootContext(throwing, checking) c } + def rootContextPostTyper(unit: CompilationUnit, tree: Tree = EmptyTree): Context = + rootContext(unit, tree, throwing = true) def resetContexts() { startContext.enclosingContextChain foreach { context => @@ -492,19 +489,13 @@ trait Contexts { self: Analyzer => } /** Use reporter (possibly buffered) for errors/warnings and enable implicit conversion **/ - def initRootContext(): Unit = { - setReportErrors() - setAmbiguousErrors(true) - this(EnrichmentEnabled | ImplicitsEnabled) = true - } + def initRootContext(throwing: Boolean = false, checking: Boolean = false): Unit = { + if (checking) this.checking = true + else if (throwing) setThrowErrors() + else setReportErrors() - /** Disable implicit conversion/enrichment, throw TypeError on error. - * TODO: can we phase out TypeError and uniformly rely on reporter? - */ - def initRootContextPostTyper(): Unit = { - setThrowErrors() - setAmbiguousErrors(false) - this(EnrichmentEnabled | ImplicitsEnabled) = false + setAmbiguousErrors(!throwing) + this(EnrichmentEnabled | ImplicitsEnabled) = !throwing } def make(tree: Tree, owner: Symbol, scope: Scope): Context = 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) -- cgit v1.2.3 From ff7c467f7d985086e351cfd8ab90d28724d393f4 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Fri, 18 Jul 2014 11:03:00 +0200 Subject: repl depends on jline-2.12 --- src/eclipse/repl/.classpath | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/eclipse/repl/.classpath b/src/eclipse/repl/.classpath index 8fed2a3f61..8ff9aabfbf 100644 --- a/src/eclipse/repl/.classpath +++ b/src/eclipse/repl/.classpath @@ -2,10 +2,10 @@ - + + - -- cgit v1.2.3 From f0fdcc010966030d55c297d5725adaaddfe7d20d Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Fri, 18 Jul 2014 14:34:46 +0200 Subject: Clarify that ThrowErrors is the default --- src/compiler/scala/tools/nsc/typechecker/Contexts.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 7464837409..9a6647a83a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -329,14 +329,14 @@ trait Contexts { self: Analyzer => /** A buffer for errors and warnings, used with `this.bufferErrors == true` */ def reportBuffer = _reportBuffer - def reportErrors = this(ReportErrors) + def reportErrors = contextMode.inNone(ThrowErrors | BufferErrors) def bufferErrors = this(BufferErrors) def ambiguousErrors = this(AmbiguousErrors) - private def throwErrors = contextMode.inNone(ReportErrors | BufferErrors) + private def throwErrors = this(ThrowErrors) - private def setReportErrors(): Unit = set(enable = ReportErrors, disable = BufferErrors) - private def setBufferErrors(): Unit = set(enable = BufferErrors, disable = ReportErrors) - private def setThrowErrors(): Unit = this(ReportErrors | BufferErrors) = false + private def setReportErrors(): Unit = set(disable = BufferErrors | ThrowErrors) + private def setBufferErrors(): Unit = set(enable = BufferErrors, disable = ThrowErrors) + private def setThrowErrors(): Unit = set(enable = ThrowErrors, disable = BufferErrors) private def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report @@ -1417,7 +1417,7 @@ object ContextMode { def apply(bits: Int): ContextMode = new ContextMode(bits) final val NOmode: ContextMode = 0 - final val ReportErrors: ContextMode = 1 << 0 + final val ThrowErrors: ContextMode = 1 << 0 final val BufferErrors: ContextMode = 1 << 1 final val AmbiguousErrors: ContextMode = 1 << 2 @@ -1479,10 +1479,10 @@ object ContextMode { PatternAlternative | StarPatterns | SuperInit | SecondTry | ReturnExpr | TypeConstructorAllowed ) - final val DefaultMode: ContextMode = MacrosEnabled + final val DefaultMode: ContextMode = MacrosEnabled | ThrowErrors private val contextModeNameMap = Map( - ReportErrors -> "ReportErrors", + ThrowErrors -> "ReportErrors", BufferErrors -> "BufferErrors", AmbiguousErrors -> "AmbiguousErrors", ConstructorSuffix -> "ConstructorSuffix", -- cgit v1.2.3 From 725c5c907ba9ecdec4ee343bd0aa0ff438b4c20c Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 17 Jul 2014 12:29:49 +0200 Subject: Encapsulate reporting mode as class of reportBuffer. Reporting mode used to be governed by contextMode. This logic is left in place by this commit, and the consistency of the new and the old is checked. Will be removed in follow-up commit. The main difference is that we no longer throw TypeErrors in buffering mode. There was one instance of context.error in implicit search the exploited the fact that implicit search runs in buffering (silent) mode and thus calls to error(pos,msg) used to throw new TypeError(pos, msg) -- made this explicit, and removed throwing behavior from the buffering context reporter. --- .../scala/tools/nsc/typechecker/Contexts.scala | 145 ++++++++++++++------- .../scala/tools/nsc/typechecker/Implicits.scala | 11 +- test/files/neg/macro-basic-mamdmi.check | 10 +- 3 files changed, 113 insertions(+), 53 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 9a6647a83a..37eff40dd8 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 @@ -325,9 +326,22 @@ trait Contexts { self: Analyzer => // Error reporting policies and buffer. // - private var _reportBuffer: ReportBuffer = new ReportBuffer + // TODO: start out with a regular reporter? + private var _reportBuffer: ContextReporter = new ThrowingReporter + /** A buffer for errors and warnings, used with `this.bufferErrors == true` */ - def reportBuffer = _reportBuffer + def reportBuffer = { + assert( + (bufferErrors == _reportBuffer.isInstanceOf[BufferingReporter]) + && (throwErrors == _reportBuffer.isInstanceOf[ThrowingReporter]) + && (checking == _reportBuffer.isInstanceOf[CheckingReporter]) + && (reportErrors != (_reportBuffer.isInstanceOf[BufferingReporter] + || _reportBuffer.isInstanceOf[ThrowingReporter] + || _reportBuffer.isInstanceOf[CheckingReporter])), + s"INCONSISTENCY $contextMode != ${_reportBuffer.getClass}") + + _reportBuffer + } def reportErrors = contextMode.inNone(ThrowErrors | BufferErrors) def bufferErrors = this(BufferErrors) @@ -349,7 +363,9 @@ trait Contexts { self: Analyzer => def apply(): Unit = if (implicitsEnabled) { val savedContextMode = contextMode + val savedReporter = _reportBuffer var fallback = false + _reportBuffer = (new BufferingReporter).sharingBuffersWith(reportBuffer) setBufferErrors() setAmbiguousErrors(false) // We cache the current buffer because it is impossible to @@ -365,6 +381,7 @@ trait Contexts { self: Analyzer => if (reportBuffer.hasErrors) { fallback = true contextMode = savedContextMode + _reportBuffer = savedReporter reportBuffer.clearAllErrors() tryOnce(true) } @@ -372,9 +389,11 @@ trait Contexts { self: Analyzer => case ex: CyclicReference => throw ex case ex: TypeError => // recoverable cyclic references contextMode = savedContextMode + _reportBuffer = savedReporter if (!fallback) tryOnce(true) else () } finally { contextMode = savedContextMode + _reportBuffer = savedReporter reportBuffer ++= errorsToRestore } } @@ -421,11 +440,16 @@ trait Contexts { self: Analyzer => /** @return true if the `expr` evaluates to true within a silent Context that incurs no errors */ @inline final def inSilentMode(expr: => Boolean): Boolean = { + val savedReporter = _reportBuffer withMode() { // withMode with no arguments to restore the mode mutated by `setBufferErrors`. + _reportBuffer = (new BufferingReporter).sharingBuffersWith(reportBuffer) setBufferErrors() setAmbiguousErrors(false) try expr && !reportBuffer.hasErrors - finally reportBuffer.clearAll() + finally { + _reportBuffer = savedReporter + _reportBuffer.clearAll() // TODO: ??? + } } } @@ -473,7 +497,7 @@ trait Contexts { self: Analyzer => c.diagUsedDefaults = diagUsedDefaults c.openImplicits = openImplicits c.contextMode = contextMode // note: ConstructorSuffix, a bit within `mode`, is conditionally overwritten below. - c._reportBuffer = reportBuffer + c._reportBuffer = _reportBuffer // Fields that may take on a different value in the child c.prefix = prefixInChild @@ -490,9 +514,10 @@ trait Contexts { self: Analyzer => /** Use reporter (possibly buffered) for errors/warnings and enable implicit conversion **/ def initRootContext(throwing: Boolean = false, checking: Boolean = false): Unit = { - if (checking) this.checking = true - else if (throwing) setThrowErrors() - else setReportErrors() + _reportBuffer = + if (checking) {this.checking = true ; new CheckingReporter} + else if (throwing) {setThrowErrors(); new ThrowingReporter} + else {setReportErrors() ; new ContextReporter} // TODO: this is likely redundant setAmbiguousErrors(!throwing) this(EnrichmentEnabled | ImplicitsEnabled) = !throwing @@ -500,7 +525,7 @@ trait Contexts { self: Analyzer => 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) @@ -508,17 +533,20 @@ trait Contexts { self: Analyzer => def makeNewScope(tree: Tree, owner: Symbol): Context = make(tree, owner, newNestedScope(scope)) + /** 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() c.setAmbiguousErrors(reportAmbiguousErrors) - c._reportBuffer = new ReportBuffer // A fresh buffer so as not to leak errors/warnings into `this`. + // A fresh buffer so as not to leak errors/warnings into `this`. + c._reportBuffer = new BufferingReporter c } def makeNonSilent(newtree: Tree): Context = { val c = make(newtree) + c._reportBuffer = (new ContextReporter).sharingBuffersWith(reportBuffer) c.setReportErrors() c.setAmbiguousErrors(true) c @@ -545,6 +573,7 @@ trait Contexts { self: Analyzer => val baseContext = enclClass.outer.nextEnclosing(!_.tree.isInstanceOf[Template]) val argContext = baseContext.makeNewScope(tree, owner) argContext.contextMode = contextMode + argContext._reportBuffer = _reportBuffer // caught by neg/t3649 TODO: make sure _reportBuffer is propagated wherever contextMode is argContext.inSelfSuperCall = true def enterElems(c: Context) { def enterLocalElems(e: ScopeEntry) { @@ -567,46 +596,17 @@ trait Contexts { self: Analyzer => // // Error and warning issuance // - private def addDiagString(msg: String) = { - val diagUsedDefaultsMsg = "Error occurred in an application involving default arguments." - if (diagUsedDefaults && !(msg endsWith diagUsedDefaultsMsg)) msg + "\n" + diagUsedDefaultsMsg - else msg - } - - private def unitError(pos: Position, msg: String): Unit = - if (checking) onTreeCheckerError(pos, msg) - else reporter.error(pos, msg) - - @inline private def issueCommon(err: AbsTypeError, reportError: Boolean) { - // 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 (reportError) unitError(err.errPos, addDiagString(err.errMsg)) - 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. */ - private[typechecker] def issue(err: AbsTypeError) = issueCommon(err, reportErrors) - + private[typechecker] def issue(err: AbsTypeError) = reportBuffer.issue(err)(this) /** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */ - private[typechecker] def issueAmbiguousError(err: AbsTypeError) = issueCommon(err, ambiguousErrors) - + private[typechecker] def issueAmbiguousError(err: AbsAmbiguousTypeError) = reportBuffer.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) = reportBuffer.error(pos, msg) /** Issue/throw the given error message according to the current mode for error reporting. */ - def warning(pos: Position, msg: String) { - if (reportErrors) reporter.warning(pos, msg) - else if (bufferErrors) reportBuffer += (pos -> msg) - } + def warning(pos: Position, msg: String) = reportBuffer.warning(pos, msg) + def echo(pos: Position, msg: String) = reportBuffer.echo(pos, msg) + def deprecationWarning(pos: Position, sym: Symbol, msg: String): Unit = currentRun.reporting.deprecationWarning(pos, sym, msg) @@ -616,7 +616,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 @@ -1254,7 +1253,7 @@ trait Contexts { self: Analyzer => } /** A buffer for warnings and errors that are accumulated during speculative type checking. */ - final class ReportBuffer { + class ContextReporter extends Reporter { type Error = AbsTypeError type Warning = (Position, String) @@ -1270,6 +1269,13 @@ trait Contexts { self: Analyzer => private def warningBuffer = {if (_warningBuffer == null) _warningBuffer = newBuffer; _warningBuffer} def warnings: immutable.Seq[Warning] = warningBuffer.toVector + def sharingBuffersWith(other: ContextReporter): this.type = { + // go through other's accessor so that its buffer will be created + _errorBuffer = other.errorBuffer + _warningBuffer = other.warningBuffer + this + } + def +=(error: AbsTypeError): this.type = { errorBuffer += error this @@ -1296,7 +1302,8 @@ trait Contexts { self: Analyzer => this } - def hasErrors = errorBuffer.nonEmpty + // TODO + override def hasErrors = super.hasErrors || errorBuffer.nonEmpty def firstError = errorBuffer.headOption // have to pass in context because multiple contexts may share the same ReportBuffer @@ -1323,8 +1330,52 @@ trait Contexts { self: Analyzer => case err: DivergentImplicitTypeError => err ne saved case _ => false } + + def isBuffering = false + + 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 issue(err: AbsTypeError)(implicit context: Context): Unit = error(err.errPos, addDiagString(err.errMsg)) + + def issueAmbiguousError(err: AbsAmbiguousTypeError)(implicit context: Context): Unit = + if (context.ambiguousErrors) error(err.errPos, addDiagString(err.errMsg)) + + protected final def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = ??? + override def echo(pos: Position, msg: String): Unit = reporter.echo(pos, msg) + override def warning(pos: Position, msg: String): Unit = reporter.warning(pos, msg) + override def error(pos: Position, msg: String): Unit = reporter.error(pos, msg) } + private class BufferingReporter extends ContextReporter { + override def isBuffering = true + + override def issue(err: AbsTypeError)(implicit context: Context): Unit = this += err + override def issueAmbiguousError(err: AbsAmbiguousTypeError)(implicit context: Context): Unit = + if (context.ambiguousErrors) reporter.error(err.errPos, addDiagString(err.errMsg)) + else this += err + + override def warning(pos: Position, msg: String): Unit = this += (pos -> msg) + + // 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 + override def error(pos: Position, msg: String): Unit = this += TypeErrorWrapper(new TypeError(pos, msg)) + } + + // TODO: remove (specialization relies on TypeError being thrown, among other post-typer phases) + private class ThrowingReporter extends ContextReporter { + override def error(pos: Position, msg: String): Unit = throw new TypeError(pos, msg) + } + + /** Used during a run of [[scala.tools.nsc.typechecker.TreeCheckers]]? */ + private class CheckingReporter extends ContextReporter { + override def error(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 diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index ed773f0178..0a5521aff4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1276,19 +1276,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) 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 -- cgit v1.2.3 From 258d95c7b1528a84514a6a609a356be840322e9b Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 17 Jul 2014 12:40:26 +0200 Subject: Remove dead code: mode setting --- .../scala/tools/nsc/typechecker/Contexts.scala | 49 ++++++---------------- 1 file changed, 12 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 37eff40dd8..06375d404b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -258,8 +258,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) @@ -329,28 +327,16 @@ trait Contexts { self: Analyzer => // TODO: start out with a regular reporter? private var _reportBuffer: ContextReporter = new ThrowingReporter - /** A buffer for errors and warnings, used with `this.bufferErrors == true` */ - def reportBuffer = { - assert( - (bufferErrors == _reportBuffer.isInstanceOf[BufferingReporter]) - && (throwErrors == _reportBuffer.isInstanceOf[ThrowingReporter]) - && (checking == _reportBuffer.isInstanceOf[CheckingReporter]) - && (reportErrors != (_reportBuffer.isInstanceOf[BufferingReporter] - || _reportBuffer.isInstanceOf[ThrowingReporter] - || _reportBuffer.isInstanceOf[CheckingReporter])), - s"INCONSISTENCY $contextMode != ${_reportBuffer.getClass}") - - _reportBuffer - } + // the reporter for this context + def reportBuffer = _reportBuffer + + // if set, errors will not be reporter/thrown + def bufferErrors = reportBuffer.isBuffering + def reportErrors = !bufferErrors - def reportErrors = contextMode.inNone(ThrowErrors | BufferErrors) - def bufferErrors = this(BufferErrors) + // whether to *report* (which is separate from buffering/throwing) ambiguity errors def ambiguousErrors = this(AmbiguousErrors) - private def throwErrors = this(ThrowErrors) - private def setReportErrors(): Unit = set(disable = BufferErrors | ThrowErrors) - private def setBufferErrors(): Unit = set(enable = BufferErrors, disable = ThrowErrors) - private def setThrowErrors(): Unit = set(enable = ThrowErrors, disable = BufferErrors) private def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report @@ -366,7 +352,6 @@ trait Contexts { self: Analyzer => val savedReporter = _reportBuffer var fallback = false _reportBuffer = (new BufferingReporter).sharingBuffersWith(reportBuffer) - setBufferErrors() setAmbiguousErrors(false) // We cache the current buffer because it is impossible to // distinguish errors that occurred before entering tryTwice @@ -441,9 +426,8 @@ trait Contexts { self: Analyzer => /** @return true if the `expr` evaluates to true within a silent Context that incurs no errors */ @inline final def inSilentMode(expr: => Boolean): Boolean = { val savedReporter = _reportBuffer - withMode() { // withMode with no arguments to restore the mode mutated by `setBufferErrors`. + withMode() { // TODO: rework -- withMode with no arguments to restore the mode mutated by `setBufferErrors` (no longer mutated!) _reportBuffer = (new BufferingReporter).sharingBuffersWith(reportBuffer) - setBufferErrors() setAmbiguousErrors(false) try expr && !reportBuffer.hasErrors finally { @@ -515,9 +499,9 @@ trait Contexts { self: Analyzer => /** Use reporter (possibly buffered) for errors/warnings and enable implicit conversion **/ def initRootContext(throwing: Boolean = false, checking: Boolean = false): Unit = { _reportBuffer = - if (checking) {this.checking = true ; new CheckingReporter} - else if (throwing) {setThrowErrors(); new ThrowingReporter} - else {setReportErrors() ; new ContextReporter} // TODO: this is likely redundant + if (checking) new CheckingReporter + else if (throwing) new ThrowingReporter + else new ContextReporter // TODO: this is likely redundant setAmbiguousErrors(!throwing) this(EnrichmentEnabled | ImplicitsEnabled) = !throwing @@ -537,7 +521,6 @@ trait Contexts { self: Analyzer => /** 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() c.setAmbiguousErrors(reportAmbiguousErrors) // A fresh buffer so as not to leak errors/warnings into `this`. c._reportBuffer = new BufferingReporter @@ -547,7 +530,6 @@ trait Contexts { self: Analyzer => def makeNonSilent(newtree: Tree): Context = { val c = make(newtree) c._reportBuffer = (new ContextReporter).sharingBuffersWith(reportBuffer) - c.setReportErrors() c.setAmbiguousErrors(true) c } @@ -1468,8 +1450,6 @@ object ContextMode { def apply(bits: Int): ContextMode = new ContextMode(bits) final val NOmode: ContextMode = 0 - final val ThrowErrors: 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? */ @@ -1492,8 +1472,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. @@ -1530,17 +1508,14 @@ object ContextMode { PatternAlternative | StarPatterns | SuperInit | SecondTry | ReturnExpr | TypeConstructorAllowed ) - final val DefaultMode: ContextMode = MacrosEnabled | ThrowErrors + final val DefaultMode: ContextMode = MacrosEnabled private val contextModeNameMap = Map( - ThrowErrors -> "ReportErrors", - BufferErrors -> "BufferErrors", AmbiguousErrors -> "AmbiguousErrors", ConstructorSuffix -> "ConstructorSuffix", SelfSuperCall -> "SelfSuperCall", ImplicitsEnabled -> "ImplicitsEnabled", MacrosEnabled -> "MacrosEnabled", - Checking -> "Checking", ReTyping -> "ReTyping", PatternAlternative -> "PatternAlternative", StarPatterns -> "StarPatterns", -- cgit v1.2.3 From 9707e1df533dd678531c653f5ddedda59df5ce9c Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 17 Jul 2014 12:47:40 +0200 Subject: s/reportBuffer/reporter --- .../scala/tools/nsc/typechecker/Contexts.scala | 59 +++++++++++----------- .../scala/tools/nsc/typechecker/Implicits.scala | 32 ++++++------ .../scala/tools/nsc/typechecker/Infer.scala | 2 +- .../scala/tools/nsc/typechecker/Macros.scala | 2 +- .../scala/tools/nsc/typechecker/Typers.scala | 36 ++++++------- 5 files changed, 65 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 06375d404b..1d4e2c8546 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -128,7 +128,7 @@ trait Contexts { self: Analyzer => case Import(qual, _) => qual setType singleType(qual.symbol.owner.thisType, qual.symbol) case _ => } - context.reportBuffer.clearAll() + context.reporter.clearAll() } } @@ -324,14 +324,13 @@ trait Contexts { self: Analyzer => // Error reporting policies and buffer. // - // TODO: start out with a regular reporter? - private var _reportBuffer: ContextReporter = new ThrowingReporter + private var _reporter: ContextReporter = new ThrowingReporter // the reporter for this context - def reportBuffer = _reportBuffer + def reporter = _reporter // if set, errors will not be reporter/thrown - def bufferErrors = reportBuffer.isBuffering + def bufferErrors = reporter.isBuffering def reportErrors = !bufferErrors // whether to *report* (which is separate from buffering/throwing) ambiguity errors @@ -349,9 +348,9 @@ trait Contexts { self: Analyzer => def apply(): Unit = if (implicitsEnabled) { val savedContextMode = contextMode - val savedReporter = _reportBuffer + val savedReporter = _reporter var fallback = false - _reportBuffer = (new BufferingReporter).sharingBuffersWith(reportBuffer) + _reporter = (new BufferingReporter).sharingBuffersWith(reporter) setAmbiguousErrors(false) // We cache the current buffer because it is impossible to // distinguish errors that occurred before entering tryTwice @@ -359,27 +358,27 @@ trait Contexts { self: Analyzer => // 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 = reportBuffer.errors - reportBuffer.clearAllErrors() + val errorsToRestore = reporter.errors + reporter.clearAllErrors() try { withImplicitsDisabled(tryOnce(false)) - if (reportBuffer.hasErrors) { + if (reporter.hasErrors) { fallback = true contextMode = savedContextMode - _reportBuffer = savedReporter - reportBuffer.clearAllErrors() + _reporter = savedReporter + reporter.clearAllErrors() tryOnce(true) } } catch { case ex: CyclicReference => throw ex case ex: TypeError => // recoverable cyclic references contextMode = savedContextMode - _reportBuffer = savedReporter + _reporter = savedReporter if (!fallback) tryOnce(true) else () } finally { contextMode = savedContextMode - _reportBuffer = savedReporter - reportBuffer ++= errorsToRestore + _reporter = savedReporter + reporter ++= errorsToRestore } } else tryOnce(true) @@ -425,14 +424,14 @@ trait Contexts { self: Analyzer => /** @return true if the `expr` evaluates to true within a silent Context that incurs no errors */ @inline final def inSilentMode(expr: => Boolean): Boolean = { - val savedReporter = _reportBuffer + val savedReporter = _reporter withMode() { // TODO: rework -- withMode with no arguments to restore the mode mutated by `setBufferErrors` (no longer mutated!) - _reportBuffer = (new BufferingReporter).sharingBuffersWith(reportBuffer) + _reporter = (new BufferingReporter).sharingBuffersWith(reporter) setAmbiguousErrors(false) - try expr && !reportBuffer.hasErrors + try expr && !reporter.hasErrors finally { - _reportBuffer = savedReporter - _reportBuffer.clearAll() // TODO: ??? + _reporter = savedReporter + _reporter.clearAll() // TODO: ??? } } } @@ -481,7 +480,7 @@ trait Contexts { self: Analyzer => c.diagUsedDefaults = diagUsedDefaults c.openImplicits = openImplicits c.contextMode = contextMode // note: ConstructorSuffix, a bit within `mode`, is conditionally overwritten below. - c._reportBuffer = _reportBuffer + c._reporter = reporter // Fields that may take on a different value in the child c.prefix = prefixInChild @@ -498,7 +497,7 @@ trait Contexts { self: Analyzer => /** Use reporter (possibly buffered) for errors/warnings and enable implicit conversion **/ def initRootContext(throwing: Boolean = false, checking: Boolean = false): Unit = { - _reportBuffer = + _reporter = if (checking) new CheckingReporter else if (throwing) new ThrowingReporter else new ContextReporter // TODO: this is likely redundant @@ -523,13 +522,13 @@ trait Contexts { self: Analyzer => val c = make(newtree) c.setAmbiguousErrors(reportAmbiguousErrors) // A fresh buffer so as not to leak errors/warnings into `this`. - c._reportBuffer = new BufferingReporter + c._reporter = new BufferingReporter c } def makeNonSilent(newtree: Tree): Context = { val c = make(newtree) - c._reportBuffer = (new ContextReporter).sharingBuffersWith(reportBuffer) + c._reporter = (new ContextReporter).sharingBuffersWith(reporter) c.setAmbiguousErrors(true) c } @@ -555,7 +554,7 @@ trait Contexts { self: Analyzer => val baseContext = enclClass.outer.nextEnclosing(!_.tree.isInstanceOf[Template]) val argContext = baseContext.makeNewScope(tree, owner) argContext.contextMode = contextMode - argContext._reportBuffer = _reportBuffer // caught by neg/t3649 TODO: make sure _reportBuffer is propagated wherever contextMode is + argContext._reporter = _reporter // caught by neg/t3649 TODO: make sure _reporter is propagated wherever contextMode is argContext.inSelfSuperCall = true def enterElems(c: Context) { def enterLocalElems(e: ScopeEntry) { @@ -580,14 +579,14 @@ trait Contexts { self: Analyzer => // /** Issue/buffer/throw the given type error according to the current mode for error reporting. */ - private[typechecker] def issue(err: AbsTypeError) = reportBuffer.issue(err)(this) + 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. */ - private[typechecker] def issueAmbiguousError(err: AbsAmbiguousTypeError) = reportBuffer.issueAmbiguousError(err)(this) + 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) = reportBuffer.error(pos, msg) + 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) = reportBuffer.warning(pos, msg) - def echo(pos: Position, msg: String) = reportBuffer.echo(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 = diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 0a5521aff4..e03877dbf9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -71,12 +71,12 @@ 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.reportBuffer.hasErrors) { - context.reportBuffer ++= (implicitSearchContext.reportBuffer.errors.collect { + if (result.isFailure && saveAmbiguousDivergent && implicitSearchContext.reporter.hasErrors) { + context.reporter ++= (implicitSearchContext.reporter.errors.collect { case dte: DivergentImplicitTypeError => dte case ate: AmbiguousImplicitTypeError => ate }) - debuglog("update buffer: " + implicitSearchContext.reportBuffer.errors) + debuglog("update buffer: " + implicitSearchContext.reporter.errors) } // SI-7944 undetermined type parameters that result from inference within typedImplicit land in // `implicitSearchContext.undetparams`, *not* in `context.undetparams` @@ -99,7 +99,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.reportBuffer.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 +635,7 @@ trait Implicits { } case _ => fallback } - context.reportBuffer.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 +658,8 @@ trait Implicits { } } - if (context.reportBuffer.hasErrors) - fail("hasMatchingSymbol reported error: " + context.reportBuffer.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 +677,7 @@ trait Implicits { // #2421: check that we correctly instantiated type parameters outside of the implicit tree: checkBounds(itree3, NoPrefix, NoSymbol, undetParams, targs, "inferred ") - context.reportBuffer.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 +716,7 @@ trait Implicits { case t => t } - context.reportBuffer.firstError match { + context.reporter.firstError match { case Some(err) => fail("typing TypeApply reported errors for the implicit tree: " + err.errMsg) case None => @@ -861,7 +861,7 @@ trait Implicits { // 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) - context.reportBuffer.retainDivergentErrorsExcept(divergentError.getOrElse(null)) + context.reporter.retainDivergentErrorsExcept(divergentError.getOrElse(null)) } search } @@ -907,7 +907,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.reportBuffer.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 => @@ -1144,7 +1144,7 @@ trait Implicits { try { val tree1 = typedPos(pos.focus)(arg) - context.reportBuffer.firstError match { + context.reporter.firstError match { case Some(err) => processMacroExpansionError(err.errPos, err.errMsg) case None => new SearchResult(tree1, EmptyTreeTypeSubstituter, Nil) } @@ -1357,8 +1357,8 @@ trait Implicits { if (Statistics.canEnable) Statistics.incCounter(inscopeImplicitHits) } if (result.isFailure) { - val previousErrs = context.reportBuffer.errors - context.reportBuffer.clearAllErrors() + val previousErrs = context.reporter.errors + context.reporter.clearAllErrors() val failstart = if (Statistics.canEnable) Statistics.startTimer(oftypeFailNanos) else null val succstart = if (Statistics.canEnable) Statistics.startTimer(oftypeSucceedNanos) else null @@ -1370,7 +1370,7 @@ trait Implicits { result = searchImplicit(implicitsOfExpectedType, isLocalToCallsite = false) if (result.isFailure) { - context.reportBuffer ++= previousErrs + context.reporter ++= previousErrs if (Statistics.canEnable) Statistics.stopTimer(oftypeFailNanos, failstart) } else { if (Statistics.canEnable) Statistics.stopTimer(oftypeSucceedNanos, succstart) @@ -1427,7 +1427,7 @@ trait Implicits { // thus, start each type var off with a fresh for every typedImplicit resetTVars() // any previous errors should not affect us now - context.reportBuffer.clearAllErrors() + context.reporter.clearAllErrors() val res = typedImplicit(ii, ptChecked = false, isLocalToCallsite) if (res.tree ne EmptyTree) List((res, tvars map (_.constr))) else Nil diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 69ac9ea61e..535aaffbbf 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -779,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.reportBuffer.hasErrors && !pt.isWildcard) + if (silent.reporter.hasErrors && !pt.isWildcard) applicableExpectingPt(WildcardType) // second try else result diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 66152a6ac2..d6870afb10 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -622,7 +622,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/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 65dc5148ad..c557af88f2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -155,7 +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.reportFirstDivergentError(fun, param, paramTp)(context) + context.reporter.reportFirstDivergentError(fun, param, paramTp)(context) paramFailed = true } /* else { @@ -466,20 +466,20 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper @inline final def typerWithLocalContext[T](c: Context)(f: Typer => T): T = { val res = f(newTyper(c)) - val errors = c.reportBuffer.errors + val errors = c.reporter.errors if (errors.nonEmpty) { - c.reportBuffer.clearAllErrors() - context.reportBuffer ++= errors + c.reporter.clearAllErrors() + context.reporter ++= errors } res } @inline final def withSavedContext[T](c: Context)(f: => T) = { - val savedErrors = c.reportBuffer.errors - c.reportBuffer.clearAllErrors() + val savedErrors = c.reporter.errors + c.reporter.clearAllErrors() val res = f - c.reportBuffer ++= savedErrors + c.reporter ++= savedErrors res } @@ -687,23 +687,23 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper context.undetparams = context1.undetparams context.savedTypeBounds = context1.savedTypeBounds context.namedApplyBlockInfo = context1.namedApplyBlockInfo - if (context1.reportBuffer.hasErrors) { + if (context1.reporter.hasErrors) { stopStats() - SilentTypeError(context1.reportBuffer.errors: _*) + SilentTypeError(context1.reporter.errors: _*) } else { // If we have a successful result, emit any warnings it created. - context1.reportBuffer.warnings foreach { + context1.reporter.warnings foreach { case (pos, msg) => reporter.warning(pos, msg) } - context1.reportBuffer.clearAllWarnings() + context1.reporter.clearAllWarnings() SilentResultValue(result) } } else { assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer") withSavedContext(context){ val res = op(this) - val errorsToReport = context.reportBuffer.errors - context.reportBuffer.clearAllErrors() + val errorsToReport = context.reporter.errors + context.reporter.clearAllErrors() if (errorsToReport.isEmpty) SilentResultValue(res) else SilentTypeError(errorsToReport.head) } } @@ -810,14 +810,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } // avoid throwing spurious DivergentImplicit errors - if (context.reportBuffer.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.reportBuffer.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 { _ => @@ -1051,7 +1051,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.reportBuffer.firstError match { + silentContext.reporter.firstError match { case Some(err) => context.issue(err) case None => return res } @@ -3223,7 +3223,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper (arg1, arg1.tpe.deconst) }.unzip } - if (context.reportBuffer.hasErrors) + if (context.reporter.hasErrors) setError(tree) else { inferMethodAlternative(fun, undetparams, argTpes, pt) @@ -4324,7 +4324,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper c.retyping = true try { val res = newTyper(c).typedArgs(args, mode) - if (c.reportBuffer.hasErrors) None else Some(res) + if (c.reporter.hasErrors) None else Some(res) } catch { case ex: CyclicReference => throw ex -- cgit v1.2.3 From 5d00b5940a9c91b6d9e13fea625d406ce53cc60c Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 15 Jul 2014 18:43:45 +0200 Subject: Cleanup ContextReporter hierarchy Next step: more principled buffering, make use of the single-entry point reporter. First example simplification in `TryTwice`: No need to store and restore errors -- just use a fresh reporter. Also, control-flow with vars ftw. --- .../scala/tools/nsc/typechecker/Contexts.scala | 178 ++++++++++----------- 1 file changed, 84 insertions(+), 94 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 1d4e2c8546..fd9d1de88e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -324,9 +324,10 @@ trait Contexts { self: Analyzer => // Error reporting policies and buffer. // + // the reporter for this context + // TODO: start off with ImmediateReporter private var _reporter: ContextReporter = new ThrowingReporter - // the reporter for this context def reporter = _reporter // if set, errors will not be reporter/thrown @@ -339,49 +340,45 @@ trait Contexts { self: Analyzer => private def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report - /** Try inference twice, once without views and once with views, + /** + * Try inference twice, once without views and once with views, * unless views are already disabled. */ abstract class TryTwice { def tryOnce(isLastTry: Boolean): Unit - def apply(): Unit = + def apply(): Unit = { + var doLastTry = !implicitsEnabled + + // do first try if implicits are enabled if (implicitsEnabled) { - val savedContextMode = contextMode val savedReporter = _reporter - var fallback = false - _reporter = (new BufferingReporter).sharingBuffersWith(reporter) - setAmbiguousErrors(false) - // We cache the current buffer because it is impossible to + val savedContextMode = contextMode + + _reporter = new BufferingReporter + setAmbiguousErrors(false) // this means ambiguous errors will not be force-fed to the reporter + + // 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* clean - // buffer but that would also flush any pre-tryTwice valid - // errors, hence some manual buffer tweaking is necessary. - val errorsToRestore = reporter.errors - reporter.clearAllErrors() + // first attempt fails, we try with implicits on + // and the original reporter. try { withImplicitsDisabled(tryOnce(false)) - if (reporter.hasErrors) { - fallback = true - contextMode = savedContextMode - _reporter = savedReporter - reporter.clearAllErrors() - tryOnce(true) - } + doLastTry = reporter.hasErrors } catch { - case ex: CyclicReference => throw ex - case ex: TypeError => // recoverable cyclic references - contextMode = savedContextMode - _reporter = savedReporter - if (!fallback) tryOnce(true) else () + case ex: CyclicReference => throw ex + case ex: TypeError => doLastTry = true // recoverable cyclic references? } finally { contextMode = savedContextMode _reporter = savedReporter - reporter ++= errorsToRestore } } - else tryOnce(true) + + // do last try if try with implicits enabled failed + // (or if it was not attempted because they were disabled) + if (doLastTry) tryOnce(true) + } } // @@ -426,7 +423,7 @@ trait Contexts { self: Analyzer => @inline final def inSilentMode(expr: => Boolean): Boolean = { val savedReporter = _reporter withMode() { // TODO: rework -- withMode with no arguments to restore the mode mutated by `setBufferErrors` (no longer mutated!) - _reporter = (new BufferingReporter).sharingBuffersWith(reporter) + _reporter = reporter.makeBuffering setAmbiguousErrors(false) try expr && !reporter.hasErrors finally { @@ -500,7 +497,7 @@ trait Contexts { self: Analyzer => _reporter = if (checking) new CheckingReporter else if (throwing) new ThrowingReporter - else new ContextReporter // TODO: this is likely redundant + else new ImmediateReporter setAmbiguousErrors(!throwing) this(EnrichmentEnabled | ImplicitsEnabled) = !throwing @@ -528,7 +525,7 @@ trait Contexts { self: Analyzer => def makeNonSilent(newtree: Tree): Context = { val c = make(newtree) - c._reporter = (new ContextReporter).sharingBuffersWith(reporter) + c._reporter = reporter.makeImmediate c.setAmbiguousErrors(true) c } @@ -1234,58 +1231,39 @@ trait Contexts { self: Analyzer => } /** A buffer for warnings and errors that are accumulated during speculative type checking. */ - class ContextReporter extends Reporter { + 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 - - private var _warningBuffer: mutable.LinkedHashSet[Warning] = _ - private def warningBuffer = {if (_warningBuffer == null) _warningBuffer = newBuffer; _warningBuffer} - def warnings: immutable.Seq[Warning] = warningBuffer.toVector - - def sharingBuffersWith(other: ContextReporter): this.type = { - // go through other's accessor so that its buffer will be created - _errorBuffer = other.errorBuffer - _warningBuffer = other.warningBuffer - this - } + 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) - 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 - } + def makeImmediate: ContextReporter = this + def makeBuffering: ContextReporter = this + def isBuffering: Boolean = false - def clearAll(): this.type = { - clearAllErrors(); clearAllWarnings(); - } + /** 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) - def clearAllErrors(): this.type = { - errorBuffer.clear() - this - } - def clearAllWarnings(): this.type = { - warningBuffer.clear() - this - } + def ++=(errors: Traversable[AbsTypeError]): Unit = errorBuffer ++= errors + + 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) + } - // TODO - override def hasErrors = super.hasErrors || errorBuffer.nonEmpty - def firstError = errorBuffer.headOption + final override def hasErrors = super.hasErrors || errorBuffer.nonEmpty // 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 = @@ -1312,48 +1290,60 @@ trait Contexts { self: Analyzer => case _ => false } - def isBuffering = false - 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 issue(err: AbsTypeError)(implicit context: Context): Unit = error(err.errPos, addDiagString(err.errMsg)) + // TODO: deprecate everything below (related to buffering) and push down to BufferingReporter + // [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 } - def issueAmbiguousError(err: AbsAmbiguousTypeError)(implicit context: Context): Unit = - if (context.ambiguousErrors) error(err.errPos, addDiagString(err.errMsg)) + final def errors: immutable.Seq[Error] = errorBuffer.toVector + final def warnings: immutable.Seq[Warning] = warningBuffer.toVector + final def firstError: Option[AbsTypeError] = errorBuffer.headOption - protected final def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = ??? - override def echo(pos: Position, msg: String): Unit = reporter.echo(pos, msg) - override def warning(pos: Position, msg: String): Unit = reporter.warning(pos, msg) - override def error(pos: Position, msg: String): Unit = reporter.error(pos, msg) + final def clearAll(): Unit = { clearAllErrors(); clearAllWarnings() } + final def clearAllErrors(): Unit = errorBuffer.clear() + final def clearAllWarnings(): Unit = warningBuffer.clear() } - private class BufferingReporter extends ContextReporter { - override def isBuffering = true + private 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) + } - override def issue(err: AbsTypeError)(implicit context: Context): Unit = this += err - override def issueAmbiguousError(err: AbsAmbiguousTypeError)(implicit context: Context): Unit = - if (context.ambiguousErrors) reporter.error(err.errPos, addDiagString(err.errMsg)) - else this += err - override def warning(pos: Position, msg: String): Unit = this += (pos -> msg) + private 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 - override def error(pos: Position, msg: String): Unit = this += TypeErrorWrapper(new TypeError(pos, msg)) + 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) } - // TODO: remove (specialization relies on TypeError being thrown, among other post-typer phases) + /** 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 class ThrowingReporter extends ContextReporter { - override def error(pos: Position, msg: String): Unit = throw new TypeError(pos, msg) + protected def handleError(pos: Position, msg: String): Unit = throw new TypeError(pos, msg) } /** Used during a run of [[scala.tools.nsc.typechecker.TreeCheckers]]? */ private class CheckingReporter extends ContextReporter { - override def error(pos: Position, msg: String): Unit = onTreeCheckerError(pos, msg) + protected def handleError(pos: Position, msg: String): Unit = onTreeCheckerError(pos, msg) } -- cgit v1.2.3 From abb58dcef1b4758f4e3af26c030e1f0630b8d145 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 27 Apr 2014 00:08:54 -0700 Subject: SI-6476 Improve error on escapement Behavior of escape processing under string interpolation can be confusing. This commit improves the exception message so you know at least what sort of escapes are handled. This came up on SO in the form `s"\d".r`, where it may not be obvious what is throwing and how to work around it. ``` scala> s"\d".r scala.StringContext$InvalidEscapeException: invalid escape '\d' not one of [\b, \t, \n, \f, \r, \\, \", \'] at index 0 in "\d". Use \\ for literal \. scala> s"a\" scala.StringContext$InvalidEscapeException: invalid escape at terminal index 1 in "a\". Use \\ for literal \. ``` Referencing SI-6476 because that has become the magnet ticket for "escape processing under string interpolation, huh?" This doesn't address `$"` and doesn't handle the more interesting parse error `s"a\"b"`. --- src/library/scala/StringContext.scala | 9 +++++++-- test/files/neg/t8266-invalid-interp.check | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/library/scala/StringContext.scala b/src/library/scala/StringContext.scala index c0468e8b02..0b08182102 100644 --- a/src/library/scala/StringContext.scala +++ b/src/library/scala/StringContext.scala @@ -175,8 +175,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/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 -- cgit v1.2.3 From b4f506749189b337bd25633d521b6b917108ee15 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 18 Jul 2014 22:34:01 -0700 Subject: SI-6476 Documentation And adjust the test. --- src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 2 ++ test/files/run/t6631.scala | 18 ------------------ test/junit/scala/StringContextTest.scala | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 18 deletions(-) delete mode 100644 test/files/run/t6631.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 883fd31dbc..90bc53721d 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1254,6 +1254,8 @@ 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 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/junit/scala/StringContextTest.scala b/test/junit/scala/StringContextTest.scala index 5abfe90cd1..bb0e8c4252 100644 --- a/test/junit/scala/StringContextTest.scala +++ b/test/junit/scala/StringContextTest.scala @@ -48,4 +48,18 @@ 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 + } } -- cgit v1.2.3 From ccbb84bb87a9dd828fa3aca1a1a14487e327ec5b Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 19 Jul 2014 19:26:39 -0700 Subject: SI-6476 Unitize procedures, readability Strictly trivial updates for readability. I used to prefer procedure syntax, but since it was scheduled for removal, I can't scan a def without an equals sign. --- .../scala/tools/nsc/ast/parser/Parsers.scala | 36 ++++++++++------------ .../scala/tools/nsc/ast/parser/Scanners.scala | 16 +++++----- 2 files changed, 24 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 90bc53721d..9350a39cbe 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -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,12 +224,12 @@ 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) @@ -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 @@ -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())) @@ -1260,7 +1256,7 @@ self => 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 diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 572497ac90..5ae9fd3f56 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -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) @@ -1068,21 +1070,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 } -- cgit v1.2.3 From eff9a584a68c65935c313a6f26d7d4f778a61703 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 19 Jul 2014 19:57:02 -0700 Subject: SI-6476 Unitize ALL the procedures! --- .../scala/tools/nsc/ast/parser/Parsers.scala | 18 +++++------ .../scala/tools/nsc/ast/parser/Scanners.scala | 35 +++++++++++----------- 2 files changed, 26 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 9350a39cbe..c90f0d0173 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) @@ -333,7 +333,7 @@ self => */ private var inScalaPackage = false private var currentPackage = "" - def resetPackage() { + def resetPackage(): Unit = { inScalaPackage = false currentPackage = "" } @@ -512,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) { @@ -715,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)) @@ -1264,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 5ae9fd3f56..0248806fff 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 { @@ -859,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) @@ -968,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 @@ -1054,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 == '\'') { @@ -1134,7 +1133,7 @@ trait Scanners extends ScannersCommon { /** Initialization method: read first char, then first token */ - def init() { + def init(): Unit = { nextChar() nextToken() } @@ -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 = () } } -- cgit v1.2.3 From ed9dfee181e56bb83afa0598523786bee5572068 Mon Sep 17 00:00:00 2001 From: Antoine Gourlay Date: Tue, 29 Jul 2014 18:12:42 +0200 Subject: SI-4563 friendlier behavior for Ctrl+D in the REPL Closing the REPL with Ctrl+D does not issue a newline, so the user's prompt displays on the same line as the `scala>` prompt. This is bad. We now force a newline before closing the interpreter, and display `:quit` while we're at it so that people know how to exit the REPL (since `exit` doesn't exist anymore). The tricky part was to only add a newline when the console is interrupted, and *not* when it is closed by a command (like `:quit`), since commands are processed after their text (including newline) has been sent to the console. --- src/compiler/scala/tools/nsc/Properties.scala | 1 + src/repl/scala/tools/nsc/interpreter/ILoop.scala | 9 ++++++++- test/files/jvm/interpreter.check | 2 +- test/files/jvm/throws-annot-from-java.check | 2 +- test/files/jvm/xml05.check | 2 +- test/files/run/class-symbol-contravariant.check | 2 +- test/files/run/constant-type.check | 2 +- test/files/run/constrained-types.check | 2 +- test/files/run/kind-repl-command.check | 2 +- test/files/run/lub-visibility.check | 2 +- test/files/run/macro-bundle-repl.check | 2 +- test/files/run/macro-repl-basic.check | 2 +- test/files/run/macro-repl-dontexpand.check | 2 +- test/files/run/macro-system-properties.check | 2 +- test/files/run/reflection-equality.check | 2 +- test/files/run/reflection-magicsymbols-repl.check | 2 +- test/files/run/reflection-repl-classes.check | 2 +- test/files/run/reflection-repl-elementary.check | 2 +- test/files/run/reify-repl-fail-gracefully.check | 2 +- test/files/run/reify_newimpl_22.check | 2 +- test/files/run/reify_newimpl_23.check | 2 +- test/files/run/reify_newimpl_25.check | 2 +- test/files/run/reify_newimpl_26.check | 2 +- test/files/run/reify_newimpl_35.check | 2 +- test/files/run/repl-assign.check | 2 +- test/files/run/repl-bare-expr.check | 2 +- test/files/run/repl-colon-type.check | 2 +- test/files/run/repl-empty-package.check | 2 +- test/files/run/repl-javap-app.check | 6 +++--- test/files/run/repl-out-dir.check | 2 +- test/files/run/repl-parens.check | 2 +- test/files/run/repl-paste-2.check | 2 +- test/files/run/repl-paste-3.check | 2 +- test/files/run/repl-paste-4.scala | 2 +- test/files/run/repl-paste-raw.scala | 2 +- test/files/run/repl-paste.check | 2 +- test/files/run/repl-power.check | 2 +- test/files/run/repl-reset.check | 2 +- test/files/run/repl-save.scala | 2 +- test/files/run/repl-term-macros.check | 2 +- test/files/run/repl-transcript.check | 2 +- test/files/run/repl-trim-stack-trace.scala | 2 +- test/files/run/repl-type-verbose.check | 2 +- test/files/run/t3376.check | 2 +- test/files/run/t4025.check | 2 +- test/files/run/t4172.check | 2 +- test/files/run/t4216.check | 2 +- test/files/run/t4285.check | 2 +- test/files/run/t4542.check | 2 +- test/files/run/t4594-repl-settings.scala | 2 +- test/files/run/t4671.check | 2 +- test/files/run/t4710.check | 2 +- test/files/run/t5072.check | 2 +- test/files/run/t5256d.check | 2 +- test/files/run/t5535.check | 2 +- test/files/run/t5537.check | 2 +- test/files/run/t5583.check | 2 +- test/files/run/t5655.check | 2 +- test/files/run/t5789.check | 2 +- test/files/run/t6086-repl.check | 2 +- test/files/run/t6146b.check | 2 +- test/files/run/t6187.check | 2 +- test/files/run/t6273.check | 2 +- test/files/run/t6320.check | 2 +- test/files/run/t6329_repl.check | 2 +- test/files/run/t6329_repl_bug.check | 2 +- test/files/run/t6381.check | 2 +- test/files/run/t6434.check | 2 +- test/files/run/t6439.check | 2 +- test/files/run/t6507.check | 2 +- test/files/run/t6549.check | 2 +- test/files/run/t6937.check | 2 +- test/files/run/t7185.check | 2 +- test/files/run/t7319.check | 2 +- test/files/run/t7482a.check | 2 +- test/files/run/t7634.check | 2 +- test/files/run/t7747-repl.check | 2 +- test/files/run/t7801.check | 2 +- test/files/run/t7805-repl-i.check | 2 +- test/files/run/tpeCache-tyconCache.check | 2 +- 80 files changed, 89 insertions(+), 81 deletions(-) (limited to 'src') 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/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/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> res0: scala.xml.Elem = -scala> +scala> :quit 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(: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 = 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 = -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 = -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/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 = 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 -- cgit v1.2.3 From bb2c24646eb8001d0efeff354c50127e47716ec8 Mon Sep 17 00:00:00 2001 From: Antoine Gourlay Date: Thu, 31 Jul 2014 18:31:15 +0200 Subject: a few missing deprecations in proxy collections. SeqProxy was properly deprecated, so were the CollProxyLike classes, and the ones in collection.immutable, but these four somehow survived the Big Proxy Deprecation (tm). --- src/library/scala/collection/IterableProxy.scala | 1 + src/library/scala/collection/MapProxy.scala | 1 + src/library/scala/collection/SetProxy.scala | 1 + src/library/scala/collection/TraversableProxy.scala | 1 + 4 files changed, 4 insertions(+) (limited to 'src') 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]] -- cgit v1.2.3 From e131424b45881ac8446235f255c8f637602857ac Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Sun, 27 Jul 2014 15:24:46 +0200 Subject: Work around weird AbstractMethodError Could not figure it out, so resorting to brain dumping. something with private classes and defaults, the default getter will be in the private companion module, but there's no accessor to get the module??? maybe something related to make non-private of the accessor? while running the repl or, e.g., the test presentation/ide-t1000567 AbstractMethodError: scala.tools.nsc.interactive.Global$$anon$5.scala$tools$nsc$typechecker$Contexts$$BufferingReporter()Lscala/tools/nsc/typechecker/Contexts$BufferingReporter$; at scala.tools.nsc.typechecker.Contexts$Context.makeSilent(Contexts.scala:518) in progress minimal repro: ``` package a class Reporter class Global { lazy val analyzer = new { val global: Global.this.type = Global.this } with Analyzer } trait Analyzer extends AnyRef with Contexts { val global : Global } trait Contexts { self: Analyzer => // the accessor to get to ContextReporter's companion object // to get the default for x is not emitted for some reason // the expected accessor is the non-private version // `def scala$tools$nsc$typechecker$Contexts$$BufferingReporter: scala.tools.nsc.typechecker.Contexts#BufferingReporter$` // (as seen in the AbstractMethodError's message) abstract class ContextReporter(x: Any = null) extends Reporter private class ThrowingReporter extends ContextReporter class Context(a: Any, private[this] var _reporter: ContextReporter = new ThrowingReporter) { def reporter = _reporter } object NoContext extends Context(null) } package b trait ReplGlobal extends a.Global { // this anon class corresponds to scala.tools.nsc.interactive.Global$$anon$5 in the above AbstractMethodError override lazy val analyzer = new { val global: ReplGlobal.this.type = ReplGlobal.this } with a.Analyzer { } } ``` --- src/compiler/scala/tools/nsc/typechecker/Contexts.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index fd9d1de88e..6573102e86 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1312,13 +1312,13 @@ trait Contexts { self: Analyzer => final def clearAllWarnings(): Unit = warningBuffer.clear() } - private class ImmediateReporter(_errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends ContextReporter(_errorBuffer, _warningBuffer) { + 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 class BufferingReporter(_errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends ContextReporter(_errorBuffer, _warningBuffer) { + 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 @@ -1337,12 +1337,12 @@ trait Contexts { self: Analyzer => * * TODO: get rid of it, use ImmediateReporter and a check for reporter.hasErrors where necessary */ - private class ThrowingReporter extends ContextReporter { + 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 class CheckingReporter extends ContextReporter { + private[typechecker] class CheckingReporter extends ContextReporter { protected def handleError(pos: Position, msg: String): Unit = onTreeCheckerError(pos, msg) } -- cgit v1.2.3 From 3a4c6db9fe8c06b78f907f182e80e908c653cf89 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Wed, 23 Jul 2014 23:30:52 +0200 Subject: Towards more privacy for _reporter. Move code that manipulates the error buffers / reporters into combinators in Context/ContextReporter. Eventually, would like to statically know when we're in silent mode, and only then use buffering (push buffering code down to BufferingReporter). Simplify TryTwice; avoid capturing mutable var in closure. Changed inSilentMode to no longer check `&& !reporter.hasErrors`; disassembling optimized code showed that this was keeping the inliner from inlining this method. Introduce a couple more combinators: - withFreshErrorBuffer - propagatingErrorsTo - propagateImplicitTypeErrorsTo --- .../scala/tools/nsc/typechecker/Contexts.scala | 180 +++++++++++++-------- .../scala/tools/nsc/typechecker/Implicits.scala | 11 +- .../scala/tools/nsc/typechecker/Infer.scala | 14 +- .../scala/tools/nsc/typechecker/Typers.scala | 33 +--- 4 files changed, 130 insertions(+), 108 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 6573102e86..a79f162140 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -182,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 @@ -325,59 +326,50 @@ trait Contexts { self: Analyzer => // // the reporter for this context - // TODO: start off with ImmediateReporter - private var _reporter: ContextReporter = new ThrowingReporter - - def reporter = _reporter + def reporter: ContextReporter = _reporter // if set, errors will not be reporter/thrown - def bufferErrors = reporter.isBuffering - def reportErrors = !bufferErrors + def bufferErrors = reporter.isBuffering + def reportErrors = !bufferErrors // whether to *report* (which is separate from buffering/throwing) ambiguity errors def ambiguousErrors = this(AmbiguousErrors) private def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report - /** - * Try inference twice, once without views and once with views, + * Try inference twice: once without views and once with views, * unless views are already disabled. */ abstract class TryTwice { def tryOnce(isLastTry: Boolean): Unit - def apply(): Unit = { - var doLastTry = !implicitsEnabled - - // do first try if implicits are enabled - if (implicitsEnabled) { - val savedReporter = _reporter - val savedContextMode = contextMode - - _reporter = new BufferingReporter - setAmbiguousErrors(false) // this means ambiguous errors will not be force-fed to the reporter - - // 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. - try { - withImplicitsDisabled(tryOnce(false)) - doLastTry = reporter.hasErrors - } catch { - case ex: CyclicReference => throw ex - case ex: TypeError => doLastTry = true // recoverable cyclic references? - } finally { - contextMode = savedContextMode - _reporter = savedReporter - } - } + 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) + if (doLastTry) + tryOnce(true) } } @@ -385,7 +377,7 @@ trait Contexts { self: Analyzer => // 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 @@ -419,17 +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 = { - val savedReporter = _reporter - withMode() { // TODO: rework -- withMode with no arguments to restore the mode mutated by `setBufferErrors` (no longer mutated!) - _reporter = reporter.makeBuffering - setAmbiguousErrors(false) - try expr && !reporter.hasErrors - finally { - _reporter = savedReporter - _reporter.clearAll() // TODO: ??? - } + val savedContextMode = contextMode + val savedReporter = reporter + + setAmbiguousErrors(false) + _reporter = new BufferingReporter + + try expr + finally { + contextMode = savedContextMode + _reporter = savedReporter } } @@ -445,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 @@ -468,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.diagUsedDefaults = diagUsedDefaults c.openImplicits = openImplicits c.contextMode = contextMode // note: ConstructorSuffix, a bit within `mode`, is conditionally overwritten below. - c._reporter = reporter // Fields that may take on a different value in the child c.prefix = prefixInChild @@ -510,22 +503,19 @@ trait Contexts { self: Analyzer => 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.setAmbiguousErrors(reportAmbiguousErrors) // A fresh buffer so as not to leak errors/warnings into `this`. - c._reporter = new BufferingReporter + val c = make(newtree, reporter = new BufferingReporter) + c.setAmbiguousErrors(reportAmbiguousErrors) c } def makeNonSilent(newtree: Tree): Context = { - val c = make(newtree) - c._reporter = reporter.makeImmediate + val c = make(newtree, reporter = reporter.makeImmediate) c.setAmbiguousErrors(true) c } @@ -549,9 +539,10 @@ 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._reporter = _reporter // caught by neg/t3649 TODO: make sure _reporter is propagated wherever contextMode is argContext.inSelfSuperCall = true def enterElems(c: Context) { def enterLocalElems(e: ScopeEntry) { @@ -1230,7 +1221,17 @@ 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. */ + /** 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) @@ -1254,7 +1255,27 @@ trait Contexts { self: Analyzer => if (context.ambiguousErrors) reporter.error(err.errPos, addDiagString(err.errMsg)) // force reporting... see TODO above else handleSuppressedAmbiguous(err) - def ++=(errors: Traversable[AbsTypeError]): Unit = errorBuffer ++= errors + @inline final def withFreshErrorBuffer[T](expr: => T): T = { + val previousBuffer = _errorBuffer + _errorBuffer = newBuffer + val res = expr // expr will read _errorBuffer + _errorBuffer = previousBuffer + res + } + + @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 + } protected final def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = severity match { @@ -1265,6 +1286,10 @@ trait Contexts { self: Analyzer => 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 { @@ -1290,13 +1315,28 @@ trait Contexts { self: Analyzer => case _ => false } + def propagateImplicitTypeErrorsTo(target: ContextReporter) = { + errors foreach { + case err@(_: DivergentImplicitTypeError | _: AmbiguousImplicitTypeError) => + target.errorBuffer += err + case _ => + } + // debuglog("propagateImplicitTypeErrorsTo: " + errors) + } + 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 } - // TODO: deprecate everything below (related to buffering) and push down to BufferingReporter + final def emitWarnings() = if (_warningBuffer != null) { + _warningBuffer foreach { + case (pos, msg) => reporter.warning(pos, msg) + } + _warningBuffer = null + } + // [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. @@ -1307,9 +1347,13 @@ trait Contexts { self: Analyzer => final def warnings: immutable.Seq[Warning] = warningBuffer.toVector final def firstError: Option[AbsTypeError] = errorBuffer.headOption - final def clearAll(): Unit = { clearAllErrors(); clearAllWarnings() } - final def clearAllErrors(): Unit = errorBuffer.clear() - final def clearAllWarnings(): Unit = warningBuffer.clear() + // 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) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index e03877dbf9..d56f4a7ce8 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.reporter.hasErrors) { - context.reporter ++= (implicitSearchContext.reporter.errors.collect { - case dte: DivergentImplicitTypeError => dte - case ate: AmbiguousImplicitTypeError => ate - }) - debuglog("update buffer: " + implicitSearchContext.reporter.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), diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 535aaffbbf..fb7651ffd6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -1375,7 +1375,7 @@ trait Infer extends Checkable { // with and without views enabled, and bestForExpectedType will try again // with pt = WildcardType if it fails with pt != WildcardType. val c = context - class InferTwice extends c.TryTwice { + class InferMethodAlternativeTwice extends c.TryTwice { private[this] val OverloadedType(pre, alts) = tree.tpe private[this] var varargsStar = false private[this] val argtpes = argtpes0 mapConserve { @@ -1384,12 +1384,12 @@ trait Infer extends Checkable { } 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 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) - ) + 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 @@ -1405,7 +1405,7 @@ trait Infer extends Checkable { } } - (new InferTwice).apply() + (new InferMethodAlternativeTwice).apply() } /** Assign `tree` the type of all polymorphic alternatives diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index c557af88f2..8549d3dbbc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -464,24 +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)) - val errors = c.reporter.errors - if (errors.nonEmpty) { - c.reporter.clearAllErrors() - context.reporter ++= errors - } - res - } - - @inline - final def withSavedContext[T](c: Context)(f: => T) = { - val savedErrors = c.reporter.errors - c.reporter.clearAllErrors() - val res = f - c.reporter ++= 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. @@ -692,19 +676,16 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper SilentTypeError(context1.reporter.errors: _*) } else { // If we have a successful result, emit any warnings it created. - context1.reporter.warnings foreach { - case (pos, msg) => reporter.warning(pos, msg) - } - context1.reporter.clearAllWarnings() + context1.reporter.emitWarnings() SilentResultValue(result) } } else { assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer") - withSavedContext(context){ + + context.reporter.withFreshErrorBuffer { val res = op(this) - val errorsToReport = context.reporter.errors - context.reporter.clearAllErrors() - if (errorsToReport.isEmpty) SilentResultValue(res) else SilentTypeError(errorsToReport.head) + if (!context.reporter.hasErrors) SilentResultValue(res) + else SilentTypeError(context.reporter.firstError.get) } } } catch { -- cgit v1.2.3 From e21096f5ccbb27587bedbf238b18661d158995e1 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 4 Aug 2014 17:24:45 +0200 Subject: Separate statistics from functional code; optimize. --- .../scala/tools/nsc/typechecker/Implicits.scala | 77 +++++++++++++--------- 1 file changed, 45 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index d56f4a7ce8..b85c8e6d42 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1342,36 +1342,47 @@ 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 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 failstart = if (Statistics.canEnable) Statistics.startTimer(oftypeFailNanos) else null - val succstart = if (Statistics.canEnable) Statistics.startTimer(oftypeSucceedNanos) else null - 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) { + if (result.isFailure) context.reporter ++= previousErrs - if (Statistics.canEnable) Statistics.stopTimer(oftypeFailNanos, failstart) - } else { - if (Statistics.canEnable) Statistics.stopTimer(oftypeSucceedNanos, succstart) - if (Statistics.canEnable) Statistics.incCounter(oftypeImplicitHits) + + if (stats) { + if (result.isFailure) Statistics.stopTimer(oftypeFailNanos, failstart) + else { + Statistics.stopTimer(oftypeSucceedNanos, succstart) + Statistics.incCounter(oftypeImplicitHits) + } } } if (result.isSuccess && isView) { @@ -1384,12 +1395,13 @@ trait Implicits { } 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)) { @@ -1397,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 } @@ -1420,19 +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.reporter.clearAllErrors() - 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 { -- cgit v1.2.3 From ca9f64dfa20fb8d5a4c022f6b9fd2f18a8ea1028 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 4 Aug 2014 17:25:45 +0200 Subject: Encapsulate creating SilentResultValue/SilentTypeError. Do it consistently... --- .../scala/tools/nsc/typechecker/Typers.scala | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 8549d3dbbc..0621fbefe4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -658,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 || @@ -671,21 +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.reporter.hasErrors) { - stopStats() - SilentTypeError(context1.reporter.errors: _*) - } else { - // If we have a successful result, emit any warnings it created. + + // If we have a successful result, emit any warnings it created. + if (!context1.reporter.hasErrors) context1.reporter.emitWarnings() - SilentResultValue(result) - } + + wrapResult(context1.reporter, result) } else { assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer") context.reporter.withFreshErrorBuffer { - val res = op(this) - if (!context.reporter.hasErrors) SilentResultValue(res) - else SilentTypeError(context.reporter.firstError.get) + wrapResult(context.reporter, op(this)) } } } catch { -- cgit v1.2.3 From cd2394ee43220e885bfa92f3ce34f41366bb4704 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 8 Aug 2014 21:36:27 -0700 Subject: SI-8787 If you love nulls, so does Regex Regex is robust when unapplying null. A null never matches. --- src/library/scala/util/matching/Regex.scala | 21 +++++++++++---------- test/junit/scala/util/matching/RegexTest.scala | 17 +++++++++++++++++ 2 files changed, 28 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/library/scala/util/matching/Regex.scala b/src/library/scala/util/matching/Regex.scala index f35ea566ba..3d77105a1e 100644 --- a/src/library/scala/util/matching/Regex.scala +++ b/src/library/scala/util/matching/Regex.scala @@ -101,7 +101,7 @@ import java.util.regex.{ Pattern, Matcher } * 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" * }}} @@ -154,7 +154,7 @@ 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 + * 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. */ @@ -178,7 +178,7 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * * This method attempts to match the entire input by default; to find the next * matching subsequence, use an unanchored Regex. - + * * For example: * * {{{ @@ -202,10 +202,12 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * @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]]. @@ -253,7 +255,7 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * 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) @@ -297,7 +299,6 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends */ 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]]. * @@ -588,7 +589,7 @@ object Regex { } - /** Provides information about a succesful match. + /** Provides information about a successful match. */ class Match(val source: CharSequence, private[matching] val matcher: Matcher, 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) + } } -- cgit v1.2.3 From 0e26910372d349c6ff7bbaa17fc8fe0bf568c5fe Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 8 Aug 2014 22:19:59 -0700 Subject: SI-8787 Update doc for Regex Simplify the exposition and update the examples. Emphasize pattern matching, especially using `r.unanchored` instead of `for (r(x,y,z) <- r findFirstIn text)`. Certain details are moved to method docs. It would be nice to fix matching package doc, but the doc must attach to the package object, it seems. Introducing a package object is not binary-compatible. Includes a doc line edit on 2.12, anticipating the merge. --- src/library/scala/util/matching/Regex.scala | 309 ++++++++++++++++------------ 1 file changed, 175 insertions(+), 134 deletions(-) (limited to 'src') diff --git a/src/library/scala/util/matching/Regex.scala b/src/library/scala/util/matching/Regex.scala index 3d77105a1e..3fdd256b86 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,109 @@ * 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. + * + * 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. * - * 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). + * 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. * - * 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. + * The canonical way to create a `Regex` is by using the method `r`, provided + * implicitly for strings: * - * 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 (`\`). + * {{{ + * val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r + * }}} * - * Using the constructor directly, on the other hand, makes - * it possible to declare names for subgroups in the pattern. + * 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"""`. * - * For example, both declarations below generate the same regex, but the second - * one associate names with the subgroups. + * 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. - * - * 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: + * To check only whether the `Regex` matches, ignoring any groups: * * {{{ - * val msg = "I love Scala" + * "2004-01-20" match { + * case date(_*) => "It's a date!" + * } + * }}} * - * // 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 + * But `findAllIn` returns a special iterator of strings that can be queried for the `MatchData` + * of the last match: * - * 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" + * {{{ + * 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: + * Text replacement can be performed unconditionally or as a function of the current match: * * {{{ - * def hasDate(text: String): Boolean = (dateP1 findFirstIn text).nonEmpty - * def printLinesWithDates(lines: Traversable[String]) { - * lines foreach { line => - * dateP1 findFirstIn line foreach { _ => println(line) } - * } - * } + * 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" }) * }}} * - * 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: + * 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 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 docSpree = """2011(?:-\d{2}){2}""".r + * val docView = date replaceAllIn (dates, _ match { + * case docSpree() => "Historic doc spree!" + * case _ => "Something else happened" + * }) * }}} * - * You can use special pattern syntax constructs like `(?idmsux-idmsux)`¹ to switch - * various regex compilation options like `CASE_INSENSITIVE` or `UNICODE_CASE`. - * - * @note ¹ A detailed description is available in [[java.util.regex.Pattern]]. * @see [[java.util.regex.Pattern]] * * @author Thibaud Hottelier @@ -155,8 +146,7 @@ import java.util.regex.{ Pattern, Matcher } * 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. + * dollar sign. Use `Regex.quoteReplacement` to escape these characters. */ @SerialVersionUID(-2094783597747625537L) class Regex private[matching](val pattern: Pattern, groupNames: String*) extends Serializable { @@ -164,20 +154,33 @@ 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: * @@ -188,6 +191,10 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * case _ => false * } * val p2 = "a(b*)c".r + * val p2Matches = "abbbc" match { + * case p2(_*) => true + * case _ => false + * } * val numberOfB = "abbbc" match { * case p2(b) => Some(b.length) * case _ => None @@ -211,6 +218,7 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends } /** 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. * @@ -249,8 +257,11 @@ 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. */ @@ -276,25 +287,43 @@ 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) + * }}} * - * 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. + * To return overlapping matches, it is possible to formulate a regular expression + * 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 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) @@ -317,8 +346,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. @@ -329,13 +358,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. @@ -346,30 +373,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. @@ -403,7 +428,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 @@ -426,10 +451,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) * }}} * @@ -470,17 +495,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" * } * }}} * @@ -495,6 +528,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 @@ -510,30 +547,34 @@ 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 @@ -544,7 +585,7 @@ object Regex { 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, @@ -636,15 +677,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 { -- cgit v1.2.3 From 2e3583b3644f3acd67933b54b9b61e6d60e9b6bb Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 2 Aug 2014 14:24:54 -0700 Subject: SI-8512 Infer a type for f"$args" The f-interpolator gets a type param that better be Any to avoid unfortunate widenings. Hey, it worked! Unfortunately, when `Any` is inferred, `-Xlint:infer-any` takes notice. This is probably a greater problem for the f-interpolator than for quasiquotes, which are a more specialized tool. --- src/library/scala/StringContext.scala | 2 +- test/files/neg/t7848-interp-warn.flags | 2 +- test/junit/scala/StringContextTest.scala | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/library/scala/StringContext.scala b/src/library/scala/StringContext.scala index 2d2601c6fb..20a328ec8f 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 { 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/junit/scala/StringContextTest.scala b/test/junit/scala/StringContextTest.scala index bb0e8c4252..608b82bd96 100644 --- a/test/junit/scala/StringContextTest.scala +++ b/test/junit/scala/StringContextTest.scala @@ -62,4 +62,17 @@ class StringContextTest { //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) + } } -- cgit v1.2.3 From 606a553e12dbc1bda25939dbda3e7fcaaaa678b9 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 5 Aug 2014 23:03:41 -0700 Subject: SI-8512 Infer Any for the q Avoid the widening bug for q. This resolution also suffers from the inference of Any, which can trigger a warning and an anxiety attack. But that's still better than doing the wrong thing. Right? --- src/reflect/scala/reflect/api/Quasiquotes.scala | 2 +- test/files/pos/t8013.flags | 2 +- test/junit/scala/reflect/QTest.scala | 23 +++++++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 test/junit/scala/reflect/QTest.scala (limited to 'src') 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/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/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) + } +} -- cgit v1.2.3 From f98c53cb03f800b3d790f3866ab90f827fd131f5 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 12 Aug 2014 09:16:26 -0700 Subject: SI-8787 Addressing feedback, additional periods. --- src/library/scala/util/matching/Regex.scala | 96 +++++++++++++++++++---------- 1 file changed, 65 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/library/scala/util/matching/Regex.scala b/src/library/scala/util/matching/Regex.scala index 3fdd256b86..5c4e706dc1 100644 --- a/src/library/scala/util/matching/Regex.scala +++ b/src/library/scala/util/matching/Regex.scala @@ -62,7 +62,8 @@ import java.util.regex.{ Pattern, Matcher } * } * }}} * - * To check only whether the `Regex` matches, ignoring any groups: + * To check only whether the `Regex` matches, ignoring any groups, + * use a sequence wildcard: * * {{{ * "2004-01-20" match { @@ -70,6 +71,16 @@ import java.util.regex.{ Pattern, Matcher } * } * }}} * + * 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: + * + * {{{ + * "2004-01-20" match { + * case date(year, _*) => s"$year was a good year for PLs." + * } + * }}} + * * In a pattern match, `Regex` normally matches the entire input. * However, an unanchored `Regex` finds the pattern anywhere * in the input. @@ -109,6 +120,13 @@ import java.util.regex.{ Pattern, Matcher } * val oldies = mi filter (_ => (mi group 1).toInt < 1960) map (s => s"$s: An oldie but goodie.") * }}} * + * Note that `findAllIn` finds matches that don't overlap. (See [[findAllIn]] for more examples.) + * + * {{{ + * val num = """(\d+)""".r + * val all = (num findAllIn "123").toList // List("123"), not List("123", "23", "3") + * }}} + * * Text replacement can be performed unconditionally or as a function of the current match: * * {{{ @@ -187,23 +205,36 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * {{{ * 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 + * 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 @@ -309,7 +340,7 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * }}} * * To return overlapping matches, it is possible to formulate a regular expression - * that does not consume the overlapping region. + * with lookahead (`?=`) that does not consume the overlapping region. * * {{{ * val madhatter = "(h)(?=(at[^a]+))".r @@ -580,7 +611,8 @@ object Regex { 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 @@ -589,32 +621,36 @@ object Regex { 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 @@ -625,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 successful 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] = @@ -650,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: @@ -714,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 @@ -723,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 -- cgit v1.2.3 From a16a635b647bb1cdb0759ab4ba1e794ec56d8081 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 15 Aug 2014 16:59:59 +1000 Subject: SI-8793 Fix patmat regression with extractors, existentials In the same vein as SI-8128 / 3e9e2c65a, revert to the 2.10.x style of determining the types of the product elements of an extractor when using `TupleN`. I believe we can discard the special casing for Option/Tuple/Seq altogether with judicious application of `repackExistential` in `unapplyMethodTypes`. That ought to also fix fix SI-8149. But I'll target that work at 2.12.x. --- src/reflect/scala/reflect/internal/Definitions.scala | 10 ++++++++-- test/files/pos/t8793.scala | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 test/files/pos/t8793.scala (limited to 'src') 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/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) + } +} -- cgit v1.2.3 From 6f54b1dfede5d9b375eab029ef682e1a61da9a70 Mon Sep 17 00:00:00 2001 From: Antoine Gourlay Date: Thu, 21 Aug 2014 11:17:46 +0200 Subject: SI-1264 fsc compiler output should go to stderr, like scalac This properly sends the compiler output of `scala -e`, scala worksheets, ... to sdterr, just like scalac does. Before: $ scala -e 'foo' > /dev/null $ After: $ scala -e 'foo' > /dev/null /tmp/scalacmd8514641341855028538.scala:1: error: not found: value foo foo ^ one error found $ --- src/compiler/scala/tools/nsc/CompileSocket.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') 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() -- cgit v1.2.3 From 066d1026d9b5783d3358bc4389b2371cee00ed8c Mon Sep 17 00:00:00 2001 From: Antoine Gourlay Date: Thu, 21 Aug 2014 11:36:44 +0200 Subject: SI-5227 make fsc notify its client upon compiler crash Fsc shoudln't just write to its own stderr when a major compiler crash happens, it should also send an error to the client (`scala -e` for example). Otherwise the client thinks everything went fine (silence == success) and tries to run something, crashes too, and displays only its own error, not the original one. --- src/compiler/scala/tools/nsc/CompileServer.scala | 1 + 1 file changed, 1 insertion(+) (limited to 'src') 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 } -- cgit v1.2.3