Skip to content

Commit

Permalink
refactor human compiler error
Browse files Browse the repository at this point in the history
  • Loading branch information
mkurnikov committed Dec 22, 2024
1 parent 0e17107 commit d0c1709
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 98 deletions.
78 changes: 0 additions & 78 deletions src/main/kotlin/org/move/cli/externalLinter/CompilerErrors.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand All @@ -41,7 +38,7 @@ data class AptosCompilerError(
severityLevel: String,
filename: String,
location: String,
): AptosCompilerError {
): HumanAptosCompilerError {
val match =
Regex("""\[\((?<lineStart>\d+), (?<columnStart>\d+)\), \((?<lineEnd>\d+), (?<columnEnd>\d+)\)]""")
.find(location) ?: error("invalid string")
Expand All @@ -54,7 +51,7 @@ data class AptosCompilerError(
colEnd.toInt(),
null
)
return AptosCompilerError(message, severityLevel, span)
return HumanAptosCompilerError(message, severityLevel, span)
}
}
}
Expand Down Expand Up @@ -106,3 +103,80 @@ data class CompilerSpan(
}
}
}


fun parseHumanCompilerErrors(outputLines: List<String>): List<HumanAptosCompilerError> {
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<String>)

private fun splitMessages(outputLines: List<String>): List<RawMessage> {
val rawMessages = mutableListOf<RawMessage>()
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("""┌─ (?<file>(?:\p{Alpha}:)?[0-9a-z_A-Z\-\\./]+):(?<line>[0-9]+):(?<column>[0-9]+)""")
private val ERROR_UNDERLINE_RE =
Regex("""^\s*│[^\^]*(\^{2,})""")

private fun parsePrimarySpan(errorLines: List<String>): 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
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -219,7 +218,7 @@ class RsExternalLinterResult(
val outputLines: List<String>,
val executionTime: Long
) {
val humanCompilerErrors: List<AptosCompilerError> get() = parseHumanCompilerErrors(outputLines)
val humanCompilerErrors: List<HumanAptosCompilerError> get() = parseHumanCompilerErrors(outputLines)
val jsonCompilerErrors: List<AptosJsonCompilerError> get() = parseJsonCompilerErrors(outputLines)
}

Expand Down
23 changes: 12 additions & 11 deletions src/test/kotlin/org/move/cli/externalLinter/CompilerErrorsTest.kt
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -258,7 +259,7 @@ error: the function takes 0 arguments but 2 were provided
)
)

private fun doTest(compilerOutput: String, expectedMessages: List<AptosCompilerError>) {
private fun doTest(compilerOutput: String, expectedMessages: List<HumanAptosCompilerError>) {
val messages = parseHumanCompilerErrors(compilerOutput.trimIndent().lines())

val messageTestStrings = messages.map { it.toTestString() }
Expand Down

0 comments on commit d0c1709

Please sign in to comment.