@@ -6,6 +6,8 @@ import scala.annotation.tailrec
66import scala .collection .mutable .{ArrayBuffer , LinkedHashMap , LinkedHashSet }
77import scala .util .control .NoStackTrace
88import _root_ .logger .Logger
9+ import java .io .File
10+ import scala .io .Source
911
1012import chisel3 .experimental .{NoSourceInfo , SourceInfo , SourceLine , UnlocatableSourceInfo }
1113
@@ -98,7 +100,29 @@ private[chisel3] object ErrorLog {
98100 val errTag = s " [ ${Console .RED }error ${Console .RESET }] "
99101}
100102
101- private [chisel3] class ErrorLog (warningsAsErrors : Boolean ) {
103+ private [chisel3] class ErrorLog (warningsAsErrors : Boolean , sourceRoots : Seq [File ]) {
104+
105+ private def getErrorLineInFile (sl : SourceLine ): List [String ] = {
106+ def tryFileInSourceRoot (sourceRoot : File ): Option [List [String ]] = {
107+ try {
108+ val file = new File (sourceRoot, sl.filename)
109+ val lines = Source .fromFile(file).getLines()
110+ var i = 0
111+ while (i < (sl.line - 1 ) && lines.hasNext) {
112+ lines.next()
113+ i += 1
114+ }
115+ val line = lines.next()
116+ val caretLine = (" " * (sl.col - 1 )) + " ^"
117+ Some (line :: caretLine :: Nil )
118+ } catch {
119+ case scala.util.control.NonFatal (_) => None
120+ }
121+ }
122+ val sourceRootsWithDefault = if (sourceRoots.nonEmpty) sourceRoots else Seq (new File (" ." ))
123+ // View allows us to search the directories one at a time and early out
124+ sourceRootsWithDefault.view.map(tryFileInSourceRoot(_)).collectFirst { case Some (value) => value }.getOrElse(Nil )
125+ }
102126
103127 /** Returns an appropriate location string for the provided source info.
104128 * If the source info is of `NoSourceInfo` type, the source location is looked up via stack trace.
@@ -119,8 +143,9 @@ private[chisel3] class ErrorLog(warningsAsErrors: Boolean) {
119143
120144 private def errorEntry (msg : String , si : Option [SourceInfo ], isFatal : Boolean ): ErrorEntry = {
121145 val location = errorLocationString(si)
146+ val sourceLineAndCaret = si.collect { case sl : SourceLine => getErrorLineInFile(sl) }.getOrElse(Nil )
122147 val fullMessage = if (location.isEmpty) msg else s " $location: $msg"
123- ErrorEntry (fullMessage, isFatal)
148+ ErrorEntry (fullMessage :: sourceLineAndCaret , isFatal)
124149 }
125150
126151 /** Log an error message */
@@ -158,7 +183,7 @@ private[chisel3] class ErrorLog(warningsAsErrors: Boolean) {
158183 case ((message, sourceLoc), count) =>
159184 logger.warn(s " ${ErrorLog .depTag} $sourceLoc ( $count calls): $message" )
160185 }
161- errors.foreach(e => logger.error(s " ${e.tag} ${e.msg} " ))
186+ errors.foreach(e => logger.error(e.serialize ))
162187
163188 if (! deprecations.isEmpty) {
164189 logger.warn(
@@ -237,6 +262,8 @@ private[chisel3] class ErrorLog(warningsAsErrors: Boolean) {
237262 private def elapsedTime : Long = System .currentTimeMillis - startTime
238263}
239264
240- private case class ErrorEntry (msg : String , isFatal : Boolean ) {
265+ private case class ErrorEntry (lines : Seq [ String ] , isFatal : Boolean ) {
241266 def tag = if (isFatal) ErrorLog .errTag else ErrorLog .warnTag
267+
268+ def serialize : String = lines.map(s " $tag " + _).mkString(" \n " )
242269}
0 commit comments