diff --git a/effekt/jvm/src/main/scala/effekt/Driver.scala b/effekt/jvm/src/main/scala/effekt/Driver.scala index c101df88e..c276495da 100644 --- a/effekt/jvm/src/main/scala/effekt/Driver.scala +++ b/effekt/jvm/src/main/scala/effekt/Driver.scala @@ -9,7 +9,7 @@ import effekt.context.{ Context, IOModuleDB } import kiama.output.PrettyPrinterTypes.Document import kiama.parsing.ParseResult import kiama.util.{ IO, Source } -import effekt.util.messages.{ BufferedMessaging, CompilerPanic, EffektError, EffektMessaging, FatalPhaseError } +import effekt.util.messages.{ BufferedMessaging, CompilerPanic, EffektError, EffektMessaging, FatalPhaseError, PlainTextError } import effekt.util.paths.file import effekt.util.{ AnsiColoredMessaging, MarkdownSource, getOrElseAborting } @@ -88,11 +88,8 @@ trait Driver extends kiama.util.Compiler[EffektConfig, EffektError] { outer => } } catch { case FatalPhaseError(msg) => context.report(msg) - case e @ CompilerPanic(msg) => - context.report(msg) - e.getStackTrace.foreach { line => - context.info(" at " + line) - } + case e @ CompilerPanic(msg) => generateCrashReport(e, context, config, msg) + // when in server-mode, do not crash but report the error to avoid // restarting the server. case e if config.server() => @@ -108,6 +105,112 @@ trait Driver extends kiama.util.Compiler[EffektConfig, EffektError] { outer => afterCompilation(source, config)(context) } + def generateCrashReport(e: Throwable, context: Context, config: EffektConfig, msg:effekt. util. messages. EffektError): Unit = { + import java.nio.file.{Files, Paths} + import java.nio.charset.StandardCharsets + + // Capture general information + val errorMessage = msg match { + case PlainTextError(content, _, _) => content + case _ => msg + } + + + val effektVersion = effekt.util.Version.effektVersion + val osInfoName = System.getProperty("os.name") + val osInfoArch = System.getProperty("os.arch") + val osInfoVersion = System.getProperty("os.version") + + val javaInfoVersion = System.getProperty("java.version") + val javaInfoVendor = System.getProperty("java.vendor") + val javaInfoRuntime = System.getProperty("java.runtime.name") + + val errorReport = "The compiler unexpectedly panicked. This is a compiler bug.\nPlease report it:" + val issueLink = "https://github.com/effekt-lang/effekt/issues/new?labels=bug" + val stringWriter = new java.io.StringWriter() + e.printStackTrace(new java.io.PrintWriter(stringWriter)) + val stackTrace = stringWriter.toString + + // Collect backend details (if applicable) + val backendInfoName = config.backend() match { + case Backend(name, _, _) => name + case null => "Backend-specific info Name not retrievable" + } + + // Arguments passed to the compiler + val argsMarkdown = config.args.mkString("\n", "\n", "\n") + + // Construct the report content + val reportContent = + s"""|# Effekt Compiler Crash Report + | + |**ERROR** : $errorMessage + | + |$errorReport + |[Submit an issue]($issueLink) + | + |### Compiler Information + |Effekt Version: $effektVersion \n + |Backend: $backendInfoName + | + |### System Information + |Operating System: $osInfoName \n Arch: $osInfoArch \n Version: $osInfoVersion + | + |JVM Version: $javaInfoVersion ($javaInfoVendor, $javaInfoRuntime) + | + |### Full Stack Trace + |``` + |$stackTrace + |``` + | + |### Arguments Passed to the Compiler + |``` + |$argsMarkdown + |``` + |""".stripMargin + + // Write the report to a Markdown file + val outputPath = Paths.get(config.outputPath().getAbsolutePath) + val outputFile = outputPath.resolve("crash-report.md") + try { + Files.createDirectories(outputPath.getParent) + Files.write(outputFile, reportContent.getBytes(StandardCharsets.UTF_8)) + //context.info(s"Crash report saved to: $outputPath") + } catch { + case ex: Exception => + context.info("Failed to save crash report to file: " + ex.getMessage) + } + + // Provide the report link in the user-visible output + val report = + s"""|=== Effekt Compiler Crash Report === + | + |[Error] $errorMessage + | + |The compiler unexpectedly panicked. This is a compiler bug. + |Please report it: $issueLink + | + |Effekt Version: $effektVersion\n + |Backend used: $backendInfoName + | + |A detailed crash report has been written to: + |${outputPath.toAbsolutePath} + | + |Operating System: $osInfoName + | + |JVM Version: $javaInfoVersion + | + |Full Stack Trace: + |$stackTrace + | + |Arguments passed to the compiler: + |$argsMarkdown + |""".stripMargin + + // Print the report to console + context.info(report) + } + /** * Outputs the timing information captured in [[effekt.util.Timers]] by [[effekt.context.Context]]. Either a JSON file * is written to disk or a plain text message is written to stdout.