summaryrefslogtreecommitdiff
path: root/chiselFrontend/src/main/scala/chisel3/internal/Error.scala
diff options
context:
space:
mode:
authorRichard Lin2018-01-23 13:53:56 -0800
committerGitHub2018-01-23 13:53:56 -0800
commit224740acda1929f1a96e165912234eec6ee90fc3 (patch)
tree155d390ea53062f2543d666e804a05dca856bfbb /chiselFrontend/src/main/scala/chisel3/internal/Error.scala
parent70ca35b9d7b3884e5f701d49bc5286f89701fd14 (diff)
Runtime API deprecation warnings (#761)
Add runtime warnings for use of deprecated Chisel methods. This is done using a macro that takes the message from a `@deprecated` annotation, and adds a call to `Builder.deprecated`. Reasoning is that by default, Scala doesn't print all deprecations, and that it's somewhat tricky to notice them - yet some support questions revolve around the use of deprecated and terribad API. This now prints warnings for uses of deprecated functions at runtime, and aggregates them by error and line to avoid spam. Also included is convenient information on enabling scalac deprecations. This also changes how line numbers for Chisel's error facility is determined, using prefix string comparison of the stack trace element classnames, instead of checking if the class is a subtype of UserModule. The previous one (specifically, calls to Class.forName) seems to interact badly with reflection-based cloneType when called at scale. This should also give more accurate reporting of errors that are in user code but outside of a UserModule. It turns out that `@deprecated` on macro functions don't do anything, so this changes the tags to the functions that the macros point to, which seems to work properly. It also turns out that there's a bunch of uses of deprecated functions in chiselTests which needs to be fixed. Not all `@deprecated` functions are also annotated with `@chiselRuntimeDeprecation`, because they're still used in Chisel internals, and we can't track whether they're called by the user or by Chisel and it will give a misleading error. These are a small amount of functions.
Diffstat (limited to 'chiselFrontend/src/main/scala/chisel3/internal/Error.scala')
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/Error.scala95
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)
}