Skip to content

Commit

Permalink
add json compiler error
Browse files Browse the repository at this point in the history
  • Loading branch information
mkurnikov committed Dec 20, 2024
1 parent 469c8ac commit be3b0d9
Show file tree
Hide file tree
Showing 17 changed files with 353 additions and 125 deletions.
41 changes: 19 additions & 22 deletions src/main/kotlin/org/move/cli/externalLinter/CompilerErrors.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package org.move.cli.externalLinter

import org.move.ide.annotator.AptosCompilerMessage
import org.move.ide.annotator.AptosCompilerSpan
import org.move.ide.annotator.externalLinter.AptosCompilerError
import org.move.ide.annotator.externalLinter.CompilerSpan

fun parseCompilerErrors(outputLines: List<String>): List<AptosCompilerMessage> {
fun parseHumanCompilerErrors(outputLines: List<String>): List<AptosCompilerError> {
val rawMessages = splitMessages(outputLines)
val messages = rawMessages.map(::rawMessageToCompilerMessage)
return messages
val compilerErrors = rawMessages.mapNotNull(::rawMessageToCompilerMessage)
return compilerErrors
}

private val ERROR_START_RE = Regex("^error(\\[E\\d+\\])?:\\s+(.+)")
Expand Down Expand Up @@ -42,40 +42,37 @@ private fun splitMessages(outputLines: List<String>): List<RawMessage> {
return rawMessages
}

private fun rawMessageToCompilerMessage(rawMessage: RawMessage): AptosCompilerMessage {
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 (_, message) = ERROR_START_RE.find(messageLine)!!.destructured
val spans = splitSpans(rawMessage.lines)
return AptosCompilerMessage(message, rawMessage.errorType.severityLevel, spans)
val primarySpan = parsePrimarySpan(rawMessage.lines) ?: return null
return AptosCompilerError(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 splitSpans(errorLines: List<String>): List<AptosCompilerSpan> {
private fun parsePrimarySpan(errorLines: List<String>): CompilerSpan? {
val filePositionMatch =
errorLines.firstNotNullOfOrNull { FILE_POSITION_RE.find(it) } ?: return emptyList()
val (fileName, lineStart, columnStart) = filePositionMatch.destructured
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 listOf(
AptosCompilerSpan(
fileName,
lineStart = lineStart.toInt(),
lineEnd = lineStart.toInt(),
columnStart = columnStart.toInt(),
columnEnd = columnStart.toInt() + columnSpan,
isPrimary = true,
label = null
)
return CompilerSpan(
fpath,
lineStart = lineStart.toInt(),
lineEnd = lineStart.toInt(),
columnStart = columnStart.toInt(),
columnEnd = columnStart.toInt() + columnSpan,
label = null
)
}
10 changes: 10 additions & 0 deletions src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.intellij.execution.process.CapturingProcessHandler
import com.intellij.execution.process.ProcessListener
import com.intellij.execution.process.ProcessOutput
import com.intellij.openapi.Disposable
import com.intellij.openapi.options.advanced.AdvancedSettings
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.vfs.VirtualFile
Expand Down Expand Up @@ -92,6 +93,9 @@ data class Aptos(val cliLocation: Path, val parentDisposable: Disposable?): Disp
) {
add("--skip-fetch-latest-git-deps")
}
if (project.isCompilerJsonOutputEnabled) {
add("--experiments"); add("compiler-message-format-json")
}
}
}
}
Expand Down Expand Up @@ -223,11 +227,17 @@ data class Aptos(val cliLocation: Path, val parentDisposable: Disposable?): Disp
if (settings.skipFetchLatestGitDeps && "--skip-fetch-latest-git-deps" !in extraArguments) {
add("--skip-fetch-latest-git-deps")
}
if (project.isCompilerJsonOutputEnabled) {
add("--experiments"); add("compiler-message-format-json")
}
}
}

override fun dispose() {}
}

val Project.isCompilerJsonOutputEnabled
get() = this.moveSettings.enableMove2
&& AdvancedSettings.getBoolean("org.move.aptos.compile.message.json")

val MoveProject.workingDirectory: Path get() = this.currentPackage.contentRoot.pathAsPath
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ sealed class AptosExitStatus(val message: String) {
}
}

private val JSON_MAPPER: ObjectMapper = ObjectMapper()
val JSON_MAPPER: ObjectMapper = ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.registerKotlinModule()
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package org.move.ide.annotator
import com.intellij.codeHighlighting.TextEditorHighlightingPassFactoryRegistrar
import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar
import com.intellij.openapi.project.Project
import org.move.ide.annotator.externalLinter.RsExternalLinterPassFactory

