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:
Mikhail Glukhikh
2019-02-15 10:36:57 +03:00
parent 060bd1b464
commit f31faafd72
56 changed files with 2042 additions and 336 deletions
@@ -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
@@ -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
)
}
@@ -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 }
@@ -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
}
}
@@ -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)
}
}