Kapt: Forbid illegal Java identifiers in Enum value literals
This commit is contained in:
@@ -17,12 +17,11 @@
|
||||
package org.jetbrains.kotlin.kapt3.javac
|
||||
|
||||
import com.sun.tools.javac.tree.JCTree
|
||||
import com.sun.tools.javac.util.Context
|
||||
import com.sun.tools.javac.util.JCDiagnostic
|
||||
import com.sun.tools.javac.util.Log
|
||||
import com.sun.tools.javac.util.*
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.kapt3.KaptContext
|
||||
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedWriter
|
||||
import java.io.PrintWriter
|
||||
import javax.tools.JavaFileObject
|
||||
@@ -38,6 +37,29 @@ class KaptJavaLog(
|
||||
context.put(Log.outKey, noticeWriter)
|
||||
}
|
||||
|
||||
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
|
||||
@@ -61,6 +83,7 @@ class KaptJavaLog(
|
||||
}
|
||||
}
|
||||
|
||||
_reportedDiagnostics += diagnostic
|
||||
super.report(diagnostic)
|
||||
}
|
||||
|
||||
@@ -114,4 +137,8 @@ class KaptJavaLog(
|
||||
class DiagnosticInterceptorData {
|
||||
var files: Map<JavaFileObject, JCTree.JCCompilationUnit> = emptyMap()
|
||||
}
|
||||
}
|
||||
|
||||
fun KaptContext<*>.kaptError(text: String): JCDiagnostic {
|
||||
return JCDiagnostic.Factory.instance(context).error(null, null, "proc.messager", text)
|
||||
}
|
||||
+6
-1
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.kapt3.*
|
||||
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
|
||||
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
|
||||
import org.jetbrains.kotlin.kapt3.javac.kaptError
|
||||
import org.jetbrains.kotlin.kapt3.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
@@ -787,7 +788,11 @@ class ClassFileToSourceStubConverter(
|
||||
is Array<*> -> { // Two-element String array for enumerations ([desc, fieldName])
|
||||
assert(value.size == 2)
|
||||
val enumType = Type.getType(value[0] as String)
|
||||
val valueName = (value[1] as String).takeIf { isValidIdentifier(it) } ?: "InvalidFieldName"
|
||||
val valueName = (value[1] as String).takeIf { isValidIdentifier(it) } ?: run {
|
||||
kaptContext.compiler.log.report(kaptContext.kaptError("'${value[1]}' is an invalid Java enum value name"))
|
||||
"InvalidFieldName"
|
||||
}
|
||||
|
||||
treeMaker.Select(treeMaker.Type(enumType), treeMaker.name(valueName))
|
||||
}
|
||||
is List<*> -> treeMaker.NewArray(null, JavacList.nil(), mapJList(value) { convertLiteralExpression(it) })
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.kapt3.test
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
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 org.jetbrains.kotlin.cli.common.messages.MessageRenderer
|
||||
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
|
||||
@@ -28,6 +29,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.kapt3.Kapt3BuilderFactory
|
||||
import org.jetbrains.kotlin.kapt3.KaptContext
|
||||
import org.jetbrains.kotlin.kapt3.doAnnotationProcessing
|
||||
import org.jetbrains.kotlin.kapt3.javac.KaptJavaLog
|
||||
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter
|
||||
import org.jetbrains.kotlin.kapt3.util.KaptLogger
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
|
||||
@@ -38,6 +40,7 @@ import org.jetbrains.kotlin.test.util.trimTrailingWhitespacesAndAddNewlineAtEOF
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.util.*
|
||||
import com.sun.tools.javac.util.List as JavacList
|
||||
|
||||
abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
|
||||
@@ -96,9 +99,17 @@ abstract class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3T
|
||||
override fun check(kaptContext: KaptContext<GenerationState>, txtFile: File, wholeFile: File) {
|
||||
fun isOptionSet(name: String) = wholeFile.useLines { lines -> lines.any { it.trim() == "// $name" } }
|
||||
|
||||
fun getOptionValues(name: String) = wholeFile.useLines { lines ->
|
||||
lines.filter { it.startsWith("// $name") }
|
||||
.map { it.drop(name.length + 3).trim() }
|
||||
.filter { it.isNotEmpty() }
|
||||
.toList()
|
||||
}
|
||||
|
||||
val generateNonExistentClass = isOptionSet("NON_EXISTENT_CLASS")
|
||||
val correctErrorTypes = isOptionSet("CORRECT_ERROR_TYPES")
|
||||
val validate = !isOptionSet("NO_VALIDATION")
|
||||
val expectedErrors = getOptionValues("EXPECTED_ERROR").sorted()
|
||||
|
||||
val javaFiles = convert(kaptContext, generateNonExistentClass, correctErrorTypes)
|
||||
|
||||
@@ -111,8 +122,23 @@ abstract class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3T
|
||||
.let { removeMetadataAnnotationContents(it) }
|
||||
|
||||
if (kaptContext.compiler.shouldStop(CompileStates.CompileState.ENTER)) {
|
||||
Log.instance(kaptContext.context).flush()
|
||||
error("There were errors during analysis. See errors above. Stubs:\n\n$actual")
|
||||
val log = Log.instance(kaptContext.context) as KaptJavaLog
|
||||
|
||||
val actualErrors = log.reportedDiagnostics
|
||||
.filter { it.type == JCDiagnostic.DiagnosticType.ERROR }
|
||||
.map { it.getMessage(Locale.US).lines().first() }
|
||||
.sorted()
|
||||
|
||||
log.flush()
|
||||
|
||||
if (expectedErrors.isEmpty()) {
|
||||
error("There were errors during analysis. See errors above. Stubs:\n\n$actual")
|
||||
} else if (actualErrors != expectedErrors) {
|
||||
error("Expected error matching assertion. Expected: \n"
|
||||
+ expectedErrors.joinToString("\n") { "'$it'" }
|
||||
+ "\n, found: \n"
|
||||
+ actualErrors.joinToString("\n") { "'$it'" })
|
||||
}
|
||||
}
|
||||
KotlinTestUtils.assertEqualsToFile(txtFile, actual)
|
||||
}
|
||||
|
||||
+6
@@ -120,6 +120,12 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("invalidFieldName.kt")
|
||||
public void testInvalidFieldName() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/invalidFieldName.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("javaKeywords.kt")
|
||||
public void testJavaKeywords() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/javaKeywords.kt");
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// EXPECTED_ERROR 'WHI-TE' is an invalid Java enum value name
|
||||
// EXPECTED_ERROR an enum annotation value must be an enum constant
|
||||
// EXPECTED_ERROR cannot find symbol
|
||||
|
||||
enum class Color {
|
||||
BLACK, `WHI-TE`
|
||||
}
|
||||
|
||||
@Anno(Color.`WHI-TE`)
|
||||
annotation class Anno(val color: Color)
|
||||
@@ -0,0 +1,18 @@
|
||||
@kotlin.Metadata()
|
||||
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||
@Anno(color = Color.InvalidFieldName)
|
||||
public abstract @interface Anno {
|
||||
|
||||
public abstract Color color();
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
@kotlin.Metadata()
|
||||
public enum Color {
|
||||
/*public static final*/ BLACK /* = new BLACK() */;
|
||||
|
||||
Color() {
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
// EXPECTED_ERROR '60x60' is an invalid Java enum value name
|
||||
|
||||
class `:)` {
|
||||
lateinit val f: String
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user