class RsHighlightingPassFactoryRegistrar : TextEditorHighlightingPassFactoryRegistrar {
override fun registerHighlightingPassFactory(registrar: TextEditorHighlightingPassRegistrar, project: Project) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
package org.move.ide.annotator
package org.move.ide.annotator.externalLinter

import com.intellij.openapi.editor.Document
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiFile
import com.intellij.util.PathUtil
import org.jetbrains.annotations.TestOnly
import org.move.stdext.capitalized

data class AptosCompilerMessage(
val text: String,
data class AptosCompilerError(
val message: String,
val severityLevel: String,
val spans: List<AptosCompilerSpan>
val primarySpan: CompilerSpan,
) {
val mainSpan: AptosCompilerSpan?
get() {
val validSpan = spans.filter { it.isValid() }.firstOrNull { it.isPrimary } ?: return null
return validSpan
// return generateSequence(validSpan) { it.expansion?.span }.last()
// .takeIf { it.isValid() && !it.file_name.startsWith("<") }
}

fun toTestString(): String {
return "$severityLevel: '$text' at ${mainSpan?.toTestString()}"
return "$severityLevel: '$message' at ${primarySpan.toTestString()}"
}

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)
if (!file.virtualFile.path.endsWith(spanFilePath)) return null

val codeLabel = this.primarySpan.toCodeLabel(document) ?: return null
val jsonCompilerError = AptosJsonCompilerError(
severity = this.severityLevel.capitalized(),
code = null,
message = this.message,
labels = listOf(codeLabel),
notes = listOf()
)
return jsonCompilerError
}

companion object {
Expand All @@ -28,39 +41,43 @@ data class AptosCompilerMessage(
severityLevel: String,
filename: String,
location: String,
): AptosCompilerMessage {
): AptosCompilerError {
val match =
Regex("""\[\((?<lineStart>\d+), (?<columnStart>\d+)\), \((?<lineEnd>\d+), (?<columnEnd>\d+)\)]""")
.find(location) ?: error("invalid string")
val (lineStart, colStart, lineEnd, colEnd) = match.destructured
val span = AptosCompilerSpan(
val span = CompilerSpan(
filename,
lineStart.toInt(),
lineEnd.toInt(),
colStart.toInt(),
colEnd.toInt(),
true,
null
)
return AptosCompilerMessage(message, severityLevel, listOf(span))
return AptosCompilerError(message, severityLevel, span)
}
}
}

// https://doc.rust-lang.org/nightly/nightly-rustc/syntax/json/struct.DiagnosticSpan.html
data class AptosCompilerSpan(
data class CompilerSpan(
val filename: String,
val lineStart: Int,
val lineEnd: Int,
val columnStart: Int,
val columnEnd: Int,
val isPrimary: Boolean,
// val text: List<String>,
val label: String?,
// val suggested_replacement: String?,
// val suggestion_applicability: Applicability?,
// val expansion: Expansion?
) {
fun toCodeLabel(document: Document): CodeLabel? {
val textRange = this.toTextRange(document) ?: return null
return CodeLabel(
"Primary",
filename,
CodeRange(textRange.startOffset, textRange.endOffset),
""
)
}

fun toTextRange(document: Document): TextRange? {
val startOffset = toOffset(document, lineStart, columnStart)
val endOffset = toOffset(document, lineEnd, columnEnd)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
@file:Suppress("PropertyName")

package org.move.ide.annotator.externalLinter

import com.fasterxml.jackson.core.JsonProcessingException
import com.intellij.openapi.util.TextRange
import org.move.cli.runConfigurations.aptos.JSON_MAPPER

fun parseJsonCompilerErrors(outputLines: List<String>): List<AptosJsonCompilerError> {
return outputLines
.mapNotNull { AptosJsonCompilerError.fromLine(it) }
}

data class AptosJsonCompilerError(
val severity: String,
val code: String?,
val message: String,
val labels: List<CodeLabel>,
val notes: List<String>,
) {
fun toTestString(): String =
"$severity: '$message' " +
"at ${labels.find { it.style == "Primary" }?.range?.toTextRange()}"

companion object {
fun fromLine(line: String): AptosJsonCompilerError? {
if (!line.startsWith("{\"")) return null
try {
val compilerMessage =
JSON_MAPPER.readValue(line, AptosJsonCompilerError::class.java)
return compilerMessage
} catch (_: JsonProcessingException) {
return null
}
}
}
}

data class CodeLabel(
val style: String,
val file_id: String,
val range: CodeRange,
val message: String,
)

data class CodeRange(
val start: Int,
val end: Int,
) {
fun toTextRange(): TextRange = TextRange(start, end)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* found in the LICENSE file.
*/

package org.move.ide.annotator
package org.move.ide.annotator.externalLinter

import com.intellij.codeHighlighting.DirtyScopeTrackingHighlightingPassFactory
import com.intellij.codeHighlighting.TextEditorHighlightingPass
Expand Down
Loading

0 comments on commit be3b0d9

Please sign in to comment.