diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt index f2479a4cf1c..7ba9ebc54ed 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt @@ -65,6 +65,10 @@ sealed class ConeKotlinType : ConeKotlinTypeProjection(), ConeTypedProjection, K abstract val nullability: ConeNullability } +val ConeKotlinType.isNullable: Boolean get() = nullability != ConeNullability.NOT_NULL + +val ConeKotlinType.isMarkedNullable: Boolean get() = nullability == ConeNullability.NULLABLE + class ConeKotlinErrorType(val reason: String) : ConeKotlinType() { override val typeArguments: Array get() = EMPTY_ARRAY diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaModuleBasedSession.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaModuleBasedSession.kt index cf543263b68..f0a783d8179 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaModuleBasedSession.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaModuleBasedSession.kt @@ -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() + ) + ) + ) } } diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt index d5d69a22de6..90209318d45 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt @@ -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 List.createArrayOfCall(@Suppress("UNUSED_PARAMETER") kind: IrConstKind): 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().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 { @@ -201,39 +44,12 @@ class JavaSymbolProvider( ?: return@lookupCacheOrCalculate emptyList() val firClass = classSymbol.fir as FirModifiableClass val callableSymbols = mutableListOf() - 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(""), - 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(""), + returnTypeRef = parameterType.toFirJavaTypeRef(session), + isVararg = valueParameter.isVararg + ).apply { + addAnnotationsFrom(valueParameter) + } + } + } + declarations += firJavaMethod } } } @@ -275,5 +123,12 @@ class JavaSymbolProvider( FqName(javaPackage.qualifiedName) } } + + fun getJavaTopLevelClasses(): List { + return classCache.values + .filterIsInstance() + .filter { it.classId.relativeClassName.parent().isRoot } + .map { it.fir } + } } diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaUtils.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaUtils.kt index e06ec8e6d1c..17d13ab1b7a 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaUtils.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaUtils.kt @@ -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, + 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().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 List.createArrayOfCall(session: FirSession, @Suppress("UNUSED_PARAMETER") kind: IrConstKind): 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() + ) +} + + diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaClass.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaClass.kt index 862a208f8dc..ccc33309647 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaClass.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaClass.kt @@ -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 -) \ No newline at end of file + 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() + + val useSiteScope: JavaClassUseSiteScope by lazy { buildUseSiteScope() } + + override val declarations = mutableListOf() + + 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): FirRegularClass { + superTypeRefs.clear() + superTypeRefs.addAll(newSupertypes) + return this + } +} \ No newline at end of file diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaMethod.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaMethod.kt index d9eed2fc877..0bb824ebfe4 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaMethod.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaMethod.kt @@ -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 diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/EnhancementSignatureParts.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/EnhancementSignatureParts.kt new file mode 100644 index 00000000000..eb0bb43e7e3 --- /dev/null +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/EnhancementSignatureParts.kt @@ -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, + private val isCovariant: Boolean, + private val context: FirJavaEnhancementContext, + private val containerApplicabilityType: AnnotationTypeQualifierResolver.QualifierApplicabilityType +) { + private val isForVarargParameter get() = typeContainer.safeAs()?.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 { + val list = ArrayList(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, second: List): List { + 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 List.ifPresent(qualifier: T) = + if (any { fqName -> + composedAnnotation.any { it.resolvedFqName == fqName } + } + ) qualifier else null + + fun 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, + 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, but in the subclass it may be Derived, which + // is declared to extend Super, 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! in the subclass and { List, (Mutable)List! } 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 + ) +} + diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/FirAnnotationTypeQualifierResolver.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/FirAnnotationTypeQualifierResolver.kt new file mode 100644 index 00000000000..094929eb630 --- /dev/null +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/FirAnnotationTypeQualifierResolver.kt @@ -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 = + 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 } diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/FirJavaEnhancementContext.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/FirJavaEnhancementContext.kt new file mode 100644 index 00000000000..978c05bbe4c --- /dev/null +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/FirJavaEnhancementContext.kt @@ -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 +) { + 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 +): 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 +): FirJavaEnhancementContext = + when { + additionalAnnotations.isEmpty() -> this + else -> FirJavaEnhancementContext(session) { + computeNewDefaultTypeQualifiers(typeQualifierResolver, jsr305State, additionalAnnotations) + } + } \ No newline at end of file diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/javaTypeUtils.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/javaTypeUtils.kt new file mode 100644 index 00000000000..c5bfbf7d8a7 --- /dev/null +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/javaTypeUtils.kt @@ -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>`, indices go as follows: `0 - A<...>, 1 - B, 2 - C, 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..C)` gives `0 - (A..C), 1 - B and D`. +private fun JavaType.enhancePossiblyFlexible( + session: FirSession, + annotations: List, + 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, + arguments: List, + position: TypeComponentPosition, + qualifiers: (Int) -> JavaTypeQualifiers, + index: Int +): ConeLookupTagBasedType { + val originalSymbol = when (val classifier = classifier) { + is JavaClass -> session.service().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 { + 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( + val result: T, + val mutabilityChanged: Boolean = false, + val nullabilityChanged: Boolean = false +) + +private fun T.noChange() = EnhanceDetailsResult(this) +private fun T.enhancedNullability() = EnhanceDetailsResult(this, nullabilityChanged = true) +private fun T.enhancedMutability() = EnhanceDetailsResult(this, mutabilityChanged = true) + +private fun ConeClassifierSymbol.enhanceMutability( + qualifiers: JavaTypeQualifiers, + position: TypeComponentPosition +): EnhanceDetailsResult { + 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 = + (this as? FirUserTypeRef)?.qualifier?.lastOrNull()?.typeArguments.orEmpty() + +internal fun JavaType.typeArguments(): List = (this as? JavaClassifierType)?.typeArguments.orEmpty() + +fun FirValueParameter.getDefaultValueFromAnnotation(): AnnotationDefaultValue? { + annotations.find { it.resolvedFqName == JvmAnnotationNames.DEFAULT_VALUE_FQ_NAME } + ?.arguments?.firstOrNull() + ?.safeAs>()?.value?.safeAs() + ?.let { return StringDefaultValue(it) } + + if (annotations.any { it.resolvedFqName == JvmAnnotationNames.DEFAULT_NULL_FQ_NAME }) { + return NullDefaultValue + } + + return null +} + diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/nullabilityUtils.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/nullabilityUtils.kt new file mode 100644 index 00000000000..f734fb58b5b --- /dev/null +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/nullabilityUtils.kt @@ -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.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 + } +} diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassEnhancementScope.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassEnhancementScope.kt new file mode 100644 index 00000000000..dbd7b048837 --- /dev/null +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassEnhancementScope.kt @@ -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() + + 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() + + 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 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()?.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>() + + private fun FirCallableMember.overriddenMembers(): List { + return overriddenMemberCache.getOrPut(this) { + val result = mutableListOf() + 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 + ) + } + +} \ No newline at end of file diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteScope.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteScope.kt new file mode 100644 index 00000000000..2ee240a247b --- /dev/null +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteScope.kt @@ -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() + + @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): 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() + 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 + } + } + } +} diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaFirScopeProvider.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaFirScopeProvider.kt new file mode 100644 index 00000000000..7876108cc3e --- /dev/null +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaFirScopeProvider.kt @@ -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) + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirModuleBasedSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirModuleBasedSession.kt index e7edbae8b70..892119bebdb 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirModuleBasedSession.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirModuleBasedSession.kt @@ -6,9 +6,7 @@ package org.jetbrains.kotlin.fir import org.jetbrains.kotlin.analyzer.ModuleInfo -import org.jetbrains.kotlin.fir.resolve.FirProvider -import org.jetbrains.kotlin.fir.resolve.FirQualifierResolver -import org.jetbrains.kotlin.fir.resolve.FirTypeResolver +import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.impl.* abstract class FirModuleBasedSession(override val moduleInfo: ModuleInfo) : FirSessionBase() { @@ -19,3 +17,4 @@ abstract class FirModuleBasedSession(override val moduleInfo: ModuleInfo) : FirS registerComponent(FirTypeResolver::class, FirTypeResolverImpl(this)) } } + diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/AbstractFirSymbolProvider.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/AbstractFirSymbolProvider.kt index 41e0169631b..5290445be36 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/AbstractFirSymbolProvider.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/AbstractFirSymbolProvider.kt @@ -8,6 +8,8 @@ package org.jetbrains.kotlin.fir.resolve 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.visitors.FirTransformer import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName @@ -38,4 +40,16 @@ abstract class AbstractFirSymbolProvider : FirSymbolProvider { calculated.first } } + + fun transformTopLevelClasses(transformer: FirTransformer, data: D) { + val symbols = classCache.values.filterNotNullTo(mutableListOf()) + // TODO: do something with new symbols which can be found during transformation of another symbols + for (symbol in symbols) { + if (symbol !is FirClassSymbol) continue + if (symbol.classId.relativeClassName.parent().isRoot) { + // Launch for top-level classes only + symbol.fir.transform(transformer, data) + } + } + } } \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirScopeProvider.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirScopeProvider.kt new file mode 100644 index 00000000000..51329feb984 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirScopeProvider.kt @@ -0,0 +1,18 @@ +/* + * 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.resolve + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.scopes.FirScope + +interface FirScopeProvider { + fun getDeclaredMemberScope(klass: FirRegularClass, session: FirSession): FirScope + + companion object { + val emptyScope = object : FirScope {} + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt index cf2b0cdbadb..38586ed3f2f 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt @@ -45,7 +45,7 @@ fun ConeClassifierLookupTag.toSymbol(useSiteSession: FirSession): ConeClassifier } -fun ConeClassifierSymbol.constructType(typeArguments: Array, isNullable: Boolean): ConeKotlinType { +fun ConeClassifierSymbol.constructType(typeArguments: Array, isNullable: Boolean): ConeLookupTagBasedType { return when (this) { is ConeTypeParameterSymbol -> { ConeTypeParameterTypeImpl(this, isNullable) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt new file mode 100644 index 00000000000..bd62950f087 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt @@ -0,0 +1,86 @@ +/* + * 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.resolve + +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.expandedConeType +import org.jetbrains.kotlin.fir.declarations.superConeTypes +import org.jetbrains.kotlin.fir.symbols.ConeClassifierSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol +import org.jetbrains.kotlin.fir.types.ConeAbbreviatedType +import org.jetbrains.kotlin.fir.types.ConeClassErrorType +import org.jetbrains.kotlin.fir.types.ConeClassLikeType + +fun lookupSuperTypes( + klass: FirRegularClass, + lookupInterfaces: Boolean, + deep: Boolean, + useSiteSession: FirSession +): List { + return mutableListOf().also { + if (lookupInterfaces) klass.symbol.collectSuperTypes(it, deep, useSiteSession) + else klass.symbol.collectSuperClasses(it, useSiteSession) + } +} + +private tailrec fun ConeClassLikeType.computePartialExpansion(useSiteSession: FirSession): ConeClassLikeType? { + return when (this) { + is ConeAbbreviatedType -> directExpansionType(useSiteSession)?.computePartialExpansion(useSiteSession) + else -> this + } +} + +private tailrec fun ConeClassifierSymbol.collectSuperClasses( + list: MutableList, + useSiteSession: FirSession +) { + when (this) { + is FirClassSymbol -> { + val superClassType = + fir.superConeTypes + .map { it.computePartialExpansion(useSiteSession) } + .firstOrNull { + it !is ConeClassErrorType && + (it?.lookupTag?.toSymbol(useSiteSession) as? FirClassSymbol)?.fir?.classKind == ClassKind.CLASS + } ?: return + list += superClassType + superClassType.lookupTag.toSymbol(useSiteSession)?.collectSuperClasses(list, useSiteSession) + } + is FirTypeAliasSymbol -> { + val expansion = fir.expandedConeType?.computePartialExpansion(useSiteSession) ?: return + expansion.lookupTag.toSymbol(useSiteSession)?.collectSuperClasses(list, useSiteSession) + } + else -> error("?!id:1") + } +} + +private fun ConeClassifierSymbol.collectSuperTypes( + list: MutableList, + deep: Boolean, + useSiteSession: FirSession +) { + when (this) { + is FirClassSymbol -> { + val superClassTypes = + fir.superConeTypes.mapNotNull { it.computePartialExpansion(useSiteSession) } + list += superClassTypes + if (deep) + superClassTypes.forEach { + if (it !is ConeClassErrorType) { + it.lookupTag.toSymbol(useSiteSession)?.collectSuperTypes(list, deep, useSiteSession) + } + } + } + is FirTypeAliasSymbol -> { + val expansion = fir.expandedConeType?.computePartialExpansion(useSiteSession) ?: return + expansion.lookupTag.toSymbol(useSiteSession)?.collectSuperTypes(list, deep, useSiteSession) + } + else -> error("?!id:1") + } +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/impl/FirCompositeScopeProvider.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/impl/FirCompositeScopeProvider.kt new file mode 100644 index 00000000000..944ca4167df --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/impl/FirCompositeScopeProvider.kt @@ -0,0 +1,18 @@ +/* + * 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.resolve.impl + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.resolve.FirScopeProvider +import org.jetbrains.kotlin.fir.scopes.FirScope +import org.jetbrains.kotlin.fir.scopes.impl.FirCompositeScope + +class FirCompositeScopeProvider(private val providers: List) : FirScopeProvider { + override fun getDeclaredMemberScope(klass: FirRegularClass, session: FirSession): FirScope { + return FirCompositeScope(providers.mapTo(mutableListOf()) { it.getDeclaredMemberScope(klass, session) }) + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/impl/FirRegularScopeProvider.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/impl/FirRegularScopeProvider.kt new file mode 100644 index 00000000000..83dcc95e84c --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/impl/FirRegularScopeProvider.kt @@ -0,0 +1,22 @@ +/* + * 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.resolve.impl + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.impl.FirClassImpl +import org.jetbrains.kotlin.fir.resolve.FirScopeProvider +import org.jetbrains.kotlin.fir.scopes.FirScope +import org.jetbrains.kotlin.fir.scopes.impl.FirClassDeclaredMemberScope + +class FirRegularScopeProvider : FirScopeProvider { + override fun getDeclaredMemberScope(klass: FirRegularClass, session: FirSession): FirScope { + if (klass !is FirClassImpl) return FirScopeProvider.emptyScope + + return FirClassDeclaredMemberScope(klass, session) + } + +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirAbstractTreeTransformerWithSuperTypes.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirAbstractTreeTransformerWithSuperTypes.kt index 0e6b1f4b19e..180676343f0 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirAbstractTreeTransformerWithSuperTypes.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirAbstractTreeTransformerWithSuperTypes.kt @@ -5,23 +5,9 @@ package org.jetbrains.kotlin.fir.resolve.transformers -import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration -import org.jetbrains.kotlin.fir.declarations.FirRegularClass -import org.jetbrains.kotlin.fir.declarations.expandedConeType -import org.jetbrains.kotlin.fir.declarations.superConeTypes -import org.jetbrains.kotlin.fir.resolve.directExpansionType -import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.scopes.impl.FirCompositeScope import org.jetbrains.kotlin.fir.scopes.impl.FirMemberTypeParameterScope -import org.jetbrains.kotlin.fir.symbols.ConeClassifierSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol -import org.jetbrains.kotlin.fir.types.ConeAbbreviatedType -import org.jetbrains.kotlin.fir.types.ConeClassErrorType -import org.jetbrains.kotlin.fir.types.ConeClassLikeType -import org.jetbrains.kotlin.utils.addToStdlib.safeAs abstract class FirAbstractTreeTransformerWithSuperTypes(reversedScopePriority: Boolean) : FirAbstractTreeTransformer() { protected val towerScope = FirCompositeScope(mutableListOf(), reversedPriority = reversedScopePriority) @@ -37,74 +23,6 @@ abstract class FirAbstractTreeTransformerWithSuperTypes(reversedScopePriority: B return result } - protected fun lookupSuperTypes( - klass: FirRegularClass, - lookupInterfaces: Boolean, - deep: Boolean, - useSiteSession: FirSession - ): List { - return mutableListOf().also { - if (lookupInterfaces) klass.symbol.collectSuperTypes(it, deep, useSiteSession) - else klass.symbol.collectSuperClasses(it, useSiteSession) - } - } - - private tailrec fun ConeClassLikeType.computePartialExpansion(useSiteSession: FirSession): ConeClassLikeType? { - return when (this) { - is ConeAbbreviatedType -> directExpansionType(useSiteSession)?.computePartialExpansion(useSiteSession) - else -> this - } - } - - private tailrec fun ConeClassifierSymbol.collectSuperClasses( - list: MutableList, - useSiteSession: FirSession - ) { - when (this) { - is FirClassSymbol -> { - val superClassType = - fir.superConeTypes - .map { it.computePartialExpansion(useSiteSession) } - .firstOrNull { - it !is ConeClassErrorType && - (it?.lookupTag?.toSymbol(useSiteSession) as? FirClassSymbol)?.fir?.classKind == ClassKind.CLASS - } ?: return - list += superClassType - superClassType.lookupTag.toSymbol(useSiteSession)?.collectSuperClasses(list, useSiteSession) - } - is FirTypeAliasSymbol -> { - val expansion = fir.expandedConeType?.computePartialExpansion(useSiteSession) ?: return - expansion.lookupTag.toSymbol(useSiteSession)?.collectSuperClasses(list, useSiteSession) - } - else -> error("?!id:1") - } - } - - private fun ConeClassifierSymbol.collectSuperTypes( - list: MutableList, - deep: Boolean, - useSiteSession: FirSession - ) { - when (this) { - is FirClassSymbol -> { - val superClassTypes = - fir.superConeTypes.mapNotNull { it.computePartialExpansion(useSiteSession) } - list += superClassTypes - if (deep) - superClassTypes.forEach { - if (it !is ConeClassErrorType) { - it.lookupTag.toSymbol(useSiteSession)?.collectSuperTypes(list, deep, useSiteSession) - } - } - } - is FirTypeAliasSymbol -> { - val expansion = fir.expandedConeType?.computePartialExpansion(useSiteSession) ?: return - expansion.lookupTag.toSymbol(useSiteSession)?.collectSuperTypes(list, deep, useSiteSession) - } - else -> error("?!id:1") - } - } - protected fun FirMemberDeclaration.addTypeParametersScope() { val scopes = towerScope.scopes if (typeParameters.isNotEmpty()) { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirAccessResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirAccessResolveTransformer.kt index c29dcf1e6fd..5f2fa89ad02 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirAccessResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirAccessResolveTransformer.kt @@ -14,10 +14,13 @@ import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.references.FirErrorNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedCallableReferenceImpl +import org.jetbrains.kotlin.fir.resolve.FirScopeProvider +import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.scopes.ProcessorAction import org.jetbrains.kotlin.fir.scopes.ProcessorAction.NEXT import org.jetbrains.kotlin.fir.scopes.impl.* +import org.jetbrains.kotlin.fir.service import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol @@ -53,7 +56,7 @@ class FirAccessResolveTransformer : FirAbstractTreeTransformerWithSuperTypes(rev private fun FirRegularClass.buildUseSiteScope(useSiteSession: FirSession = session): FirClassUseSiteScope { val superTypeScope = FirCompositeScope(mutableListOf()) - val declaredScope = FirClassDeclaredMemberScope(this, useSiteSession) + val declaredScope = useSiteSession.service().getDeclaredMemberScope(this, useSiteSession) lookupSuperTypes(this, lookupInterfaces = true, deep = false, useSiteSession = useSiteSession) .mapNotNullTo(superTypeScope.scopes) { useSiteSuperType -> if (useSiteSuperType is ConeClassErrorType) return@mapNotNullTo null diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSupertypeResolverTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSupertypeResolverTransformer.kt index 7bab2463037..76119401888 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSupertypeResolverTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSupertypeResolverTransformer.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.resolve.FirProvider import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider +import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes import org.jetbrains.kotlin.fir.scopes.FirPosition import org.jetbrains.kotlin.fir.scopes.addImportingScopes import org.jetbrains.kotlin.fir.scopes.impl.* diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTypeResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTypeResolveTransformer.kt index 6bc3f6a1877..f4be697cf71 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTypeResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTypeResolveTransformer.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.resolve.FirProvider import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider +import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes import org.jetbrains.kotlin.fir.scopes.FirPosition import org.jetbrains.kotlin.fir.scopes.addImportingScopes import org.jetbrains.kotlin.fir.scopes.impl.* diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractProviderBasedScope.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractProviderBasedScope.kt index 17b2d37597a..937ac4270ff 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractProviderBasedScope.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractProviderBasedScope.kt @@ -11,7 +11,8 @@ import org.jetbrains.kotlin.fir.resolve.impl.FirCompositeSymbolProvider import org.jetbrains.kotlin.fir.resolve.impl.FirDependenciesSymbolProviderImpl import org.jetbrains.kotlin.fir.scopes.FirScope -abstract class FirAbstractProviderBasedScope(val session: FirSession, lookupInFir: Boolean = true) : FirScope { +abstract class FirAbstractProviderBasedScope(val session: FirSession, lookupInFir: Boolean = true) : + FirScope { val provider = when (val symbolProvider = FirSymbolProvider.getInstance(session)) { is FirCompositeSymbolProvider -> symbolProvider.takeIf { !lookupInFir }?.providers?.find { it is FirDependenciesSymbolProviderImpl diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt index c8c4131fe28..7047a1d26b0 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt @@ -56,6 +56,10 @@ class FirRenderer(builder: StringBuilder) : FirVisitorVoid() { printer.popIndent() } + fun newLine() { + println() + } + override fun visitElement(element: FirElement) { element.acceptChildren(this) } @@ -214,22 +218,32 @@ class FirRenderer(builder: StringBuilder) : FirVisitorVoid() { } private fun FirDeclarationContainer.renderDeclarations() { + renderInBraces { + for (declaration in declarations) { + declaration.accept(this@FirRenderer) + println() + } + } + } + + fun renderInBraces(f: () -> Unit) { println(" {") pushIndent() - for (declaration in declarations) { - declaration.accept(this@FirRenderer) - println() - } + f() popIndent() println("}") } - override fun visitRegularClass(regularClass: FirRegularClass) { - visitMemberDeclaration(regularClass) + fun renderSupertypes(regularClass: FirRegularClass) { if (regularClass.superTypeRefs.isNotEmpty()) { print(" : ") regularClass.superTypeRefs.renderSeparated() } + } + + override fun visitRegularClass(regularClass: FirRegularClass) { + visitMemberDeclaration(regularClass) + renderSupertypes(regularClass) regularClass.renderDeclarations() } @@ -345,14 +359,12 @@ class FirRenderer(builder: StringBuilder) : FirVisitorVoid() { } override fun visitBlock(block: FirBlock) { - println(" {") - pushIndent() - for (statement in block.statements) { - statement.accept(this) - println() + renderInBraces { + for (statement in block.statements) { + statement.accept(this) + println() + } } - popIndent() - println("}") } override fun visitTypeAlias(typeAlias: FirTypeAlias) { @@ -657,8 +669,10 @@ class FirRenderer(builder: StringBuilder) : FirVisitorVoid() { buildString { append("ft<") append(lowerBound.asString()) + append(lowerBound.nullability.suffix) append(", ") append(upperBound.asString()) + append(upperBound.nullability.suffix) append(">") } } diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirSession.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirSession.kt index 7085807cfe7..2c53316e18b 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirSession.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirSession.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir import org.jetbrains.kotlin.analyzer.ModuleInfo +import org.jetbrains.kotlin.utils.Jsr305State import kotlin.reflect.KClass interface FirSession { @@ -13,6 +14,8 @@ interface FirSession { val sessionProvider: FirSessionProvider? get() = null + val jsr305State: Jsr305State? get() = null + val components: Map, Any> fun getService(kclass: KClass): T = diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/Utils.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/Utils.kt index 423d343e4e9..f19a129c5c7 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/Utils.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/Utils.kt @@ -29,6 +29,30 @@ fun MutableList.transformInplace(transformer: FirTransfor } } +fun MutableList.transformInplaceWithBeforeOperation( + transformer: FirTransformer, data: D, operation: (T, Int) -> Unit +) { + val iterator = this.listIterator() + var index = 0 + while (iterator.hasNext()) { + val next = iterator.next() + operation(next, index++) + val result = next.transform(transformer, data) + if (result.isSingle) { + iterator.set(result.single) + } else { + val resultIterator = result.list.listIterator() + if (!resultIterator.hasNext()) { + iterator.remove() + } else { + iterator.set(resultIterator.next()) + } + while (resultIterator.hasNext()) { + iterator.add(resultIterator.next()) + } + } + } +} fun T.transformSingle(transformer: FirTransformer, data: D): T { return this.transform(transformer, data).single diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/FirAnnotationCall.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/FirAnnotationCall.kt index a5f463796ba..501d76df122 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/FirAnnotationCall.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/FirAnnotationCall.kt @@ -7,8 +7,11 @@ package org.jetbrains.kotlin.fir.expressions import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.fir.BaseTransformedType +import org.jetbrains.kotlin.fir.types.ConeClassLikeType +import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.fir.visitors.FirVisitor +import org.jetbrains.kotlin.name.FqName @BaseTransformedType interface FirAnnotationCall : FirCall { @@ -24,4 +27,7 @@ interface FirAnnotationCall : FirCall { annotationTypeRef.accept(visitor, data) super.acceptChildren(visitor, data) } -} \ No newline at end of file +} + +val FirAnnotationCall.resolvedFqName: FqName? + get() = ((annotationTypeRef as? FirResolvedTypeRef)?.type as? ConeClassLikeType)?.lookupTag?.classId?.asSingleFqName() diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/FirExpression.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/FirExpression.kt index 8ed4cfb634e..007a5a73b36 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/FirExpression.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/FirExpression.kt @@ -5,9 +5,19 @@ package org.jetbrains.kotlin.fir.expressions +import org.jetbrains.kotlin.fir.FirResolvedCallableReference +import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol import org.jetbrains.kotlin.fir.visitors.FirVisitor interface FirExpression : FirStatement { override fun accept(visitor: FirVisitor, data: D): R = visitor.visitExpression(this, data) +} + +fun FirExpression.toResolvedCallableReference(): FirResolvedCallableReference? { + return (this as? FirQualifiedAccess)?.calleeReference as? FirResolvedCallableReference +} + +fun FirExpression.toResolvedCallableSymbol(): ConeCallableSymbol? { + return toResolvedCallableReference()?.callableSymbol } \ No newline at end of file diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/builtins/jvm/JavaToKotlinClassMap.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/builtins/jvm/JavaToKotlinClassMap.kt index bffddc031cf..40c7d5fa469 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/builtins/jvm/JavaToKotlinClassMap.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/builtins/jvm/JavaToKotlinClassMap.kt @@ -213,14 +213,22 @@ object JavaToKotlinClassMap : PlatformToKotlinClassMap { emptySet() } - fun isMutable(mutable: ClassDescriptor): Boolean = mutableToReadOnly.containsKey(DescriptorUtils.getFqName(mutable)) + fun mutableToReadOnly(fqNameUnsafe: FqNameUnsafe?): FqName? = mutableToReadOnly[fqNameUnsafe] + + fun readOnlyToMutable(fqNameUnsafe: FqNameUnsafe?): FqName? = readOnlyToMutable[fqNameUnsafe] + + fun isMutable(fqNameUnsafe: FqNameUnsafe?): Boolean = mutableToReadOnly.containsKey(fqNameUnsafe) + + fun isMutable(mutable: ClassDescriptor): Boolean = isMutable(DescriptorUtils.getFqName(mutable)) fun isMutable(type: KotlinType): Boolean { val classDescriptor = TypeUtils.getClassDescriptor(type) return classDescriptor != null && isMutable(classDescriptor) } - fun isReadOnly(readOnly: ClassDescriptor): Boolean = readOnlyToMutable.containsKey(DescriptorUtils.getFqName(readOnly)) + fun isReadOnly(fqNameUnsafe: FqNameUnsafe?): Boolean = readOnlyToMutable.containsKey(fqNameUnsafe) + + fun isReadOnly(readOnly: ClassDescriptor): Boolean = isReadOnly(DescriptorUtils.getFqName(readOnly)) fun isReadOnly(type: KotlinType): Boolean { val classDescriptor = TypeUtils.getClassDescriptor(type) diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/AnnotationTypeQualifierResolver.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/AnnotationTypeQualifierResolver.kt index 2223590e910..414d87ff63b 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/AnnotationTypeQualifierResolver.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/AnnotationTypeQualifierResolver.kt @@ -34,13 +34,13 @@ import org.jetbrains.kotlin.utils.Jsr305State import org.jetbrains.kotlin.utils.ReportLevel import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult -private val TYPE_QUALIFIER_NICKNAME_FQNAME = FqName("javax.annotation.meta.TypeQualifierNickname") -private val TYPE_QUALIFIER_FQNAME = FqName("javax.annotation.meta.TypeQualifier") -private val TYPE_QUALIFIER_DEFAULT_FQNAME = FqName("javax.annotation.meta.TypeQualifierDefault") +val TYPE_QUALIFIER_NICKNAME_FQNAME = FqName("javax.annotation.meta.TypeQualifierNickname") +val TYPE_QUALIFIER_FQNAME = FqName("javax.annotation.meta.TypeQualifier") +val TYPE_QUALIFIER_DEFAULT_FQNAME = FqName("javax.annotation.meta.TypeQualifierDefault") -private val MIGRATION_ANNOTATION_FQNAME = FqName("kotlin.annotations.jvm.UnderMigration") +val MIGRATION_ANNOTATION_FQNAME = FqName("kotlin.annotations.jvm.UnderMigration") -private val BUILT_IN_TYPE_QUALIFIER_DEFAULT_ANNOTATIONS = mapOf( +val BUILT_IN_TYPE_QUALIFIER_DEFAULT_ANNOTATIONS = mapOf( FqName("javax.annotation.ParametersAreNullableByDefault") to NullabilityQualifierWithApplicability( NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE), diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/context.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/context.kt index d8096bb67fe..c1b5e3a7b41 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/context.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/context.kt @@ -93,9 +93,9 @@ interface JavaResolverSettings { } } -private typealias QualifierByApplicabilityType = EnumMap +typealias QualifierByApplicabilityType = EnumMap -class JavaTypeQualifiersByElementType(internal val nullabilityQualifiers: QualifierByApplicabilityType) { +class JavaTypeQualifiersByElementType(val nullabilityQualifiers: QualifierByApplicabilityType) { operator fun get(applicabilityType: AnnotationTypeQualifierResolver.QualifierApplicabilityType?): JavaTypeQualifiers? { val nullabilityQualifierWithMigrationStatus = nullabilityQualifiers[applicabilityType] ?: return null diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt index d542f03ea58..abeb879ab3e 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt @@ -462,7 +462,7 @@ class SignatureEnhancement( } } -private fun createJavaTypeQualifiers( +fun createJavaTypeQualifiers( nullability: NullabilityQualifier?, mutability: MutabilityQualifier?, forWarning: Boolean, @@ -474,7 +474,7 @@ private fun createJavaTypeQualifiers( return JavaTypeQualifiers(nullability, mutability, true, forWarning) } -private fun Set.select(low: T, high: T, own: T?, isCovariant: Boolean): T? { +fun Set.select(low: T, high: T, own: T?, isCovariant: Boolean): T? { if (isCovariant) { val supertypeQualifier = if (low in this) low else if (high in this) high else null return if (supertypeQualifier == low && own == high) null else own ?: supertypeQualifier @@ -488,7 +488,7 @@ private fun Set.select(low: T, high: T, own: T?, isCovariant: Boole return effectiveSet.singleOrNull() } -private fun Set.select(own: NullabilityQualifier?, isCovariant: Boolean) = +fun Set.select(own: NullabilityQualifier?, isCovariant: Boolean) = if (own == NullabilityQualifier.FORCE_FLEXIBILITY) NullabilityQualifier.FORCE_FLEXIBILITY else diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/typeEnhancement.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/typeEnhancement.kt index bdb28502d39..b2002a19f43 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/typeEnhancement.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/typeEnhancement.kt @@ -45,7 +45,7 @@ fun KotlinType.enhance(qualifiers: (Int) -> JavaTypeQualifiers) = unwrap().enhan fun KotlinType.hasEnhancedNullability() = annotations.findAnnotation(JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION) != null -private enum class TypeComponentPosition { +enum class TypeComponentPosition { FLEXIBLE_LOWER, FLEXIBLE_UPPER, INFLEXIBLE @@ -147,7 +147,7 @@ private fun List.compositeAnnotationsOrSingle() = when (size) { else -> CompositeAnnotations(this.toList()) } -private fun TypeComponentPosition.shouldEnhance() = this != TypeComponentPosition.INFLEXIBLE +fun TypeComponentPosition.shouldEnhance() = this != TypeComponentPosition.INFLEXIBLE private data class EnhancementResult(val result: T, val enhancementAnnotations: Annotations?) private fun T.noChange() = EnhancementResult(this, null) diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/typeQualifiers.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/typeQualifiers.kt index f4cd64abff0..d73603d3c10 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/typeQualifiers.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/typeQualifiers.kt @@ -27,11 +27,11 @@ enum class MutabilityQualifier { MUTABLE } -class JavaTypeQualifiers internal constructor( - val nullability: NullabilityQualifier?, - val mutability: MutabilityQualifier?, - internal val isNotNullTypeParameter: Boolean, - internal val isNullabilityQualifierForWarning: Boolean = false +class JavaTypeQualifiers( + val nullability: NullabilityQualifier?, + val mutability: MutabilityQualifier?, + val isNotNullTypeParameter: Boolean, + val isNullabilityQualifierForWarning: Boolean = false ) { companion object { val NONE = JavaTypeQualifiers(null, null, false) diff --git a/idea/testData/fir/multiModule/basicWithAnnotatedJava/extraDump.java.txt b/idea/testData/fir/multiModule/basicWithAnnotatedJava/extraDump.java.txt new file mode 100644 index 00000000000..03fe461403f --- /dev/null +++ b/idea/testData/fir/multiModule/basicWithAnnotatedJava/extraDump.java.txt @@ -0,0 +1,4 @@ +public open class Annotated : R|java/lang/Object| { + @R|org/jetbrains/annotations/NotNull|() public open operator function foo(@R|org/jetbrains/annotations/Nullable|() param: R|ft|?): R|ft| + +} diff --git a/idea/testData/fir/multiModule/basicWithAnnotatedJava/jvm/Annotated.java b/idea/testData/fir/multiModule/basicWithAnnotatedJava/jvm/Annotated.java new file mode 100644 index 00000000000..1b8dc1e486f --- /dev/null +++ b/idea/testData/fir/multiModule/basicWithAnnotatedJava/jvm/Annotated.java @@ -0,0 +1,10 @@ +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class Annotated { + @NotNull + public String foo(@Nullable String param) { + if (param != null) return param; + else return ""; + } +} \ No newline at end of file diff --git a/idea/testData/fir/multiModule/basicWithAnnotatedJava/jvm/jvm.kt b/idea/testData/fir/multiModule/basicWithAnnotatedJava/jvm/jvm.kt new file mode 100644 index 00000000000..41669bcbb07 --- /dev/null +++ b/idea/testData/fir/multiModule/basicWithAnnotatedJava/jvm/jvm.kt @@ -0,0 +1,6 @@ +class User : Annotated() { + fun test() { + val x = foo("123") + val y = foo(null) + } +} \ No newline at end of file diff --git a/idea/testData/fir/multiModule/basicWithAnnotatedJava/jvm/jvm.txt b/idea/testData/fir/multiModule/basicWithAnnotatedJava/jvm/jvm.txt new file mode 100644 index 00000000000..17add33dc44 --- /dev/null +++ b/idea/testData/fir/multiModule/basicWithAnnotatedJava/jvm/jvm.txt @@ -0,0 +1,10 @@ +FILE: jvm.kt + public final class User : R|Annotated| { + public constructor(): super() + + public final function test(): R|kotlin/Unit| { + val x: R|error: Not supported: FirImplicitTypeRefImpl| = R|/Annotated.foo|(String(123)) + val y: R|error: Not supported: FirImplicitTypeRefImpl| = R|/Annotated.foo|(Null(null)) + } + + } diff --git a/idea/testData/fir/multiModule/basicWithAnnotatedOverriddenJava/extraDump.java.txt b/idea/testData/fir/multiModule/basicWithAnnotatedOverriddenJava/extraDump.java.txt new file mode 100644 index 00000000000..1eb692bece8 --- /dev/null +++ b/idea/testData/fir/multiModule/basicWithAnnotatedOverriddenJava/extraDump.java.txt @@ -0,0 +1,8 @@ +public open class Annotated : R|java/lang/Object| { + @R|org/jetbrains/annotations/NotNull|() public open operator function foo(@R|org/jetbrains/annotations/Nullable|() param: R|ft|?): R|ft| + +} +public open class AnnotatedDerived : R|Annotated| { + public open operator function foo(param: R|ft|?): R|ft| + +} diff --git a/idea/testData/fir/multiModule/basicWithAnnotatedOverriddenJava/jvm/Annotated.java b/idea/testData/fir/multiModule/basicWithAnnotatedOverriddenJava/jvm/Annotated.java new file mode 100644 index 00000000000..1b8dc1e486f --- /dev/null +++ b/idea/testData/fir/multiModule/basicWithAnnotatedOverriddenJava/jvm/Annotated.java @@ -0,0 +1,10 @@ +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class Annotated { + @NotNull + public String foo(@Nullable String param) { + if (param != null) return param; + else return ""; + } +} \ No newline at end of file diff --git a/idea/testData/fir/multiModule/basicWithAnnotatedOverriddenJava/jvm/AnnotatedDerived.java b/idea/testData/fir/multiModule/basicWithAnnotatedOverriddenJava/jvm/AnnotatedDerived.java new file mode 100644 index 00000000000..4d74b83e1b7 --- /dev/null +++ b/idea/testData/fir/multiModule/basicWithAnnotatedOverriddenJava/jvm/AnnotatedDerived.java @@ -0,0 +1,5 @@ +public class AnnotatedDerived extends Annotated { + public String foo(String param) { + return super.foo(param); + } +} \ No newline at end of file diff --git a/idea/testData/fir/multiModule/basicWithAnnotatedOverriddenJava/jvm/jvm.kt b/idea/testData/fir/multiModule/basicWithAnnotatedOverriddenJava/jvm/jvm.kt new file mode 100644 index 00000000000..b3c8f0afcdf --- /dev/null +++ b/idea/testData/fir/multiModule/basicWithAnnotatedOverriddenJava/jvm/jvm.kt @@ -0,0 +1,6 @@ +class User : AnnotatedDerived() { + fun test() { + val x = foo("123") + val y = foo(null) + } +} \ No newline at end of file diff --git a/idea/testData/fir/multiModule/basicWithAnnotatedOverriddenJava/jvm/jvm.txt b/idea/testData/fir/multiModule/basicWithAnnotatedOverriddenJava/jvm/jvm.txt new file mode 100644 index 00000000000..a5f48a80b63 --- /dev/null +++ b/idea/testData/fir/multiModule/basicWithAnnotatedOverriddenJava/jvm/jvm.txt @@ -0,0 +1,10 @@ +FILE: jvm.kt + public final class User : R|AnnotatedDerived| { + public constructor(): super() + + public final function test(): R|kotlin/Unit| { + val x: R|error: Not supported: FirImplicitTypeRefImpl| = #(String(123)) + val y: R|error: Not supported: FirImplicitTypeRefImpl| = #(Null(null)) + } + + } diff --git a/idea/testData/fir/multiModule/basicWithJava/extraDump.java.txt b/idea/testData/fir/multiModule/basicWithJava/extraDump.java.txt new file mode 100644 index 00000000000..40be4417ed3 --- /dev/null +++ b/idea/testData/fir/multiModule/basicWithJava/extraDump.java.txt @@ -0,0 +1,2 @@ +public open class Some : R|java/lang/Object| { +} diff --git a/idea/testData/fir/multiModule/basicWithJavaFakeOverride/extraDump.java.txt b/idea/testData/fir/multiModule/basicWithJavaFakeOverride/extraDump.java.txt new file mode 100644 index 00000000000..90c084aac81 --- /dev/null +++ b/idea/testData/fir/multiModule/basicWithJavaFakeOverride/extraDump.java.txt @@ -0,0 +1,4 @@ + public open class A : R|java/lang/Object| { + public open operator function foo(t: R|ft|!): R|ft|! + +} diff --git a/idea/testData/fir/multiModule/basicWithJavaFakeOverride/jvm/simpleFakeOverride.txt b/idea/testData/fir/multiModule/basicWithJavaFakeOverride/jvm/simpleFakeOverride.txt index 06a184d6a51..d8d94406590 100644 --- a/idea/testData/fir/multiModule/basicWithJavaFakeOverride/jvm/simpleFakeOverride.txt +++ b/idea/testData/fir/multiModule/basicWithJavaFakeOverride/jvm/simpleFakeOverride.txt @@ -7,7 +7,7 @@ FILE: simpleFakeOverride.kt public constructor(): super|>() public final function test(): R|kotlin/Unit| { - R|FakeOverride|!>|(#()) + R|FakeOverride|!>|(#()) } } diff --git a/idea/testData/fir/multiModule/basicWithPrimitiveJava/extraDump.java.txt b/idea/testData/fir/multiModule/basicWithPrimitiveJava/extraDump.java.txt new file mode 100644 index 00000000000..762da8c5aff --- /dev/null +++ b/idea/testData/fir/multiModule/basicWithPrimitiveJava/extraDump.java.txt @@ -0,0 +1,6 @@ +public open class Some : R|java/lang/Object| { + public open operator function foo(param: R|kotlin/Int|): R|kotlin/Boolean| + + public open operator function bar(arr: R|kotlin/IntArray|): R|kotlin/Array>| + +} diff --git a/idea/testData/fir/multiModule/basicWithPrimitiveJava/jvm/Some.java b/idea/testData/fir/multiModule/basicWithPrimitiveJava/jvm/Some.java new file mode 100644 index 00000000000..136d4550702 --- /dev/null +++ b/idea/testData/fir/multiModule/basicWithPrimitiveJava/jvm/Some.java @@ -0,0 +1,14 @@ +public class Some { + public boolean foo(int param) { + return param > 0; + } + + public String[] bar(int[] arr) { + String[] result = new String[arr.length]; + int i = 0; + for (int elem: arr) { + result[i++] = elem; + } + return result; + } +} \ No newline at end of file diff --git a/idea/testData/fir/multiModule/basicWithPrimitiveJava/jvm/jvm.kt b/idea/testData/fir/multiModule/basicWithPrimitiveJava/jvm/jvm.kt new file mode 100644 index 00000000000..30a7d26c902 --- /dev/null +++ b/idea/testData/fir/multiModule/basicWithPrimitiveJava/jvm/jvm.kt @@ -0,0 +1,7 @@ +class A : Some() { + fun test() { + val res1 = foo(1) + val res2 = foo(-1) + val res3 = bar(intArrayOf(0, 2, -2)) + } +} \ No newline at end of file diff --git a/idea/testData/fir/multiModule/basicWithPrimitiveJava/jvm/jvm.txt b/idea/testData/fir/multiModule/basicWithPrimitiveJava/jvm/jvm.txt new file mode 100644 index 00000000000..ad2a5b08ab2 --- /dev/null +++ b/idea/testData/fir/multiModule/basicWithPrimitiveJava/jvm/jvm.txt @@ -0,0 +1,11 @@ +FILE: jvm.kt + public final class A : R|Some| { + public constructor(): super() + + public final function test(): R|kotlin/Unit| { + val res1: R|error: Not supported: FirImplicitTypeRefImpl| = R|/Some.foo|(Int(1)) + val res2: R|error: Not supported: FirImplicitTypeRefImpl| = R|/Some.foo|(#(Int(1))) + val res3: R|error: Not supported: FirImplicitTypeRefImpl| = R|/Some.bar|(#(Int(0), Int(2), #(Int(2)))) + } + + } diff --git a/idea/testData/fir/multiModule/overrideWithJava/extraDump.java.txt b/idea/testData/fir/multiModule/overrideWithJava/extraDump.java.txt new file mode 100644 index 00000000000..cfd6fccdb7a --- /dev/null +++ b/idea/testData/fir/multiModule/overrideWithJava/extraDump.java.txt @@ -0,0 +1,6 @@ +public open class A : R|java/lang/Object| { + public open operator function foo(): R|ft|! + + public open operator function bar(): R|ft|! + +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/fir/AbstractFirMultiModuleResolveTest.kt b/idea/tests/org/jetbrains/kotlin/idea/fir/AbstractFirMultiModuleResolveTest.kt index f505b9e27f9..11a261852c6 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/fir/AbstractFirMultiModuleResolveTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/fir/AbstractFirMultiModuleResolveTest.kt @@ -12,15 +12,26 @@ import com.intellij.psi.PsiManager import com.intellij.psi.search.FileTypeIndex import org.jetbrains.kotlin.fir.FirRenderer import org.jetbrains.kotlin.fir.builder.RawFirBuilder +import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.dependenciesWithoutSelf import org.jetbrains.kotlin.fir.java.FirJavaModuleBasedSession import org.jetbrains.kotlin.fir.java.FirLibrarySession import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider +import org.jetbrains.kotlin.fir.java.JavaSymbolProvider +import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass +import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod +import org.jetbrains.kotlin.fir.java.scopes.JavaClassEnhancementScope 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.FirProviderImpl import org.jetbrains.kotlin.fir.resolve.transformers.FirTotalResolveTransformer +import org.jetbrains.kotlin.fir.scopes.ProcessorAction +import org.jetbrains.kotlin.fir.scopes.impl.FirCompositeScope import org.jetbrains.kotlin.fir.service +import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo import org.jetbrains.kotlin.idea.caches.project.isLibraryClasses @@ -64,9 +75,11 @@ abstract class AbstractFirMultiModuleResolveTest : AbstractMultiModuleTest() { private fun doFirResolveTest(dirPath: String) { val firFiles = mutableListOf() + val sessions = mutableListOf() val provider = FirProjectSessionProvider(project) for (module in project.allModules().drop(1)) { val session = createSession(module, provider) + sessions += session val builder = RawFirBuilder(session, stubMode = false) val psiManager = PsiManager.getInstance(project) @@ -112,5 +125,54 @@ abstract class AbstractFirMultiModuleResolveTest : AbstractMultiModuleTest() { val expectedPath = expectedTxtPath((file.psi as PsiFile).virtualFile) KotlinTestUtils.assertEqualsToFile(File(expectedPath), firFileDump) } + val processedJavaClasses = mutableSetOf() + val javaFirDump = StringBuilder().also { builder -> + val renderer = FirRenderer(builder) + for (session in sessions) { + val symbolProvider = session.service() as FirCompositeSymbolProvider + val javaProvider = symbolProvider.providers.filterIsInstance().first() + for (javaClass in javaProvider.getJavaTopLevelClasses().sortedBy { it.name }) { + if (javaClass !is FirJavaClass || javaClass in processedJavaClasses) continue + val enhancementScope = session.service().getDeclaredMemberScope(javaClass, session).let { + when (it) { + is FirCompositeScope -> it.scopes.filterIsInstance().first() + is JavaClassEnhancementScope -> it + else -> null + } + } + if (enhancementScope == null) { + javaClass.accept(renderer, null) + } else { + renderer.visitMemberDeclaration(javaClass) + renderer.renderSupertypes(javaClass) + renderer.renderInBraces { + val renderedDeclarations = mutableListOf() + for (declaration in javaClass.declarations) { + if (declaration in renderedDeclarations) continue + if (declaration !is FirJavaMethod) { + declaration.accept(renderer, null) + renderer.newLine() + renderedDeclarations += declaration + } else { + enhancementScope.processFunctionsByName(declaration.name) { symbol -> + val enhanced = (symbol as? FirFunctionSymbol)?.fir + if (enhanced != null && enhanced !in renderedDeclarations) { + enhanced.accept(renderer, null) + renderer.newLine() + renderedDeclarations += enhanced + } + ProcessorAction.NEXT + } + } + } + } + } + processedJavaClasses += javaClass + } + } + }.toString() + if (javaFirDump.isNotEmpty()) { + KotlinTestUtils.assertEqualsToFile(File("$dirPath/extraDump.java.txt"), javaFirDump) + } } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/fir/FirMultiModuleResolveTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/fir/FirMultiModuleResolveTestGenerated.java index ab743cd46b1..cbbbee78362 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/fir/FirMultiModuleResolveTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/fir/FirMultiModuleResolveTestGenerated.java @@ -34,6 +34,16 @@ public class FirMultiModuleResolveTestGenerated extends AbstractFirMultiModuleRe runTest("idea/testData/fir/multiModule/basic/"); } + @TestMetadata("basicWithAnnotatedJava") + public void testBasicWithAnnotatedJava() throws Exception { + runTest("idea/testData/fir/multiModule/basicWithAnnotatedJava/"); + } + + @TestMetadata("basicWithAnnotatedOverriddenJava") + public void testBasicWithAnnotatedOverriddenJava() throws Exception { + runTest("idea/testData/fir/multiModule/basicWithAnnotatedOverriddenJava/"); + } + @TestMetadata("basicWithJava") public void testBasicWithJava() throws Exception { runTest("idea/testData/fir/multiModule/basicWithJava/"); @@ -44,6 +54,11 @@ public class FirMultiModuleResolveTestGenerated extends AbstractFirMultiModuleRe runTest("idea/testData/fir/multiModule/basicWithJavaFakeOverride/"); } + @TestMetadata("basicWithPrimitiveJava") + public void testBasicWithPrimitiveJava() throws Exception { + runTest("idea/testData/fir/multiModule/basicWithPrimitiveJava/"); + } + @TestMetadata("mppFakeOverrides") public void testMppFakeOverrides() throws Exception { runTest("idea/testData/fir/multiModule/mppFakeOverrides/");