Introduce initial version of FIR Java type enhancement
Java type enhancement is performed by a special scope kind Java FIR dump was added for multiplatform tests to look at enhancements Overrides, J2K mapping, special cases does not work yet Related to KT-29937
This commit is contained in:
@@ -9,11 +9,11 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.java.scopes.JavaFirScopeProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.FirProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.FirScopeProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.impl.FirCompositeSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.impl.FirDependenciesSymbolProviderImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.impl.FirLibrarySymbolProviderImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.impl.*
|
||||
|
||||
class FirJavaModuleBasedSession(
|
||||
moduleInfo: ModuleInfo,
|
||||
@@ -21,7 +21,6 @@ class FirJavaModuleBasedSession(
|
||||
scope: GlobalSearchScope,
|
||||
dependenciesProvider: FirSymbolProvider? = null
|
||||
) : FirModuleBasedSession(moduleInfo) {
|
||||
|
||||
init {
|
||||
sessionProvider.sessionCache[moduleInfo] = this
|
||||
registerComponent(
|
||||
@@ -35,6 +34,15 @@ class FirJavaModuleBasedSession(
|
||||
)
|
||||
)
|
||||
)
|
||||
registerComponent(
|
||||
FirScopeProvider::class,
|
||||
FirCompositeScopeProvider(
|
||||
listOf(
|
||||
JavaFirScopeProvider(),
|
||||
FirRegularScopeProvider()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +63,15 @@ class FirLibrarySession(
|
||||
)
|
||||
)
|
||||
)
|
||||
registerComponent(
|
||||
FirScopeProvider::class,
|
||||
FirCompositeScopeProvider(
|
||||
listOf(
|
||||
JavaFirScopeProvider(),
|
||||
FirRegularScopeProvider()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,37 +9,23 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirNamedFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirModifiableClass
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirTypeParameterImpl
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirArrayOfCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaValueParameter
|
||||
import org.jetbrains.kotlin.fir.java.types.FirJavaTypeRef
|
||||
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedCallableReferenceImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.AbstractFirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.service
|
||||
import org.jetbrains.kotlin.fir.symbols.*
|
||||
import org.jetbrains.kotlin.fir.symbols.CallableId
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstKind
|
||||
import org.jetbrains.kotlin.load.java.JavaClassFinder
|
||||
import org.jetbrains.kotlin.load.java.structure.*
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
class JavaSymbolProvider(
|
||||
val session: FirSession,
|
||||
@@ -49,149 +35,6 @@ class JavaSymbolProvider(
|
||||
|
||||
private val facade: KotlinJavaPsiFacade get() = KotlinJavaPsiFacade.getInstance(project)
|
||||
|
||||
private fun JavaAnnotation.toFirAnnotationCall(): FirAnnotationCall {
|
||||
return FirAnnotationCallImpl(
|
||||
session, psi = null, useSiteTarget = null,
|
||||
annotationTypeRef = FirResolvedTypeRefImpl(
|
||||
session = session,
|
||||
psi = null,
|
||||
type = ConeClassTypeImpl(FirClassSymbol(classId!!).toLookupTag(), emptyArray(), isNullable = false),
|
||||
isMarkedNullable = true,
|
||||
annotations = emptyList()
|
||||
)
|
||||
).apply {
|
||||
for (argument in this@toFirAnnotationCall.arguments) {
|
||||
arguments += argument.toFirExpression()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: use kind here
|
||||
private fun <T> List<T>.createArrayOfCall(@Suppress("UNUSED_PARAMETER") kind: IrConstKind<T>): FirArrayOfCall {
|
||||
return FirArrayOfCallImpl(session, null).apply {
|
||||
for (element in this@createArrayOfCall) {
|
||||
arguments += element.createConstant()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Any?.createConstant(): FirExpression {
|
||||
return when (this) {
|
||||
is Byte -> FirConstExpressionImpl(session, null, IrConstKind.Byte, this)
|
||||
is Short -> FirConstExpressionImpl(session, null, IrConstKind.Short, this)
|
||||
is Int -> FirConstExpressionImpl(session, null, IrConstKind.Int, this)
|
||||
is Long -> FirConstExpressionImpl(session, null, IrConstKind.Long, this)
|
||||
is Char -> FirConstExpressionImpl(session, null, IrConstKind.Char, this)
|
||||
is Float -> FirConstExpressionImpl(session, null, IrConstKind.Float, this)
|
||||
is Double -> FirConstExpressionImpl(session, null, IrConstKind.Double, this)
|
||||
is Boolean -> FirConstExpressionImpl(session, null, IrConstKind.Boolean, this)
|
||||
is String -> FirConstExpressionImpl(session, null, IrConstKind.String, this)
|
||||
null -> FirConstExpressionImpl(session, null, IrConstKind.Null, null)
|
||||
|
||||
else -> FirErrorExpressionImpl(session, null, "Unknown value in JavaLiteralAnnotationArgument: $this")
|
||||
}
|
||||
}
|
||||
|
||||
private fun JavaAnnotationArgument.toFirExpression(): FirExpression {
|
||||
// TODO: this.name
|
||||
return when (this) {
|
||||
is JavaLiteralAnnotationArgument -> {
|
||||
when (val value = value) {
|
||||
is ByteArray -> value.toList().createArrayOfCall(IrConstKind.Byte)
|
||||
is ShortArray -> value.toList().createArrayOfCall(IrConstKind.Short)
|
||||
is IntArray -> value.toList().createArrayOfCall(IrConstKind.Int)
|
||||
is LongArray -> value.toList().createArrayOfCall(IrConstKind.Long)
|
||||
is CharArray -> value.toList().createArrayOfCall(IrConstKind.Char)
|
||||
is FloatArray -> value.toList().createArrayOfCall(IrConstKind.Float)
|
||||
is DoubleArray -> value.toList().createArrayOfCall(IrConstKind.Double)
|
||||
is BooleanArray -> value.toList().createArrayOfCall(IrConstKind.Boolean)
|
||||
else -> value.createConstant()
|
||||
}
|
||||
}
|
||||
is JavaArrayAnnotationArgument -> FirArrayOfCallImpl(session, null).apply {
|
||||
for (element in getElements()) {
|
||||
arguments += element.toFirExpression()
|
||||
}
|
||||
}
|
||||
is JavaEnumValueAnnotationArgument -> {
|
||||
FirFunctionCallImpl(session, null).apply {
|
||||
val classId = this@toFirExpression.enumClassId
|
||||
val entryName = this@toFirExpression.entryName
|
||||
val calleeReference = if (classId != null && entryName != null) {
|
||||
val callableSymbol = session.service<FirSymbolProvider>().getCallableSymbols(
|
||||
CallableId(classId.packageFqName, classId.relativeClassName, entryName)
|
||||
).firstOrNull()
|
||||
callableSymbol?.let {
|
||||
FirResolvedCallableReferenceImpl(session, null, entryName, it)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
this.calleeReference = calleeReference
|
||||
?: FirErrorNamedReference(session, null, "Strange Java enum value: ${this@toFirExpression}")
|
||||
}
|
||||
}
|
||||
is JavaClassObjectAnnotationArgument -> FirGetClassCallImpl(session, null).apply {
|
||||
val referencedType = getReferencedType()
|
||||
arguments += FirClassReferenceExpressionImpl(session, null, referencedType.toFirResolvedTypeRef())
|
||||
}
|
||||
is JavaAnnotationAsAnnotationArgument -> getAnnotation().toFirAnnotationCall()
|
||||
else -> FirErrorExpressionImpl(session, null, "Unknown JavaAnnotationArgument: ${this::class.java}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun JavaClassifierType.toFirResolvedTypeRef(): FirResolvedTypeRef {
|
||||
val coneType = when (val classifier = classifier) {
|
||||
is JavaClass -> {
|
||||
val lookupTag = ConeClassLikeLookupTagImpl(classifier.classId!!)
|
||||
ConeClassTypeImpl(lookupTag, typeArguments.map { it.toConeProjection() }.toTypedArray(), isNullable = false)
|
||||
}
|
||||
is JavaTypeParameter -> {
|
||||
// TODO: it's unclear how to identify type parameter by the symbol
|
||||
// TODO: some type parameter cache (provider?)
|
||||
val symbol = createTypeParameterSymbol(classifier.name)
|
||||
ConeTypeParameterTypeImpl(symbol, isNullable = false)
|
||||
}
|
||||
else -> ConeClassErrorType(reason = "Unexpected classifier: $classifier")
|
||||
}
|
||||
return FirResolvedTypeRefImpl(
|
||||
session, psi = null, type = coneType,
|
||||
isMarkedNullable = false, annotations = annotations.map { it.toFirAnnotationCall() }
|
||||
)
|
||||
}
|
||||
|
||||
private fun JavaType.toFirJavaTypeRef(): FirJavaTypeRef {
|
||||
val annotations = (this as? JavaClassifierType)?.annotations.orEmpty()
|
||||
return FirJavaTypeRef(session, annotations = annotations.map { it.toFirAnnotationCall() }, type = this)
|
||||
}
|
||||
|
||||
private fun JavaType.toFirResolvedTypeRef(): FirResolvedTypeRef {
|
||||
if (this is JavaClassifierType) return toFirResolvedTypeRef()
|
||||
return FirResolvedTypeRefImpl(
|
||||
session, psi = null, type = ConeClassErrorType("Unexpected JavaType: $this"),
|
||||
isMarkedNullable = false, annotations = emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
private fun JavaType.toConeProjection(): ConeKotlinTypeProjection {
|
||||
if (this is JavaClassifierType) {
|
||||
return toFirResolvedTypeRef().type
|
||||
}
|
||||
return ConeClassErrorType("Unexpected type argument: $this")
|
||||
}
|
||||
|
||||
private fun createTypeParameterSymbol(name: Name): FirTypeParameterSymbol {
|
||||
val firSymbol = FirTypeParameterSymbol()
|
||||
FirTypeParameterImpl(session, null, firSymbol, name, variance = Variance.INVARIANT, isReified = false)
|
||||
return firSymbol
|
||||
}
|
||||
|
||||
private fun FirAbstractAnnotatedElement.addAnnotationsFrom(javaAnnotationOwner: JavaAnnotationOwner) {
|
||||
for (annotation in javaAnnotationOwner.annotations) {
|
||||
annotations += annotation.toFirAnnotationCall()
|
||||
}
|
||||
}
|
||||
|
||||
private fun findClass(classId: ClassId): JavaClass? = facade.findClass(JavaClassFinder.Request(classId), searchScope)
|
||||
|
||||
override fun getCallableSymbols(callableId: CallableId): List<ConeCallableSymbol> {
|
||||
@@ -201,39 +44,12 @@ class JavaSymbolProvider(
|
||||
?: return@lookupCacheOrCalculate emptyList()
|
||||
val firClass = classSymbol.fir as FirModifiableClass
|
||||
val callableSymbols = mutableListOf<ConeCallableSymbol>()
|
||||
findClass(classId)?.let { javaClass ->
|
||||
if (firClass.declarations.isEmpty()) {
|
||||
for (javaMethod in javaClass.methods) {
|
||||
val methodName = javaMethod.name
|
||||
val methodId = CallableId(callableId.packageName, callableId.className, methodName)
|
||||
val methodSymbol = FirFunctionSymbol(methodId)
|
||||
val memberFunction = FirJavaMethod(
|
||||
session, methodSymbol, methodName,
|
||||
javaMethod.visibility, javaMethod.modality,
|
||||
returnTypeRef = javaMethod.returnType.toFirJavaTypeRef()
|
||||
).apply {
|
||||
for (typeParameter in javaMethod.typeParameters) {
|
||||
typeParameters += createTypeParameterSymbol(typeParameter.name).fir
|
||||
}
|
||||
addAnnotationsFrom(javaMethod)
|
||||
for (valueParameter in javaMethod.valueParameters) {
|
||||
valueParameters += FirJavaValueParameter(
|
||||
session, valueParameter.name ?: Name.special("<anonymous Java parameter>"),
|
||||
returnTypeRef = valueParameter.type.toFirJavaTypeRef(),
|
||||
isVararg = valueParameter.isVararg
|
||||
)
|
||||
}
|
||||
}
|
||||
firClass.declarations += memberFunction
|
||||
}
|
||||
}
|
||||
for (declaration in firClass.declarations) {
|
||||
if (declaration is FirNamedFunction) {
|
||||
val methodId = CallableId(callableId.packageName, callableId.className, declaration.name)
|
||||
if (methodId == callableId) {
|
||||
val symbol = declaration.symbol as ConeCallableSymbol
|
||||
callableSymbols += symbol
|
||||
}
|
||||
for (declaration in firClass.declarations) {
|
||||
if (declaration is FirNamedFunction) {
|
||||
val methodId = CallableId(callableId.packageName, callableId.className, declaration.name)
|
||||
if (methodId == callableId) {
|
||||
val symbol = declaration.symbol as ConeCallableSymbol
|
||||
callableSymbols += symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,14 +70,46 @@ class JavaSymbolProvider(
|
||||
FirJavaClass(
|
||||
session, firSymbol as FirClassSymbol, javaClass.name,
|
||||
javaClass.visibility, javaClass.modality,
|
||||
javaClass.classKind, javaClass.isStatic
|
||||
javaClass.classKind,
|
||||
isTopLevel = classId.relativeClassName.parent().isRoot, isStatic = javaClass.isStatic
|
||||
).apply {
|
||||
for (typeParameter in javaClass.typeParameters) {
|
||||
typeParameters += createTypeParameterSymbol(typeParameter.name).fir
|
||||
typeParameters += createTypeParameterSymbol(session, typeParameter.name).fir
|
||||
}
|
||||
addAnnotationsFrom(javaClass)
|
||||
for (supertype in javaClass.supertypes) {
|
||||
superTypeRefs += supertype.toFirResolvedTypeRef()
|
||||
superTypeRefs += supertype.toFirResolvedTypeRef(session)
|
||||
}
|
||||
// TODO: fields
|
||||
// TODO: may be we can process methods later.
|
||||
// However, they should be built up to override resolve stage
|
||||
for (javaMethod in javaClass.methods) {
|
||||
if (javaMethod.isStatic) continue // TODO: statics
|
||||
val methodName = javaMethod.name
|
||||
val methodId = CallableId(classId.packageFqName, classId.relativeClassName, methodName)
|
||||
val methodSymbol = FirFunctionSymbol(methodId)
|
||||
val returnType = javaMethod.returnType
|
||||
val firJavaMethod = FirJavaMethod(
|
||||
session, methodSymbol, methodName,
|
||||
javaMethod.visibility, javaMethod.modality,
|
||||
returnTypeRef = returnType.toFirJavaTypeRef(session)
|
||||
).apply {
|
||||
for (typeParameter in javaMethod.typeParameters) {
|
||||
typeParameters += createTypeParameterSymbol(session, typeParameter.name).fir
|
||||
}
|
||||
addAnnotationsFrom(javaMethod)
|
||||
for (valueParameter in javaMethod.valueParameters) {
|
||||
val parameterType = valueParameter.type
|
||||
valueParameters += FirJavaValueParameter(
|
||||
session, valueParameter.name ?: Name.special("<anonymous Java parameter>"),
|
||||
returnTypeRef = parameterType.toFirJavaTypeRef(session),
|
||||
isVararg = valueParameter.isVararg
|
||||
).apply {
|
||||
addAnnotationsFrom(valueParameter)
|
||||
}
|
||||
}
|
||||
}
|
||||
declarations += firJavaMethod
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -275,5 +123,12 @@ class JavaSymbolProvider(
|
||||
FqName(javaPackage.qualifiedName)
|
||||
}
|
||||
}
|
||||
|
||||
fun getJavaTopLevelClasses(): List<FirRegularClass> {
|
||||
return classCache.values
|
||||
.filterIsInstance<FirClassSymbol>()
|
||||
.filter { it.classId.relativeClassName.parent().isRoot }
|
||||
.map { it.fir }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,31 @@ package org.jetbrains.kotlin.fir.java
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaModifierListOwner
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirTypeParameterImpl
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirArrayOfCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.fir.java.types.FirJavaTypeRef
|
||||
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedCallableReferenceImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.service
|
||||
import org.jetbrains.kotlin.fir.symbols.CallableId
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTagImpl
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstKind
|
||||
import org.jetbrains.kotlin.load.java.structure.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
internal val JavaModifierListOwner.modality: Modality
|
||||
get() = when {
|
||||
@@ -24,3 +47,210 @@ internal val JavaClass.classKind: ClassKind
|
||||
isEnum -> ClassKind.ENUM_CLASS
|
||||
else -> ClassKind.CLASS
|
||||
}
|
||||
|
||||
internal fun ClassId.toConeKotlinType(
|
||||
typeArguments: Array<ConeKotlinTypeProjection>,
|
||||
isNullable: Boolean
|
||||
): ConeLookupTagBasedType {
|
||||
val lookupTag = ConeClassLikeLookupTagImpl(this)
|
||||
return ConeClassTypeImpl(lookupTag, typeArguments, isNullable)
|
||||
}
|
||||
|
||||
internal fun FirTypeRef.toNotNullConeKotlinType(): ConeKotlinType =
|
||||
when (this) {
|
||||
is FirResolvedTypeRef -> type
|
||||
is FirJavaTypeRef -> {
|
||||
val javaType = type
|
||||
javaType.toNotNullConeKotlinType(session)
|
||||
}
|
||||
else -> ConeKotlinErrorType("Unexpected type reference in JavaClassUseSiteScope: ${this::class.java}")
|
||||
}
|
||||
|
||||
internal fun JavaType.toNotNullConeKotlinType(session: FirSession): ConeLookupTagBasedType {
|
||||
return toConeKotlinTypeWithNullability(session, isNullable = false)
|
||||
}
|
||||
|
||||
internal fun JavaType.toFirJavaTypeRef(session: FirSession): FirJavaTypeRef {
|
||||
val annotations = (this as? JavaClassifierType)?.annotations.orEmpty()
|
||||
return FirJavaTypeRef(session, annotations = annotations.map { it.toFirAnnotationCall(session) }, type = this)
|
||||
}
|
||||
|
||||
internal fun JavaClassifierType.toFirResolvedTypeRef(session: FirSession): FirResolvedTypeRef {
|
||||
val coneType = this.toConeKotlinTypeWithNullability(session, isNullable = false)
|
||||
return FirResolvedTypeRefImpl(
|
||||
session, psi = null, type = coneType,
|
||||
isMarkedNullable = false, annotations = annotations.map { it.toFirAnnotationCall(session) }
|
||||
)
|
||||
}
|
||||
|
||||
internal fun JavaType.toConeKotlinTypeWithNullability(session: FirSession, isNullable: Boolean): ConeLookupTagBasedType {
|
||||
return when (this) {
|
||||
is JavaClassifierType -> {
|
||||
toConeKotlinTypeWithNullability(session, isNullable)
|
||||
}
|
||||
is JavaPrimitiveType -> {
|
||||
val primitiveType = type
|
||||
val kotlinPrimitiveName = when (val javaName = primitiveType?.typeName?.asString()) {
|
||||
null -> "Unit"
|
||||
else -> javaName.capitalize()
|
||||
}
|
||||
val classId = ClassId(FqName("kotlin"), FqName(kotlinPrimitiveName), false)
|
||||
classId.toConeKotlinType(emptyArray(), isNullable)
|
||||
}
|
||||
is JavaArrayType -> {
|
||||
val componentType = componentType
|
||||
if (componentType !is JavaPrimitiveType) {
|
||||
val classId = ClassId(FqName("kotlin"), FqName("Array"), false)
|
||||
val argumentType = ConeFlexibleType(
|
||||
componentType.toConeKotlinTypeWithNullability(session, isNullable = false),
|
||||
componentType.toConeKotlinTypeWithNullability(session, isNullable = true)
|
||||
)
|
||||
classId.toConeKotlinType(arrayOf(argumentType), isNullable)
|
||||
} else {
|
||||
val javaComponentName = componentType.type?.typeName?.asString()?.capitalize() ?: error("Array of voids")
|
||||
val classId = ClassId(FqName("kotlin"), FqName(javaComponentName + "Array"), false)
|
||||
classId.toConeKotlinType(emptyArray(), isNullable)
|
||||
}
|
||||
}
|
||||
is JavaWildcardType -> bound?.toNotNullConeKotlinType(session) ?: run {
|
||||
val classId = ClassId(FqName("kotlin"), FqName("Any"), false)
|
||||
classId.toConeKotlinType(emptyArray(), isNullable)
|
||||
}
|
||||
else -> error("Strange JavaType: ${this::class.java}")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun JavaClassifierType.toConeKotlinTypeWithNullability(session: FirSession, isNullable: Boolean): ConeLookupTagBasedType {
|
||||
return when (val classifier = classifier) {
|
||||
is JavaClass -> {
|
||||
classifier.classId!!.toConeKotlinType(typeArguments.map { it.toConeProjection(session) }.toTypedArray(), isNullable)
|
||||
}
|
||||
is JavaTypeParameter -> {
|
||||
// TODO: it's unclear how to identify type parameter by the symbol
|
||||
// TODO: some type parameter cache (provider?)
|
||||
val symbol = createTypeParameterSymbol(session, classifier.name)
|
||||
ConeTypeParameterTypeImpl(symbol, isNullable)
|
||||
}
|
||||
else -> ConeClassErrorType(reason = "Unexpected classifier: $classifier")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun createTypeParameterSymbol(session: FirSession, name: Name): FirTypeParameterSymbol {
|
||||
val firSymbol = FirTypeParameterSymbol()
|
||||
FirTypeParameterImpl(session, null, firSymbol, name, variance = Variance.INVARIANT, isReified = false)
|
||||
return firSymbol
|
||||
}
|
||||
|
||||
internal fun JavaAnnotation.toFirAnnotationCall(session: FirSession): FirAnnotationCall {
|
||||
return FirAnnotationCallImpl(
|
||||
session, psi = null, useSiteTarget = null,
|
||||
annotationTypeRef = FirResolvedTypeRefImpl(
|
||||
session = session,
|
||||
psi = null,
|
||||
type = ConeClassTypeImpl(FirClassSymbol(classId!!).toLookupTag(), emptyArray(), isNullable = false),
|
||||
isMarkedNullable = true,
|
||||
annotations = emptyList()
|
||||
)
|
||||
).apply {
|
||||
for (argument in this@toFirAnnotationCall.arguments) {
|
||||
arguments += argument.toFirExpression(session)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun FirAbstractAnnotatedElement.addAnnotationsFrom(javaAnnotationOwner: JavaAnnotationOwner) {
|
||||
for (annotation in javaAnnotationOwner.annotations) {
|
||||
annotations += annotation.toFirAnnotationCall(session)
|
||||
}
|
||||
}
|
||||
|
||||
private fun JavaType.toConeProjection(session: FirSession): ConeKotlinTypeProjection {
|
||||
if (this is JavaClassifierType) {
|
||||
return toConeKotlinTypeWithNullability(session, isNullable = false)
|
||||
}
|
||||
return ConeClassErrorType("Unexpected type argument: $this")
|
||||
}
|
||||
|
||||
private fun JavaAnnotationArgument.toFirExpression(session: FirSession): FirExpression {
|
||||
// TODO: this.name
|
||||
return when (this) {
|
||||
is JavaLiteralAnnotationArgument -> {
|
||||
when (val value = value) {
|
||||
is ByteArray -> value.toList().createArrayOfCall(session, IrConstKind.Byte)
|
||||
is ShortArray -> value.toList().createArrayOfCall(session, IrConstKind.Short)
|
||||
is IntArray -> value.toList().createArrayOfCall(session, IrConstKind.Int)
|
||||
is LongArray -> value.toList().createArrayOfCall(session, IrConstKind.Long)
|
||||
is CharArray -> value.toList().createArrayOfCall(session, IrConstKind.Char)
|
||||
is FloatArray -> value.toList().createArrayOfCall(session, IrConstKind.Float)
|
||||
is DoubleArray -> value.toList().createArrayOfCall(session, IrConstKind.Double)
|
||||
is BooleanArray -> value.toList().createArrayOfCall(session, IrConstKind.Boolean)
|
||||
else -> value.createConstant(session)
|
||||
}
|
||||
}
|
||||
is JavaArrayAnnotationArgument -> FirArrayOfCallImpl(session, null).apply {
|
||||
for (element in getElements()) {
|
||||
arguments += element.toFirExpression(session)
|
||||
}
|
||||
}
|
||||
is JavaEnumValueAnnotationArgument -> {
|
||||
FirFunctionCallImpl(session, null).apply {
|
||||
val classId = this@toFirExpression.enumClassId
|
||||
val entryName = this@toFirExpression.entryName
|
||||
val calleeReference = if (classId != null && entryName != null) {
|
||||
val callableSymbol = session.service<FirSymbolProvider>().getCallableSymbols(
|
||||
CallableId(classId.packageFqName, classId.relativeClassName, entryName)
|
||||
).firstOrNull()
|
||||
callableSymbol?.let {
|
||||
FirResolvedCallableReferenceImpl(session, null, entryName, it)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
this.calleeReference = calleeReference
|
||||
?: FirErrorNamedReference(session, null, "Strange Java enum value: ${this@toFirExpression}")
|
||||
}
|
||||
}
|
||||
is JavaClassObjectAnnotationArgument -> FirGetClassCallImpl(session, null).apply {
|
||||
val referencedType = getReferencedType()
|
||||
arguments += FirClassReferenceExpressionImpl(session, null, referencedType.toFirResolvedTypeRef(session))
|
||||
}
|
||||
is JavaAnnotationAsAnnotationArgument -> getAnnotation().toFirAnnotationCall(session)
|
||||
else -> FirErrorExpressionImpl(session, null, "Unknown JavaAnnotationArgument: ${this::class.java}")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: use kind here
|
||||
private fun <T> List<T>.createArrayOfCall(session: FirSession, @Suppress("UNUSED_PARAMETER") kind: IrConstKind<T>): FirArrayOfCall {
|
||||
return FirArrayOfCallImpl(session, null).apply {
|
||||
for (element in this@createArrayOfCall) {
|
||||
arguments += element.createConstant(session)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Any?.createConstant(session: FirSession): FirExpression {
|
||||
return when (this) {
|
||||
is Byte -> FirConstExpressionImpl(session, null, IrConstKind.Byte, this)
|
||||
is Short -> FirConstExpressionImpl(session, null, IrConstKind.Short, this)
|
||||
is Int -> FirConstExpressionImpl(session, null, IrConstKind.Int, this)
|
||||
is Long -> FirConstExpressionImpl(session, null, IrConstKind.Long, this)
|
||||
is Char -> FirConstExpressionImpl(session, null, IrConstKind.Char, this)
|
||||
is Float -> FirConstExpressionImpl(session, null, IrConstKind.Float, this)
|
||||
is Double -> FirConstExpressionImpl(session, null, IrConstKind.Double, this)
|
||||
is Boolean -> FirConstExpressionImpl(session, null, IrConstKind.Boolean, this)
|
||||
is String -> FirConstExpressionImpl(session, null, IrConstKind.String, this)
|
||||
null -> FirConstExpressionImpl(session, null, IrConstKind.Null, null)
|
||||
|
||||
else -> FirErrorExpressionImpl(session, null, "Unknown value in JavaLiteralAnnotationArgument: $this")
|
||||
}
|
||||
}
|
||||
|
||||
private fun JavaType.toFirResolvedTypeRef(session: FirSession): FirResolvedTypeRef {
|
||||
if (this is JavaClassifierType) return toFirResolvedTypeRef(session)
|
||||
return FirResolvedTypeRefImpl(
|
||||
session, psi = null, type = ConeClassErrorType("Unexpected JavaType: $this"),
|
||||
isMarkedNullable = false, annotations = emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,26 +5,72 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.java.declarations
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirClassImpl
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirAbstractMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirModifiableClass
|
||||
import org.jetbrains.kotlin.fir.java.scopes.JavaClassEnhancementScope
|
||||
import org.jetbrains.kotlin.fir.java.scopes.JavaClassUseSiteScope
|
||||
import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirClassDeclaredMemberScope
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirCompositeScope
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassErrorType
|
||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class FirJavaClass(
|
||||
session: FirSession,
|
||||
symbol: FirClassSymbol,
|
||||
override val symbol: FirClassSymbol,
|
||||
name: Name,
|
||||
visibility: Visibility,
|
||||
modality: Modality?,
|
||||
classKind: ClassKind,
|
||||
override val classKind: ClassKind,
|
||||
isTopLevel: Boolean,
|
||||
isStatic: Boolean
|
||||
) : FirClassImpl(
|
||||
session, psi = null, symbol = symbol, name = name,
|
||||
) : FirAbstractMemberDeclaration(
|
||||
session, psi = null, name = name,
|
||||
visibility = visibility, modality = modality,
|
||||
isExpect = false, isActual = false, classKind = classKind, isInner = !isStatic,
|
||||
isCompanion = false, isData = false, isInline = false
|
||||
)
|
||||
isExpect = false, isActual = false
|
||||
), FirRegularClass, FirModifiableClass {
|
||||
init {
|
||||
symbol.bind(this)
|
||||
status.isInner = !isTopLevel && !isStatic
|
||||
status.isCompanion = false
|
||||
status.isData = false
|
||||
status.isInline = false
|
||||
}
|
||||
|
||||
override val superTypeRefs = mutableListOf<FirTypeRef>()
|
||||
|
||||
val useSiteScope: JavaClassUseSiteScope by lazy { buildUseSiteScope() }
|
||||
|
||||
override val declarations = mutableListOf<FirDeclaration>()
|
||||
|
||||
private fun FirRegularClass.buildUseSiteScope(useSiteSession: FirSession = session): JavaClassUseSiteScope {
|
||||
val superTypeEnhancementScope = FirCompositeScope(mutableListOf())
|
||||
val declaredScope = FirClassDeclaredMemberScope(this, useSiteSession)
|
||||
lookupSuperTypes(this, lookupInterfaces = true, deep = false, useSiteSession = useSiteSession)
|
||||
.mapNotNullTo(superTypeEnhancementScope.scopes) { useSiteSuperType ->
|
||||
if (useSiteSuperType is ConeClassErrorType) return@mapNotNullTo null
|
||||
val symbol = useSiteSuperType.lookupTag.toSymbol(useSiteSession)
|
||||
if (symbol is FirClassSymbol) {
|
||||
JavaClassEnhancementScope(useSiteSession, symbol.fir.buildUseSiteScope(useSiteSession))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
return JavaClassUseSiteScope(this, useSiteSession, superTypeEnhancementScope, declaredScope)
|
||||
}
|
||||
|
||||
override fun replaceSupertypes(newSupertypes: List<FirTypeRef>): FirRegularClass {
|
||||
superTypeRefs.clear()
|
||||
superTypeRefs.addAll(newSupertypes)
|
||||
return this
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ class FirJavaMethod(
|
||||
session, null, symbol, name,
|
||||
visibility, modality,
|
||||
false, isActual = false,
|
||||
isOverride = false, // TODO: really it's unknown whether Java methods are overrides or not
|
||||
isOverride = false,
|
||||
isOperator = true, // All Java methods with name that allows to use it in operator form are considered operators
|
||||
isInfix = false, isInline = false, isTailRec = false, isExternal = false, isSuspend = false,
|
||||
receiverTypeRef = null, returnTypeRef = returnTypeRef
|
||||
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.fir.java.enhancement
|
||||
|
||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationContainer
|
||||
import org.jetbrains.kotlin.fir.expressions.resolvedFqName
|
||||
import org.jetbrains.kotlin.fir.java.toConeKotlinTypeWithNullability
|
||||
import org.jetbrains.kotlin.fir.java.toFirJavaTypeRef
|
||||
import org.jetbrains.kotlin.fir.java.toNotNullConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.java.types.FirJavaTypeRef
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.load.java.AnnotationTypeQualifierResolver
|
||||
import org.jetbrains.kotlin.load.java.MUTABLE_ANNOTATIONS
|
||||
import org.jetbrains.kotlin.load.java.READ_ONLY_ANNOTATIONS
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaWildcardType
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.utils.Jsr305State
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
internal class EnhancementSignatureParts(
|
||||
private val typeQualifierResolver: FirAnnotationTypeQualifierResolver,
|
||||
private val typeContainer: FirAnnotationContainer?,
|
||||
private val current: FirJavaTypeRef,
|
||||
private val fromOverridden: Collection<FirTypeRef>,
|
||||
private val isCovariant: Boolean,
|
||||
private val context: FirJavaEnhancementContext,
|
||||
private val containerApplicabilityType: AnnotationTypeQualifierResolver.QualifierApplicabilityType
|
||||
) {
|
||||
private val isForVarargParameter get() = typeContainer.safeAs<FirValueParameter>()?.isVararg == true
|
||||
|
||||
private fun ConeKotlinType.toFqNameUnsafe(): FqNameUnsafe? =
|
||||
((this as? ConeLookupTagBasedType)?.lookupTag as? ConeClassLikeLookupTag)?.classId?.asSingleFqName()?.toUnsafe()
|
||||
|
||||
internal fun enhance(
|
||||
jsr305State: Jsr305State,
|
||||
predefined: TypeEnhancementInfo? = null
|
||||
): PartEnhancementResult {
|
||||
val qualifiers = computeIndexedQualifiersForOverride(jsr305State)
|
||||
|
||||
val qualifiersWithPredefined: ((Int) -> JavaTypeQualifiers)? = predefined?.let {
|
||||
{ index ->
|
||||
predefined.map[index] ?: qualifiers(index)
|
||||
}
|
||||
}
|
||||
|
||||
val containsFunctionN = current.toNotNullConeKotlinType().contains {
|
||||
val classId = it.lookupTag.classId
|
||||
classId.shortClassName == JavaToKotlinClassMap.FUNCTION_N_FQ_NAME.shortName() &&
|
||||
classId.asSingleFqName() == JavaToKotlinClassMap.FUNCTION_N_FQ_NAME
|
||||
}
|
||||
|
||||
val enhancedCurrent = current.enhance(qualifiersWithPredefined ?: qualifiers)
|
||||
return PartEnhancementResult(
|
||||
enhancedCurrent, wereChanges = true, containsFunctionN = containsFunctionN
|
||||
)
|
||||
}
|
||||
|
||||
private fun ConeKotlinType.contains(isSpecialType: (ConeClassLikeType) -> Boolean): Boolean {
|
||||
return when (this) {
|
||||
is ConeClassLikeType -> isSpecialType(this)
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun FirTypeRef.toIndexed(
|
||||
typeQualifierResolver: FirAnnotationTypeQualifierResolver,
|
||||
jsr305State: Jsr305State,
|
||||
context: FirJavaEnhancementContext
|
||||
): List<TypeAndDefaultQualifiers> {
|
||||
val list = ArrayList<TypeAndDefaultQualifiers>(1)
|
||||
|
||||
fun add(type: FirTypeRef) {
|
||||
val c = context.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, jsr305State, type.annotations)
|
||||
|
||||
list.add(
|
||||
TypeAndDefaultQualifiers(
|
||||
type,
|
||||
c.defaultTypeQualifiers
|
||||
?.get(AnnotationTypeQualifierResolver.QualifierApplicabilityType.TYPE_USE)
|
||||
)
|
||||
)
|
||||
|
||||
if (type is FirJavaTypeRef) {
|
||||
for (arg in type.type.typeArguments()) {
|
||||
if (arg is JavaWildcardType) {
|
||||
// TODO: wildcards
|
||||
} else {
|
||||
add(arg.toFirJavaTypeRef(context.session))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (arg in type.typeArguments()) {
|
||||
if (arg is FirStarProjection) {
|
||||
// TODO: wildcards
|
||||
} else if (arg is FirTypeProjectionWithVariance) {
|
||||
add(arg.typeRef)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add(this)
|
||||
return list
|
||||
}
|
||||
|
||||
private fun extractQualifiers(lower: ConeKotlinType, upper: ConeKotlinType): JavaTypeQualifiers {
|
||||
val mapping = JavaToKotlinClassMap
|
||||
return JavaTypeQualifiers(
|
||||
when {
|
||||
lower.isMarkedNullable -> NullabilityQualifier.NULLABLE
|
||||
!upper.isMarkedNullable -> NullabilityQualifier.NOT_NULL
|
||||
else -> null
|
||||
},
|
||||
when {
|
||||
mapping.isReadOnly(lower.toFqNameUnsafe()) -> MutabilityQualifier.READ_ONLY
|
||||
mapping.isMutable(upper.toFqNameUnsafe()) -> MutabilityQualifier.MUTABLE
|
||||
else -> null
|
||||
},
|
||||
isNotNullTypeParameter = false //TODO: unwrap() is NotNullTypeParameter
|
||||
)
|
||||
}
|
||||
|
||||
private fun FirTypeRef.extractQualifiers(): JavaTypeQualifiers {
|
||||
val (lower, upper) = when (this) {
|
||||
is FirResolvedTypeRef -> {
|
||||
val type = this.type
|
||||
if (type is ConeFlexibleType) {
|
||||
Pair(type.lowerBound, type.upperBound)
|
||||
} else {
|
||||
Pair(type, type)
|
||||
}
|
||||
}
|
||||
is FirJavaTypeRef -> {
|
||||
Pair(
|
||||
// TODO: optimize
|
||||
type.toConeKotlinTypeWithNullability(session, isNullable = false),
|
||||
type.toConeKotlinTypeWithNullability(session, isNullable = true)
|
||||
)
|
||||
}
|
||||
else -> return JavaTypeQualifiers.NONE
|
||||
}
|
||||
|
||||
return extractQualifiers(lower, upper)
|
||||
}
|
||||
|
||||
private fun composeAnnotations(first: List<FirAnnotationCall>, second: List<FirAnnotationCall>): List<FirAnnotationCall> {
|
||||
return when {
|
||||
first.isEmpty() -> second
|
||||
second.isEmpty() -> first
|
||||
else -> first + second
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirTypeRef.extractQualifiersFromAnnotations(
|
||||
isHeadTypeConstructor: Boolean,
|
||||
defaultQualifiersForType: JavaTypeQualifiers?,
|
||||
jsr305State: Jsr305State
|
||||
): JavaTypeQualifiers {
|
||||
val composedAnnotation =
|
||||
if (isHeadTypeConstructor && typeContainer != null)
|
||||
composeAnnotations(typeContainer.annotations, annotations)
|
||||
else
|
||||
annotations
|
||||
|
||||
fun <T : Any> List<FqName>.ifPresent(qualifier: T) =
|
||||
if (any { fqName ->
|
||||
composedAnnotation.any { it.resolvedFqName == fqName }
|
||||
}
|
||||
) qualifier else null
|
||||
|
||||
fun <T : Any> uniqueNotNull(x: T?, y: T?) = if (x == null || y == null || x == y) x ?: y else null
|
||||
|
||||
val defaultTypeQualifier =
|
||||
if (isHeadTypeConstructor)
|
||||
context.defaultTypeQualifiers?.get(containerApplicabilityType)
|
||||
else
|
||||
defaultQualifiersForType
|
||||
|
||||
val nullabilityInfo = composedAnnotation.extractNullability(typeQualifierResolver, jsr305State)
|
||||
?: defaultTypeQualifier?.nullability?.let { nullability ->
|
||||
NullabilityQualifierWithMigrationStatus(
|
||||
nullability,
|
||||
defaultTypeQualifier.isNullabilityQualifierForWarning
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("SimplifyBooleanWithConstants")
|
||||
return JavaTypeQualifiers(
|
||||
nullabilityInfo?.qualifier,
|
||||
uniqueNotNull(
|
||||
READ_ONLY_ANNOTATIONS.ifPresent(
|
||||
MutabilityQualifier.READ_ONLY
|
||||
),
|
||||
MUTABLE_ANNOTATIONS.ifPresent(
|
||||
MutabilityQualifier.MUTABLE
|
||||
)
|
||||
),
|
||||
isNotNullTypeParameter = nullabilityInfo?.qualifier == NullabilityQualifier.NOT_NULL && true, /* TODO: isTypeParameter()*/
|
||||
isNullabilityQualifierForWarning = nullabilityInfo?.isForWarningOnly == true
|
||||
)
|
||||
}
|
||||
|
||||
private fun FirTypeRef.computeQualifiersForOverride(
|
||||
fromSupertypes: Collection<FirTypeRef>,
|
||||
defaultQualifiersForType: JavaTypeQualifiers?,
|
||||
isHeadTypeConstructor: Boolean,
|
||||
jsr305State: Jsr305State
|
||||
): JavaTypeQualifiers {
|
||||
val superQualifiers = fromSupertypes.map { it.extractQualifiers() }
|
||||
val mutabilityFromSupertypes = superQualifiers.mapNotNull { it.mutability }.toSet()
|
||||
val nullabilityFromSupertypes = superQualifiers.mapNotNull { it.nullability }.toSet()
|
||||
val nullabilityFromSupertypesWithWarning = fromOverridden
|
||||
.mapNotNull { it.extractQualifiers().nullability }
|
||||
.toSet()
|
||||
|
||||
val own = extractQualifiersFromAnnotations(isHeadTypeConstructor, defaultQualifiersForType, jsr305State)
|
||||
val ownNullability = own.takeIf { !it.isNullabilityQualifierForWarning }?.nullability
|
||||
val ownNullabilityForWarning = own.nullability
|
||||
|
||||
val isCovariantPosition = isCovariant && isHeadTypeConstructor
|
||||
val nullability =
|
||||
nullabilityFromSupertypes.select(ownNullability, isCovariantPosition)
|
||||
// Vararg value parameters effectively have non-nullable type in Kotlin
|
||||
// and having nullable types in Java may lead to impossibility of overriding them in Kotlin
|
||||
?.takeUnless { isForVarargParameter && isHeadTypeConstructor && it == NullabilityQualifier.NULLABLE }
|
||||
|
||||
val mutability =
|
||||
mutabilityFromSupertypes
|
||||
.select(MutabilityQualifier.MUTABLE, MutabilityQualifier.READ_ONLY, own.mutability, isCovariantPosition)
|
||||
|
||||
val canChange = ownNullabilityForWarning != ownNullability || nullabilityFromSupertypesWithWarning != nullabilityFromSupertypes
|
||||
val isAnyNonNullTypeParameter = own.isNotNullTypeParameter || superQualifiers.any { it.isNotNullTypeParameter }
|
||||
if (nullability == null && canChange) {
|
||||
val nullabilityWithWarning =
|
||||
nullabilityFromSupertypesWithWarning.select(ownNullabilityForWarning, isCovariantPosition)
|
||||
|
||||
return createJavaTypeQualifiers(
|
||||
nullabilityWithWarning, mutability,
|
||||
forWarning = true, isAnyNonNullTypeParameter = isAnyNonNullTypeParameter
|
||||
)
|
||||
}
|
||||
|
||||
return createJavaTypeQualifiers(
|
||||
nullability, mutability,
|
||||
forWarning = nullability == null,
|
||||
isAnyNonNullTypeParameter = isAnyNonNullTypeParameter
|
||||
)
|
||||
}
|
||||
|
||||
private fun computeIndexedQualifiersForOverride(jsr305State: Jsr305State): (Int) -> JavaTypeQualifiers {
|
||||
val indexedFromSupertypes = fromOverridden.map { it.toIndexed(typeQualifierResolver, jsr305State, context) }
|
||||
val indexedThisType = current.toIndexed(typeQualifierResolver, jsr305State, context)
|
||||
|
||||
// The covariant case may be hard, e.g. in the superclass the return may be Super<T>, but in the subclass it may be Derived, which
|
||||
// is declared to extend Super<T>, and propagating data here is highly non-trivial, so we only look at the head type constructor
|
||||
// (outermost type), unless the type in the subclass is interchangeable with the all the types in superclasses:
|
||||
// e.g. we have (Mutable)List<String!>! in the subclass and { List<String!>, (Mutable)List<String>! } from superclasses
|
||||
// Note that `this` is flexible here, so it's equal to it's bounds
|
||||
val onlyHeadTypeConstructor = isCovariant && fromOverridden.any { true /*equalTypes(it, this)*/ }
|
||||
|
||||
val treeSize = if (onlyHeadTypeConstructor) 1 else indexedThisType.size
|
||||
val computedResult = Array(treeSize) { index ->
|
||||
val isHeadTypeConstructor = index == 0
|
||||
assert(isHeadTypeConstructor || !onlyHeadTypeConstructor) { "Only head type constructors should be computed" }
|
||||
|
||||
val (qualifiers, defaultQualifiers) = indexedThisType[index]
|
||||
val verticalSlice = indexedFromSupertypes.mapNotNull { it.getOrNull(index)?.type }
|
||||
|
||||
// Only the head type constructor is safely co-variant
|
||||
qualifiers.computeQualifiersForOverride(verticalSlice, defaultQualifiers, isHeadTypeConstructor, jsr305State)
|
||||
}
|
||||
|
||||
return { index -> computedResult.getOrElse(index) { JavaTypeQualifiers.NONE } }
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
internal open class PartEnhancementResult(
|
||||
val type: FirResolvedTypeRef,
|
||||
val wereChanges: Boolean,
|
||||
val containsFunctionN: Boolean
|
||||
)
|
||||
}
|
||||
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.fir.java.enhancement
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||
import org.jetbrains.kotlin.load.java.*
|
||||
import org.jetbrains.kotlin.load.java.lazy.NullabilityQualifierWithApplicability
|
||||
import org.jetbrains.kotlin.utils.Jsr305State
|
||||
import org.jetbrains.kotlin.utils.ReportLevel
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
|
||||
class FirAnnotationTypeQualifierResolver(private val jsr305State: Jsr305State) {
|
||||
|
||||
class TypeQualifierWithApplicability(
|
||||
private val typeQualifier: FirAnnotationCall,
|
||||
private val applicability: Int
|
||||
) {
|
||||
operator fun component1() = typeQualifier
|
||||
operator fun component2() = AnnotationTypeQualifierResolver.QualifierApplicabilityType.values().filter(this::isApplicableTo)
|
||||
|
||||
private fun isApplicableTo(elementType: AnnotationTypeQualifierResolver.QualifierApplicabilityType) =
|
||||
isApplicableConsideringMask(AnnotationTypeQualifierResolver.QualifierApplicabilityType.TYPE_USE) || isApplicableConsideringMask(
|
||||
elementType
|
||||
)
|
||||
|
||||
private fun isApplicableConsideringMask(elementType: AnnotationTypeQualifierResolver.QualifierApplicabilityType) =
|
||||
(applicability and (1 shl elementType.ordinal)) != 0
|
||||
}
|
||||
|
||||
// TODO: memoize this function
|
||||
private fun computeTypeQualifierNickname(klass: FirRegularClass): FirAnnotationCall? {
|
||||
if (klass.annotations.none { it.resolvedFqName == TYPE_QUALIFIER_NICKNAME_FQNAME }) return null
|
||||
|
||||
return klass.annotations.firstNotNullResult(this::resolveTypeQualifierAnnotation)
|
||||
}
|
||||
|
||||
private fun resolveTypeQualifierNickname(klass: FirRegularClass): FirAnnotationCall? {
|
||||
if (klass.classKind != ClassKind.ANNOTATION_CLASS) return null
|
||||
|
||||
return computeTypeQualifierNickname(klass)
|
||||
}
|
||||
|
||||
private val FirAnnotationCall.resolvedClass: FirRegularClass?
|
||||
get() {
|
||||
val lookupTag = ((annotationTypeRef as? FirResolvedTypeRef)?.type as? ConeClassLikeType)?.lookupTag
|
||||
return (lookupTag?.toSymbol(session) as? FirClassSymbol)?.fir
|
||||
}
|
||||
|
||||
fun resolveTypeQualifierAnnotation(annotationCall: FirAnnotationCall): FirAnnotationCall? {
|
||||
if (jsr305State.disabled) {
|
||||
return null
|
||||
}
|
||||
|
||||
val annotationClass = annotationCall.resolvedClass ?: return null
|
||||
if (annotationClass.isAnnotatedWithTypeQualifier) return annotationCall
|
||||
|
||||
return resolveTypeQualifierNickname(annotationClass)
|
||||
}
|
||||
|
||||
fun resolveQualifierBuiltInDefaultAnnotation(annotationCall: FirAnnotationCall): NullabilityQualifierWithApplicability? {
|
||||
if (jsr305State.disabled) {
|
||||
return null
|
||||
}
|
||||
|
||||
return BUILT_IN_TYPE_QUALIFIER_DEFAULT_ANNOTATIONS[annotationCall.resolvedFqName]?.let { (qualifier, applicability) ->
|
||||
val state = resolveJsr305ReportLevel(annotationCall).takeIf { it != ReportLevel.IGNORE } ?: return null
|
||||
return NullabilityQualifierWithApplicability(qualifier.copy(isForWarningOnly = state.isWarning), applicability)
|
||||
}
|
||||
}
|
||||
|
||||
fun resolveTypeQualifierDefaultAnnotation(annotationCall: FirAnnotationCall): TypeQualifierWithApplicability? {
|
||||
if (jsr305State.disabled) {
|
||||
return null
|
||||
}
|
||||
|
||||
val typeQualifierDefaultAnnotatedClass =
|
||||
annotationCall.resolvedClass?.takeIf { klass ->
|
||||
klass.annotations.any { it.resolvedFqName == TYPE_QUALIFIER_DEFAULT_FQNAME }
|
||||
} ?: return null
|
||||
|
||||
val elementTypesMask =
|
||||
annotationCall.resolvedClass!!
|
||||
.annotations.find { it.resolvedFqName == TYPE_QUALIFIER_DEFAULT_FQNAME }!!
|
||||
.arguments
|
||||
.flatMap { argument ->
|
||||
// TODO: replace after implementation of KT-24081
|
||||
if (true /*parameter == JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME*/)
|
||||
argument.mapConstantToQualifierApplicabilityTypes()
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
.fold(0) { acc: Int, applicabilityType -> acc or (1 shl applicabilityType.ordinal) }
|
||||
|
||||
val typeQualifier = typeQualifierDefaultAnnotatedClass.annotations.firstOrNull { resolveTypeQualifierAnnotation(it) != null }
|
||||
?: return null
|
||||
|
||||
return TypeQualifierWithApplicability(
|
||||
typeQualifier,
|
||||
elementTypesMask
|
||||
)
|
||||
}
|
||||
|
||||
fun resolveJsr305ReportLevel(annotationCall: FirAnnotationCall): ReportLevel {
|
||||
resolveJsr305CustomLevel(annotationCall)?.let { return it }
|
||||
return jsr305State.global
|
||||
}
|
||||
|
||||
fun resolveJsr305CustomLevel(annotationCall: FirAnnotationCall): ReportLevel? {
|
||||
jsr305State.user[annotationCall.resolvedFqName?.asString()]?.let { return it }
|
||||
return annotationCall.resolvedClass?.migrationAnnotationStatus()
|
||||
}
|
||||
|
||||
private fun FirRegularClass.migrationAnnotationStatus(): ReportLevel? {
|
||||
val enumEntryName = annotations.find {
|
||||
it.resolvedFqName == MIGRATION_ANNOTATION_FQNAME
|
||||
}?.arguments?.firstOrNull()?.toResolvedCallableSymbol()?.callableId?.callableName ?: return null
|
||||
|
||||
jsr305State.migration?.let { return it }
|
||||
|
||||
return when (enumEntryName.asString()) {
|
||||
"STRICT" -> ReportLevel.STRICT
|
||||
"WARN" -> ReportLevel.WARN
|
||||
"IGNORE" -> ReportLevel.IGNORE
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirExpression.mapConstantToQualifierApplicabilityTypes(): List<AnnotationTypeQualifierResolver.QualifierApplicabilityType> =
|
||||
when (this) {
|
||||
is FirArrayOfCall -> arguments.flatMap { it.mapConstantToQualifierApplicabilityTypes() }
|
||||
else -> listOfNotNull(
|
||||
when (toResolvedCallableSymbol()?.callableId?.callableName?.asString()) {
|
||||
"METHOD" -> AnnotationTypeQualifierResolver.QualifierApplicabilityType.METHOD_RETURN_TYPE
|
||||
"FIELD" -> AnnotationTypeQualifierResolver.QualifierApplicabilityType.FIELD
|
||||
"PARAMETER" -> AnnotationTypeQualifierResolver.QualifierApplicabilityType.VALUE_PARAMETER
|
||||
"TYPE_USE" -> AnnotationTypeQualifierResolver.QualifierApplicabilityType.TYPE_USE
|
||||
else -> null
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val disabled: Boolean = jsr305State.disabled
|
||||
|
||||
}
|
||||
|
||||
private val FirRegularClass.isAnnotatedWithTypeQualifier: Boolean
|
||||
get() = this.symbol.classId.asSingleFqName() in BUILT_IN_TYPE_QUALIFIER_FQ_NAMES ||
|
||||
annotations.any { it.resolvedFqName == TYPE_QUALIFIER_FQNAME }
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.fir.java.enhancement
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.load.java.AnnotationTypeQualifierResolver
|
||||
import org.jetbrains.kotlin.load.java.lazy.JavaTypeQualifiersByElementType
|
||||
import org.jetbrains.kotlin.load.java.lazy.NullabilityQualifierWithApplicability
|
||||
import org.jetbrains.kotlin.load.java.lazy.QualifierByApplicabilityType
|
||||
import org.jetbrains.kotlin.utils.Jsr305State
|
||||
|
||||
class FirJavaEnhancementContext private constructor(
|
||||
val session: FirSession,
|
||||
delegateForDefaultTypeQualifiers: Lazy<JavaTypeQualifiersByElementType?>
|
||||
) {
|
||||
constructor(session: FirSession, typeQualifiersComputation: () -> JavaTypeQualifiersByElementType?) :
|
||||
this(session, lazy(LazyThreadSafetyMode.NONE, typeQualifiersComputation))
|
||||
|
||||
val defaultTypeQualifiers: JavaTypeQualifiersByElementType? by delegateForDefaultTypeQualifiers
|
||||
|
||||
val moduleInfo get() = session.moduleInfo
|
||||
}
|
||||
|
||||
fun extractDefaultNullabilityQualifier(
|
||||
typeQualifierResolver: FirAnnotationTypeQualifierResolver,
|
||||
jsr305State: Jsr305State,
|
||||
annotationCall: FirAnnotationCall
|
||||
): NullabilityQualifierWithApplicability? {
|
||||
typeQualifierResolver.resolveQualifierBuiltInDefaultAnnotation(annotationCall)?.let { return it }
|
||||
|
||||
val (typeQualifier, applicability) =
|
||||
typeQualifierResolver.resolveTypeQualifierDefaultAnnotation(annotationCall)
|
||||
?: return null
|
||||
|
||||
val jsr305ReportLevel = with(typeQualifierResolver) {
|
||||
resolveJsr305CustomLevel(annotationCall) ?: resolveJsr305ReportLevel(typeQualifier)
|
||||
}
|
||||
|
||||
if (jsr305ReportLevel.isIgnore) {
|
||||
return null
|
||||
}
|
||||
|
||||
val nullabilityQualifier = typeQualifier.extractNullability(
|
||||
typeQualifierResolver, jsr305State
|
||||
)?.copy(isForWarningOnly = jsr305ReportLevel.isWarning) ?: return null
|
||||
|
||||
return NullabilityQualifierWithApplicability(nullabilityQualifier, applicability)
|
||||
}
|
||||
|
||||
fun FirJavaEnhancementContext.computeNewDefaultTypeQualifiers(
|
||||
typeQualifierResolver: FirAnnotationTypeQualifierResolver,
|
||||
jsr305State: Jsr305State,
|
||||
additionalAnnotations: List<FirAnnotationCall>
|
||||
): JavaTypeQualifiersByElementType? {
|
||||
if (typeQualifierResolver.disabled) return defaultTypeQualifiers
|
||||
|
||||
val nullabilityQualifiersWithApplicability =
|
||||
additionalAnnotations.mapNotNull { annotationCall ->
|
||||
extractDefaultNullabilityQualifier(
|
||||
typeQualifierResolver,
|
||||
jsr305State,
|
||||
annotationCall
|
||||
)
|
||||
}
|
||||
|
||||
if (nullabilityQualifiersWithApplicability.isEmpty()) return defaultTypeQualifiers
|
||||
|
||||
val nullabilityQualifiersByType =
|
||||
defaultTypeQualifiers?.nullabilityQualifiers?.let(::QualifierByApplicabilityType)
|
||||
?: QualifierByApplicabilityType(AnnotationTypeQualifierResolver.QualifierApplicabilityType::class.java)
|
||||
|
||||
var wasUpdate = false
|
||||
for ((nullability, applicableTo) in nullabilityQualifiersWithApplicability) {
|
||||
for (applicabilityType in applicableTo) {
|
||||
nullabilityQualifiersByType[applicabilityType] = nullability
|
||||
wasUpdate = true
|
||||
}
|
||||
}
|
||||
|
||||
return if (!wasUpdate) defaultTypeQualifiers else JavaTypeQualifiersByElementType(nullabilityQualifiersByType)
|
||||
}
|
||||
|
||||
fun FirJavaEnhancementContext.copyWithNewDefaultTypeQualifiers(
|
||||
typeQualifierResolver: FirAnnotationTypeQualifierResolver,
|
||||
jsr305State: Jsr305State,
|
||||
additionalAnnotations: List<FirAnnotationCall>
|
||||
): FirJavaEnhancementContext =
|
||||
when {
|
||||
additionalAnnotations.isEmpty() -> this
|
||||
else -> FirJavaEnhancementContext(session) {
|
||||
computeNewDefaultTypeQualifiers(typeQualifierResolver, jsr305State, additionalAnnotations)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.fir.java.enhancement
|
||||
|
||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.resolvedFqName
|
||||
import org.jetbrains.kotlin.fir.java.createTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.java.toConeKotlinTypeWithNullability
|
||||
import org.jetbrains.kotlin.fir.java.toNotNullConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.java.types.FirJavaTypeRef
|
||||
import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.constructType
|
||||
import org.jetbrains.kotlin.fir.resolve.toTypeProjection
|
||||
import org.jetbrains.kotlin.fir.service
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassifierSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.load.java.descriptors.AnnotationDefaultValue
|
||||
import org.jetbrains.kotlin.load.java.descriptors.NullDefaultValue
|
||||
import org.jetbrains.kotlin.load.java.descriptors.StringDefaultValue
|
||||
import org.jetbrains.kotlin.load.java.structure.*
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
internal fun FirJavaTypeRef.enhance(
|
||||
qualifiers: (Int) -> JavaTypeQualifiers
|
||||
): FirResolvedTypeRef {
|
||||
return type.enhancePossiblyFlexible(session, annotations, qualifiers, 0)
|
||||
}
|
||||
|
||||
// The index in the lambda is the position of the type component:
|
||||
// Example: for `A<B, C<D, E>>`, indices go as follows: `0 - A<...>, 1 - B, 2 - C<D, E>, 3 - D, 4 - E`,
|
||||
// which corresponds to the left-to-right breadth-first walk of the tree representation of the type.
|
||||
// For flexible types, both bounds are indexed in the same way: `(A<B>..C<D>)` gives `0 - (A<B>..C<D>), 1 - B and D`.
|
||||
private fun JavaType.enhancePossiblyFlexible(
|
||||
session: FirSession,
|
||||
annotations: List<FirAnnotationCall>,
|
||||
qualifiers: (Int) -> JavaTypeQualifiers,
|
||||
index: Int
|
||||
): FirResolvedTypeRef {
|
||||
val type = this
|
||||
val arguments = typeArguments()
|
||||
return when (type) {
|
||||
is JavaClassifierType -> {
|
||||
val lowerResult = type.enhanceInflexibleType(
|
||||
session, annotations, arguments, TypeComponentPosition.FLEXIBLE_LOWER, qualifiers, index
|
||||
)
|
||||
val upperResult = type.enhanceInflexibleType(
|
||||
session, annotations, arguments, TypeComponentPosition.FLEXIBLE_UPPER, qualifiers, index
|
||||
)
|
||||
|
||||
FirResolvedTypeRefImpl(
|
||||
session, psi = null,
|
||||
type = ConeFlexibleType(lowerResult, upperResult),
|
||||
isMarkedNullable = false, annotations = annotations
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
val enhanced = type.toNotNullConeKotlinType(session)
|
||||
FirResolvedTypeRefImpl(session, psi = null, type = enhanced, isMarkedNullable = false, annotations = annotations)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun JavaType.subtreeSize(): Int {
|
||||
if (this !is JavaClassifierType) return 1
|
||||
return 1 + typeArguments.sumBy { it.subtreeSize() }
|
||||
}
|
||||
|
||||
private fun JavaClassifierType.enhanceInflexibleType(
|
||||
session: FirSession,
|
||||
annotations: List<FirAnnotationCall>,
|
||||
arguments: List<JavaType>,
|
||||
position: TypeComponentPosition,
|
||||
qualifiers: (Int) -> JavaTypeQualifiers,
|
||||
index: Int
|
||||
): ConeLookupTagBasedType {
|
||||
val originalSymbol = when (val classifier = classifier) {
|
||||
is JavaClass -> session.service<FirSymbolProvider>().getClassLikeSymbolByFqName(classifier.classId!!)!!
|
||||
is JavaTypeParameter -> createTypeParameterSymbol(session, classifier.name)
|
||||
else -> return toNotNullConeKotlinType(session)
|
||||
}
|
||||
|
||||
val effectiveQualifiers = qualifiers(index)
|
||||
val (enhancedSymbol, mutabilityChanged) = originalSymbol.enhanceMutability(effectiveQualifiers, position)
|
||||
|
||||
var globalArgIndex = index + 1
|
||||
var wereChanges = mutabilityChanged
|
||||
val enhancedArguments = arguments.mapIndexed { localArgIndex, arg ->
|
||||
if (arg is JavaWildcardType) {
|
||||
globalArgIndex++
|
||||
ConeStarProjection
|
||||
// TODO: (?) TypeUtils.makeStarProjection(enhancedClassifier.typeConstructor.parameters[localArgIndex])
|
||||
} else {
|
||||
val argEnhancedTypeRef = arg.enhancePossiblyFlexible(session, annotations, qualifiers, globalArgIndex)
|
||||
wereChanges = wereChanges || argEnhancedTypeRef !== arg
|
||||
globalArgIndex += arg.subtreeSize()
|
||||
argEnhancedTypeRef.type.type.toTypeProjection(Variance.INVARIANT)
|
||||
}
|
||||
}
|
||||
|
||||
val (enhancedNullability, _, nullabilityChanged) = getEnhancedNullability(effectiveQualifiers, position)
|
||||
wereChanges = wereChanges || nullabilityChanged
|
||||
|
||||
if (!wereChanges) return toConeKotlinTypeWithNullability(
|
||||
session, isNullable = position == TypeComponentPosition.FLEXIBLE_UPPER
|
||||
)
|
||||
|
||||
val enhancedType = enhancedSymbol.constructType(enhancedArguments.toTypedArray(), enhancedNullability)
|
||||
|
||||
// TODO: why all of these is needed
|
||||
// val enhancement = if (effectiveQualifiers.isNotNullTypeParameter) NotNullTypeParameter(enhancedType) else enhancedType
|
||||
// val nullabilityForWarning = nullabilityChanged && effectiveQualifiers.isNullabilityQualifierForWarning
|
||||
// val result = if (nullabilityForWarning) wrapEnhancement(enhancement) else enhancement
|
||||
|
||||
return enhancedType
|
||||
}
|
||||
|
||||
private fun getEnhancedNullability(
|
||||
qualifiers: JavaTypeQualifiers,
|
||||
position: TypeComponentPosition
|
||||
): EnhanceDetailsResult<Boolean> {
|
||||
if (!position.shouldEnhance()) return false.noChange()
|
||||
|
||||
return when (qualifiers.nullability) {
|
||||
NullabilityQualifier.NULLABLE -> true.enhancedNullability()
|
||||
NullabilityQualifier.NOT_NULL -> false.enhancedNullability()
|
||||
else -> false.noChange()
|
||||
}
|
||||
}
|
||||
|
||||
private data class EnhanceDetailsResult<out T>(
|
||||
val result: T,
|
||||
val mutabilityChanged: Boolean = false,
|
||||
val nullabilityChanged: Boolean = false
|
||||
)
|
||||
|
||||
private fun <T> T.noChange() = EnhanceDetailsResult(this)
|
||||
private fun <T> T.enhancedNullability() = EnhanceDetailsResult(this, nullabilityChanged = true)
|
||||
private fun <T> T.enhancedMutability() = EnhanceDetailsResult(this, mutabilityChanged = true)
|
||||
|
||||
private fun ConeClassifierSymbol.enhanceMutability(
|
||||
qualifiers: JavaTypeQualifiers,
|
||||
position: TypeComponentPosition
|
||||
): EnhanceDetailsResult<ConeClassifierSymbol> {
|
||||
if (!position.shouldEnhance()) return this.noChange()
|
||||
if (this !is FirClassSymbol) return this.noChange() // mutability is not applicable for type parameters
|
||||
val fqNameUnsafe = classId.asSingleFqName().toUnsafe()
|
||||
|
||||
when (qualifiers.mutability) {
|
||||
MutabilityQualifier.READ_ONLY -> {
|
||||
val readOnlyFqName = JavaToKotlinClassMap.mutableToReadOnly(fqNameUnsafe)
|
||||
if (position == TypeComponentPosition.FLEXIBLE_LOWER && readOnlyFqName != null) {
|
||||
return FirClassSymbol(ClassId(classId.packageFqName, readOnlyFqName, false)).apply {
|
||||
bind(fir)
|
||||
}.enhancedMutability()
|
||||
}
|
||||
}
|
||||
MutabilityQualifier.MUTABLE -> {
|
||||
val mutableFqName = JavaToKotlinClassMap.readOnlyToMutable(fqNameUnsafe)
|
||||
if (position == TypeComponentPosition.FLEXIBLE_UPPER && mutableFqName != null) {
|
||||
return FirClassSymbol(ClassId(classId.packageFqName, mutableFqName, false)).apply {
|
||||
bind(fir)
|
||||
}.enhancedMutability()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.noChange()
|
||||
}
|
||||
|
||||
|
||||
internal data class TypeAndDefaultQualifiers(
|
||||
val type: FirTypeRef,
|
||||
val defaultQualifiers: JavaTypeQualifiers?
|
||||
)
|
||||
|
||||
internal fun FirTypeRef.typeArguments(): List<FirTypeProjection> =
|
||||
(this as? FirUserTypeRef)?.qualifier?.lastOrNull()?.typeArguments.orEmpty()
|
||||
|
||||
internal fun JavaType.typeArguments(): List<JavaType> = (this as? JavaClassifierType)?.typeArguments.orEmpty()
|
||||
|
||||
fun FirValueParameter.getDefaultValueFromAnnotation(): AnnotationDefaultValue? {
|
||||
annotations.find { it.resolvedFqName == JvmAnnotationNames.DEFAULT_VALUE_FQ_NAME }
|
||||
?.arguments?.firstOrNull()
|
||||
?.safeAs<FirConstExpression<*>>()?.value?.safeAs<String>()
|
||||
?.let { return StringDefaultValue(it) }
|
||||
|
||||
if (annotations.any { it.resolvedFqName == JvmAnnotationNames.DEFAULT_NULL_FQ_NAME }) {
|
||||
return NullDefaultValue
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.fir.java.enhancement
|
||||
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.expressions.resolvedFqName
|
||||
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
|
||||
import org.jetbrains.kotlin.load.java.*
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.NullabilityQualifier
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.NullabilityQualifierWithMigrationStatus
|
||||
import org.jetbrains.kotlin.utils.Jsr305State
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
|
||||
fun List<FirAnnotationCall>.extractNullability(
|
||||
annotationTypeQualifierResolver: FirAnnotationTypeQualifierResolver,
|
||||
jsr305State: Jsr305State
|
||||
): NullabilityQualifierWithMigrationStatus? =
|
||||
this.firstNotNullResult { annotationCall ->
|
||||
annotationCall.extractNullability(annotationTypeQualifierResolver, jsr305State)
|
||||
}
|
||||
|
||||
|
||||
fun FirAnnotationCall.extractNullability(
|
||||
annotationTypeQualifierResolver: FirAnnotationTypeQualifierResolver,
|
||||
jsr305State: Jsr305State
|
||||
): NullabilityQualifierWithMigrationStatus? {
|
||||
this.extractNullabilityFromKnownAnnotations(jsr305State)?.let { return it }
|
||||
|
||||
val typeQualifierAnnotation =
|
||||
annotationTypeQualifierResolver.resolveTypeQualifierAnnotation(this)
|
||||
?: return null
|
||||
|
||||
val jsr305ReportLevel = annotationTypeQualifierResolver.resolveJsr305ReportLevel(this)
|
||||
if (jsr305ReportLevel.isIgnore) return null
|
||||
|
||||
return typeQualifierAnnotation.extractNullabilityFromKnownAnnotations(jsr305State)?.copy(isForWarningOnly = jsr305ReportLevel.isWarning)
|
||||
}
|
||||
|
||||
private fun FirAnnotationCall.extractNullabilityFromKnownAnnotations(jsr305State: Jsr305State): NullabilityQualifierWithMigrationStatus? {
|
||||
val annotationFqName = resolvedFqName ?: return null
|
||||
|
||||
return when {
|
||||
annotationFqName in NULLABLE_ANNOTATIONS -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE)
|
||||
annotationFqName in NOT_NULL_ANNOTATIONS -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
|
||||
annotationFqName == JAVAX_NONNULL_ANNOTATION -> extractNullabilityTypeFromArgument()
|
||||
|
||||
annotationFqName == COMPATQUAL_NULLABLE_ANNOTATION && jsr305State.enableCompatqualCheckerFrameworkAnnotations ->
|
||||
NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE)
|
||||
|
||||
annotationFqName == COMPATQUAL_NONNULL_ANNOTATION && jsr305State.enableCompatqualCheckerFrameworkAnnotations ->
|
||||
NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
|
||||
|
||||
annotationFqName == ANDROIDX_RECENTLY_NON_NULL_ANNOTATION -> NullabilityQualifierWithMigrationStatus(
|
||||
NullabilityQualifier.NOT_NULL,
|
||||
isForWarningOnly = true
|
||||
)
|
||||
|
||||
annotationFqName == ANDROIDX_RECENTLY_NULLABLE_ANNOTATION -> NullabilityQualifierWithMigrationStatus(
|
||||
NullabilityQualifier.NULLABLE,
|
||||
isForWarningOnly = true
|
||||
)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirAnnotationCall.extractNullabilityTypeFromArgument(): NullabilityQualifierWithMigrationStatus? {
|
||||
val enumValue = this.arguments.firstOrNull()?.toResolvedCallableSymbol()?.callableId?.callableName
|
||||
// if no argument is specified, use default value: NOT_NULL
|
||||
?: return NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
|
||||
|
||||
return when (enumValue.asString()) {
|
||||
"ALWAYS" -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
|
||||
"MAYBE", "NEVER" -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE)
|
||||
"UNKNOWN" -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.FORCE_FLEXIBILITY)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.fir.java.scopes
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirMemberFunctionImpl
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirValueParameterImpl
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationContainer
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaValueParameter
|
||||
import org.jetbrains.kotlin.fir.java.enhancement.EnhancementSignatureParts
|
||||
import org.jetbrains.kotlin.fir.java.enhancement.FirAnnotationTypeQualifierResolver
|
||||
import org.jetbrains.kotlin.fir.java.enhancement.FirJavaEnhancementContext
|
||||
import org.jetbrains.kotlin.fir.java.enhancement.copyWithNewDefaultTypeQualifiers
|
||||
import org.jetbrains.kotlin.fir.java.types.FirJavaTypeRef
|
||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||
import org.jetbrains.kotlin.load.java.AnnotationTypeQualifierResolver
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaPrimitiveType
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.PREDEFINED_FUNCTION_ENHANCEMENT_INFO_BY_SIGNATURE
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.PredefinedFunctionEnhancementInfo
|
||||
import org.jetbrains.kotlin.load.kotlin.SignatureBuildingComponents
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.Jsr305State
|
||||
|
||||
class JavaClassEnhancementScope(
|
||||
session: FirSession,
|
||||
private val useSiteScope: JavaClassUseSiteScope
|
||||
) : FirScope {
|
||||
private val owner: FirRegularClass get() = useSiteScope.symbol.fir
|
||||
|
||||
private val jsr305State: Jsr305State = session.jsr305State ?: Jsr305State.DEFAULT
|
||||
|
||||
private val typeQualifierResolver = FirAnnotationTypeQualifierResolver(jsr305State)
|
||||
|
||||
private val context: FirJavaEnhancementContext =
|
||||
FirJavaEnhancementContext(session) { null }
|
||||
|
||||
private val enhancements = mutableMapOf<ConeCallableSymbol, ConeCallableSymbol>()
|
||||
|
||||
override fun processFunctionsByName(name: Name, processor: (ConeFunctionSymbol) -> ProcessorAction): ProcessorAction {
|
||||
useSiteScope.processFunctionsByName(name) process@{ original ->
|
||||
|
||||
val function = enhancements.getOrPut(original) { enhance(original, name) }
|
||||
processor(function as ConeFunctionSymbol)
|
||||
}
|
||||
|
||||
return super.processFunctionsByName(name, processor)
|
||||
}
|
||||
|
||||
private fun enhance(
|
||||
original: ConeFunctionSymbol,
|
||||
name: Name
|
||||
): FirFunctionSymbol {
|
||||
val firMethod = (original as FirBasedSymbol<*>).fir as? FirJavaMethod ?: error("Can't fake override for $original")
|
||||
|
||||
val memberContext = context.copyWithNewDefaultTypeQualifiers(
|
||||
typeQualifierResolver, jsr305State, firMethod.annotations
|
||||
)
|
||||
// TODO: When loading method as an override for a property, all annotations are stick to its getter
|
||||
// if (this is FirProperty && getter !is FirDefaultPropertyAccessor)
|
||||
// getter
|
||||
// else
|
||||
// this
|
||||
|
||||
val predefinedEnhancementInfo =
|
||||
SignatureBuildingComponents.signature(owner.symbol.classId, firMethod.computeJvmDescriptor()).let { signature ->
|
||||
PREDEFINED_FUNCTION_ENHANCEMENT_INFO_BY_SIGNATURE[signature]
|
||||
}
|
||||
|
||||
predefinedEnhancementInfo?.let {
|
||||
assert(it.parametersInfo.size == firMethod.valueParameters.size) {
|
||||
"Predefined enhancement info for $this has ${it.parametersInfo.size}, but ${firMethod.valueParameters.size} expected"
|
||||
}
|
||||
}
|
||||
|
||||
val newReceiverTypeRef = if (firMethod.receiverTypeRef != null) enhanceReceiverType(firMethod, memberContext) else null
|
||||
val newReturnTypeRef = enhanceReturnType(firMethod, memberContext, predefinedEnhancementInfo)
|
||||
|
||||
val newValueParameterTypeRefs = mutableListOf<FirResolvedTypeRef>()
|
||||
|
||||
for ((index, valueParameter) in firMethod.valueParameters.withIndex()) {
|
||||
newValueParameterTypeRefs += enhanceValueParameterType(
|
||||
firMethod, memberContext, predefinedEnhancementInfo, valueParameter as FirJavaValueParameter, index
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
val symbol = FirFunctionSymbol(original.callableId)
|
||||
with(firMethod) {
|
||||
FirMemberFunctionImpl(
|
||||
session, null, symbol, name,
|
||||
newReceiverTypeRef, newReturnTypeRef
|
||||
).apply {
|
||||
status = firMethod.status
|
||||
annotations += firMethod.annotations
|
||||
valueParameters += firMethod.valueParameters.zip(newValueParameterTypeRefs) { valueParameter, newTypeRef ->
|
||||
with(valueParameter) {
|
||||
FirValueParameterImpl(
|
||||
session, psi,
|
||||
this.name, newTypeRef,
|
||||
defaultValue, isCrossinline, isNoinline, isVararg
|
||||
).apply {
|
||||
annotations += valueParameter.annotations
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return symbol
|
||||
}
|
||||
|
||||
private fun FirJavaMethod.computeJvmDescriptor(): String = buildString {
|
||||
append(name.asString()) // TODO: Java constructors
|
||||
|
||||
append("(")
|
||||
for (parameter in valueParameters) {
|
||||
// TODO: appendErasedType(parameter.returnTypeRef)
|
||||
}
|
||||
append(")")
|
||||
|
||||
if ((returnTypeRef as FirJavaTypeRef).isVoid()) {
|
||||
append("V")
|
||||
} else {
|
||||
// TODO: appendErasedType(returnTypeRef)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirJavaTypeRef.isVoid(): Boolean {
|
||||
return type is JavaPrimitiveType && type.type == null
|
||||
}
|
||||
|
||||
// ================================================================================================
|
||||
|
||||
private fun enhanceReceiverType(
|
||||
ownerFunction: FirJavaMethod,
|
||||
memberContext: FirJavaEnhancementContext
|
||||
): FirResolvedTypeRef {
|
||||
val signatureParts = ownerFunction.partsForValueParameter(
|
||||
typeQualifierResolver,
|
||||
// TODO: check me
|
||||
parameterContainer = ownerFunction,
|
||||
methodContext = memberContext
|
||||
) {
|
||||
it.receiverTypeRef!!
|
||||
}.enhance(jsr305State)
|
||||
return signatureParts.type
|
||||
}
|
||||
|
||||
private val FirTypedDeclaration.valueParameters: List<FirValueParameter> get() = (this as? FirFunction)?.valueParameters.orEmpty()
|
||||
|
||||
private fun enhanceValueParameterType(
|
||||
ownerFunction: FirJavaMethod,
|
||||
memberContext: FirJavaEnhancementContext,
|
||||
predefinedEnhancementInfo: PredefinedFunctionEnhancementInfo?,
|
||||
ownerParameter: FirJavaValueParameter,
|
||||
index: Int
|
||||
): FirResolvedTypeRef {
|
||||
val signatureParts = ownerFunction.partsForValueParameter(
|
||||
typeQualifierResolver,
|
||||
parameterContainer = ownerParameter,
|
||||
methodContext = memberContext
|
||||
) {
|
||||
it.valueParameters[index].returnTypeRef
|
||||
}.enhance(jsr305State, predefinedEnhancementInfo?.parametersInfo?.getOrNull(index))
|
||||
return signatureParts.type
|
||||
}
|
||||
|
||||
private fun enhanceReturnType(
|
||||
ownerFunction: FirJavaMethod,
|
||||
memberContext: FirJavaEnhancementContext,
|
||||
predefinedEnhancementInfo: PredefinedFunctionEnhancementInfo?
|
||||
): FirResolvedTypeRef {
|
||||
val signatureParts = ownerFunction.parts(
|
||||
typeQualifierResolver,
|
||||
typeContainer = ownerFunction, isCovariant = true,
|
||||
containerContext = memberContext,
|
||||
containerApplicabilityType =
|
||||
if (false) // TODO: this.safeAs<FirProperty>()?.isJavaField == true
|
||||
AnnotationTypeQualifierResolver.QualifierApplicabilityType.FIELD
|
||||
else
|
||||
AnnotationTypeQualifierResolver.QualifierApplicabilityType.METHOD_RETURN_TYPE
|
||||
) { it.returnTypeRef }.enhance(jsr305State, predefinedEnhancementInfo?.returnTypeInfo)
|
||||
return signatureParts.type
|
||||
}
|
||||
|
||||
private fun FirCallableMember.partsForValueParameter(
|
||||
typeQualifierResolver: FirAnnotationTypeQualifierResolver,
|
||||
// TODO: investigate if it's really can be a null (check properties' with extension overrides in Java)
|
||||
parameterContainer: FirAnnotationContainer?,
|
||||
methodContext: FirJavaEnhancementContext,
|
||||
collector: (FirCallableMember) -> FirTypeRef
|
||||
) = parts(
|
||||
typeQualifierResolver,
|
||||
parameterContainer, false,
|
||||
parameterContainer?.let {
|
||||
methodContext.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, jsr305State, it.annotations)
|
||||
} ?: methodContext,
|
||||
AnnotationTypeQualifierResolver.QualifierApplicabilityType.VALUE_PARAMETER,
|
||||
collector
|
||||
)
|
||||
|
||||
private val overriddenMemberCache = mutableMapOf<FirCallableMember, List<FirCallableMember>>()
|
||||
|
||||
private fun FirCallableMember.overriddenMembers(): List<FirCallableMember> {
|
||||
return overriddenMemberCache.getOrPut(this) {
|
||||
val result = mutableListOf<FirCallableMember>()
|
||||
if (this is FirNamedFunction) {
|
||||
val superTypesScope = useSiteScope.superTypesScope
|
||||
superTypesScope.processFunctionsByName(this.name) { basicFunctionSymbol ->
|
||||
val overriddenBy = with(useSiteScope) {
|
||||
basicFunctionSymbol.getOverridden(setOf(this@overriddenMembers.symbol as ConeFunctionSymbol))
|
||||
}
|
||||
val overriddenByFir = (overriddenBy as? FirFunctionSymbol)?.fir
|
||||
if (overriddenByFir === this@overriddenMembers) {
|
||||
result += (basicFunctionSymbol as FirFunctionSymbol).fir
|
||||
}
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirCallableMember.parts(
|
||||
typeQualifierResolver: FirAnnotationTypeQualifierResolver,
|
||||
typeContainer: FirAnnotationContainer?,
|
||||
isCovariant: Boolean,
|
||||
containerContext: FirJavaEnhancementContext,
|
||||
containerApplicabilityType: AnnotationTypeQualifierResolver.QualifierApplicabilityType,
|
||||
collector: (FirCallableMember) -> FirTypeRef
|
||||
): EnhancementSignatureParts {
|
||||
return EnhancementSignatureParts(
|
||||
typeQualifierResolver,
|
||||
typeContainer,
|
||||
collector(this) as FirJavaTypeRef,
|
||||
this.overriddenMembers().map {
|
||||
collector(it)
|
||||
},
|
||||
isCovariant,
|
||||
// recompute default type qualifiers using type annotations
|
||||
containerContext.copyWithNewDefaultTypeQualifiers(
|
||||
typeQualifierResolver, jsr305State, collector(this).annotations
|
||||
),
|
||||
containerApplicabilityType
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.fir.java.scopes
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirNamedFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.java.toNotNullConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractProviderBasedScope
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirClassDeclaredMemberScope
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class JavaClassUseSiteScope(
|
||||
klass: FirRegularClass,
|
||||
session: FirSession,
|
||||
internal val superTypesScope: FirScope,
|
||||
private val declaredMemberScope: FirClassDeclaredMemberScope
|
||||
) : FirAbstractProviderBasedScope(session, lookupInFir = true) {
|
||||
internal val symbol = klass.symbol
|
||||
|
||||
//base symbol as key, overridden as value
|
||||
private val overriddenByBase = mutableMapOf<ConeFunctionSymbol, ConeFunctionSymbol?>()
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun isSubtypeOf(subType: ConeKotlinType, superType: ConeKotlinType): Boolean {
|
||||
// TODO: introduce normal sub-typing
|
||||
return true
|
||||
}
|
||||
|
||||
private fun isSubtypeOf(subType: FirTypeRef, superType: FirTypeRef) =
|
||||
isSubtypeOf(subType.toNotNullConeKotlinType(), superType.toNotNullConeKotlinType())
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun isEqualTypes(a: ConeKotlinType, b: ConeKotlinType): Boolean {
|
||||
// TODO: introduce normal type comparison
|
||||
return true
|
||||
}
|
||||
|
||||
private fun isEqualTypes(a: FirTypeRef, b: FirTypeRef) =
|
||||
isEqualTypes(a.toNotNullConeKotlinType(), b.toNotNullConeKotlinType())
|
||||
|
||||
private fun isOverriddenFunCheck(member: FirNamedFunction, self: FirNamedFunction): Boolean {
|
||||
return member.valueParameters.size == self.valueParameters.size &&
|
||||
member.valueParameters.zip(self.valueParameters).all { (memberParam, selfParam) ->
|
||||
isEqualTypes(memberParam.returnTypeRef, selfParam.returnTypeRef)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ConeFunctionSymbol.getOverridden(candidates: Set<ConeFunctionSymbol>): ConeCallableSymbol? {
|
||||
if (overriddenByBase.containsKey(this)) return overriddenByBase[this]
|
||||
|
||||
fun sameReceivers(memberTypeRef: FirTypeRef?, selfTypeRef: FirTypeRef?): Boolean {
|
||||
return when {
|
||||
memberTypeRef != null && selfTypeRef != null -> isEqualTypes(memberTypeRef, selfTypeRef)
|
||||
else -> memberTypeRef == null && selfTypeRef == null
|
||||
}
|
||||
}
|
||||
|
||||
val self = (this as FirFunctionSymbol).fir as FirNamedFunction
|
||||
val overriding = candidates.firstOrNull {
|
||||
val member = (it as FirFunctionSymbol).fir as FirNamedFunction
|
||||
self.modality != Modality.FINAL
|
||||
&& sameReceivers(member.receiverTypeRef, self.receiverTypeRef)
|
||||
&& isSubtypeOf(member.returnTypeRef, self.returnTypeRef)
|
||||
&& isOverriddenFunCheck(member, self)
|
||||
} // TODO: two or more overrides for one fun?
|
||||
overriddenByBase[this] = overriding
|
||||
return overriding
|
||||
}
|
||||
|
||||
override fun processFunctionsByName(name: Name, processor: (ConeFunctionSymbol) -> ProcessorAction): ProcessorAction {
|
||||
val overrideCandidates = mutableSetOf<ConeFunctionSymbol>()
|
||||
if (!declaredMemberScope.processFunctionsByName(name) {
|
||||
overrideCandidates += it
|
||||
processor(it)
|
||||
}
|
||||
) return ProcessorAction.STOP
|
||||
|
||||
return superTypesScope.processFunctionsByName(name) {
|
||||
|
||||
val overriddenBy = it.getOverridden(overrideCandidates)
|
||||
if (overriddenBy == null) {
|
||||
processor(it)
|
||||
} else {
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.fir.java.scopes
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass
|
||||
import org.jetbrains.kotlin.fir.resolve.FirScopeProvider
|
||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||
|
||||
class JavaFirScopeProvider : FirScopeProvider {
|
||||
override fun getDeclaredMemberScope(klass: FirRegularClass, session: FirSession): FirScope {
|
||||
if (klass !is FirJavaClass) return FirScopeProvider.emptyScope
|
||||
|
||||
return JavaClassEnhancementScope(session, klass.useSiteScope)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user