From d0c170962019b8ab390c8548f7d6740188204caa Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Sun, 22 Dec 2024 12:41:13 +0100 Subject: [PATCH] refactor human compiler error --- .../move/cli/externalLinter/CompilerErrors.kt | 78 ---------------- ...lerError.kt => HumanAptosCompilerError.kt} | 88 +++++++++++++++++-- .../externalLinter/RsExternalLinterUtils.kt | 3 +- .../cli/externalLinter/CompilerErrorsTest.kt | 23 ++--- 4 files changed, 94 insertions(+), 98 deletions(-) delete mode 100644 src/main/kotlin/org/move/cli/externalLinter/CompilerErrors.kt rename src/main/kotlin/org/move/ide/annotator/externalLinter/{CodespanAptosCompilerError.kt => HumanAptosCompilerError.kt} (50%) diff --git a/src/main/kotlin/org/move/cli/externalLinter/CompilerErrors.kt b/src/main/kotlin/org/move/cli/externalLinter/CompilerErrors.kt deleted file mode 100644 index 7f6a1ace..00000000 --- a/src/main/kotlin/org/move/cli/externalLinter/CompilerErrors.kt +++ /dev/null @@ -1,78 +0,0 @@ -package org.move.cli.externalLinter - -import org.move.ide.annotator.externalLinter.AptosCompilerError -import org.move.ide.annotator.externalLinter.CompilerSpan - -fun parseHumanCompilerErrors(outputLines: List): List { - val rawMessages = splitMessages(outputLines) - val compilerErrors = rawMessages.mapNotNull(::rawMessageToCompilerMessage) - return compilerErrors -} - -private val ERROR_START_RE = Regex("^error(\\[E\\d+\\])?:\\s+(.+)") - -enum class ErrorType(val severityLevel: String) { - ERROR("error"), WARNING("warning"), WARNING_LINT("warning [lint]"); -} - -data class RawMessage(val errorType: ErrorType, val lines: MutableList) - -private fun splitMessages(outputLines: List): List { - val rawMessages = mutableListOf() - var message: RawMessage? = null - for (line in outputLines) { - if (line.startsWith("{")) { - break - } - val newErrorType = when { - ERROR_START_RE.find(line) != null -> ErrorType.ERROR - line.startsWith("warning: [lint]") -> ErrorType.WARNING_LINT - line.startsWith("warning:") -> ErrorType.WARNING - else -> null - } - if (newErrorType != null) { - // flush - message?.let { rawMessages.add(it) } - message = RawMessage(newErrorType, mutableListOf()) - } - message?.lines?.add(line) - } - // flush - message?.let { rawMessages.add(it) } - return rawMessages -} - -private fun rawMessageToCompilerMessage(rawMessage: RawMessage): AptosCompilerError? { - val messageLine = rawMessage.lines.first() - val message = when (rawMessage.errorType) { - ErrorType.ERROR -> ERROR_START_RE.find(messageLine)!!.destructured.component2() - ErrorType.WARNING -> messageLine.substringAfter("warning: ") - ErrorType.WARNING_LINT -> messageLine.substringAfter("warning: [lint] ") - } - val primarySpan = parsePrimarySpan(rawMessage.lines) ?: return null - return AptosCompilerError(message, rawMessage.errorType.severityLevel, primarySpan) -} - -private val FILE_POSITION_RE = - Regex("""┌─ (?(?:\p{Alpha}:)?[0-9a-z_A-Z\-\\./]+):(?[0-9]+):(?[0-9]+)""") -private val ERROR_UNDERLINE_RE = - Regex("""^\s*│[^\^]*(\^{2,})""") - -private fun parsePrimarySpan(errorLines: List): CompilerSpan? { - val filePositionMatch = - errorLines.firstNotNullOfOrNull { FILE_POSITION_RE.find(it) } ?: return null - val (fpath, lineStart, columnStart) = filePositionMatch.destructured - - val columnSpan = errorLines - .firstNotNullOfOrNull { ERROR_UNDERLINE_RE.find(it) } - ?.groupValues?.get(1) - ?.length ?: 1 - return CompilerSpan( - fpath, - lineStart = lineStart.toInt(), - lineEnd = lineStart.toInt(), - columnStart = columnStart.toInt(), - columnEnd = columnStart.toInt() + columnSpan, - label = null - ) -} \ No newline at end of file diff --git a/src/main/kotlin/org/move/ide/annotator/externalLinter/CodespanAptosCompilerError.kt b/src/main/kotlin/org/move/ide/annotator/externalLinter/HumanAptosCompilerError.kt similarity index 50% rename from src/main/kotlin/org/move/ide/annotator/externalLinter/CodespanAptosCompilerError.kt rename to src/main/kotlin/org/move/ide/annotator/externalLinter/HumanAptosCompilerError.kt index d2e7458e..ffb0a358 100644 --- a/src/main/kotlin/org/move/ide/annotator/externalLinter/CodespanAptosCompilerError.kt +++ b/src/main/kotlin/org/move/ide/annotator/externalLinter/HumanAptosCompilerError.kt @@ -7,7 +7,7 @@ import com.intellij.util.PathUtil import org.jetbrains.annotations.TestOnly import org.move.stdext.capitalized -data class AptosCompilerError( +data class HumanAptosCompilerError( val message: String, val severityLevel: String, val primarySpan: CompilerSpan, @@ -17,10 +17,7 @@ data class AptosCompilerError( } fun toJsonError(file: PsiFile, document: Document): AptosJsonCompilerError? { - // Some error messages are global, and we *could* show then atop of the editor, - // but they look rather ugly, so just skip them. - val errorSpan = this.primarySpan - val spanFilePath = PathUtil.toSystemIndependentName(errorSpan.filename) + val spanFilePath = PathUtil.toSystemIndependentName(this.primarySpan.filename) if (!file.virtualFile.path.endsWith(spanFilePath)) return null val codeLabel = this.primarySpan.toCodeLabel(document) ?: return null @@ -41,7 +38,7 @@ data class AptosCompilerError( severityLevel: String, filename: String, location: String, - ): AptosCompilerError { + ): HumanAptosCompilerError { val match = Regex("""\[\((?\d+), (?\d+)\), \((?\d+), (?\d+)\)]""") .find(location) ?: error("invalid string") @@ -54,7 +51,7 @@ data class AptosCompilerError( colEnd.toInt(), null ) - return AptosCompilerError(message, severityLevel, span) + return HumanAptosCompilerError(message, severityLevel, span) } } } @@ -106,3 +103,80 @@ data class CompilerSpan( } } } + + +fun parseHumanCompilerErrors(outputLines: List): List { + val rawMessages = splitMessages(outputLines) + val compilerErrors = rawMessages.mapNotNull(::rawMessageToCompilerMessage) + return compilerErrors +} + +private val ERROR_START_RE = Regex("^error(\\[E\\d+\\])?:\\s+(.+)") + +enum class ErrorType(val severityLevel: String) { + ERROR("error"), WARNING("warning"), WARNING_LINT("warning [lint]"); +} + +data class RawMessage(val errorType: ErrorType, val lines: MutableList) + +private fun splitMessages(outputLines: List): List { + val rawMessages = mutableListOf() + var message: RawMessage? = null + for (line in outputLines) { + if (line.startsWith("{")) { + break + } + val newErrorType = when { + ERROR_START_RE.find(line) != null -> ErrorType.ERROR + line.startsWith("warning: [lint]") -> ErrorType.WARNING_LINT + line.startsWith("warning:") -> ErrorType.WARNING + else -> null + } + if (newErrorType != null) { + // flush + message?.let { rawMessages.add(it) } + message = RawMessage(newErrorType, mutableListOf()) + } + message?.lines?.add(line) + } + // flush + message?.let { rawMessages.add(it) } + return rawMessages +} + +private fun rawMessageToCompilerMessage(rawMessage: RawMessage): HumanAptosCompilerError? { + val messageLine = rawMessage.lines.first() + val message = when (rawMessage.errorType) { + ErrorType.ERROR -> ERROR_START_RE.find(messageLine)!!.destructured.component2() + ErrorType.WARNING -> messageLine.substringAfter("warning: ") + ErrorType.WARNING_LINT -> messageLine.substringAfter("warning: [lint] ") + } + // Some error messages are global, and we *could* show then atop of the editor, + // but they look rather ugly, so just skip them. + val primarySpan = parsePrimarySpan(rawMessage.lines) ?: return null + return HumanAptosCompilerError(message, rawMessage.errorType.severityLevel, primarySpan) +} + +private val FILE_POSITION_RE = + Regex("""┌─ (?(?:\p{Alpha}:)?[0-9a-z_A-Z\-\\./]+):(?[0-9]+):(?[0-9]+)""") +private val ERROR_UNDERLINE_RE = + Regex("""^\s*│[^\^]*(\^{2,})""") + +private fun parsePrimarySpan(errorLines: List): CompilerSpan? { + val filePositionMatch = + errorLines.firstNotNullOfOrNull { FILE_POSITION_RE.find(it) } ?: return null + val (fpath, lineStart, columnStart) = filePositionMatch.destructured + + val columnSpan = errorLines + .firstNotNullOfOrNull { ERROR_UNDERLINE_RE.find(it) } + ?.groupValues?.get(1) + ?.length ?: 1 + return CompilerSpan( + fpath, + lineStart = lineStart.toInt(), + lineEnd = lineStart.toInt(), + columnStart = columnStart.toInt(), + columnEnd = columnStart.toInt() + columnSpan, + label = null + ) +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/ide/annotator/externalLinter/RsExternalLinterUtils.kt b/src/main/kotlin/org/move/ide/annotator/externalLinter/RsExternalLinterUtils.kt index 931bd470..88dfd687 100644 --- a/src/main/kotlin/org/move/ide/annotator/externalLinter/RsExternalLinterUtils.kt +++ b/src/main/kotlin/org/move/ide/annotator/externalLinter/RsExternalLinterUtils.kt @@ -26,7 +26,6 @@ import org.apache.commons.text.StringEscapeUtils import org.jetbrains.annotations.Nls import org.move.cli.externalLinter.RsExternalLinterWidget import org.move.cli.externalLinter.externalLinterSettings -import org.move.cli.externalLinter.parseHumanCompilerErrors import org.move.cli.runConfigurations.aptos.Aptos import org.move.cli.runConfigurations.aptos.AptosExternalLinterArgs import org.move.cli.runConfigurations.aptos.isCompilerJsonOutputEnabled @@ -219,7 +218,7 @@ class RsExternalLinterResult( val outputLines: List, val executionTime: Long ) { - val humanCompilerErrors: List get() = parseHumanCompilerErrors(outputLines) + val humanCompilerErrors: List get() = parseHumanCompilerErrors(outputLines) val jsonCompilerErrors: List get() = parseJsonCompilerErrors(outputLines) } diff --git a/src/test/kotlin/org/move/cli/externalLinter/CompilerErrorsTest.kt b/src/test/kotlin/org/move/cli/externalLinter/CompilerErrorsTest.kt index 03d803fa..60adeecc 100644 --- a/src/test/kotlin/org/move/cli/externalLinter/CompilerErrorsTest.kt +++ b/src/test/kotlin/org/move/cli/externalLinter/CompilerErrorsTest.kt @@ -1,6 +1,7 @@ package org.move.cli.externalLinter -import org.move.ide.annotator.externalLinter.AptosCompilerError +import org.move.ide.annotator.externalLinter.HumanAptosCompilerError +import org.move.ide.annotator.externalLinter.parseHumanCompilerErrors import org.move.utils.tests.MvTestBase class CompilerErrorsTest: MvTestBase() { @@ -40,7 +41,7 @@ error: no function named `match` found "Error": "Move compilation failed: exiting with checking errors" } """, listOf( - AptosCompilerError.forTest( + HumanAptosCompilerError.forTest( message = "no function named `match` found", severityLevel = "error", filename = "/home/mkurnikov/main/sources/main.move", @@ -70,7 +71,7 @@ error: missing acquires annotation for `S` "Error": "Move compilation failed: exiting with checking errors" } """, listOf( - AptosCompilerError.forTest( + HumanAptosCompilerError.forTest( "missing acquires annotation for `S`", "error", "/home/mkurnikov/main/sources/main.move", @@ -108,13 +109,13 @@ error: missing acquires annotation for `S` "Error": "Move compilation failed: exiting with checking errors" } """, listOf( - AptosCompilerError.forTest( + HumanAptosCompilerError.forTest( "missing acquires annotation for `S`", "error", filename = "/home/mkurnikov/main/sources/main.move", location = "[(8, 9), (8, 13)]" ), - AptosCompilerError.forTest( + HumanAptosCompilerError.forTest( "missing acquires annotation for `S`", "error", filename = "/home/mkurnikov/main/sources/main2.move", @@ -140,7 +141,7 @@ error[E04007]: incompatible types │ Found: 'u8'. It is not compatible with the other type. } """, listOf( - AptosCompilerError.forTest( + HumanAptosCompilerError.forTest( message = "incompatible types", severityLevel = "error", filename = "/home/mkurnikov/main/sources/main2.move", @@ -164,7 +165,7 @@ error[E04007]: incompatible types "Error": "Move compilation failed: Compilation error" } """, listOf( - AptosCompilerError.forTest( + HumanAptosCompilerError.forTest( message = "incompatible types", severityLevel = "error", filename = "/tmp/main/sources/main.move", @@ -195,7 +196,7 @@ error[E05001]: ability constraint not satisfied "Error": "Move compilation failed: Compilation error" } """, listOf( - AptosCompilerError.forTest( + HumanAptosCompilerError.forTest( message = "ability constraint not satisfied", severityLevel = "error", filename = "/tmp/main/sources/main.move", @@ -222,7 +223,7 @@ error: value of type `main2::S` does not have the `drop` ability "Error": "Move compilation failed: exiting with stackless-bytecode analysis errors" } """, listOf( - AptosCompilerError.forTest( + HumanAptosCompilerError.forTest( message = "value of type `main2::S` does not have the `drop` ability", severityLevel = "error", filename = "/tmp/main/sources/main2.move", @@ -249,7 +250,7 @@ error: the function takes 0 arguments but 2 were provided "Error": "Move compilation failed: exiting with checking errors" } """, listOf( - AptosCompilerError.forTest( + HumanAptosCompilerError.forTest( message = "the function takes 0 arguments but 2 were provided", severityLevel = "error", filename = "/home/mkurnikov/code/move-test-location-example/sources/main2.move", @@ -258,7 +259,7 @@ error: the function takes 0 arguments but 2 were provided ) ) - private fun doTest(compilerOutput: String, expectedMessages: List) { + private fun doTest(compilerOutput: String, expectedMessages: List) { val messages = parseHumanCompilerErrors(compilerOutput.trimIndent().lines()) val messageTestStrings = messages.map { it.toTestString() }