mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-10 08:31:29 +00:00
[KAPT] Add experimental JDK 17 support
#KT-47583 Fixed
This commit is contained in:
@@ -15,10 +15,9 @@ import org.jetbrains.kotlin.base.kapt3.KaptFlag
|
||||
import org.jetbrains.kotlin.base.kapt3.KaptOptions
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.JavaClassCacheManager
|
||||
import org.jetbrains.kotlin.kapt3.base.incremental.SourcesToReprocess
|
||||
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaCompiler
|
||||
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaFileManager
|
||||
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaLog
|
||||
import org.jetbrains.kotlin.kapt3.base.javac.*
|
||||
import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
|
||||
import org.jetbrains.kotlin.kapt3.base.util.isJava17OrLater
|
||||
import org.jetbrains.kotlin.kapt3.base.util.isJava9OrLater
|
||||
import org.jetbrains.kotlin.kapt3.base.util.putJavacOption
|
||||
import java.io.Closeable
|
||||
@@ -30,7 +29,7 @@ open class KaptContext(val options: KaptOptions, val withJdk: Boolean, val logge
|
||||
val compiler: KaptJavaCompiler
|
||||
val fileManager: KaptJavaFileManager
|
||||
private val javacOptions: Options
|
||||
val javaLog: KaptJavaLog
|
||||
val javaLog: KaptJavaLogBase
|
||||
val cacheManager: JavaClassCacheManager?
|
||||
|
||||
val sourcesToReprocess: SourcesToReprocess
|
||||
@@ -38,12 +37,24 @@ open class KaptContext(val options: KaptOptions, val withJdk: Boolean, val logge
|
||||
protected open fun preregisterTreeMaker(context: Context) {}
|
||||
|
||||
private fun preregisterLog(context: Context) {
|
||||
val interceptorData = KaptJavaLog.DiagnosticInterceptorData()
|
||||
val interceptorData = KaptJavaLogBase.DiagnosticInterceptorData()
|
||||
context.put(Log.logKey, Context.Factory<Log> { newContext ->
|
||||
KaptJavaLog(
|
||||
options.projectBaseDir, newContext, logger.errorWriter, logger.warnWriter, logger.infoWriter,
|
||||
interceptorData, options[KaptFlag.MAP_DIAGNOSTIC_LOCATIONS]
|
||||
)
|
||||
if (isJava17OrLater()) {
|
||||
newContext.put(Log.outKey, logger.infoWriter)
|
||||
val errKey = (Log::class.java.fields.firstOrNull() { it.name == "errKey" }
|
||||
?: error("Can't find errKey field in Log.class")).get(null)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
newContext.put(errKey as Context.Key<java.io.PrintWriter>, logger.errorWriter)
|
||||
KaptJavaLog17(
|
||||
options.projectBaseDir, newContext,
|
||||
interceptorData, options[KaptFlag.MAP_DIAGNOSTIC_LOCATIONS]
|
||||
)
|
||||
} else {
|
||||
KaptJavaLog(
|
||||
options.projectBaseDir, newContext, logger.errorWriter, logger.warnWriter, logger.infoWriter,
|
||||
interceptorData, options[KaptFlag.MAP_DIAGNOSTIC_LOCATIONS]
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -154,7 +165,7 @@ open class KaptContext(val options: KaptOptions, val withJdk: Boolean, val logge
|
||||
|
||||
ClassReader.instance(context).saveParameterNames = true
|
||||
|
||||
javaLog = compiler.log as KaptJavaLog
|
||||
javaLog = compiler.log as KaptJavaLogBase
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
|
||||
@@ -17,7 +17,20 @@ 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
|
||||
|
||||
interface KaptJavaLogBase {
|
||||
val interceptorData: DiagnosticInterceptorData
|
||||
|
||||
val reportedDiagnostics: List<JCDiagnostic>
|
||||
|
||||
fun flush(kind: Log.WriterKind?)
|
||||
|
||||
fun flush()
|
||||
|
||||
class DiagnosticInterceptorData {
|
||||
var files: Map<JavaFileObject, JCTree.JCCompilationUnit> = emptyMap()
|
||||
}
|
||||
}
|
||||
|
||||
class KaptJavaLog(
|
||||
private val projectBaseDir: File?,
|
||||
@@ -25,9 +38,9 @@ class KaptJavaLog(
|
||||
errWriter: PrintWriter,
|
||||
warnWriter: PrintWriter,
|
||||
noticeWriter: PrintWriter,
|
||||
val interceptorData: DiagnosticInterceptorData,
|
||||
override val interceptorData: KaptJavaLogBase.DiagnosticInterceptorData,
|
||||
private val mapDiagnosticLocations: Boolean
|
||||
) : Log(context, errWriter, warnWriter, noticeWriter) {
|
||||
) : Log(context, errWriter, warnWriter, noticeWriter), KaptJavaLogBase {
|
||||
private val stubLineInfo = KaptStubLineInformation()
|
||||
private val javacMessages = JavacMessages.instance(context)
|
||||
|
||||
@@ -35,7 +48,7 @@ class KaptJavaLog(
|
||||
context.put(Log.outKey, noticeWriter)
|
||||
}
|
||||
|
||||
val reportedDiagnostics: List<JCDiagnostic>
|
||||
override val reportedDiagnostics: List<JCDiagnostic>
|
||||
get() = _reportedDiagnostics
|
||||
|
||||
private val _reportedDiagnostics = mutableListOf<JCDiagnostic>()
|
||||
@@ -217,10 +230,6 @@ class KaptJavaLog(
|
||||
"compiler.err.not.def.public.cant.access"
|
||||
)
|
||||
}
|
||||
|
||||
class DiagnosticInterceptorData {
|
||||
var files: Map<JavaFileObject, JCTree.JCCompilationUnit> = emptyMap()
|
||||
}
|
||||
}
|
||||
|
||||
private val LINE_SEPARATOR: String = System.getProperty("line.separator")
|
||||
@@ -256,7 +265,7 @@ private fun JCDiagnostic.Factory.errorJava9Aware(
|
||||
}
|
||||
}
|
||||
|
||||
private data class KotlinFileObject(val file: File) : SimpleJavaFileObject(file.toURI(), Kind.SOURCE) {
|
||||
/*private*/internal 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()
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.kapt3.base.javac
|
||||
|
||||
import com.sun.tools.javac.tree.JCTree
|
||||
import com.sun.tools.javac.util.*
|
||||
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType
|
||||
import org.jetbrains.kotlin.kapt3.base.stubs.KaptStubLineInformation
|
||||
import org.jetbrains.kotlin.kapt3.base.stubs.KotlinPosition
|
||||
import java.io.*
|
||||
import javax.tools.Diagnostic
|
||||
|
||||
// It's copy/paste as is of KaptJavaLog
|
||||
// TODO: most likely KaptJavaLog could be substituted with this version.
|
||||
// The only difference that is writers are passed through context
|
||||
class KaptJavaLog17(
|
||||
private val projectBaseDir: File?,
|
||||
context: Context,
|
||||
override val interceptorData: KaptJavaLogBase.DiagnosticInterceptorData,
|
||||
private val mapDiagnosticLocations: Boolean
|
||||
) : Log(context), KaptJavaLogBase {
|
||||
private val stubLineInfo = KaptStubLineInformation()
|
||||
private val javacMessages = JavacMessages.instance(context)
|
||||
|
||||
override val reportedDiagnostics: List<JCDiagnostic>
|
||||
get() = _reportedDiagnostics
|
||||
|
||||
private val _reportedDiagnostics = mutableListOf<JCDiagnostic>()
|
||||
|
||||
override fun flush(kind: WriterKind?) {
|
||||
super.flush(kind)
|
||||
|
||||
val diagnosticKind = when (kind) {
|
||||
WriterKind.ERROR -> JCDiagnostic.DiagnosticType.ERROR
|
||||
WriterKind.WARNING -> JCDiagnostic.DiagnosticType.WARNING
|
||||
WriterKind.NOTICE -> JCDiagnostic.DiagnosticType.NOTE
|
||||
else -> return
|
||||
}
|
||||
|
||||
_reportedDiagnostics.removeAll { it.type == diagnosticKind }
|
||||
}
|
||||
|
||||
override fun flush() {
|
||||
super.flush()
|
||||
_reportedDiagnostics.clear()
|
||||
}
|
||||
|
||||
override fun report(diagnostic: JCDiagnostic) {
|
||||
if (diagnostic.type == JCDiagnostic.DiagnosticType.ERROR && diagnostic.code in IGNORED_DIAGNOSTICS) {
|
||||
return
|
||||
}
|
||||
|
||||
if (diagnostic.type == JCDiagnostic.DiagnosticType.WARNING
|
||||
&& diagnostic.code == "compiler.warn.proc.unmatched.processor.options"
|
||||
&& diagnostic.args.singleOrNull() == "[kapt.kotlin.generated]"
|
||||
) {
|
||||
// Do not report the warning about "kapt.kotlin.generated" option being ignored if it's the only ignored option
|
||||
return
|
||||
}
|
||||
|
||||
val targetElement = diagnostic.diagnosticPosition
|
||||
val sourceFile = interceptorData.files[diagnostic.source]
|
||||
|
||||
if (diagnostic.code.contains("err.cant.resolve") && targetElement != null) {
|
||||
if (sourceFile != null) {
|
||||
val insideImports = targetElement.tree in sourceFile.imports
|
||||
// Ignore resolve errors in import statements
|
||||
if (insideImports) return
|
||||
}
|
||||
}
|
||||
|
||||
if (mapDiagnosticLocations && sourceFile != null && targetElement?.tree != null) {
|
||||
val kotlinPosition = stubLineInfo.getPositionInKotlinFile(sourceFile, targetElement.tree)
|
||||
val kotlinFile = kotlinPosition?.let { getKotlinSourceFile(it) }
|
||||
if (kotlinPosition != null && kotlinFile != null) {
|
||||
val flags = JCDiagnostic.DiagnosticFlag.values().filterTo(mutableSetOf(), diagnostic::isFlagSet)
|
||||
|
||||
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
|
||||
super.report(diagnostic)
|
||||
if (nerrors > oldErrors) {
|
||||
_reportedDiagnostics += diagnostic
|
||||
}
|
||||
} else if (diagnostic.kind == Diagnostic.Kind.WARNING) {
|
||||
val oldWarnings = nwarnings
|
||||
super.report(diagnostic)
|
||||
if (nwarnings > oldWarnings) {
|
||||
_reportedDiagnostics += diagnostic
|
||||
}
|
||||
} else {
|
||||
super.report(diagnostic)
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeDiagnostic(diagnostic: JCDiagnostic) {
|
||||
if (hasDiagnosticListener()) {
|
||||
diagListener.report(diagnostic)
|
||||
return
|
||||
}
|
||||
|
||||
val writer = when (diagnostic.type) {
|
||||
DiagnosticType.FRAGMENT, null -> kotlin.error("Invalid root diagnostic type: ${diagnostic.type}")
|
||||
DiagnosticType.NOTE -> super.getWriter(WriterKind.NOTICE)
|
||||
DiagnosticType.WARNING -> super.getWriter(WriterKind.WARNING)
|
||||
DiagnosticType.ERROR -> super.getWriter(WriterKind.ERROR)
|
||||
}
|
||||
|
||||
val formattedMessage = diagnosticFormatter.format(diagnostic, javacMessages.currentLocale)
|
||||
.lines()
|
||||
.joinToString(LINE_SEPARATOR, postfix = LINE_SEPARATOR) { original ->
|
||||
// Kotlin location is put as a sub-diagnostic, so the formatter indents it with four additional spaces (6 in total).
|
||||
// It looks weird, especially in the build log inside IntelliJ, so let's make things a bit better.
|
||||
val trimmed = original.trimStart()
|
||||
// Typically, javac places additional details about the diagnostics indented by two spaces
|
||||
if (trimmed.startsWith(KOTLIN_LOCATION_PREFIX)) " " + trimmed else original
|
||||
}
|
||||
|
||||
writer.print(formattedMessage)
|
||||
writer.flush()
|
||||
}
|
||||
|
||||
private fun getKotlinSourceFile(pos: KotlinPosition): File? {
|
||||
return if (pos.isRelativePath) {
|
||||
val basePath = this.projectBaseDir
|
||||
if (basePath != null) File(basePath, pos.path) else null
|
||||
} else {
|
||||
File(pos.path)
|
||||
}
|
||||
}
|
||||
|
||||
private operator fun <T : JCTree> Iterable<T>.contains(element: JCTree?): Boolean {
|
||||
if (element == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
var found = false
|
||||
val visitor = object : JCTree.Visitor() {
|
||||
override fun visitImport(that: JCTree.JCImport) {
|
||||
super.visitImport(that)
|
||||
if (!found) that.qualid.accept(this)
|
||||
}
|
||||
|
||||
override fun visitSelect(that: JCTree.JCFieldAccess) {
|
||||
super.visitSelect(that)
|
||||
if (!found) that.selected.accept(this)
|
||||
}
|
||||
|
||||
override fun visitTree(that: JCTree) {
|
||||
if (!found && element == that) found = true
|
||||
}
|
||||
}
|
||||
this.forEach { if (!found) it.accept(visitor) }
|
||||
return found
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LINE_SEPARATOR: String = System.getProperty("line.separator")
|
||||
private val KOTLIN_LOCATION_PREFIX = "Kotlin location: "
|
||||
|
||||
private val IGNORED_DIAGNOSTICS = setOf(
|
||||
"compiler.err.name.clash.same.erasure",
|
||||
"compiler.err.name.clash.same.erasure.no.override",
|
||||
"compiler.err.name.clash.same.erasure.no.override.1",
|
||||
"compiler.err.name.clash.same.erasure.no.hide",
|
||||
"compiler.err.already.defined",
|
||||
"compiler.err.annotation.type.not.applicable",
|
||||
"compiler.err.doesnt.exist",
|
||||
"compiler.err.cant.resolve.location",
|
||||
"compiler.err.duplicate.annotation.missing.container",
|
||||
"compiler.err.not.def.access.package.cant.access",
|
||||
"compiler.err.package.not.visible",
|
||||
"compiler.err.not.def.public.cant.access"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import java.io.PrintWriter
|
||||
class WriterBackedKaptLogger(
|
||||
override val isVerbose: Boolean,
|
||||
override val infoWriter: PrintWriter = PrintWriter(System.out),
|
||||
override val warnWriter: PrintWriter = PrintWriter(System.out),
|
||||
override val warnWriter: PrintWriter = if (isJava17OrLater()) infoWriter else PrintWriter(System.out),
|
||||
override val errorWriter: PrintWriter = PrintWriter(System.err)
|
||||
) : KaptLogger {
|
||||
override fun info(message: String) {
|
||||
|
||||
@@ -28,6 +28,7 @@ private fun getJavaVersion(): Int =
|
||||
|
||||
fun isJava9OrLater() = getJavaVersion() >= 9
|
||||
fun isJava11OrLater() = getJavaVersion() >= 11
|
||||
fun isJava17OrLater() = getJavaVersion() >= 17
|
||||
|
||||
fun Options.putJavacOption(jdk8Name: String, jdk9Name: String, value: String) {
|
||||
val option = if (isJava9OrLater()) {
|
||||
|
||||
@@ -43,7 +43,7 @@ import org.jetbrains.kotlin.kapt3.Kapt3ComponentRegistrar.KaptComponentContribut
|
||||
import org.jetbrains.kotlin.kapt3.KaptContextForStubGeneration
|
||||
import org.jetbrains.kotlin.kapt3.base.KaptContext
|
||||
import org.jetbrains.kotlin.kapt3.base.doAnnotationProcessing
|
||||
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaLog
|
||||
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaLogBase
|
||||
import org.jetbrains.kotlin.kapt3.base.parseJavaFiles
|
||||
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
|
||||
import org.jetbrains.kotlin.kapt3.prettyPrint
|
||||
@@ -287,7 +287,7 @@ open class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3Test(
|
||||
.let { removeMetadataAnnotationContents(it) }
|
||||
|
||||
if (kaptContext.compiler.shouldStop(CompileStates.CompileState.ENTER)) {
|
||||
val log = Log.instance(kaptContext.context) as KaptJavaLog
|
||||
val log = Log.instance(kaptContext.context) as KaptJavaLogBase
|
||||
|
||||
val actualErrors = log.reportedDiagnostics
|
||||
.filter { it.type == JCDiagnostic.DiagnosticType.ERROR }
|
||||
|
||||
Reference in New Issue
Block a user