Kapt: Replace original Javac diagnostic messages with those with Kotlin location mapped
There is no Messages dialog in newer versions of IDEA/Android Studio in which the error messages were mapped before. The new Build window shows only the original locations, so now we need to replace Java file diagnostics with ones mapped to Kotlin source files. The side effect is that diagnostics on the same locations are automatically merged.
This commit is contained in:
+2
-20
@@ -57,9 +57,8 @@ fun parse(lineText: String, reader: OutputLineReader, messages: MutableList<Mess
|
||||
val column = if (matcher.groupCount() >= 2) matcher.group(2)?.toInt() ?: 1 else 1
|
||||
|
||||
if (line != null) {
|
||||
val mainPosition = SourceFilePosition(file, SourcePosition(line, column, column))
|
||||
val kotlinKaptPosition = findAdditionalKotlinLocationFromKapt(message)
|
||||
return addMessage(Message(getMessageKind(severity), message.trim(), kotlinKaptPosition ?: mainPosition), messages)
|
||||
val position = SourceFilePosition(file, SourcePosition(line, column, column))
|
||||
return addMessage(Message(getMessageKind(severity), message.trim(), position), messages)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,26 +72,9 @@ fun parse(lineText: String, reader: OutputLineReader, messages: MutableList<Mess
|
||||
return false
|
||||
}
|
||||
|
||||
private fun findAdditionalKotlinLocationFromKapt(message: String): SourceFilePosition? {
|
||||
for (lineText in message.lines()) {
|
||||
val matcher = ADDITIONAL_KOTLIN_POSITION_FROM_KAPT_PATTERN.matcher(lineText.trim()).takeIf { it.matches() } ?: continue
|
||||
val filePath = matcher.group(1) ?: break
|
||||
|
||||
// Check both Unix (/...) and Windows (C:\...) patterns
|
||||
if (!filePath.startsWith("/") && !filePath.drop(1).startsWith(":\\")) break
|
||||
|
||||
val line = matcher.group(2).toIntOrNull() ?: break
|
||||
val column = matcher.group(3).toIntOrNull() ?: break
|
||||
return SourceFilePosition(File(filePath), SourcePosition(line, column, column))
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private val COLON = ":"
|
||||
private val KOTLIN_POSITION_PATTERN = Pattern.compile("\\(([0-9]*), ([0-9]*)\\)")
|
||||
private val JAVAC_POSITION_PATTERN = Pattern.compile("([0-9]+)")
|
||||
private val ADDITIONAL_KOTLIN_POSITION_FROM_KAPT_PATTERN = Pattern.compile("^Kotlin location: (.+): \\((\\d+), (\\d+)\\)$")
|
||||
|
||||
private fun String.amendNextLinesIfNeeded(reader: OutputLineReader): String {
|
||||
var nextLine = reader.readLine()
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.jetbrains.kotlin.kapt3.javac
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.sun.tools.javac.tree.JCTree
|
||||
import com.sun.tools.javac.util.*
|
||||
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType
|
||||
@@ -29,10 +28,11 @@ import org.jetbrains.kotlin.kapt3.stubs.KaptStubLineInformation
|
||||
import org.jetbrains.kotlin.kapt3.stubs.KotlinPosition
|
||||
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedWriter
|
||||
import org.jetbrains.kotlin.kapt3.util.isJava9OrLater
|
||||
import java.io.File
|
||||
import java.io.PrintWriter
|
||||
import java.io.*
|
||||
import javax.tools.Diagnostic
|
||||
import javax.tools.JavaFileObject
|
||||
import javax.tools.JavaFileObject.Kind
|
||||
import javax.tools.SimpleJavaFileObject
|
||||
import com.sun.tools.javac.util.List as JavacList
|
||||
|
||||
class KaptJavaLog(
|
||||
@@ -101,18 +101,39 @@ class KaptJavaLog(
|
||||
val kotlinPosition = stubLineInfo.getPositionInKotlinFile(sourceFile, targetElement.tree)
|
||||
val kotlinFile = kotlinPosition?.let { getKotlinSourceFile(it) }
|
||||
if (kotlinPosition != null && kotlinFile != null) {
|
||||
val locationMessage = "$KOTLIN_LOCATION_PREFIX${kotlinFile.absolutePath}: (${kotlinPosition.line}, ${kotlinPosition.column})"
|
||||
val locationDiagnostic = diags.note(null as DiagnosticSource?, null, "proc.messager", locationMessage)
|
||||
val wrappedDiagnostic = JCDiagnostic.MultilineDiagnostic(diagnostic, JavacList.of(locationDiagnostic))
|
||||
val flags = JCDiagnostic.DiagnosticFlag.values().filterTo(mutableSetOf(), diagnostic::isFlagSet)
|
||||
|
||||
reportDiagnostic(wrappedDiagnostic)
|
||||
return // Avoid reporting the diagnostic twice
|
||||
val kotlinDiagnostic = diags.create(
|
||||
diagnostic.type,
|
||||
diagnostic.lintCategory,
|
||||
flags,
|
||||
DiagnosticSource(KotlinFileObject(kotlinFile), this),
|
||||
JCDiagnostic.SimpleDiagnosticPosition(kotlinPosition.pos),
|
||||
diagnostic.code.stripCompilerKeyPrefix(),
|
||||
*diagnostic.args
|
||||
)
|
||||
|
||||
reportDiagnostic(kotlinDiagnostic)
|
||||
|
||||
// Avoid reporting the diagnostic twice
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
reportDiagnostic(diagnostic)
|
||||
}
|
||||
|
||||
private fun String.stripCompilerKeyPrefix(): String {
|
||||
for (kind in listOf("err", "warn", "misc", "note")) {
|
||||
val prefix = "compiler.$kind."
|
||||
if (startsWith(prefix)) {
|
||||
return drop(prefix.length)
|
||||
}
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
private fun reportDiagnostic(diagnostic: JCDiagnostic) {
|
||||
if (diagnostic.kind == Diagnostic.Kind.ERROR) {
|
||||
val oldErrors = nerrors
|
||||
@@ -249,4 +270,14 @@ private fun JCDiagnostic.Factory.errorJava9Aware(
|
||||
else {
|
||||
this.error(source, pos, key, *args)
|
||||
}
|
||||
}
|
||||
|
||||
private data class KotlinFileObject(val file: File) : SimpleJavaFileObject(file.toURI(), Kind.SOURCE) {
|
||||
override fun openOutputStream() = file.outputStream()
|
||||
override fun openWriter() = file.writer()
|
||||
override fun openInputStream() = file.inputStream()
|
||||
override fun getCharContent(ignoreEncodingErrors: Boolean) = file.readText()
|
||||
override fun getLastModified() = file.lastModified()
|
||||
override fun openReader(ignoreEncodingErrors: Boolean) = file.reader()
|
||||
override fun delete() = file.delete()
|
||||
}
|
||||
+6
-14
@@ -19,15 +19,13 @@ package org.jetbrains.kotlin.kapt3.stubs
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.sun.tools.javac.tree.JCTree
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
||||
import org.jetbrains.kotlin.kapt3.KaptContext
|
||||
import org.jetbrains.org.objectweb.asm.tree.ClassNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.FieldNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
|
||||
data class KotlinPosition(val path: String, val isRelativePath: Boolean, val line: Int, val column: Int)
|
||||
data class KotlinPosition(val path: String, val isRelativePath: Boolean, val pos: Int)
|
||||
|
||||
private typealias LineInfoMap = MutableMap<String, KotlinPosition>
|
||||
|
||||
@@ -67,10 +65,9 @@ class KaptLineMappingCollector(private val kaptContext: KaptContext<*>) {
|
||||
val fqName = ois.readUTF()
|
||||
val path = ois.readUTF()
|
||||
val isRelative = ois.readBoolean()
|
||||
val line = ois.readInt()
|
||||
val column = ois.readInt()
|
||||
val pos = ois.readInt()
|
||||
|
||||
lineInfo[fqName] = KotlinPosition(path, isRelative, line, column)
|
||||
lineInfo[fqName] = KotlinPosition(path, isRelative, pos)
|
||||
}
|
||||
|
||||
val signatureCount = ois.readInt()
|
||||
@@ -119,13 +116,9 @@ class KaptLineMappingCollector(private val kaptContext: KaptContext<*>) {
|
||||
|
||||
private fun register(fqName: String, psiElement: PsiElement) {
|
||||
val textRange = psiElement.textRange ?: return
|
||||
val psiFile = psiElement.containingFile
|
||||
val lineAndColumn = DiagnosticUtils.getLineAndColumnInPsiFile(psiFile, textRange)
|
||||
|
||||
if (lineAndColumn.line >= 0 && lineAndColumn.column >= 0) {
|
||||
val (path, isRelative) = getFilePathRelativePreferred(psiFile)
|
||||
lineInfo[fqName] = KotlinPosition(path, isRelative, lineAndColumn.line, lineAndColumn.column)
|
||||
}
|
||||
val (path, isRelative) = getFilePathRelativePreferred(psiElement.containingFile)
|
||||
lineInfo[fqName] = KotlinPosition(path, isRelative, textRange.startOffset)
|
||||
}
|
||||
|
||||
private fun getFilePathRelativePreferred(file: PsiFile): Pair<String, Boolean> {
|
||||
@@ -156,8 +149,7 @@ class KaptLineMappingCollector(private val kaptContext: KaptContext<*>) {
|
||||
oos.writeUTF(fqName)
|
||||
oos.writeUTF(kotlinPosition.path)
|
||||
oos.writeBoolean(kotlinPosition.isRelativePath)
|
||||
oos.writeInt(kotlinPosition.line)
|
||||
oos.writeInt(kotlinPosition.column)
|
||||
oos.writeInt(kotlinPosition.pos)
|
||||
}
|
||||
|
||||
oos.writeInt(signatureInfo.size)
|
||||
|
||||
+35
-7
@@ -17,16 +17,20 @@
|
||||
package org.jetbrains.kotlin.kapt3.test
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.sun.tools.javac.comp.CompileStates
|
||||
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit
|
||||
import com.sun.tools.javac.util.JCDiagnostic
|
||||
import com.sun.tools.javac.util.Log
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.checkers.CheckerTestUtil
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
|
||||
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestCase
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestFiles
|
||||
import org.jetbrains.kotlin.codegen.GenerationUtils
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.kapt3.Kapt3BuilderFactory
|
||||
@@ -38,6 +42,7 @@ import org.jetbrains.kotlin.kapt3.parseJavaFiles
|
||||
import org.jetbrains.kotlin.kapt3.prettyPrint
|
||||
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter
|
||||
import org.jetbrains.kotlin.kapt3.util.KaptLogger
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
@@ -65,6 +70,26 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadMultiFiles(files: List<TestFile>) {
|
||||
val project = myEnvironment.project
|
||||
val psiManager = PsiManager.getInstance(project)
|
||||
|
||||
val tmpDir = Files.createTempDirectory("kaptTest").toFile()
|
||||
tempFiles += tmpDir
|
||||
|
||||
val ktFiles = ArrayList<KtFile>(files.size)
|
||||
for (file in files.sorted()) {
|
||||
if (file.name.endsWith(".kt")) {
|
||||
val content = CheckerTestUtil.parseDiagnosedRanges(file.content, ArrayList<CheckerTestUtil.DiagnosedRange>(0))
|
||||
val tmpKtFile = File(tmpDir, file.name).apply { writeText(content) }
|
||||
val virtualFile = StandardFileSystems.local().findFileByPath(tmpKtFile.path) ?: error("Can't find ${file.name}")
|
||||
ktFiles.add(psiManager.findFile(virtualFile) as? KtFile ?: error("Can't load ${file.name}"))
|
||||
}
|
||||
}
|
||||
|
||||
myFiles = CodegenTestFiles.create(ktFiles)
|
||||
}
|
||||
|
||||
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>, javaFilesDir: File?) {
|
||||
val javaSources = javaFilesDir?.let { arrayOf(it) } ?: emptyArray()
|
||||
|
||||
@@ -102,7 +127,7 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
javaFiles.forEach { it.delete() }
|
||||
tempFiles.forEach { it.delete() }
|
||||
tempFiles.forEach { if (it.isFile) it.delete() else it.deleteRecursively() }
|
||||
tempFiles.clear()
|
||||
kaptContext.close()
|
||||
}
|
||||
@@ -213,13 +238,16 @@ open class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3Test(
|
||||
val actualErrors = log.reportedDiagnostics
|
||||
.filter { it.type == JCDiagnostic.DiagnosticType.ERROR }
|
||||
.map {
|
||||
val location = it.subdiagnostics
|
||||
.firstOrNull { it.getMessage(Locale.US).startsWith("Kotlin location:") }
|
||||
?.getMessage(Locale.US)
|
||||
// Unfortunately, we can't use the file name as it can contain temporary prefix
|
||||
val name = it.source?.name?.substringAfterLast("/") ?: ""
|
||||
val kind = when (name.substringAfterLast(".").toLowerCase()) {
|
||||
"kt" -> "kotlin"
|
||||
"java" -> "java"
|
||||
else -> "other"
|
||||
}
|
||||
|
||||
val javaLocation = "(${it.lineNumber};${it.columnNumber}) "
|
||||
val message = javaLocation + it.getMessage(Locale.US).lines().first()
|
||||
if (location != null) "$message ($location)" else message
|
||||
val javaLocation = "($kind:${it.lineNumber}:${it.columnNumber}) "
|
||||
javaLocation + it.getMessage(Locale.US).lines().first()
|
||||
}
|
||||
.map { "// " + EXPECTED_ERROR + it }
|
||||
.sorted()
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
// CORRECT_ERROR_TYPES
|
||||
|
||||
// EXPECTED_ERROR(11;12) cannot find symbol (Kotlin location: /errorLocationMapping.kt: (34, 5))
|
||||
// EXPECTED_ERROR(11;34) cannot find symbol (Kotlin location: /errorLocationMapping.kt: (26, 62))
|
||||
// EXPECTED_ERROR(19;18) cannot find symbol (Kotlin location: /errorLocationMapping.kt: (26, 50))
|
||||
// EXPECTED_ERROR(23;18) cannot find symbol (Kotlin location: /errorLocationMapping.kt: (33, 5))
|
||||
// EXPECTED_ERROR(24;33) cannot find symbol (Kotlin location: /errorLocationMapping.kt: (26, 62))
|
||||
// EXPECTED_ERROR(28;5) cannot find symbol (Kotlin location: /errorLocationMapping.kt: (33, 5))
|
||||
// EXPECTED_ERROR(30;5) cannot find symbol (Kotlin location: /errorLocationMapping.kt: (26, 34))
|
||||
// EXPECTED_ERROR(31;30) cannot find symbol (Kotlin location: /errorLocationMapping.kt: (26, 34))
|
||||
// EXPECTED_ERROR(32;18) cannot find symbol (Kotlin location: /errorLocationMapping.kt: (34, 5))
|
||||
// EXPECTED_ERROR(37;5) cannot find symbol (Kotlin location: /errorLocationMapping.kt: (34, 5))
|
||||
// EXPECTED_ERROR(45;5) cannot find symbol (Kotlin location: /errorLocationMapping.kt: (37, 5))
|
||||
// EXPECTED_ERROR(50;5) cannot find symbol (Kotlin location: /errorLocationMapping.kt: (39, 5))
|
||||
// EXPECTED_ERROR(5;11) cannot find symbol (Kotlin location: /errorLocationMapping.kt: (23, 1))
|
||||
// EXPECTED_ERROR(60;18) cannot find symbol (Kotlin location: /errorLocationMapping.kt: (42, 5))
|
||||
// EXPECTED_ERROR(9;12) cannot find symbol (Kotlin location: /errorLocationMapping.kt: (33, 5))
|
||||
// EXPECTED_ERROR(9;19) cannot find symbol (Kotlin location: /errorLocationMapping.kt: (26, 50))
|
||||
// EXPECTED_ERROR(kotlin:16:1) cannot find symbol
|
||||
// EXPECTED_ERROR(kotlin:19:34) cannot find symbol
|
||||
// EXPECTED_ERROR(kotlin:19:50) cannot find symbol
|
||||
// EXPECTED_ERROR(kotlin:19:62) cannot find symbol
|
||||
// EXPECTED_ERROR(kotlin:26:5) cannot find symbol
|
||||
// EXPECTED_ERROR(kotlin:27:5) cannot find symbol
|
||||
// EXPECTED_ERROR(kotlin:30:5) cannot find symbol
|
||||
// EXPECTED_ERROR(kotlin:32:5) cannot find symbol
|
||||
// EXPECTED_ERROR(kotlin:35:5) cannot find symbol
|
||||
|
||||
@file:Suppress("UNRESOLVED_REFERENCE", "ANNOTATION_PARAMETER_MUST_BE_CONST", "NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION")
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// EXPECTED_ERROR(-1;-1) 'WHI-TE' is an invalid Java enum value name
|
||||
// EXPECTED_ERROR(6;20) cannot find symbol (Kotlin location: /invalidFieldName.kt: (8, 1))
|
||||
// EXPECTED_ERROR(kotlin:8:1) cannot find symbol
|
||||
// EXPECTED_ERROR(other:-1:-1) 'WHI-TE' is an invalid Java enum value name
|
||||
|
||||
enum class Color {
|
||||
BLACK, `WHI-TE`
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// CORRECT_ERROR_TYPES
|
||||
// EXPECTED_ERROR(8;5) cannot find symbol (Kotlin location: /maxErrorCount.kt: (9, 5))
|
||||
// EXPECTED_ERROR(kotlin:9:5) cannot find symbol
|
||||
// JAVAC_OPTION -Xmaxerrs=1
|
||||
|
||||
@file:Suppress("UNRESOLVED_REFERENCE")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// EXPECTED_ERROR(-1;-1) '60x60' is an invalid Java enum value name
|
||||
// EXPECTED_ERROR(other:-1:-1) '60x60' is an invalid Java enum value name
|
||||
|
||||
class `:)` {
|
||||
lateinit val f: String
|
||||
|
||||
Reference in New Issue
Block a user