Skip to content

Commit

Permalink
Merge pull request scala#6345 from hrhino/reflect-assert
Browse files Browse the repository at this point in the history
Also augment assertion errors raised in reflect.
  • Loading branch information
lrytz authored Mar 1, 2018
2 parents 7d6d5a7 + 32ad4a2 commit 253005c
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 54 deletions.
58 changes: 20 additions & 38 deletions src/compiler/scala/tools/nsc/Global.scala
Original file line number Diff line number Diff line change
Expand Up @@ -261,27 +261,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter)

// ------------------ Debugging -------------------------------------

// Getting in front of Predef's asserts to supplement with more info.
// This has the happy side effect of masking the one argument forms
// of assert and require (but for now I've reproduced them here,
// because there are a million to fix.)
@inline final def assert(assertion: Boolean, message: => Any) {
// calling Predef.assert would send a freshly allocated closure wrapping the one received as argument.
if (!assertion)
throw new java.lang.AssertionError("assertion failed: "+ supplementErrorMessage("" + message))
}
@inline final def assert(assertion: Boolean) {
assert(assertion, "")
}
@inline final def require(requirement: Boolean, message: => Any) {
// calling Predef.require would send a freshly allocated closure wrapping the one received as argument.
if (!requirement)
throw new IllegalArgumentException("requirement failed: "+ supplementErrorMessage("" + message))
}
@inline final def require(requirement: Boolean) {
require(requirement, "")
}

@inline final def ifDebug(body: => Unit) {
if (settings.debug)
body
Expand Down Expand Up @@ -966,7 +945,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
/** Let's share a lot more about why we crash all over the place.
* People will be very grateful.
*/
protected var lastSeenContext: analyzer.Context = null
protected var lastSeenContext: analyzer.Context = analyzer.NoContext

/** The currently active run
*/
Expand Down Expand Up @@ -1015,46 +994,49 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
else sym.ownerChain takeWhile (!_.isPackageClass) mkString " -> "
)

private def formatExplain(pairs: (String, Any)*): String = (
pairs collect { case (k, v) if v != null => f"$k%20s: $v" } mkString "\n"
)

/** Don't want to introduce new errors trying to report errors,
* so swallow exceptions.
*/
override def supplementTyperState(errorMessage: String): String = try {
def formatExplain(pairs: List[(String, Any)]): String =
pairs collect { case (k, v) if v != null => f"$k%20s: $v" } mkString "\n"

val tree = analyzer.lastTreeToTyper
val sym = tree.symbol
val tpe = tree.tpe
val site = lastSeenContext.enclClassOrMethod.owner
val pos_s = if (tree.pos.isDefined) s"line ${tree.pos.line} of ${tree.pos.source.file}" else "<unknown>"
val context_s = try {
import scala.reflect.io.{File => SFile}
// Taking 3 before, 3 after the fingered line.
val start = 1 max (tree.pos.line - 3)
val xs = SFile(tree.pos.source.file.file).lines.drop(start-1).take(7)
val strs = xs.zipWithIndex map { case (line, idx) => f"${start + idx}%6d $line" }
val start = 0 max (tree.pos.line - 4)
val xs = tree.pos.source.lines(start, start + 7)
val strs = xs.zipWithIndex map { case (line, idx) => f"${start + idx + 1}%6d $line" }
strs.mkString("== Source file context for tree position ==\n\n", "\n", "")
}
catch { case t: Exception => devWarning("" + t) ; "<Cannot read source file>" }

val info1 = formatExplain(
val info1 = formatExplain(List(
"while compiling" -> currentSource.path,
"during phase" -> ( if (globalPhase eq phase) phase else "globalPhase=%s, enteringPhase=%s".format(globalPhase, phase) ),
"library version" -> scala.util.Properties.versionString,
"compiler version" -> Properties.versionString,
"compiler version" -> scala.tools.nsc.Properties.versionString,
"reconstructed args" -> settings.recreateArgs.mkString(" ")
))
// useful things to know if we have a sym
val symbolInfos = if (sym eq null) List("symbol" -> "null") else List(
"symbol" -> sym.debugLocationString,
"symbol definition" -> s"${sym.defString} (a ${sym.shortSymbolClass})",
"symbol package" -> sym.enclosingPackage.fullName,
"symbol owners" -> ownerChainString(sym),
)
val info2 = formatExplain(
val info2 = formatExplain(List(
"last tree to typer" -> tree.summaryString,
"tree position" -> pos_s,
"tree tpe" -> tpe,
"symbol" -> Option(sym).fold("null")(_.debugLocationString),
"symbol definition" -> Option(sym).fold("null")(s => s.defString + s" (a ${s.shortSymbolClass})"),
"symbol package" -> sym.enclosingPackage.fullName,
"symbol owners" -> ownerChainString(sym),
"tree tpe" -> tpe
) ::: symbolInfos ::: List(
"call site" -> (site.fullLocationString + " in " + site.enclosingPackage)
)
))
("\n " + errorMessage + "\n" + info1) :: info2 :: context_s :: Nil mkString "\n\n"
} catch { case _: Exception | _: TypeError => errorMessage }

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/reflect/ToolBoxFactory.scala
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
undoLog.clear()
analyzer.lastTreeToTyper = EmptyTree
lastSeenSourceFile = NoSourceFile
lastSeenContext = null
lastSeenContext = analyzer.NoContext
}

def verify(expr: Tree): Tree = {
Expand Down
27 changes: 27 additions & 0 deletions src/reflect/scala/reflect/internal/SymbolTable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,33 @@ abstract class SymbolTable extends macros.Universe
result
}

