The compiler option for this is -Yissue-debug
. It outputs a stack trace in 2.10 when an error is issued.
The code supporting it was removed in 2.11 during the refactor of reporting, but the option is still valid. (I restored it at some point because, in fact, it's the quickest way to see what is emitting an error; but apparently that PR died on the vine and disappeared. Probably a victim of push -f
.)
In 2.12, you can supply a custom reporter that does just about the same thing. They claim that they will augment the reporter API with access to context, so you could perhaps directly query the current phase, inspect trees, and so on.
Here's an example of the situation you describe:
class C { def f: Int ; def g: Int = "" ; def h = "\000" }
There are three errors, but only one is reported at a time because they are emitted in different compiler phases.
To clarify the question, various phases besides "typer" can create and typecheck trees, and additionally can enforce well-typedness even after trees are typed. (There are also other kinds of errors, such as "unable to write the output file.")
For C
, the parser emits an error (under -Xfuture
) for the deprecated octal syntax, the typer reports the type mismatch in g
, and the grab bag refchecks phase checks the declared but undefined (empty) member f
. You would normally wind up fixing one error at a time. If the parser error is emitted as a warning, then the warning would be suppressed until the errors were fixed, so that would be the last to pop up instead of the first.
Here is a sample reporter that tries to do more than output huge stack traces.
package myrep
import scala.tools.nsc.Settings
import scala.tools.nsc.reporters.ConsoleReporter
import scala.reflect.internal.util._
class DebugReporter(ss: Settings) extends ConsoleReporter(ss) {
override def warning(pos: Position, msg: String) = debug {
super.warning(pos, msg)
}
override def error(pos: Position, msg: String) = debug {
super.error(pos, msg)
}
// let it ride
override def hasErrors = false
private def debug(body: => Unit): Unit = {
val pkgs = Set("nsc.ast.parser", "nsc.typechecker", "nsc.transform")
def compilerPackages(e: StackTraceElement): Boolean = pkgs exists (e.getClassName contains _)
def classname(e: StackTraceElement): String = (e.getClassName split """\.""").last
if (ss.Yissuedebug) echo {
((new Throwable).getStackTrace filter compilerPackages map classname).distinct mkString ("Issued from: ", ",", "\n")
}
body
}
}
It lies about having no errors so that the compiler won't abort early.
It would be invoked this way, with the reporter class on the "tool class path":
scalacm -toolcp repdir -Xreporter myrep.DebugReporter -Yissue-debug -deprecation errs.scala
where
$ scalacm -version
Scala compiler version 2.12.0-M2 -- Copyright 2002-2013, LAMP/EPFL
Sample output:
Issued from: Scanners$UnitScanner,Scanners$Scanner,Parsers$Parser,Parsers$Parser$$anonfun$templateStat$1,Parsers$Parser$$anonfun$topStat$1,Parsers$SourceFileParser,Parsers$UnitParser,SyntaxAnalyzer,SyntaxAnalyzer$ParserPhase
errs.scala:4: warning: Octal escape literals are deprecated, use \u0000 instead.
class C { def f: Int ; def g: Int = "" ; def h = "\000" }
^
Issued from: Contexts$ImmediateReporter,Contexts$ContextReporter,Contexts$Context,ContextErrors$ErrorUtils$,ContextErrors$TyperContextErrors$TyperErrorGen$,Typers$Typer,Analyzer$typerFactory$$anon$3
errs.scala:4: error: type mismatch;
found : String("")
required: Int
class C { def f: Int ; def g: Int = "" ; def h = "\000" }
^
Issued from: RefChecks$RefCheckTransformer,Transform$Phase
errs.scala:4: error: class C needs to be abstract, since method f is not defined
class C { def f: Int ; def g: Int = "" ; def h = "\000" }
^
one warning found
two errors found