From c1aaf1fc7ad3d76bb5376d796577e0effdd70bf4 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 9 Aug 2011 17:35:29 +0000 Subject: Don't discard deprecation/unchecked warnings re... Don't discard deprecation/unchecked warnings regardless of settings. Changed warnings code to accumulate them rather than thoughtlessly discarding them and issuing its well-known taunt. In the repl you can take advantage of this with the :warnings command, which will show the suppressed warnings from the last line which had any. Be advised that at the moment it has some issues: unchecked warnings aren't making it out, and near repl startup neither are deprecation warnings, so don't open a bunch of tickets please. References SI-4594, no review. --- .../scala/tools/nsc/CompilationUnits.scala | 4 +- src/compiler/scala/tools/nsc/Global.scala | 8 +- .../scala/tools/nsc/interpreter/ILoop.scala | 6 +- .../scala/tools/nsc/interpreter/ILoopInit.scala | 8 ++ .../scala/tools/nsc/interpreter/IMain.scala | 89 ++++++++++++++++++---- test/files/run/repl-suppressed-warnings.check | 58 ++++++++++++++ test/files/run/repl-suppressed-warnings.scala | 26 +++++++ 7 files changed, 178 insertions(+), 21 deletions(-) create mode 100644 test/files/run/repl-suppressed-warnings.check create mode 100644 test/files/run/repl-suppressed-warnings.scala diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index f5e32fbb09..470207fd35 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -83,11 +83,11 @@ trait CompilationUnits { self: Global => def deprecationWarning(pos: Position, msg: String) = if (opt.deprecation) warning(pos, msg) - else currentRun.deprecationWarnings += 1 + else currentRun.deprecationWarnings ::= ((pos, msg)) def uncheckedWarning(pos: Position, msg: String) = if (opt.unchecked) warning(pos, msg) - else currentRun.uncheckedWarnings += 1 + else currentRun.uncheckedWarnings ::= ((pos, msg)) def incompleteInputError(pos: Position, msg:String) = reporter.incompleteInputError(pos, msg) diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 83f4ce438f..57b5ed45ea 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -772,8 +772,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb var currentUnit: CompilationUnit = _ /** Counts for certain classes of warnings during this run. */ - var deprecationWarnings: Int = 0 - var uncheckedWarnings: Int = 0 + var deprecationWarnings: List[(Position, String)] = Nil + var uncheckedWarnings: List[(Position, String)] = Nil /** Progress tracking. Measured in "progress units" which are 1 per * compilation unit per phase completed. @@ -954,8 +954,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb if (option.isDefault && count > 0) warning("there were %d %s warnings; re-run with %s for details".format(count, what, option.name)) ) - warn(deprecationWarnings, "deprecation", settings.deprecation) - warn(uncheckedWarnings, "unchecked", settings.unchecked) + warn(deprecationWarnings.size, "deprecation", settings.deprecation) + warn(uncheckedWarnings.size, "unchecked", settings.unchecked) // todo: migrationWarnings } } diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala index 2ff8bb343f..457a34f9dc 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala @@ -225,7 +225,8 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) nullary("replay", "reset execution and replay all previous commands", replay), shCommand, nullary("silent", "disable/enable automatic printing of results", verbosity), - cmd("type", "", "display the type of an expression without evaluating it", typeCommand) + cmd("type", "", "display the type of an expression without evaluating it", typeCommand), + nullary("warnings", "show the suppressed warnings from the most recent line which had any", warningsCommand) ) /** Power user commands */ @@ -393,6 +394,9 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) case _ => "" // the error message was already printed } } + private def warningsCommand(): Result = { + intp.lastWarnings foreach { case (pos, msg) => intp.reporter.warning(pos, msg) } + } private def javapCommand(line: String): Result = { if (javap == null) diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala b/src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala index d711428d22..a9c092861f 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala @@ -6,6 +6,7 @@ package scala.tools.nsc package interpreter +import util.Position import scala.tools.util.SignalManager import scala.util.control.Exception.ignoring @@ -98,12 +99,19 @@ trait ILoopInit { if (!initIsComplete) withLock { while (!initIsComplete) initLoopCondition.await() } } + // private def warningsThunks = List( + // () => intp.bind("lastWarnings", "" + manifest[List[(Position, String)]], intp.lastWarnings _), + // ) + protected def postInitThunks = List[Option[() => Unit]]( Some(intp.setContextClassLoader _), if (isReplPower) Some(() => enablePowerMode(true)) else None, // do this last to avoid annoying uninterruptible startups Some(installSigIntHandler _) ).flatten + // ++ ( + // warningsThunks + // ) // called once after init condition is signalled protected def postInitialization() { postInitThunks foreach (f => addThunk(f())) diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index 3abcb3e416..b516e137e8 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -60,9 +60,33 @@ import IMain._ * @author Moez A. Abdel-Gawad * @author Lex Spoon */ -class IMain(val settings: Settings, protected val out: JPrintWriter) extends Imports { +class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends Imports { imain => + private var currentSettings: Settings = initialSettings + def settings = currentSettings + def savingSettings[T](fn: Settings => Unit)(body: => T): T = { + val saved = currentSettings + currentSettings = saved.copy() + fn(currentSettings) + try body + finally currentSettings = saved + } + def mostRecentLine = prevRequestList match { + case Nil => "" + case req :: _ => req.originalLine + } + def rerunWith(names: String*) = { + savingSettings((ss: Settings) => { + import ss._ + names flatMap lookupSetting foreach { + case s: BooleanSetting => s.value = true + case _ => () + } + })(interpret(mostRecentLine)) + } + def rerunForWarnings = rerunWith("-deprecation", "-unchecked", "-Xlint") + /** construct an interpreter that reports to Console */ def this(settings: Settings) = this(settings, new NewLinePrintWriter(new ConsoleWriter, true)) def this() = this(new Settings()) @@ -408,14 +432,18 @@ class IMain(val settings: Settings, protected val out: JPrintWriter) extends Imp } } + def compileSourcesKeepingRun(sources: SourceFile*) = { + val run = new Run() + reporter.reset() + run compileSources sources.toList + (!reporter.hasErrors, run) + } + /** Compile an nsc SourceFile. Returns true if there are * no compilation errors, or false otherwise. */ - def compileSources(sources: SourceFile*): Boolean = { - reporter.reset() - new Run() compileSources sources.toList - !reporter.hasErrors - } + def compileSources(sources: SourceFile*): Boolean = + compileSourcesKeepingRun(sources: _*)._1 /** Compile a string. Returns true if there are no * compilation errors, or false otherwise. @@ -461,9 +489,11 @@ class IMain(val settings: Settings, protected val out: JPrintWriter) extends Imp case Some(trees) => trees } repltrace( - trees map { t => - t map { t0 => t0.getClass + " at " + safePos(t0, -1) + "\n" } - } mkString + trees map (t => + t map (t0 => + " " + safePos(t0, -1) + ": " + t0.shortClass + "\n" + ) mkString "" + ) mkString "\n" ) // If the last tree is a bare expression, pinpoint where it begins using the // AST node position and snap the line off there. Rewrite the code embodied @@ -747,6 +777,29 @@ class IMain(val settings: Settings, protected val out: JPrintWriter) extends Imp lineAfterTyper(sym.info member newTermName(name)) } } + /** We get a bunch of repeated warnings for reasons I haven't + * entirely figured out yet. For now, squash. + */ + private def removeDupWarnings(xs: List[(Position, String)]): List[(Position, String)] = { + if (xs.isEmpty) + return Nil + + val ((pos, msg)) :: rest = xs + val filtered = rest filter { case (pos0, msg0) => + (msg != msg0) || (pos.lineContent.trim != pos0.lineContent.trim) || { + // same messages and same line content after whitespace removal + // but we want to let through multiple warnings on the same line + // from the same run. The untrimmed line will be the same since + // there's no whitespace indenting blowing it. + (pos.lineContent == pos0.lineContent) + } + } + ((pos, msg)) :: removeDupWarnings(filtered) + } + def lastWarnings: List[(Position, String)] = ( + if (lastRun == null) Nil + else removeDupWarnings(lastRun.deprecationWarnings.reverse) ++ lastRun.uncheckedWarnings.reverse + ) private var lastRun: Run = _ private def evalMethod(name: String) = evalClass.getMethods filter (_.getName == name) match { case Array(method) => method @@ -754,10 +807,9 @@ class IMain(val settings: Settings, protected val out: JPrintWriter) extends Imp } private def compileAndSaveRun(label: String, code: String) = { showCodeIfDebugging(code) - reporter.reset() - lastRun = new Run() - lastRun.compileSources(List(new BatchSourceFile(label, packaged(code)))) - !reporter.hasErrors + val (success, run) = compileSourcesKeepingRun(new BatchSourceFile(label, packaged(code))) + lastRun = run + success } } @@ -880,7 +932,9 @@ class IMain(val settings: Settings, protected val out: JPrintWriter) extends Imp // compile the result-extraction object beSilentDuring { - lineRep compile ResultObjectSourceCode(handlers) + savingSettings(_.nowarn.value = true) { + lineRep compile ResultObjectSourceCode(handlers) + } } } } @@ -949,6 +1003,13 @@ class IMain(val settings: Settings, protected val out: JPrintWriter) extends Imp case _ => naming.mostRecentVar }) + def lastWarnings: List[(global.Position, String)] = ( + prevRequests.reverseIterator + map (_.lineRep.lastWarnings) + find (_.nonEmpty) + getOrElse Nil + ) + def requestForName(name: Name): Option[Request] = { assert(definedNameMap != null, "definedNameMap is null") definedNameMap get name diff --git a/test/files/run/repl-suppressed-warnings.check b/test/files/run/repl-suppressed-warnings.check new file mode 100644 index 0000000000..0c449f348c --- /dev/null +++ b/test/files/run/repl-suppressed-warnings.check @@ -0,0 +1,58 @@ +Type in expressions to have them evaluated. +Type :help for more information. + +scala> + +scala> + +scala> // "Is this thing on?" Not working on first couple + +scala> // commands, needs investigation. + +scala> 123 +res0: Int = 123 + +scala> 123 +res1: Int = 123 + +scala> 123 +res2: Int = 123 + +scala> + +scala> object o { + case class Bippy() + case class Dingus { + def f[T](xs: TraversableOnce[T]) = xs match { + case _: List[Int] => 1 + case _: Set[String] => 2 + case _ => xs.isInstanceOf[Iterator[Double]] + } + } + case class DingDangDoobie(ding: Int, dang: Int, doobie: Double) + case class Dongoo ; case class Heyooooo ; for (x <- 1 to 10 ; val y = x ; z = y) yield x +} +warning: there were 4 deprecation warnings; re-run with -deprecation for details +warning: there were 3 unchecked warnings; re-run with -unchecked for details +defined module o + +scala> :warnings +:3: warning: case classes without a parameter list have been deprecated; +use either case objects or case classes with `()' as parameter list. + case class Dingus { + ^ +:11: warning: case classes without a parameter list have been deprecated; +use either case objects or case classes with `()' as parameter list. + case class Dongoo ; case class Heyooooo ; for (x <- 1 to 10 ; val y = x ; z = y) yield x + ^ +:11: warning: case classes without a parameter list have been deprecated; +use either case objects or case classes with `()' as parameter list. + case class Dongoo ; case class Heyooooo ; for (x <- 1 to 10 ; val y = x ; z = y) yield x + ^ +:11: warning: for comprehension assignment without a `val' declaration is deprecated. + case class Dongoo ; case class Heyooooo ; for (x <- 1 to 10 ; val y = x ; z = y) yield x + ^ + +scala> + +scala> diff --git a/test/files/run/repl-suppressed-warnings.scala b/test/files/run/repl-suppressed-warnings.scala new file mode 100644 index 0000000000..5ecc16f34c --- /dev/null +++ b/test/files/run/repl-suppressed-warnings.scala @@ -0,0 +1,26 @@ +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + def code = """ + +// "Is this thing on?" Not working on first couple +// commands, needs investigation. +123 +123 +123 + +object o { + case class Bippy() + case class Dingus { + def f[T](xs: TraversableOnce[T]) = xs match { + case _: List[Int] => 1 + case _: Set[String] => 2 + case _ => xs.isInstanceOf[Iterator[Double]] + } + } + case class DingDangDoobie(ding: Int, dang: Int, doobie: Double) + case class Dongoo ; case class Heyooooo ; for (x <- 1 to 10 ; val y = x ; z = y) yield x +} +:warnings + """ +} -- cgit v1.2.3