// Getting in front of Predef's asserts to supplement with more info; see `supplementErrorMessage`.
// This has the happy side effect of masking the one argument form of assert
// (but for now it's reproduced here, because there are a million uses to fix).
@inline
final def assert(assertion: Boolean, message: => Any): Unit = {
// calling Predef.assert would send a freshly allocated closure wrapping the one received as argument.
if (!assertion) throwAssertionError(message)
}

// for those of us who use IDEs, this will now at least show up struck-through
@deprecated("prefer to use the two-argument form", since = "2.12.5")
final def assert(assertion: Boolean): Unit = {
assert(assertion, "")
}

@inline
final def require(requirement: Boolean, message: => Any): Unit = {
// calling Predef.require would send a freshly allocated closure wrapping the one received as argument.
if (!requirement) throwRequirementError(message)
}

// extracted from `assert`/`require` to make them as small (and inlineable) as possible
private[internal] def throwAssertionError(msg: Any): Nothing =
throw new java.lang.AssertionError(s"assertion failed: ${supplementErrorMessage(String valueOf msg)}")
private[internal] def throwRequirementError(msg: Any): Nothing =
throw new java.lang.IllegalArgumentException(s"requirement failed: ${supplementErrorMessage(String valueOf msg)}")

@inline final def findSymbol(xs: TraversableOnce[Symbol])(p: Symbol => Boolean): Symbol = {
xs find p getOrElse NoSymbol
}
Expand Down
41 changes: 29 additions & 12 deletions src/reflect/scala/reflect/internal/util/SourceFile.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/* NSC -- new Scala compiler
* Copyright 2005-2013 LAMP/EPFL
* Copyright 2005-2018 LAMP/EPFL
* @author Martin Odersky
*/


package scala
package reflect.internal.util

Expand All @@ -22,6 +21,7 @@ abstract class SourceFile {
def isEndOfLine(idx: Int): Boolean
def isSelfContained: Boolean
def length : Int
def lineCount: Int
def position(offset: Int): Position = {
assert(offset < length, file + ": " + offset + " >= " + length)
Position.offset(this, offset)
Expand Down Expand Up @@ -49,20 +49,28 @@ abstract class SourceFile {
if (content(offset).isWhitespace) skipWhitespace(offset + 1) else offset

def identifier(pos: Position): Option[String] = None

/** An iterator over the lines between `start` and `end`.
*
* Bounds are checked and clipped as necessary.
*/
def lines(start: Int = 0, end: Int = lineCount): Iterator[String]
}

/** An object representing a missing source file.
*/
object NoSourceFile extends SourceFile {
def content = Array()
def file = NoFile
def isLineBreak(idx: Int) = false
def isEndOfLine(idx: Int) = false
def isSelfContained = true
def length = -1
def offsetToLine(offset: Int) = -1
def lineToOffset(index : Int) = -1
override def toString = "<no source file>"
def content = Array()
def file = NoFile
def isLineBreak(idx: Int) = false
def isEndOfLine(idx: Int) = false
def isSelfContained = true
def length = -1
def lineCount = 0
def offsetToLine(offset: Int) = -1
def lineToOffset(index : Int) = -1
def lines(start: Int, end: Int) = Iterator.empty
override def toString = "<no source file>"
}

object NoFile extends VirtualFile("<no file>", "<no file>")
Expand Down Expand Up @@ -122,7 +130,8 @@ class BatchSourceFile(val file : AbstractFile, content0: Array[Char]) extends So
content0 :+ '\n'
else content0
)
val length = content.length
def length = content.length
def lineCount = lineIndices.length - 1
def start = 0
def isSelfContained = true

Expand Down Expand Up @@ -187,6 +196,14 @@ class BatchSourceFile(val file : AbstractFile, content0: Array[Char]) extends So
lastLine
}

override def lines(start: Int, end: Int): Iterator[String] =
((start max 0) until (end min lineCount)).iterator.map { ix =>
val off = lineIndices(ix)
val len = 0 max (lineIndices(ix + 1) - off - 1) // drop newline character
String.valueOf(content, off, len)
}


override def equals(that : Any) = that match {
case that : BatchSourceFile => file.path == that.file.path && start == that.start
case _ => false
Expand Down
1 change: 1 addition & 0 deletions test/files/presentation/t7678/Runner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ object Test extends InteractiveTest {
() => {
val runDefinitions = currentRun.runDefinitions
import runDefinitions._
import Predef._
assert(TypeTagsClass.map(sym => getMemberClass(sym, tpnme.TypeTag)) == TypeTagClass)
assert(TypeTagsClass.map(sym => getMemberClass(sym, tpnme.WeakTypeTag)) == WeakTypeTagClass)
assert(TypeTagsClass.map(sym => getMemberModule(sym, nme.WeakTypeTag)) == WeakTypeTagModule)
Expand Down
4 changes: 2 additions & 2 deletions test/files/run/t5294.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ object Test {
val TClass = reflect.runtime.universe.symbolOf[p.T[_, _]].asInstanceOf[symtab.Symbol]
import symtab._
val from = CTpe.member(TermName("test")).paramss.head.head
assert(from.baseClasses contains TClass)
assert(from.info.baseTypeIndex(TClass) != -1) // was failing!
assert(from.baseClasses contains TClass, from.baseClasses)
assert(from.info.baseTypeIndex(TClass) != -1, from.info.baseTypeSeq) // was failing!
}
}
2 changes: 1 addition & 1 deletion test/files/run/t8029.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ package object p4 {
val sourceFile = newSources(code).head
global.reporter.reset()
r.compileSources(sourceFile :: Nil)
assert(!global.reporter.hasErrors)
assert(!global.reporter.hasErrors, global.reporter.errorCount)
}

def typecheckTwice(code: String): Unit = {
Expand Down

0 comments on commit 253005c

Please sign in to comment.