New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SI-6640 Better reporting of deficient classpaths. #1607
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1385,7 +1385,12 @@ 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 { | ||
assert(sym.isClass, sym) | ||
if (!sym.isClass) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. one way to whack the mole more reliably and get more encapsulation to boot: I'm figuring out whether this is doable. There's also risk with missing an override of course. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The other stub methods return We need to know what the callers expectation is. def checkIsClass(expectation: Boolean) = if (expectation != isClass) abort(...) So... tricky. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I imagined isClass on a StubSymbol would throw the MissingRequirementException There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that would nicely encapsulate the trigger, but it would (potentially) blow up more often that it does now. Because we don't have tests for all the variations, I erred on the side of "crash rather than fail with a nice error" rather than "fail with a nice error rather than soldier on." While I could be persuaded to the other side, I just wanted to point out we can't encapsulate without changing behaviour. For sure we should revisit this. But right now, the most valuable input for the PR would be additional test cases (even ideas for them, I'm happy to implement them.) |
||
// 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 | ||
override def isNotNull = true | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,7 +20,7 @@ import scala.annotation.switch | |
/** @author Martin Odersky | ||
* @version 1.0 | ||
*/ | ||
abstract class UnPickler /*extends scala.reflect.generic.UnPickler*/ { | ||
abstract class UnPickler { | ||
val global: SymbolTable | ||
import global._ | ||
|
||
|
@@ -233,7 +233,12 @@ abstract class UnPickler /*extends scala.reflect.generic.UnPickler*/ { | |
// (4) Call the mirror's "missing" hook. | ||
adjust(mirrorThatLoaded(owner).missingHook(owner, name)) orElse { | ||
// (5) Create a stub symbol to defer hard failure a little longer. | ||
owner.newStubSymbol(name) | ||
val missingMessage = | ||
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) | ||
} | ||
} | ||
} | ||
|
@@ -827,11 +832,6 @@ abstract class UnPickler /*extends scala.reflect.generic.UnPickler*/ { | |
protected def errorBadSignature(msg: String) = | ||
throw new RuntimeException("malformed Scala signature of " + classRoot.name + " at " + readIndex + "; " + msg) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why has this method been removed? upd. Interesting... Apparently it wasn't used at all! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was used before Paul's patch. |
||
protected def errorMissingRequirement(name: Name, owner: Symbol): Symbol = | ||
mirrorThatLoaded(owner).missingHook(owner, name) orElse MissingRequirementError.signal( | ||
s"bad reference while unpickling $filename: ${name.longString} not found in ${owner.tpe.widen}" | ||
) | ||
|
||
def inferMethodAlternative(fun: Tree, argtpes: List[Type], restpe: Type) {} // can't do it; need a compiler for that. | ||
|
||
def newLazyTypeRef(i: Int): LazyType = new LazyTypeRef(i) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ending with ERROR has a bit of a DOS feel. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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")) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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")) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder why Paul wrote
locally
here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd wager that as he was working on this he few vals before that line that he didn't want retained as fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like the way constructor bodies blend seamlessly with member definitions and intersperse arbitrarily; sometimes I use locally only to bring emphasis to the fact that a constructor exists.