Kapt3: Replace error/NonExistentClass with the actual type (from PSI) (KT-15421)

This commit is contained in:
Yan Zhulanow
2017-01-11 16:58:52 +09:00
committed by Yan Zhulanow
parent ee57446397
commit ec291455fa
12 changed files with 385 additions and 27 deletions
@@ -198,7 +198,7 @@ abstract class AbstractKapt3Extension(
logger.info { "Stubs compilation took $classFilesCompilationTime ms" }
logger.info { "Compiled classes: " + compiledClasses.joinToString { it.name } }
return Pair(KaptContext(logger, compiledClasses, origins, options), generationState)
return Pair(KaptContext(logger, bindingContext, compiledClasses, origins, options), generationState)
}
private fun generateKotlinSourceStubs(kaptContext: KaptContext, generationState: GenerationState) {
@@ -24,12 +24,14 @@ import org.jetbrains.kotlin.kapt3.javac.KaptJavaCompiler
import org.jetbrains.kotlin.kapt3.javac.KaptJavaLog
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
import org.jetbrains.kotlin.kapt3.util.KaptLogger
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.tree.ClassNode
import javax.tools.JavaFileManager
class KaptContext(
val logger: KaptLogger,
val bindingContext: BindingContext,
val compiledClasses: List<ClassNode>,
val origins: Map<Any, JvmDeclarationOrigin>,
processorOptions: Map<String, String>
@@ -28,10 +28,11 @@ import org.jetbrains.kotlin.kapt3.*
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
import org.jetbrains.kotlin.kapt3.util.*
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
@@ -76,7 +77,7 @@ class ClassFileToSourceStubConverter(
get() = _bindings
private val fileManager = kaptContext.context.get(JavaFileManager::class.java) as JavacFileManager
private val treeMaker = TreeMaker.instance(kaptContext.context) as KaptTreeMaker
val treeMaker = TreeMaker.instance(kaptContext.context) as KaptTreeMaker
private val signatureParser = SignatureParser(treeMaker)
private var done = false
@@ -228,7 +229,11 @@ class ClassFileToSourceStubConverter(
val typeExpression = if (isEnum(field.access))
treeMaker.SimpleName(type.className.substringAfterLast('.'))
else
getNotAnonymousType(descriptor) { signatureParser.parseFieldSignature(field.signature, treeMaker.Type(type)) }
getNotAnonymousType(descriptor) {
getNonErrorType((descriptor as? CallableDescriptor)?.returnType,
ktTypeProvider = { (kaptContext.origins[field]?.element as? KtVariableDeclaration)?.typeReference },
ifNonError = { signatureParser.parseFieldSignature(field.signature, treeMaker.Type(type)) })
}
val value = field.value
@@ -288,8 +293,9 @@ class ClassFileToSourceStubConverter(
val exceptionTypes = mapJList(method.exceptions) { treeMaker.FqName(it) }
val genericSignature = signatureParser.parseMethodSignature(method.signature, parameters, exceptionTypes, jcReturnType)
val returnType = getNotAnonymousType(descriptor) { genericSignature.returnType }
val valueParametersFromDescriptor = descriptor.valueParameters
val (genericSignature, returnType) = extractMethodSignatureTypes(
descriptor, exceptionTypes, jcReturnType, method, parameters, valueParametersFromDescriptor)
val defaultValue = method.annotationDefault?.let { convertLiteralExpression(it) }
@@ -329,6 +335,65 @@ class ClassFileToSourceStubConverter(
body, defaultValue)
}
private fun extractMethodSignatureTypes(
descriptor: CallableDescriptor,
exceptionTypes: JavacList<JCExpression>,
jcReturnType: JCExpression?, method: MethodNode,
parameters: JavacList<JCVariableDecl>,
valueParametersFromDescriptor: List<ValueParameterDescriptor>
): Pair<SignatureParser.MethodGenericSignature, JCExpression?> {
val genericSignature = signatureParser.parseMethodSignature(
method.signature, parameters, exceptionTypes, jcReturnType,
nonErrorParameterTypeProvider = { index, lazyType ->
if (descriptor is PropertySetterDescriptor && valueParametersFromDescriptor.size == 1 && index == 0) {
getNonErrorType(descriptor.correspondingProperty.returnType,
ktTypeProvider = { (kaptContext.origins[method]?.element as? KtVariableDeclaration)?.typeReference },
ifNonError = { lazyType() })
}
else if (descriptor is FunctionDescriptor && valueParametersFromDescriptor.size == parameters.size) {
getNonErrorType(valueParametersFromDescriptor[index].type,
ktTypeProvider = {
(kaptContext.origins[method]?.element as? KtFunction)?.valueParameters?.get(index)?.typeReference
},
ifNonError = { lazyType() })
}
else {
lazyType()
}
})
val returnType = getNotAnonymousType(descriptor) {
getNonErrorType(descriptor.returnType,
ktTypeProvider = {
val element = kaptContext.origins[method]?.element
when (element) {
is KtFunction -> element.typeReference
is KtProperty -> if (method.name.startsWith("get")) element.typeReference else null
else -> null
}
},
ifNonError = { genericSignature.returnType })
}
return Pair(genericSignature, returnType)
}
private inline fun <T : JCExpression?> getNonErrorType(
type: KotlinType?,
ktTypeProvider: () -> KtTypeReference?,
ifNonError: () -> T
): T {
if (type?.containsErrorTypes() ?: false) {
val ktType = ktTypeProvider()
if (ktType != null) {
@Suppress("UNCHECKED_CAST")
return convertKtType(ktType, this) as T
}
}
return ifNonError()
}
private inline fun <T : JCExpression?> getNotAnonymousType(descriptor: DeclarationDescriptor?, f: () -> T): T {
if (descriptor is CallableDescriptor) {
val returnTypeDescriptor = descriptor.returnType?.constructor?.declarationDescriptor
@@ -131,10 +131,15 @@ class SignatureParser(val treeMaker: KaptTreeMaker) {
signature: String?,
rawParameters: JavacList<JCVariableDecl>,
rawExceptionTypes: JavacList<JCExpression>,
rawReturnType: JCExpression?
rawReturnType: JCExpression?,
nonErrorParameterTypeProvider: (Int, () -> JCExpression) -> JCExpression
): MethodGenericSignature {
if (signature == null) {
return MethodGenericSignature(JavacList.nil(), rawParameters, rawExceptionTypes, rawReturnType)
val parameters = mapJListIndexed(rawParameters) { index, it ->
val nonErrorType = nonErrorParameterTypeProvider(index) { it.vartype }
treeMaker.VarDef(it.modifiers, it.getName(), nonErrorType, it.initializer)
}
return MethodGenericSignature(JavacList.nil(), parameters, rawExceptionTypes, rawReturnType)
}
val root = parse(signature)
@@ -149,7 +154,9 @@ class SignatureParser(val treeMaker: KaptTreeMaker) {
val offset = rawParameters.size - parameterTypes.size
val jcParameters = mapJListIndexed(parameterTypes) { index, it ->
val rawParameter = rawParameters[index + offset]
treeMaker.VarDef(rawParameter.modifiers, rawParameter.getName(), parseType(it.children.single()), rawParameter.initializer)
val nonErrorType = nonErrorParameterTypeProvider(index) { parseType(it.children.single()) }
treeMaker.VarDef(rawParameter.modifiers, rawParameter.getName(), nonErrorType, rawParameter.initializer)
}
val jcExceptionTypes = mapJList(exceptionTypes) { parseType(it) }
val jcReturnType = if (rawReturnType == null) null else parseType(returnTypes.single().children.single())
@@ -0,0 +1,114 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.kapt3.stubs
import com.sun.tools.javac.code.BoundKind
import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
import org.jetbrains.kotlin.kapt3.mapJList
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.types.KotlinType
internal fun convertKtType(
reference: KtTypeReference?,
converter: ClassFileToSourceStubConverter,
shouldBeBoxed: Boolean = false,
gotTypeElement: KtTypeElement? = null
): JCTree.JCExpression {
val type = gotTypeElement ?: reference?.typeElement
if (reference != null) {
val kotlinType = converter.kaptContext.bindingContext[BindingContext.TYPE, reference]
if (kotlinType != null && !kotlinType.containsErrorTypes()) {
val signatureWriter = BothSignatureWriter(BothSignatureWriter.Mode.TYPE)
converter.typeMapper.mapType(kotlinType, signatureWriter,
if (shouldBeBoxed) TypeMappingMode.GENERIC_ARGUMENT else TypeMappingMode.DEFAULT)
return SignatureParser(converter.treeMaker).parseFieldSignature(
signatureWriter.toString(), getDefaultTypeForUnknownType(converter))
}
}
return when (type) {
is KtUserType -> convertUserType(type, converter)
is KtNullableType -> {
// Prevent infinite recursion
val innerType = type.innerType ?: return getDefaultTypeForUnknownType(converter)
convertKtType(reference, converter, shouldBeBoxed = true, gotTypeElement = innerType)
}
is KtFunctionType -> convertFunctionType(type, converter)
else -> getDefaultTypeForUnknownType(converter)
}
}
private fun getDefaultTypeForUnknownType(converter: ClassFileToSourceStubConverter) = converter.treeMaker.FqName("error.NonExistentClass")
private fun convertUserType(type: KtUserType, converter: ClassFileToSourceStubConverter): JCTree.JCExpression {
val qualifierExpression = type.qualifier?.let { convertUserType(it, converter) }
val referencedName = type.referencedName ?: "error"
val treeMaker = converter.treeMaker
val baseExpression = if (qualifierExpression == null) {
treeMaker.SimpleName(referencedName)
} else {
treeMaker.Select(qualifierExpression, treeMaker.name(referencedName))
}
val arguments = type.typeArguments
if (arguments.isEmpty()) {
return baseExpression
}
return treeMaker.TypeApply(baseExpression, mapJList(arguments) { convertTypeProjection(it, converter) })
}
private fun convertTypeProjection(type: KtTypeProjection, converter: ClassFileToSourceStubConverter): JCTree.JCExpression {
val reference = type.typeReference
val treeMaker = converter.treeMaker
return when (type.projectionKind) {
KtProjectionKind.IN -> treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.SUPER),
convertKtType(reference, converter, shouldBeBoxed = true))
KtProjectionKind.OUT -> treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.EXTENDS),
convertKtType(reference, converter, shouldBeBoxed = true))
KtProjectionKind.STAR -> treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.UNBOUND), null)
KtProjectionKind.NONE -> return convertKtType(reference, converter, shouldBeBoxed = true)
}
}
private fun convertFunctionType(type: KtFunctionType, converter: ClassFileToSourceStubConverter): JCTree.JCExpression {
val receiverType = type.receiverTypeReference
var parameterTypes = mapJList(type.parameters) { convertKtType(it.typeReference, converter) }
val returnType = convertKtType(type.returnTypeReference, converter)
if (receiverType != null) {
parameterTypes = parameterTypes.prepend(convertKtType(receiverType, converter))
}
parameterTypes = parameterTypes.append(returnType)
val treeMaker = converter.treeMaker
return treeMaker.TypeApply(treeMaker.SimpleName("Function" + (parameterTypes.size - 1)), parameterTypes)
}
fun KotlinType.containsErrorTypes(): Boolean {
if (this.isError) return true
if (this.arguments.any { it.type.containsErrorTypes() }) return true
return false
}
@@ -59,7 +59,7 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
val typeMapper = factory.generationState.typeMapper
val logger = KaptLogger(isVerbose = true, messageCollector = messageCollector)
val kaptContext = KaptContext(logger, classBuilderFactory.compiledClasses,
val kaptContext = KaptContext(logger, factory.generationState.bindingContext, classBuilderFactory.compiledClasses,
classBuilderFactory.origins, processorOptions = emptyMap())
try {
check(kaptContext, typeMapper, txtFile, wholeFile)
@@ -82,9 +82,14 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
abstract class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3Test() {
override fun check(kaptRunner: KaptContext, typeMapper: KotlinTypeMapper, txtFile: File, wholeFile: File) {
val generateNonExistentClass = wholeFile.useLines { lines -> lines.any { it.trim() == "// NON_EXISTENT_CLASS" } }
fun isOptionSet(name: String) = wholeFile.useLines { lines -> lines.any { it.trim() == "// $name" } }
val generateNonExistentClass = isOptionSet("NON_EXISTENT_CLASS")
val validate = !isOptionSet("NO_VALIDATION")
val javaFiles = convert(kaptRunner, typeMapper, generateNonExistentClass)
kaptRunner.compiler.enterTrees(javaFiles)
if (validate) kaptRunner.compiler.enterTrees(javaFiles)
val actualRaw = javaFiles.sortedBy { it.sourceFile.name }.joinToString (FILE_SEPARATOR)
val actual = StringUtil.convertLineSeparators(actualRaw.trim({ it <= ' ' })).trimTrailingWhitespacesAndAddNewlineAtEOF()
@@ -186,6 +186,12 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi
doTest(fileName);
}
@TestMetadata("nonExistentClassTypesConversion.kt")
public void testNonExistentClassTypesConversion() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/nonExistentClassTypesConversion.kt");
doTest(fileName);
}
@TestMetadata("primitiveTypes.kt")
public void testPrimitiveTypes() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/primitiveTypes.kt");
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.kapt3.KaptContext
import org.jetbrains.kotlin.kapt3.diagnostic.KaptError
import org.jetbrains.kotlin.kapt3.doAnnotationProcessing
import org.jetbrains.kotlin.kapt3.util.KaptLogger
import org.jetbrains.kotlin.resolve.BindingContext
import org.junit.Assert.*
import org.junit.Test
import java.io.File
@@ -70,7 +71,7 @@ class JavaKaptContextTest {
}
}
return true;
return true
}
override fun getSupportedAnnotationTypes() = setOf("test.MyAnnotation")
@@ -79,6 +80,7 @@ class JavaKaptContextTest {
private fun doAnnotationProcessing(javaSourceFile: File, processor: Processor, outputDir: File) {
KaptContext(KaptLogger(isVerbose = true, messageCollector = messageCollector),
bindingContext = BindingContext.EMPTY,
compiledClasses = emptyList(),
origins = emptyMap(),
processorOptions = emptyMap()
+1
View File
@@ -1,4 +1,5 @@
// NON_EXISTENT_CLASS
// NO_VALIDATION
@Suppress("UNRESOLVED_REFERENCE")
object NonExistentType {
+10 -14
View File
@@ -1,32 +1,32 @@
@kotlin.Suppress(names = {"UNRESOLVED_REFERENCE"})
public final class NonExistentType {
private static final error.NonExistentClass a = null;
private static final java.util.List<error.NonExistentClass> b = null;
private static final kotlin.jvm.functions.Function1<error.NonExistentClass, kotlin.Unit> c = null;
private static final error.NonExistentClass d = null;
private static final ABCDEF a = null;
private static final List<ABCDEF> b = null;
private static final Function1<ABCDEF, kotlin.Unit> c = null;
private static final ABCDEF<java.lang.String, Function1<List<ABCDEF>, kotlin.Unit>> d = null;
public static final NonExistentType INSTANCE = null;
public final error.NonExistentClass getA() {
public final ABCDEF getA() {
return null;
}
public final java.util.List<error.NonExistentClass> getB() {
public final List<ABCDEF> getB() {
return null;
}
public final kotlin.jvm.functions.Function1<error.NonExistentClass, kotlin.Unit> getC() {
public final Function1<ABCDEF, kotlin.Unit> getC() {
return null;
}
public final error.NonExistentClass getD() {
public final ABCDEF<java.lang.String, Function1<List<ABCDEF>, kotlin.Unit>> getD() {
return null;
}
public final error.NonExistentClass a(error.NonExistentClass a, java.lang.String s) {
public final ABCDEF a(ABCDEF a, java.lang.String s) {
return null;
}
public final error.NonExistentClass b(java.lang.String s) {
public final ABCDEF b(java.lang.String s) {
return null;
}
@@ -40,8 +40,4 @@ public final class NonExistentType {
package error;
public final class NonExistentClass {
public NonExistentClass() {
super();
}
}
@@ -0,0 +1,34 @@
// NON_EXISTENT_CLASS
// NO_VALIDATION
@Suppress("UNRESOLVED_REFERENCE")
class Test {
lateinit var a: ABC
val b: ABC? = null
val c: List<ABC>? = null
val d: List<Map<BCD, ABC<List<BCD>>>>? = null
lateinit var e: List<out Map<out ABC, out BCD>?>
lateinit var f: ABC<*>
lateinit var g: List<*>
lateinit var h: ABC<Int, String>
lateinit var i: (ABC, List<BCD>) -> CDE
lateinit var j: () -> CDE
lateinit var k: ABC.(List<BCD>) -> CDE
lateinit var l: ABC.BCD.EFG
val m = ABC()
val n = "".toString()
fun f1(a: ABC): BCD? {
return null
}
fun <T> f2(a: ABC<String, Int, () -> BCD>) {}
fun <T> f3(a: ABC, b: Int): Long {
return 0
}
fun f4() = ABC()
}
@@ -0,0 +1,126 @@
@kotlin.Suppress(names = {"UNRESOLVED_REFERENCE"})
public final class Test {
public ABC a;
private final ABC b = null;
private final List<ABC> c = null;
private final List<Map<BCD, ABC<List<BCD>>>> d = null;
public List<? extends Map<? extends ABC, ? extends BCD>> e;
public ABC<?> f;
public java.util.List<?> g;
public ABC<java.lang.Integer, java.lang.String> h;
public Function2<ABC, List<BCD>, CDE> i;
public Function0<CDE> j;
public Function2<ABC, List<BCD>, CDE> k;
public ABC.BCD.EFG l;
private final error.NonExistentClass m = null;
private final java.lang.String n = "";
public final ABC getA() {
return null;
}
public final void setA(ABC p0) {
}
public final ABC getB() {
return null;
}
public final List<ABC> getC() {
return null;
}
public final List<Map<BCD, ABC<List<BCD>>>> getD() {
return null;
}
public final List<? extends Map<? extends ABC, ? extends BCD>> getE() {
return null;
}
public final void setE(List<? extends Map<? extends ABC, ? extends BCD>> p0) {
}
public final ABC<?> getF() {
return null;
}
public final void setF(ABC<?> p0) {
}
public final java.util.List<?> getG() {
return null;
}
public final void setG(java.util.List<?> p0) {
}
public final ABC<java.lang.Integer, java.lang.String> getH() {
return null;
}
public final void setH(ABC<java.lang.Integer, java.lang.String> p0) {
}
public final Function2<ABC, List<BCD>, CDE> getI() {
return null;
}
public final void setI(Function2<ABC, List<BCD>, CDE> p0) {
}
public final Function0<CDE> getJ() {
return null;
}
public final void setJ(Function0<CDE> p0) {
}
public final Function2<ABC, List<BCD>, CDE> getK() {
return null;
}
public final void setK(Function2<ABC, List<BCD>, CDE> p0) {
}
public final ABC.BCD.EFG getL() {
return null;
}
public final void setL(ABC.BCD.EFG p0) {
}
public final error.NonExistentClass getM() {
return null;
}
public final java.lang.String getN() {
return null;
}
public final BCD f1(ABC a) {
return null;
}
public final <T extends java.lang.Object>void f2(ABC<java.lang.String, java.lang.Integer, Function0<BCD>> a) {
}
public final <T extends java.lang.Object>long f3(ABC a, int b) {
return 0L;
}
public final error.NonExistentClass f4() {
return null;
}
public Test() {
super();
}
}
////////////////////
package error;
public final class NonExistentClass {
}