Add info about the end of range in scripting REPL compiler messages

This commit is contained in:
Ilya Muradyan
2020-02-12 16:57:52 +01:00
committed by Ilya Chernikov
parent 5e33612238
commit 489290263f
9 changed files with 119 additions and 17 deletions
@@ -22,6 +22,8 @@ interface CompilerMessageSourceLocation : Serializable {
val path: String
val line: Int
val column: Int
val lineEnd: Int get() = -1
val columnEnd: Int get() = -1
val lineContent: String?
}
@@ -46,3 +48,31 @@ data class CompilerMessageLocation private constructor(
private val serialVersionUID: Long = 8228357578L
}
}
data class CompilerMessageLocationWithRange private constructor(
override val path: String,
override val line: Int,
override val column: Int,
override val lineEnd: Int,
override val columnEnd: Int,
override val lineContent: String?
) : CompilerMessageSourceLocation {
override fun toString(): String =
path + (if (line != -1 || column != -1) " ($line:$column)" else "")
companion object {
@JvmStatic
fun create(
path: String?,
lineStart: Int,
columnStart: Int,
lineEnd: Int?,
columnEnd: Int?,
lineContent: String?
): CompilerMessageLocationWithRange? =
if (path == null) null else CompilerMessageLocationWithRange(path, lineStart, columnStart, lineEnd ?: -1, columnEnd ?: -1, lineContent)
private val serialVersionUID: Long = 8228357578L
}
}
@@ -31,6 +31,6 @@ interface MessageCollectorBasedReporter : DiagnosticMessageReporter {
override fun report(diagnostic: Diagnostic, file: PsiFile, render: String) = messageCollector.report(
AnalyzerWithCompilerReport.convertSeverity(diagnostic.severity),
render,
MessageUtil.psiFileToMessageLocation(file, file.name, DiagnosticUtils.getLineAndColumn(diagnostic))
MessageUtil.psiFileToMessageLocation(file, file.name, DiagnosticUtils.getLineAndColumnRange(diagnostic))
)
}
@@ -32,21 +32,23 @@ public class MessageUtil {
private MessageUtil() {}
@Nullable
public static CompilerMessageLocation psiElementToMessageLocation(@Nullable PsiElement element) {
public static CompilerMessageSourceLocation psiElementToMessageLocation(@Nullable PsiElement element) {
if (element == null) return null;
PsiFile file = element.getContainingFile();
return psiFileToMessageLocation(file, "<no path>", DiagnosticUtils.getLineAndColumnInPsiFile(file, element.getTextRange()));
return psiFileToMessageLocation(file, "<no path>", DiagnosticUtils.getLineAndColumnRangeInPsiFile(file, element.getTextRange()));
}
@Nullable
public static CompilerMessageLocation psiFileToMessageLocation(
public static CompilerMessageSourceLocation psiFileToMessageLocation(
@NotNull PsiFile file,
@Nullable String defaultValue,
@NotNull PsiDiagnosticUtils.LineAndColumn lineAndColumn
@NotNull PsiDiagnosticUtils.LineAndColumnRange range
) {
VirtualFile virtualFile = file.getVirtualFile();
String path = virtualFile != null ? virtualFileToPath(virtualFile) : defaultValue;
return CompilerMessageLocation.create(path, lineAndColumn.getLine(), lineAndColumn.getColumn(), lineAndColumn.getLineContent());
PsiDiagnosticUtils.LineAndColumn start = range.getStart();
PsiDiagnosticUtils.LineAndColumn end = range.getEnd();
return CompilerMessageLocationWithRange.create(path, start.getLine(), start.getColumn(), end.getLine(), end.getColumn(), start.getLineContent());
}
@NotNull
@@ -84,6 +84,24 @@ public class DiagnosticUtils {
return PsiDiagnosticUtils.offsetToLineAndColumn(document, range.getStartOffset());
}
@NotNull
public static PsiDiagnosticUtils.LineAndColumnRange getLineAndColumnRange(@NotNull Diagnostic diagnostic) {
PsiFile file = diagnostic.getPsiFile();
List<TextRange> textRanges = diagnostic.getTextRanges();
if (textRanges.isEmpty()) return PsiDiagnosticUtils.LineAndColumnRange.NONE;
TextRange firstRange = firstRange(textRanges);
return getLineAndColumnRangeInPsiFile(file, firstRange);
}
@NotNull
public static PsiDiagnosticUtils.LineAndColumnRange getLineAndColumnRangeInPsiFile(PsiFile file, TextRange range) {
Document document = file.getViewProvider().getDocument();
return new PsiDiagnosticUtils.LineAndColumnRange(
PsiDiagnosticUtils.offsetToLineAndColumn(document, range.getStartOffset()),
PsiDiagnosticUtils.offsetToLineAndColumn(document, range.getEndOffset())
);
}
public static void throwIfRunningOnServer(Throwable e) {
// This is needed for the Web Demo server to log the exceptions coming from the analyzer instead of showing them in the editor.
if (System.getProperty("kotlin.running.in.server.mode", "false").equals("true") || ApplicationManager.getApplication().isUnitTestMode()) {
@@ -116,4 +116,35 @@ public class PsiDiagnosticUtils {
return "(" + line + "," + column + ")";
}
}
public static final class LineAndColumnRange {
public static final LineAndColumnRange NONE = new LineAndColumnRange(LineAndColumn.NONE, LineAndColumn.NONE);
private final LineAndColumn start;
private final LineAndColumn end;
public LineAndColumnRange(LineAndColumn start, LineAndColumn end) {
this.start = start;
this.end = end;
}
public LineAndColumn getStart() {
return start;
}
public LineAndColumn getEnd() {
return end;
}
// NOTE: This method is used for presenting positions to the user
@Override
public String toString() {
if (start.line == end.line) {
return "(" + start.line + "," + start.column + "-" + end.column + ")";
}
return start.toString() + " - " + end.toString();
}
}
}
@@ -100,7 +100,9 @@ class ReplTest : TestCase() {
),
sequenceOf(
makeFailureResult(
"Unresolved reference: ppp", location = SourceCode.Location(SourceCode.Position(3, 11))
"Unresolved reference: ppp", location = SourceCode.Location(
SourceCode.Position(3, 11), SourceCode.Position(3, 14)
)
)
)
)
@@ -37,10 +37,20 @@ class ScriptDiagnosticsMessageCollector(private val parentMessageCollector: Mess
if (mappedSeverity != null) {
val mappedLocation = location?.let {
if (it.line < 0 && it.column < 0) null // special location created by CompilerMessageLocation.create
else if (it.lineEnd < 0 && it.columnEnd < 0) SourceCode.Location(
SourceCode.Position(
it.line,
it.column
)
)
else SourceCode.Location(
SourceCode.Position(
it.line,
it.column
),
SourceCode.Position(
it.lineEnd,
it.columnEnd
)
)
}
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.scripting.ide_services
import junit.framework.TestCase
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocationWithRange
import org.jetbrains.kotlin.scripting.ide_services.test_util.JvmTestRepl
import org.jetbrains.kotlin.scripting.ide_services.test_util.SourceCodeTestImpl
import java.io.File
@@ -86,6 +86,8 @@ class JvmIdeServicesTest : TestCase() {
} else {
assertEquals(3, loc.line)
assertEquals(11, loc.column)
assertEquals(3, loc.lineEnd)
assertEquals(14, loc.columnEnd)
}
} else {
fail("Result should be an error")
@@ -115,6 +117,8 @@ class JvmIdeServicesTest : TestCase() {
} else {
assertEquals(3, loc.line)
assertEquals(13, loc.column)
assertEquals(3, loc.lineEnd)
assertEquals(16, loc.columnEnd)
}
} else {
fail("Result should be an error")
@@ -383,17 +387,19 @@ private fun checkCompile(repl: JvmTestRepl, line: String): LinkedSnippet<KJvmCom
private data class CompilationErrors(
val message: String,
val location: CompilerMessageLocation?
val location: CompilerMessageLocationWithRange?
)
private fun <T> ResultWithDiagnostics<T>.getErrors(): CompilationErrors =
CompilationErrors(
reports.joinToString("\n") { report ->
report.location?.let { loc ->
CompilerMessageLocation.create(
CompilerMessageLocationWithRange.create(
report.sourcePath,
loc.start.line,
loc.start.col,
loc.end?.line,
loc.end?.col,
null
)?.toString()?.let {
"$it "
@@ -408,10 +414,12 @@ private fun <T> ResultWithDiagnostics<T>.getErrors(): CompilationErrors =
}
}?.let {
val loc = it.location ?: return@let null
CompilerMessageLocation.create(
CompilerMessageLocationWithRange.create(
it.sourcePath,
loc.start.line,
loc.start.col,
loc.end?.line,
loc.end?.col,
null
)
}
@@ -189,10 +189,10 @@ class ReplCompletionAndErrorsAnalysisTest : TestCase() {
val c = foob
""".trimIndent()
expect {
addError(1, 16, "Type mismatch: inferred type is String but Int was expected", "ERROR")
addError(1, 22, "The floating-point literal does not conform to the expected type String", "ERROR")
addError(2, 14, "Type mismatch: inferred type is String but Int was expected", "ERROR")
addError(3, 9, "Unresolved reference: foob", "ERROR")
addError(1, 16, 1, 20, "Type mismatch: inferred type is String but Int was expected", "ERROR")
addError(1, 22, 1, 26, "The floating-point literal does not conform to the expected type String", "ERROR")
addError(2, 14, 2, 19, "Type mismatch: inferred type is String but Int was expected", "ERROR")
addError(3, 9, 3, 13, "Unresolved reference: foob", "ERROR")
}
}
}
@@ -341,14 +341,15 @@ class ReplCompletionAndErrorsAnalysisTest : TestCase() {
}
val errors = ExpectedList<ScriptDiagnostic>(run::doErrorCheck)
fun addError(startLine: Int, startCol: Int, message: String, severity: String) {
fun addError(startLine: Int, startCol: Int, endLine: Int, endCol: Int, message: String, severity: String) {
errors.add(
ScriptDiagnostic(
ScriptDiagnostic.unspecifiedError,
message,
ScriptDiagnostic.Severity.valueOf(severity),
location = SourceCode.Location(
SourceCode.Position(startLine, startCol)
SourceCode.Position(startLine, startCol),
SourceCode.Position(endLine, endCol)
)
)
)