Skip to content

Commit 4bbae6e

Browse files
committed
Merge pull request scala#4544 from som-snytt/topic/reporter
SI-9350 Command option -Xreporter
2 parents 5330319 + 421955c commit 4bbae6e

File tree

7 files changed

+70
-26
lines changed

7 files changed

+70
-26
lines changed
Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,51 @@
11
package scala
22
package tools.nsc
33

4-
import scala.tools.nsc.reporters.ConsoleReporter
4+
import scala.tools.nsc.reporters.{ ConsoleReporter, Reporter }
55
import Properties.{ versionMsg, residentPromptString }
66
import scala.reflect.internal.util.FakePos
77

88
abstract class Driver {
99

1010
val prompt = residentPromptString
1111

12-
var reporter: ConsoleReporter = _
12+
var reporter: Reporter = _
1313
protected var command: CompilerCommand = _
1414
protected var settings: Settings = _
1515

16+
/** Forward errors to the (current) reporter. */
1617
protected def scalacError(msg: String): Unit = {
1718
reporter.error(FakePos("scalac"), msg + "\n scalac -help gives more information")
1819
}
1920

21+
/** True to continue compilation. */
2022
protected def processSettingsHook(): Boolean = {
21-
if (settings.version) { reporter echo versionMsg ; false } else true
23+
if (settings.version) { reporter echo versionMsg ; false }
24+
else !reporter.hasErrors
2225
}
2326

2427
protected def newCompiler(): Global
2528

26-
protected def doCompile(compiler: Global) {
29+
protected def doCompile(compiler: Global): Unit = {
2730
if (command.files.isEmpty) {
2831
reporter.echo(command.usageMsg)
2932
reporter.echo(compiler.pluginOptionsHelp)
3033
} else {
3134
val run = new compiler.Run()
3235
run compile command.files
33-
reporter.printSummary()
36+
reporter.finish()
3437
}
3538
}
3639

37-
def process(args: Array[String]) {
40+
def process(args: Array[String]): Boolean = {
3841
val ss = new Settings(scalacError)
39-
reporter = new ConsoleReporter(ss)
42+
reporter = new ConsoleReporter(ss) // for reporting early config errors, before compiler is constructed
4043
command = new CompilerCommand(args.toList, ss)
4144
settings = command.settings
4245

4346
if (processSettingsHook()) {
4447
val compiler = newCompiler()
48+
reporter = compiler.reporter // adopt the configured reporter
4549
try {
4650
if (reporter.hasErrors)
4751
reporter.flush()
@@ -57,11 +61,9 @@ abstract class Driver {
5761
case _ => throw ex // unexpected error, tell the outside world.
5862
}
5963
}
60-
}
64+
} else if (reporter.hasErrors) reporter.flush()
65+
!reporter.hasErrors
6166
}
6267

63-
def main(args: Array[String]) {
64-
process(args)
65-
sys.exit(if (reporter.hasErrors) 1 else 0)
66-
}
68+
def main(args: Array[String]): Unit = sys.exit(if (process(args)) 0 else 1)
6769
}

src/compiler/scala/tools/nsc/Global.scala

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import io.{ SourceReader, AbstractFile, Path }
1515
import reporters.{ Reporter, ConsoleReporter }
1616
import util.{ ClassFileLookup, ClassPath, MergedClassPath, StatisticsInfo, returning }
1717
import scala.reflect.ClassTag
18-
import scala.reflect.internal.util.{ SourceFile, NoSourceFile, BatchSourceFile, ScriptSourceFile }
18+
import scala.reflect.internal.util.{ ScalaClassLoader, SourceFile, NoSourceFile, BatchSourceFile, ScriptSourceFile }
1919
import scala.reflect.internal.pickling.PickleBuffer
2020
import symtab.{ Flags, SymbolTable, SymbolTrackers }
2121
import symtab.classfile.Pickler
@@ -90,7 +90,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
9090
this(new Settings(err => reporter.error(null, err)), reporter)
9191

9292
def this(settings: Settings) =
93-
this(settings, new ConsoleReporter(settings))
93+
this(settings, Global.reporter(settings))
9494

9595
def picklerPhase: Phase = if (currentRun.isDefined) currentRun.picklerPhase else NoPhase
9696

@@ -1703,4 +1703,12 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
17031703

17041704
object Global {
17051705
def apply(settings: Settings, reporter: Reporter): Global = new Global(settings, reporter)
1706+
1707+
def apply(settings: Settings): Global = new Global(settings, reporter(settings))
1708+
1709+
private def reporter(settings: Settings): Reporter = {
1710+
//val loader = ScalaClassLoader(getClass.getClassLoader) // apply does not make delegate
1711+
val loader = new ClassLoader(getClass.getClassLoader) with ScalaClassLoader
1712+
loader.create[Reporter](settings.reporter.value, settings.errorFn)(settings)
1713+
}
17061714
}

src/compiler/scala/tools/nsc/Main.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ class MainClass extends Driver with EvalLoop {
1717
new compiler.Run() compile command.files
1818
}
1919

20-
override def newCompiler(): Global = Global(settings, reporter)
20+
override def newCompiler(): Global = Global(settings)
21+
2122
override def doCompile(compiler: Global) {
2223
if (settings.resident) resident(compiler)
2324
else super.doCompile(compiler)

src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ import java.io.{ BufferedReader, IOException, PrintWriter }
1111
import scala.reflect.internal.util._
1212
import StringOps._
1313

14-
/**
15-
* This class implements a Reporter that displays messages on a text console.
14+
/** This class implements a Reporter that displays messages on a text console.
1615
*/
1716
class ConsoleReporter(val settings: Settings, reader: BufferedReader, writer: PrintWriter) extends AbstractReporter {
1817
def this(settings: Settings) = this(settings, Console.in, new PrintWriter(Console.err, true))
@@ -85,5 +84,7 @@ class ConsoleReporter(val settings: Settings, reader: BufferedReader, writer: Pr
8584
}
8685
}
8786

88-
override def flush() { writer.flush() }
87+
override def flush() = writer.flush()
88+
89+
override def finish() = printSummary()
8990
}

src/compiler/scala/tools/nsc/settings/ScalaSettings.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ trait ScalaSettings extends AbsScalaSettings
134134
val Xshowobj = StringSetting ("-Xshow-object", "object", "Show internal representation of object.", "")
135135
val showPhases = BooleanSetting ("-Xshow-phases", "Print a synopsis of compiler phases.")
136136
val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files.", "")
137+
val reporter = StringSetting ("-Xreporter", "classname", "Specify a custom reporter for compiler messages.", "scala.tools.nsc.reporters.ConsoleReporter")
137138
val strictInference = BooleanSetting ("-Xstrict-inference", "Don't infer known-unsound types")
138139
val source = ScalaVersionSetting ("-Xsource", "version", "Treat compiler input as Scala source for the specified version, see SI-8126.", initial = ScalaVersion("2.11"))
139140

src/reflect/scala/reflect/internal/Reporting.scala

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ package reflect
88
package internal
99

1010
/** Provides delegates to the reporter doing the actual work.
11-
* All forwarding methods should be marked final,
12-
* but some subclasses out of our reach stil override them.
11+
* All forwarding methods should be marked final,
12+
* but some subclasses out of our reach stil override them.
1313
*
14-
* Eventually, this interface should be reduced to one method: `reporter`,
15-
* and clients should indirect themselves (reduce duplication of forwarders).
14+
* Eventually, this interface should be reduced to one method: `reporter`,
15+
* and clients should indirect themselves (reduce duplication of forwarders).
1616
*/
1717
trait Reporting { self : Positions =>
1818
def reporter: Reporter
@@ -71,8 +71,8 @@ import util.Position
7171

7272
/** Report information, warnings and errors.
7373
*
74-
* This describes the (future) external interface for issuing information, warnings and errors.
75-
* Currently, scala.tools.nsc.Reporter is used by sbt/ide/partest.
74+
* This describes the (future) external interface for issuing information, warnings and errors.
75+
* Currently, scala.tools.nsc.Reporter is used by sbt/ide/partest.
7676
*/
7777
abstract class Reporter {
7878
protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit
@@ -101,7 +101,10 @@ abstract class Reporter {
101101
resetCount(ERROR)
102102
}
103103

104-
def flush(): Unit = { }
104+
def flush(): Unit = ()
105+
106+
/** Finish reporting: print summaries, release resources. */
107+
def finish(): Unit = ()
105108
}
106109

107110
// TODO: move into superclass once partest cuts tie on Severity

src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import java.lang.reflect.{ Constructor, Modifier, Method }
1111
import java.io.{ File => JFile }
1212
import java.net.{ URLClassLoader => JURLClassLoader }
1313
import java.net.URL
14-
import scala.reflect.runtime.ReflectionUtils.unwrapHandler
14+
import scala.reflect.internal.FatalError
15+
import scala.reflect.runtime.ReflectionUtils.{ show, unwrapHandler }
1516
import ScalaClassLoader._
1617
import scala.util.control.Exception.{ catching }
1718
import scala.language.implicitConversions
@@ -46,6 +47,33 @@ trait ScalaClassLoader extends JClassLoader {
4647
def create(path: String): AnyRef =
4748
tryToInitializeClass[AnyRef](path).map(_.newInstance()).orNull
4849

50+
/** Create an instance with ctor args, or invoke errorFn before throwing. */
51+
def create[T <: AnyRef : ClassTag](path: String, errorFn: String => Unit)(args: AnyRef*): T = {
52+
def fail(msg: String) = error(msg, new IllegalArgumentException(msg))
53+
def error(msg: String, e: Throwable) = { errorFn(msg) ; throw e }
54+
try {
55+
val clazz = Class.forName(path, /*initialize =*/ true, /*loader =*/ this)
56+
if (classTag[T].runtimeClass isAssignableFrom clazz) {
57+
val ctor = {
58+
val maybes = clazz.getConstructors filter (c => c.getParameterCount == args.size &&
59+
(c.getParameterTypes zip args).forall { case (k, a) => k isAssignableFrom a.getClass })
60+
if (maybes.size == 1) maybes.head
61+
else fail(s"Constructor must accept arg list (${args map (_.getClass.getName) mkString ", "}): ${path}")
62+
}
63+
(ctor.newInstance(args: _*)).asInstanceOf[T]
64+
} else {
65+
errorFn(s"""Loader for ${classTag[T]}: [${show(classTag[T].runtimeClass.getClassLoader)}]
66+
|Loader for ${clazz.getName}: [${show(clazz.getClassLoader)}]""".stripMargin)
67+
fail(s"Not a ${classTag[T]}: ${path}")
68+
}
69+
} catch {
70+
case e: ClassNotFoundException =>
71+
error(s"Class not found: ${path}", e)
72+
case e @ (_: LinkageError | _: ReflectiveOperationException) =>
73+
error(s"Unable to create instance: ${path}: ${e.toString}", e)
74+
}
75+
}
76+
4977
/** The actual bytes for a class file, or an empty array if it can't be found. */
5078
def classBytes(className: String): Array[Byte] = classAsStream(className) match {
5179
case null => Array()

0 commit comments

Comments
 (0)