diff options
Diffstat (limited to 'chiselFrontend/src/main/scala/chisel3/internal')
| -rw-r--r-- | chiselFrontend/src/main/scala/chisel3/internal/Error.scala | 95 |
1 files changed, 61 insertions, 34 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Error.scala b/chiselFrontend/src/main/scala/chisel3/internal/Error.scala index 25a3ec2a..6e7e4002 100644 --- a/chiselFrontend/src/main/scala/chisel3/internal/Error.scala +++ b/chiselFrontend/src/main/scala/chisel3/internal/Error.scala @@ -2,7 +2,7 @@ package chisel3.internal -import scala.collection.mutable.ArrayBuffer +import scala.collection.mutable.{ArrayBuffer, LinkedHashMap} import chisel3.core._ @@ -15,8 +15,6 @@ private[chisel3] object throwException { /** Records and reports runtime errors and warnings. */ private[chisel3] class ErrorLog { - def hasErrors: Boolean = errors.exists(_.isFatal) - /** Log an error message */ def error(m: => String): Unit = errors += new Error(m, getUserLineNumber) @@ -25,50 +23,83 @@ private[chisel3] class ErrorLog { def warning(m: => String): Unit = errors += new Warning(m, getUserLineNumber) - /** Log a deprecation warning message */ - def deprecated(m: => String): Unit = - errors += new DeprecationWarning(m, getUserLineNumber) - /** Emit an informational message */ def info(m: String): Unit = println(new Info("[%2.3f] %s".format(elapsedTime/1e3, m), None)) // scalastyle:ignore regex - /** Prints error messages generated by Chisel at runtime. */ - def report(): Unit = errors foreach println // scalastyle:ignore regex + /** Log a deprecation warning message */ + def deprecated(m: => String): Unit = { + val thisEntry = (m, getUserLineNumber) + deprecations += ((thisEntry, deprecations.getOrElse(thisEntry, 0) + 1)) + } /** Throw an exception if any errors have yet occurred. */ def checkpoint(): Unit = { - if(hasErrors) { - import Console._ - throwException(errors.map(_ + "\n").reduce(_ + _) + - UNDERLINED + "CODE HAS " + errors.filter(_.isFatal).length + RESET + - UNDERLINED + " " + RED + "ERRORS" + RESET + - UNDERLINED + " and " + errors.filterNot(_.isFatal).length + RESET + - UNDERLINED + " " + YELLOW + "WARNINGS" + RESET) + val depTag = s"[${Console.BLUE}deprecated${Console.RESET}]" + val warnTag = s"[${Console.YELLOW}warn${Console.RESET}]" + val errTag = s"[${Console.RED}error${Console.RESET}]" + deprecations foreach { case ((message, stackElement), count) => + val line = stackElement match { + case Some(stackElement) => s"$depTag ${stackElement.getFileName}:${stackElement.getLineNumber} ($count calls): $message" + case None => s"$depTag (unknown location) ($count calls): $message" + } + println(line) + } + errors foreach println + + if (!deprecations.isEmpty) { + println(s"$warnTag ${Console.YELLOW}There were ${deprecations.size} deprecated function(s) used." + + s" These may stop compiling in a future release, you are encouraged to fix these issues.${Console.RESET}") + println(s"$warnTag Line numbers for deprecations reported by Chisel may be inaccurate, enable scalac compiler deprecation warnings by either:") + println(s"$warnTag In the sbt interactive console, enter:") + println(s"""$warnTag set scalacOptions in ThisBuild ++= Seq("-unchecked", "-deprecation")""") + println(s"$warnTag or, in your build.sbt, add the line:") + println(s"""$warnTag scalacOptions := Seq("-unchecked", "-deprecation")""") + } + + val allErrors = errors.filter(_.isFatal) + val allWarnings = errors.filter(!_.isFatal) + + if (!allWarnings.isEmpty && !allErrors.isEmpty) { + println(s"$errTag There were ${Console.RED}${allErrors.size} error(s)${Console.RESET} and ${Console.YELLOW}${allWarnings.size} warning(s)${Console.RESET} during hardware elaboration.") + } else if (!allWarnings.isEmpty) { + println(s"$warnTag There were ${Console.YELLOW}${allWarnings.size} warning(s)${Console.RESET} during hardware elaboration.") + } else if (!allErrors.isEmpty) { + println(s"$errTag There were ${Console.RED}${allErrors.size} error(s)${Console.RESET} during hardware elaboration.") + } + + if (!allErrors.isEmpty) { + throwException("Fatal errors during hardware elaboration") } else { - // No fatal errors. Report accumulated warnings and clear them. - report() + // No fatal errors, clear accumulated warnings since they've been reported errors.clear() } } - private def findFirstUserFrame(stack: Array[StackTraceElement]): Option[StackTraceElement] = { - def isUserCode(ste: StackTraceElement): Boolean = { - def isUserModule(c: Class[_]): Boolean = - c != null && (c == classOf[UserModule] || isUserModule(c.getSuperclass)) - isUserModule(Class.forName(ste.getClassName)) + /** Returns the best guess at the first stack frame that belongs to user code. + */ + private def getUserLineNumber = { + def isChiselClassname(className: String): Boolean = { + // List of classpath prefixes that are Chisel internals and should be ignored when looking for user code + // utils are not part of internals and errors there can be reported + val chiselPrefixes = Set( + "java.", + "scala.", + "chisel3.internal.", + "chisel3.core.", + "chisel3.package$" // for some compatibility / deprecated types + ) + !chiselPrefixes.filter(className.startsWith(_)).isEmpty } - stack.indexWhere(isUserCode) match { - case x if x < 0 => None - case x => Some(stack(x)) - } + Thread.currentThread().getStackTrace.toList.dropWhile( + // Get rid of everything in Chisel core + ste => isChiselClassname(ste.getClassName) + ).headOption } - private def getUserLineNumber = - findFirstUserFrame(Thread.currentThread().getStackTrace) - private val errors = ArrayBuffer[LogEntry]() + private val deprecations = LinkedHashMap[(String, Option[StackTraceElement]), Int]() private val startTime = System.currentTimeMillis private def elapsedTime: Long = System.currentTimeMillis - startTime @@ -96,10 +127,6 @@ private class Warning(msg: => String, line: Option[StackTraceElement]) extends L def format: String = tag("warn", Console.YELLOW) } -private class DeprecationWarning(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) { - def format: String = tag("warn", Console.CYAN) -} - private class Info(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) { def format: String = tag("info", Console.MAGENTA) } |
