Permalink
Browse files

SI-6440 Address regressions around MissingRequirementError

 Go back to using globalError to report when a stub's info is referenced,
 and only throw the MissingRequirementError when compilation really
 must abort due to having a StubTermSymbol in a place where a
 StubClassSymbol would have been a better choice.

 This situation arises when an entire package is missing from the
 classpath, as was the case in the reported bug.

 Adds `StoreReporterDirectTest`, which buffers messages issued
 during compilation for more structured interrogation. Use this
 in two test for manifests -- these tests were using a crude means
 of grepping compiler console output to focus on the relevant output,
 but this approach was insufficient with the new multi-line error
 message emitted as part of this change.

 Also used that base test class to add two new tests: one for
 the reported error (package missing), and another for a simpler
 error (class missing). The latter test shows how stub symbols
 allow code to compile if it doesn't the subset of signatures
 in some type that refer to a missing class.

 Gave the INFO/WARNING/ERROR members of Reporter sensible
 toString implementations; they inherit from Enumeration#Value
 in an unusual manner (why?) that means the built in toString of
 Enumeration printed `Severity@0`.
  • Loading branch information...
1 parent 86e045e commit 079296632d8ef5ecc40aafa83757231599c78783 @retronym retronym committed Nov 13, 2012
@@ -20,9 +20,15 @@ abstract class Reporter {
class Severity(val id: Int) extends severity.Value {
var count: Int = 0
}
- val INFO = new Severity(0)
- val WARNING = new Severity(1)
- val ERROR = new Severity(2)
+ val INFO = new Severity(0) {
+ override def toString: String = "INFO"
+ }
+ val WARNING = new Severity(1) {
+ override def toString: String = "WARNING"
+ }
+ val ERROR = new Severity(2) {
+ override def toString: String = "ERROR"
+ }
/** Whether very long lines can be truncated. This exists so important
* debugging information (like printing the classpath) is not rendered
@@ -8,6 +8,7 @@ package scala.tools.partest
import scala.tools.nsc._
import io.Directory
import util.{BatchSourceFile, CommandLineParser}
+import reporters.{Reporter, ConsoleReporter}
/** A class for testing code which is embedded as a string.
* It allows for more complete control over settings, compiler
@@ -38,9 +39,12 @@ abstract class DirectTest extends App {
// new compiler
def newCompiler(args: String*): Global = {
val settings = newSettings((CommandLineParser tokenize ("-d \"" + testOutput.path + "\" " + extraSettings)) ++ args.toList)
- if (settings.Yrangepos.value) new Global(settings) with interactive.RangePositions
- else new Global(settings)
+ if (settings.Yrangepos.value) new Global(settings, reporter(settings)) with interactive.RangePositions
+ else new Global(settings, reporter(settings))
}
+
+ def reporter(settings: Settings): Reporter = new ConsoleReporter(settings)
+
def newSources(sourceCodes: String*) = sourceCodes.toList.zipWithIndex map {
case (src, idx) => new BatchSourceFile("newSource" + (idx + 1), src)
}
@@ -0,0 +1,15 @@
+package scala.tools.partest
+
+import scala.tools.nsc.Settings
+import scala.tools.nsc.reporters.StoreReporter
+import scala.collection.mutable
+
+trait StoreReporterDirectTest extends DirectTest {
+ lazy val storeReporter: StoreReporter = new scala.tools.nsc.reporters.StoreReporter()
+
+ /** Discards all but the first message issued at a given position. */
+ def filteredInfos: Seq[storeReporter.Info] = storeReporter.infos.groupBy(_.pos).map(_._2.head).toList
+
+ /** Hook into [[scala.tools.partest.DirectTest]] to install the custom reporter */
+ override def reporter(settings: Settings) = storeReporter
+}
@@ -3103,12 +3103,18 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
)
}
trait StubSymbol extends Symbol {
- override final def failIfStub() = fail(())
protected def missingMessage: String
+
+ /** Fail the stub by throwing a [[scala.reflect.internal.MissingRequirementError]]. */
+ override final def failIfStub() = {MissingRequirementError.signal(missingMessage)} //
+
+ /** Fail the stub by reporting an error to the reporter, setting the IS_ERROR flag
+ * on this symbol, and returning the dummy value `alt`.
+ */
private def fail[T](alt: T): T = {
// Avoid issuing lots of redundant errors
if (!hasFlag(IS_ERROR)) {
- MissingRequirementError.signal(missingMessage)
+ globalError(missingMessage)
if (settings.debug.value)
(new Throwable).printStackTrace
@@ -1385,10 +1385,11 @@ trait Types extends api.Types { self: SymbolTable =>
/** A class for this-types of the form <sym>.this.type
*/
abstract case class ThisType(sym: Symbol) extends SingletonType with ThisTypeApi {
- // SI-6640 allow StubSymbols to reveal what's missing from the classpath
- // before we trip the assertion.
- sym.failIfStub()
- assert(sym.isClass, sym)
+ if (!sym.isClass) {
+ // SI-6640 allow StubSymbols to reveal what's missing from the classpath before we trip the assertion.
+ sym.failIfStub()
+ assert(false, sym)
+ }
//assert(sym.isClass && !sym.isModuleClass || sym.isRoot, sym)
override def isTrivial: Boolean = sym.isPackageClass
@@ -234,10 +234,10 @@ abstract class UnPickler {
adjust(mirrorThatLoaded(owner).missingHook(owner, name)) orElse {
// (5) Create a stub symbol to defer hard failure a little longer.
val missingMessage =
- s"""|A signature in $filename refers to ${name.longString} in ${owner.fullLocationString}
- |which is not available. It may be completely missing from the current classpath,
- |or the version on the classpath might be incompatible with the version used when
- |compiling $filename.""".stripMargin
+ s"""|bad symbolic reference. A signature in $filename refers to ${name.longString}
+ |in ${owner.kindString} ${owner.fullName} which is not available.
+ |It may be completely missing from the current classpath, or the version on
+ |the classpath might be incompatible with the version used when compiling $filename.""".stripMargin
owner.newStubSymbol(name, missingMessage)
}
}
View
@@ -1,3 +1,9 @@
-error: bad symbolic reference to value global in class IMain - referenced from t5148.scala (a classfile may be missing)
-error: bad symbolic reference to value memberHandlers in class IMain - referenced from t5148.scala (a classfile may be missing)
+error: bad symbolic reference. A signature in Imports.class refers to term global
+in class scala.tools.nsc.interpreter.IMain which is not available.
+It may be completely missing from the current classpath, or the version on
+the classpath might be incompatible with the version used when compiling Imports.class.
+error: bad symbolic reference. A signature in Imports.class refers to term memberHandlers
+in class scala.tools.nsc.interpreter.IMain which is not available.
+It may be completely missing from the current classpath, or the version on
+the classpath might be incompatible with the version used when compiling Imports.class.
two errors found
@@ -1 +1,4 @@
-Stream((), ?)
+pos: source-newSource1,line-9,offset=109 bad symbolic reference. A signature in U.class refers to term pack1
+in package <root> which is not available.
+It may be completely missing from the current classpath, or the version on
+the classpath might be incompatible with the version used when compiling U.class. ERROR
View
@@ -1,7 +1,48 @@
-object Test {
+import scala.tools.partest._
+import java.io.File
- def main(args: Array[String]): Unit = {
- println(Stream.continually(()).filterNot(_ => false).take(2))
+object Test extends StoreReporterDirectTest {
+ def code = ???
+
+ def compileCode(code: String) = {
+ val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator")
+ compileString(newCompiler("-cp", classpath, "-d", testOutput.path))(code)
}
+ def library1 = """
+ package pack1
+ trait T
+ """
+
+ def library2 = """
+ package pack2
+ trait U extends pack1.T
+ """
+
+ def app = """
+ package pack3
+ object X {
+ trait U
+ }
+ import X._
+ import pack2._
+
+ trait V extends U
+ """
+
+ def show(): Unit = {
+ Seq(library1, library2) foreach compileCode
+ assert(filteredInfos.isEmpty, filteredInfos)
+
+ // blow away the entire package
+ val pack1 = new File(testOutput.path, "pack1")
+ val tClass = new File(pack1, "T.class")
+ assert(tClass.exists)
+ assert(tClass.delete())
+ assert(pack1.delete())
+
+ // bad symbolic reference error expected (but no stack trace!)
+ compileCode(app)
+ println(filteredInfos.mkString("\n"))
+ }
}
@@ -0,0 +1,4 @@
+pos: NoPosition bad symbolic reference. A signature in U.class refers to type T
+in package pack1 which is not available.
+It may be completely missing from the current classpath, or the version on
+the classpath might be incompatible with the version used when compiling U.class. ERROR
@@ -0,0 +1,61 @@
+import scala.tools.partest._
+import java.io.File
+
+object Test extends StoreReporterDirectTest {
+ def code = ???
+
+ def compileCode(code: String) = {
+ val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator")
+ compileString(newCompiler("-cp", classpath, "-d", testOutput.path))(code)
+ }
+
+ def library1 = """
+ package pack1
+ trait T
+ class U {
+ def t = new T {}
+ def one = 1
+ }
+ """
+
+ def library2 = """
+ package pack2
+ object V {
+ def u = new pack1.U
+ }
+ """
+
+ def app1 = """
+ package pack3
+ object Test {
+ pack2.V.u.one // okay
+ }
+ """
+
+ def app2 = """
+ package pack3
+ object Test {
+ pack2.V.u.t // we have to fail if T.class is misisng
+ }
+ """
+
+ def show(): Unit = {
+ compileCode(library1)
+ val pack1 = new File(testOutput.path, "pack1")
+ val tClass = new File(pack1, "T.class")
+ assert(tClass.exists)
+ assert(tClass.delete())
+
+ // allowed to compile, no direct reference to `T`
+ compileCode(library2)
+ assert(filteredInfos.isEmpty, filteredInfos)
+
+ // allowed to compile, no direct reference to `T`
+ compileCode(app1)
+ assert(filteredInfos.isEmpty, filteredInfos)
+
+ // bad symbolic reference error expected (but no stack trace!)
+ compileCode(app2)
+ println(filteredInfos.mkString("\n"))
+ }
+}
@@ -1,3 +1,2 @@
-newSource1:9: error: could not find implicit value for evidence parameter of type reflect.runtime.package.universe.TypeTag[Int]
- Library.foo[Int]
- ^
+
+pos: source-newSource1,line-9,offset=466 could not find implicit value for evidence parameter of type reflect.runtime.package.universe.TypeTag[Int] ERROR
@@ -1,6 +1,6 @@
import scala.tools.partest._
-object Test extends DirectTest {
+object Test extends StoreReporterDirectTest {
def code = ???
def library = """
@@ -32,14 +32,12 @@ object Test extends DirectTest {
}
def show(): Unit = {
- val prevErr = System.err
- val baos = new java.io.ByteArrayOutputStream();
- System.setErr(new java.io.PrintStream(baos));
compileLibrary();
+ println(filteredInfos.mkString("\n"))
+ storeReporter.infos.clear()
compileApp();
- // we should get bad symbolic reference errors, because we're trying to call a method that can't be unpickled
+ // we should get bad symbolic reference errors, because we're trying to use an implicit that can't be unpickled
// but we don't know the number of these errors and their order, so I just ignore them all
- baos.toString.split("\n") filter (!_.startsWith("error: bad symbolic reference")) foreach println
- System.setErr(prevErr)
+ println(filteredInfos.filterNot(_.msg.contains("bad symbolic reference")).mkString("\n"))
}
-}
+}
@@ -1,3 +1,2 @@
-newSource1:9: error: No Manifest available for App.this.T.
- manifest[T]
- ^
+
+pos: source-newSource1,line-9,offset=479 No Manifest available for App.this.T. ERROR
@@ -1,6 +1,7 @@
import scala.tools.partest._
+import scala.tools.nsc.Settings
-object Test extends DirectTest {
+object Test extends StoreReporterDirectTest {
def code = ???
def library = """
@@ -29,18 +30,18 @@ object Test extends DirectTest {
"""
def compileApp() = {
val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator")
+ val global = newCompiler("-cp", classpath, "-d", testOutput.path)
compileString(newCompiler("-cp", classpath, "-d", testOutput.path))(app)
+ //global.reporter.ERROR.foreach(println)
}
def show(): Unit = {
- val prevErr = System.err
- val baos = new java.io.ByteArrayOutputStream();
- System.setErr(new java.io.PrintStream(baos));
compileLibrary();
+ println(filteredInfos.mkString("\n"))
+ storeReporter.infos.clear()
compileApp();
// we should get bad symbolic reference errors, because we're trying to use an implicit that can't be unpickled
// but we don't know the number of these errors and their order, so I just ignore them all
- baos.toString.split("\n") filter (!_.startsWith("error: bad symbolic reference")) foreach println
- System.setErr(prevErr)
+ println(filteredInfos.filterNot (_.msg.contains("bad symbolic reference")).mkString("\n"))
}
-}
+}

0 comments on commit 0792966

Please sign in to comment.