Create expect / actual refactoring: extract common part to utilities
This commit is contained in:
+1
-21
@@ -119,32 +119,12 @@ interface OverrideMemberChooserObject : ClassMember {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun OverrideMemberChooserObject.generateTopLevelActual(
|
|
||||||
project: Project,
|
|
||||||
copyDoc: Boolean
|
|
||||||
) = generateMember(null, copyDoc, project, forceActual = true, forceExpect = false)
|
|
||||||
|
|
||||||
fun OverrideMemberChooserObject.generateActualMember(
|
|
||||||
targetClass: KtClassOrObject,
|
|
||||||
copyDoc: Boolean
|
|
||||||
) = generateMember(targetClass, copyDoc, targetClass.project, forceActual = true, forceExpect = false)
|
|
||||||
|
|
||||||
fun OverrideMemberChooserObject.generateTopLevelExpect(
|
|
||||||
project: Project,
|
|
||||||
copyDoc: Boolean
|
|
||||||
) = generateMember(null, copyDoc, project, forceActual = false, forceExpect = true)
|
|
||||||
|
|
||||||
fun OverrideMemberChooserObject.generateExpectMember(
|
|
||||||
targetClass: KtClassOrObject,
|
|
||||||
copyDoc: Boolean
|
|
||||||
) = generateMember(targetClass, copyDoc, targetClass.project, forceActual = false, forceExpect = true)
|
|
||||||
|
|
||||||
fun OverrideMemberChooserObject.generateMember(
|
fun OverrideMemberChooserObject.generateMember(
|
||||||
targetClass: KtClassOrObject,
|
targetClass: KtClassOrObject,
|
||||||
copyDoc: Boolean
|
copyDoc: Boolean
|
||||||
) = generateMember(targetClass, copyDoc, targetClass.project, forceActual = false, forceExpect = false)
|
) = generateMember(targetClass, copyDoc, targetClass.project, forceActual = false, forceExpect = false)
|
||||||
|
|
||||||
private fun OverrideMemberChooserObject.generateMember(
|
fun OverrideMemberChooserObject.generateMember(
|
||||||
targetClass: KtClassOrObject?,
|
targetClass: KtClassOrObject?,
|
||||||
copyDoc: Boolean,
|
copyDoc: Boolean,
|
||||||
project: Project,
|
project: Project,
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ class AddActualFix(
|
|||||||
val missedDeclarations = missedDeclarationPointers.mapNotNull { it.element }
|
val missedDeclarations = missedDeclarationPointers.mapNotNull { it.element }
|
||||||
if (missedDeclarations.isEmpty()) return
|
if (missedDeclarations.isEmpty()) return
|
||||||
val factory = KtPsiFactory(element)
|
val factory = KtPsiFactory(element)
|
||||||
val pureActualClass = factory.generateClassOrObjectByExpectedClass(
|
val pureActualClass = factory.generateClassOrObject(
|
||||||
project, expectedClass,
|
project, false, expectedClass,
|
||||||
missedDeclarations = missedDeclarations
|
missedDeclarations = missedDeclarations
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -24,31 +24,21 @@ import com.intellij.openapi.project.DumbService
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
|
|
||||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.BodyType.EMPTY_OR_TEMPLATE
|
|
||||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.BodyType.NO_BODY
|
|
||||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.Companion.create
|
|
||||||
import org.jetbrains.kotlin.idea.core.overrideImplement.generateActualMember
|
|
||||||
import org.jetbrains.kotlin.idea.core.overrideImplement.generateTopLevelActual
|
|
||||||
import org.jetbrains.kotlin.idea.core.toDescriptor
|
import org.jetbrains.kotlin.idea.core.toDescriptor
|
||||||
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
|
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
|
||||||
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
|
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
|
||||||
import org.jetbrains.kotlin.idea.util.actualsForExpected
|
import org.jetbrains.kotlin.idea.util.actualsForExpected
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
|
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
import org.jetbrains.kotlin.resolve.MultiTargetPlatform
|
import org.jetbrains.kotlin.resolve.MultiTargetPlatform
|
||||||
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
|
|
||||||
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
|
|
||||||
import org.jetbrains.kotlin.resolve.getMultiTargetPlatform
|
import org.jetbrains.kotlin.resolve.getMultiTargetPlatform
|
||||||
|
|
||||||
sealed class CreateActualFix<out D : KtNamedDeclaration>(
|
sealed class CreateActualFix<out D : KtNamedDeclaration>(
|
||||||
@@ -136,7 +126,7 @@ class CreateActualClassFix(
|
|||||||
actualModule: Module,
|
actualModule: Module,
|
||||||
actualPlatform: MultiTargetPlatform.Specific
|
actualPlatform: MultiTargetPlatform.Specific
|
||||||
) : CreateActualFix<KtClassOrObject>(klass, actualModule, actualPlatform, { project, element ->
|
) : CreateActualFix<KtClassOrObject>(klass, actualModule, actualPlatform, { project, element ->
|
||||||
generateClassOrObjectByExpectedClass(project, element)
|
generateClassOrObject(project, false, element)
|
||||||
})
|
})
|
||||||
|
|
||||||
class CreateActualPropertyFix(
|
class CreateActualPropertyFix(
|
||||||
@@ -145,7 +135,7 @@ class CreateActualPropertyFix(
|
|||||||
actualPlatform: MultiTargetPlatform.Specific
|
actualPlatform: MultiTargetPlatform.Specific
|
||||||
) : CreateActualFix<KtProperty>(property, actualModule, actualPlatform, { project, element ->
|
) : CreateActualFix<KtProperty>(property, actualModule, actualPlatform, { project, element ->
|
||||||
val descriptor = element.toDescriptor() as? PropertyDescriptor
|
val descriptor = element.toDescriptor() as? PropertyDescriptor
|
||||||
descriptor?.let { generateProperty(project, element, descriptor) }
|
descriptor?.let { generateProperty(project, false, element, descriptor) }
|
||||||
})
|
})
|
||||||
|
|
||||||
class CreateActualFunctionFix(
|
class CreateActualFunctionFix(
|
||||||
@@ -154,120 +144,6 @@ class CreateActualFunctionFix(
|
|||||||
actualPlatform: MultiTargetPlatform.Specific
|
actualPlatform: MultiTargetPlatform.Specific
|
||||||
) : CreateActualFix<KtFunction>(function, actualModule, actualPlatform, { project, element ->
|
) : CreateActualFix<KtFunction>(function, actualModule, actualPlatform, { project, element ->
|
||||||
val descriptor = element.toDescriptor() as? FunctionDescriptor
|
val descriptor = element.toDescriptor() as? FunctionDescriptor
|
||||||
descriptor?.let { generateFunction(project, element, descriptor) }
|
descriptor?.let { generateFunction(project, false, element, descriptor) }
|
||||||
})
|
})
|
||||||
|
|
||||||
internal fun KtPsiFactory.generateClassOrObjectByExpectedClass(
|
|
||||||
project: Project,
|
|
||||||
expectedClass: KtClassOrObject,
|
|
||||||
// If null, all expect class declarations are missed (so none from them exists)
|
|
||||||
missedDeclarations: List<KtDeclaration>? = null
|
|
||||||
): KtClassOrObject {
|
|
||||||
fun areCompatible(first: KtFunction, second: KtFunction) =
|
|
||||||
first.valueParameters.size == second.valueParameters.size &&
|
|
||||||
first.valueParameters.zip(second.valueParameters).all { (firstParam, secondParam) ->
|
|
||||||
firstParam.name == secondParam.name && firstParam.typeReference?.text == secondParam.typeReference?.text
|
|
||||||
}
|
|
||||||
|
|
||||||
fun KtDeclaration.exists() =
|
|
||||||
missedDeclarations != null && missedDeclarations.none {
|
|
||||||
name == it.name && when (this) {
|
|
||||||
is KtConstructor<*> -> it is KtConstructor<*> && areCompatible(this, it)
|
|
||||||
is KtNamedFunction -> it is KtNamedFunction && areCompatible(this, it)
|
|
||||||
is KtProperty -> it is KtProperty || it is KtParameter && it.hasValOrVar()
|
|
||||||
else -> this.javaClass == it.javaClass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val actualClass = createClassHeaderCopyByText(expectedClass)
|
|
||||||
|
|
||||||
val context = expectedClass.analyzeWithContent()
|
|
||||||
actualClass.superTypeListEntries.zip(expectedClass.superTypeListEntries).forEach { (actualEntry, expectedEntry) ->
|
|
||||||
if (actualEntry !is KtSuperTypeEntry) return@forEach
|
|
||||||
val superType = context[BindingContext.TYPE, expectedEntry.typeReference]
|
|
||||||
val superClassDescriptor = superType?.constructor?.declarationDescriptor as? ClassDescriptor ?: return@forEach
|
|
||||||
if (superClassDescriptor.kind == ClassKind.CLASS || superClassDescriptor.kind == ClassKind.ENUM_CLASS) {
|
|
||||||
actualEntry.replace(createSuperTypeCallEntry("${actualEntry.typeReference!!.text}()"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (actualClass.isAnnotation()) {
|
|
||||||
actualClass.annotationEntries.zip(expectedClass.annotationEntries).forEach { (actualEntry, expectedEntry) ->
|
|
||||||
val annotationDescriptor = context.get(BindingContext.ANNOTATION, expectedEntry) ?: return@forEach
|
|
||||||
if (annotationDescriptor.fqName in forbiddenAnnotationFqNames) {
|
|
||||||
actualEntry.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (actualClass !is KtEnumEntry) {
|
|
||||||
actualClass.addModifier(KtTokens.ACTUAL_KEYWORD)
|
|
||||||
}
|
|
||||||
|
|
||||||
declLoop@ for (expectedDeclaration in expectedClass.declarations.filter { !it.exists() }) {
|
|
||||||
val descriptor = expectedDeclaration.toDescriptor() ?: continue
|
|
||||||
val actualDeclaration: KtDeclaration = when (expectedDeclaration) {
|
|
||||||
is KtClassOrObject -> {
|
|
||||||
generateClassOrObjectByExpectedClass(project, expectedDeclaration)
|
|
||||||
}
|
|
||||||
is KtCallableDeclaration -> {
|
|
||||||
when (expectedDeclaration) {
|
|
||||||
is KtFunction -> generateFunction(project, expectedDeclaration, descriptor as FunctionDescriptor, actualClass)
|
|
||||||
is KtProperty -> generateProperty(project, expectedDeclaration, descriptor as PropertyDescriptor, actualClass)
|
|
||||||
else -> continue@declLoop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> continue@declLoop
|
|
||||||
}
|
|
||||||
actualClass.addDeclaration(actualDeclaration)
|
|
||||||
}
|
|
||||||
val expectedPrimaryConstructor = expectedClass.primaryConstructor
|
|
||||||
if (actualClass is KtClass && expectedPrimaryConstructor?.exists() == false) {
|
|
||||||
val descriptor = expectedPrimaryConstructor.toDescriptor()
|
|
||||||
if (descriptor is FunctionDescriptor) {
|
|
||||||
val actualPrimaryConstructor = generateFunction(project, expectedPrimaryConstructor, descriptor, actualClass)
|
|
||||||
actualClass.createPrimaryConstructorIfAbsent().replace(actualPrimaryConstructor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return actualClass
|
|
||||||
}
|
|
||||||
|
|
||||||
private val forbiddenAnnotationFqNames = setOf(
|
|
||||||
ExpectedActualDeclarationChecker.OPTIONAL_EXPECTATION_FQ_NAME,
|
|
||||||
FqName("kotlin.ExperimentalMultiplatform"),
|
|
||||||
ExperimentalUsageChecker.USE_EXPERIMENTAL_FQ_NAME
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun generateFunction(
|
|
||||||
project: Project,
|
|
||||||
expectedFunction: KtFunction,
|
|
||||||
descriptor: FunctionDescriptor,
|
|
||||||
targetClass: KtClassOrObject? = null
|
|
||||||
): KtFunction {
|
|
||||||
val memberChooserObject = create(
|
|
||||||
expectedFunction, descriptor, descriptor,
|
|
||||||
if (descriptor.modality == Modality.ABSTRACT) NO_BODY else EMPTY_OR_TEMPLATE
|
|
||||||
)
|
|
||||||
return if (targetClass != null) {
|
|
||||||
memberChooserObject.generateActualMember(targetClass = targetClass, copyDoc = true)
|
|
||||||
} else {
|
|
||||||
memberChooserObject.generateTopLevelActual(project = project, copyDoc = true)
|
|
||||||
} as KtFunction
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateProperty(
|
|
||||||
project: Project,
|
|
||||||
expectedProperty: KtProperty,
|
|
||||||
descriptor: PropertyDescriptor,
|
|
||||||
targetClass: KtClassOrObject? = null
|
|
||||||
): KtProperty {
|
|
||||||
val memberChooserObject = create(
|
|
||||||
expectedProperty, descriptor, descriptor,
|
|
||||||
if (descriptor.modality == Modality.ABSTRACT) NO_BODY else EMPTY_OR_TEMPLATE
|
|
||||||
)
|
|
||||||
return if (targetClass != null) {
|
|
||||||
memberChooserObject.generateActualMember(targetClass = targetClass, copyDoc = true)
|
|
||||||
} else {
|
|
||||||
memberChooserObject.generateTopLevelActual(project = project, copyDoc = true)
|
|
||||||
} as KtProperty
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,37 +12,24 @@ import com.intellij.openapi.module.Module
|
|||||||
import com.intellij.openapi.project.DumbService
|
import com.intellij.openapi.project.DumbService
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.idea.caches.project.implementedDescriptors
|
|
||||||
import org.jetbrains.kotlin.idea.caches.project.implementedModules
|
import org.jetbrains.kotlin.idea.caches.project.implementedModules
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
|
|
||||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||||
import org.jetbrains.kotlin.idea.core.overrideImplement.*
|
|
||||||
import org.jetbrains.kotlin.idea.core.toDescriptor
|
import org.jetbrains.kotlin.idea.core.toDescriptor
|
||||||
import org.jetbrains.kotlin.idea.quickfix.KotlinIntentionActionsFactory
|
import org.jetbrains.kotlin.idea.quickfix.KotlinIntentionActionsFactory
|
||||||
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
|
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHint
|
import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHint
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||||
import org.jetbrains.kotlin.idea.util.hasDeclarationOf
|
|
||||||
import org.jetbrains.kotlin.idea.util.isEffectivelyActual
|
|
||||||
import org.jetbrains.kotlin.idea.util.liftToExpected
|
import org.jetbrains.kotlin.idea.util.liftToExpected
|
||||||
import org.jetbrains.kotlin.idea.util.module
|
import org.jetbrains.kotlin.idea.util.module
|
||||||
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
|
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
|
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
import org.jetbrains.kotlin.resolve.MultiTargetPlatform
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
|
||||||
import org.jetbrains.kotlin.resolve.getMultiTargetPlatform
|
|
||||||
import org.jetbrains.kotlin.types.AbbreviatedType
|
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
|
||||||
|
|
||||||
sealed class CreateExpectedFix<out D : KtNamedDeclaration>(
|
sealed class CreateExpectedFix<out D : KtNamedDeclaration>(
|
||||||
declaration: D,
|
declaration: D,
|
||||||
@@ -151,7 +138,7 @@ class CreateExpectedClassFix(
|
|||||||
outerExpectedClass: KtClassOrObject?,
|
outerExpectedClass: KtClassOrObject?,
|
||||||
commonModule: Module
|
commonModule: Module
|
||||||
) : CreateExpectedFix<KtClassOrObject>(klass, outerExpectedClass, commonModule, { project, element ->
|
) : CreateExpectedFix<KtClassOrObject>(klass, outerExpectedClass, commonModule, { project, element ->
|
||||||
generateClassOrObjectByActualClass(project, element, listOfNotNull(outerExpectedClass))
|
generateClassOrObject(project, true, element, listOfNotNull(outerExpectedClass))
|
||||||
})
|
})
|
||||||
|
|
||||||
class CreateExpectedPropertyFix(
|
class CreateExpectedPropertyFix(
|
||||||
@@ -160,7 +147,7 @@ class CreateExpectedPropertyFix(
|
|||||||
commonModule: Module
|
commonModule: Module
|
||||||
) : CreateExpectedFix<KtNamedDeclaration>(property, targetExpectedClass, commonModule, { project, element ->
|
) : CreateExpectedFix<KtNamedDeclaration>(property, targetExpectedClass, commonModule, { project, element ->
|
||||||
val descriptor = element.toDescriptor() as? PropertyDescriptor
|
val descriptor = element.toDescriptor() as? PropertyDescriptor
|
||||||
descriptor?.let { generateProperty(project, element, descriptor, targetExpectedClass, emptyList()) }
|
descriptor?.let { generateProperty(project, true, element, descriptor, targetExpectedClass) }
|
||||||
})
|
})
|
||||||
|
|
||||||
class CreateExpectedFunctionFix(
|
class CreateExpectedFunctionFix(
|
||||||
@@ -169,160 +156,6 @@ class CreateExpectedFunctionFix(
|
|||||||
commonModule: Module
|
commonModule: Module
|
||||||
) : CreateExpectedFix<KtFunction>(function, targetExpectedClass, commonModule, { project, element ->
|
) : CreateExpectedFix<KtFunction>(function, targetExpectedClass, commonModule, { project, element ->
|
||||||
val descriptor = element.toDescriptor() as? FunctionDescriptor
|
val descriptor = element.toDescriptor() as? FunctionDescriptor
|
||||||
descriptor?.let { generateFunction(project, element, descriptor, targetExpectedClass, emptyList()) }
|
descriptor?.let { generateFunction(project, true, element, descriptor, targetExpectedClass) }
|
||||||
})
|
})
|
||||||
|
|
||||||
private fun KtPsiFactory.generateClassOrObjectByActualClass(
|
|
||||||
project: Project,
|
|
||||||
actualClass: KtClassOrObject,
|
|
||||||
outerExpectedClasses: List<KtClassOrObject>
|
|
||||||
): KtClassOrObject {
|
|
||||||
val expectedClass = createClassHeaderCopyByText(actualClass)
|
|
||||||
|
|
||||||
val context = actualClass.analyzeWithContent()
|
|
||||||
expectedClass.superTypeListEntries.zip(actualClass.superTypeListEntries).forEach { (expectedEntry, actualEntry) ->
|
|
||||||
if (expectedEntry !is KtSuperTypeCallEntry) return@forEach
|
|
||||||
val superType = context[BindingContext.TYPE, actualEntry.typeReference]
|
|
||||||
val superClassDescriptor = superType?.constructor?.declarationDescriptor as? ClassDescriptor ?: return@forEach
|
|
||||||
if (superClassDescriptor.kind == ClassKind.CLASS || superClassDescriptor.kind == ClassKind.ENUM_CLASS) {
|
|
||||||
expectedEntry.replace(createSuperTypeEntry(expectedEntry.typeReference!!.text))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (outerExpectedClasses.isEmpty()) {
|
|
||||||
expectedClass.addModifier(KtTokens.EXPECT_KEYWORD)
|
|
||||||
} else {
|
|
||||||
expectedClass.makeNotActual()
|
|
||||||
}
|
|
||||||
expectedClass.removeModifier(KtTokens.DATA_KEYWORD)
|
|
||||||
|
|
||||||
declLoop@ for (actualDeclaration in actualClass.declarations) {
|
|
||||||
val descriptor = actualDeclaration.toDescriptor() ?: continue
|
|
||||||
if (!actualDeclaration.isEffectivelyActual()) continue@declLoop
|
|
||||||
val expectedDeclaration: KtDeclaration = when (actualDeclaration) {
|
|
||||||
is KtClassOrObject -> {
|
|
||||||
generateClassOrObjectByActualClass(project, actualDeclaration, outerExpectedClasses + expectedClass)
|
|
||||||
}
|
|
||||||
is KtCallableDeclaration -> {
|
|
||||||
when (actualDeclaration) {
|
|
||||||
is KtFunction ->
|
|
||||||
generateFunction(project, actualDeclaration, descriptor as FunctionDescriptor, expectedClass, outerExpectedClasses)
|
|
||||||
is KtProperty ->
|
|
||||||
generateProperty(project, actualDeclaration, descriptor as PropertyDescriptor, expectedClass, outerExpectedClasses)
|
|
||||||
else -> continue@declLoop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> continue@declLoop
|
|
||||||
}
|
|
||||||
expectedClass.addDeclaration(expectedDeclaration)
|
|
||||||
}
|
|
||||||
if (!actualClass.isAnnotation()) {
|
|
||||||
for (actualProperty in actualClass.primaryConstructorParameters) {
|
|
||||||
if (!actualProperty.hasValOrVar() || !actualProperty.hasActualModifier()) continue
|
|
||||||
val descriptor = actualProperty.toDescriptor() as? PropertyDescriptor ?: continue
|
|
||||||
val expectedProperty =
|
|
||||||
generateProperty(project, actualProperty, descriptor, expectedClass, outerExpectedClasses)
|
|
||||||
expectedClass.addDeclaration(expectedProperty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val actualPrimaryConstructor = actualClass.primaryConstructor
|
|
||||||
if (expectedClass is KtClass && actualPrimaryConstructor != null) {
|
|
||||||
val descriptor = actualPrimaryConstructor.toDescriptor()
|
|
||||||
if (descriptor is FunctionDescriptor) {
|
|
||||||
val expectedPrimaryConstructor =
|
|
||||||
generateFunction(project, actualPrimaryConstructor, descriptor, expectedClass, outerExpectedClasses)
|
|
||||||
expectedClass.createPrimaryConstructorIfAbsent().replace(expectedPrimaryConstructor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return expectedClass
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateFunction(
|
|
||||||
project: Project,
|
|
||||||
actualFunction: KtFunction,
|
|
||||||
descriptor: FunctionDescriptor,
|
|
||||||
targetClass: KtClassOrObject?,
|
|
||||||
outerExpectedClasses: List<KtClassOrObject>
|
|
||||||
): KtFunction {
|
|
||||||
val accessibleClasses = outerExpectedClasses + listOfNotNull(targetClass)
|
|
||||||
descriptor.checkTypeParameterBoundsAccessibility(accessibleClasses)
|
|
||||||
descriptor.returnType?.checkAccessibility(accessibleClasses)
|
|
||||||
descriptor.valueParameters.forEach {
|
|
||||||
it.type.checkAccessibility(accessibleClasses)
|
|
||||||
}
|
|
||||||
val memberChooserObject = OverrideMemberChooserObject.create(
|
|
||||||
actualFunction, descriptor, descriptor,
|
|
||||||
OverrideMemberChooserObject.BodyType.NO_BODY
|
|
||||||
)
|
|
||||||
return if (targetClass != null) {
|
|
||||||
memberChooserObject.generateExpectMember(targetClass = targetClass, copyDoc = true)
|
|
||||||
} else {
|
|
||||||
memberChooserObject.generateTopLevelExpect(project = project, copyDoc = true)
|
|
||||||
} as KtFunction
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateProperty(
|
|
||||||
project: Project,
|
|
||||||
actualProperty: KtNamedDeclaration,
|
|
||||||
descriptor: PropertyDescriptor,
|
|
||||||
targetClass: KtClassOrObject?,
|
|
||||||
outerExpectedClasses: List<KtClassOrObject>
|
|
||||||
): KtProperty {
|
|
||||||
val accessibleClasses = outerExpectedClasses + listOfNotNull(targetClass)
|
|
||||||
descriptor.checkTypeParameterBoundsAccessibility(accessibleClasses)
|
|
||||||
descriptor.type.checkAccessibility(accessibleClasses)
|
|
||||||
val memberChooserObject = OverrideMemberChooserObject.create(
|
|
||||||
actualProperty, descriptor, descriptor,
|
|
||||||
OverrideMemberChooserObject.BodyType.NO_BODY
|
|
||||||
)
|
|
||||||
return if (targetClass != null) {
|
|
||||||
memberChooserObject.generateExpectMember(targetClass = targetClass, copyDoc = true)
|
|
||||||
} else {
|
|
||||||
memberChooserObject.generateTopLevelExpect(project = project, copyDoc = true)
|
|
||||||
} as KtProperty
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun CallableMemberDescriptor.checkTypeParameterBoundsAccessibility(accessibleClasses: List<KtClassOrObject>) {
|
|
||||||
typeParameters.forEach { typeParameter ->
|
|
||||||
typeParameter.upperBounds.forEach { upperBound ->
|
|
||||||
upperBound.checkAccessibility(accessibleClasses)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KotlinType.checkAccessibility(accessibleClasses: List<KtClassOrObject>) {
|
|
||||||
for (argument in arguments) {
|
|
||||||
if (argument.isStarProjection) continue
|
|
||||||
argument.type.checkAccessibility(accessibleClasses)
|
|
||||||
}
|
|
||||||
val classifierDescriptor = constructor.declarationDescriptor as? ClassifierDescriptorWithTypeParameters ?: return
|
|
||||||
val moduleDescriptor = classifierDescriptor.module
|
|
||||||
if (moduleDescriptor.getMultiTargetPlatform() == MultiTargetPlatform.Common) {
|
|
||||||
// Common classes are Ok
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val implementedDescriptors = moduleDescriptor.implementedDescriptors
|
|
||||||
if (implementedDescriptors.isEmpty()) {
|
|
||||||
// This happens now if we are not in sources, in this case yet we cannot answer question about accessibility
|
|
||||||
// Very rude check about JDK classes
|
|
||||||
if (!classifierDescriptor.fqNameSafe.toString().startsWith("java.")) return
|
|
||||||
}
|
|
||||||
if (implementedDescriptors.any { it.hasDeclarationOf(classifierDescriptor) }) {
|
|
||||||
// Platform classes with expected class are also Ok
|
|
||||||
return
|
|
||||||
}
|
|
||||||
accessibleClasses.forEach {
|
|
||||||
if (classifierDescriptor.name == it.nameAsName) return
|
|
||||||
}
|
|
||||||
if (this is AbbreviatedType) {
|
|
||||||
// For type aliases without expected class, check expansions instead
|
|
||||||
expandedType.checkAccessibility(accessibleClasses)
|
|
||||||
} else {
|
|
||||||
throw KotlinTypeInaccessibleException(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class KotlinTypeInaccessibleException(val type: KotlinType) : Exception() {
|
|
||||||
override val message: String
|
|
||||||
get() = "Type ${type.getJetTypeFqName(true)} is not accessible from common code"
|
|
||||||
}
|
|
||||||
@@ -6,13 +6,37 @@
|
|||||||
package org.jetbrains.kotlin.idea.quickfix.expectactual
|
package org.jetbrains.kotlin.idea.quickfix.expectactual
|
||||||
|
|
||||||
import com.intellij.openapi.module.Module
|
import com.intellij.openapi.module.Module
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.JavaDirectoryService
|
import com.intellij.psi.JavaDirectoryService
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.idea.caches.project.implementedDescriptors
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
|
||||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||||
import org.jetbrains.kotlin.idea.core.findOrCreateDirectoryForPackage
|
import org.jetbrains.kotlin.idea.core.findOrCreateDirectoryForPackage
|
||||||
import org.jetbrains.kotlin.idea.core.getPackage
|
import org.jetbrains.kotlin.idea.core.getPackage
|
||||||
|
import org.jetbrains.kotlin.idea.core.overrideImplement.*
|
||||||
|
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.BodyType.EMPTY_OR_TEMPLATE
|
||||||
|
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.BodyType.NO_BODY
|
||||||
|
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.Companion.create
|
||||||
|
import org.jetbrains.kotlin.idea.core.toDescriptor
|
||||||
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
|
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
|
||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
|
import org.jetbrains.kotlin.idea.util.hasDeclarationOf
|
||||||
|
import org.jetbrains.kotlin.idea.util.isEffectivelyActual
|
||||||
|
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.MultiTargetPlatform
|
||||||
|
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
|
||||||
|
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
|
import org.jetbrains.kotlin.resolve.getMultiTargetPlatform
|
||||||
|
import org.jetbrains.kotlin.types.AbbreviatedType
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
|
||||||
fun createFileForDeclaration(module: Module, declaration: KtNamedDeclaration): KtFile? {
|
fun createFileForDeclaration(module: Module, declaration: KtNamedDeclaration): KtFile? {
|
||||||
val fileName = declaration.name ?: return null
|
val fileName = declaration.name ?: return null
|
||||||
@@ -75,3 +99,219 @@ fun KtNamedDeclaration?.getTypeDescription(): String = when (this) {
|
|||||||
is KtFunction -> "function"
|
is KtFunction -> "function"
|
||||||
else -> "declaration"
|
else -> "declaration"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun KtPsiFactory.generateClassOrObject(
|
||||||
|
project: Project,
|
||||||
|
generateExpectClass: Boolean,
|
||||||
|
originalClass: KtClassOrObject,
|
||||||
|
outerClasses: List<KtClassOrObject> = emptyList(),
|
||||||
|
// If null, all class declarations are missed (so none from them exists)
|
||||||
|
missedDeclarations: List<KtDeclaration>? = null
|
||||||
|
): KtClassOrObject {
|
||||||
|
fun areCompatible(first: KtFunction, second: KtFunction) =
|
||||||
|
first.valueParameters.size == second.valueParameters.size &&
|
||||||
|
first.valueParameters.zip(second.valueParameters).all { (firstParam, secondParam) ->
|
||||||
|
firstParam.name == secondParam.name && firstParam.typeReference?.text == secondParam.typeReference?.text
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtDeclaration.exists() =
|
||||||
|
missedDeclarations != null && missedDeclarations.none {
|
||||||
|
name == it.name && when (this) {
|
||||||
|
is KtConstructor<*> -> it is KtConstructor<*> && areCompatible(this, it)
|
||||||
|
is KtNamedFunction -> it is KtNamedFunction && areCompatible(this, it)
|
||||||
|
is KtProperty -> it is KtProperty || it is KtParameter && it.hasValOrVar()
|
||||||
|
else -> this.javaClass == it.javaClass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val generatedClass = createClassHeaderCopyByText(originalClass)
|
||||||
|
val context = originalClass.analyzeWithContent()
|
||||||
|
|
||||||
|
generatedClass.superTypeListEntries.zip(originalClass.superTypeListEntries).forEach { (generatedEntry, originalEntry) ->
|
||||||
|
if (generateExpectClass) {
|
||||||
|
if (generatedEntry !is KtSuperTypeCallEntry) return@forEach
|
||||||
|
} else {
|
||||||
|
if (generatedEntry !is KtSuperTypeEntry) return@forEach
|
||||||
|
}
|
||||||
|
val superType = context[BindingContext.TYPE, originalEntry.typeReference]
|
||||||
|
val superClassDescriptor = superType?.constructor?.declarationDescriptor as? ClassDescriptor ?: return@forEach
|
||||||
|
if (superClassDescriptor.kind == ClassKind.CLASS || superClassDescriptor.kind == ClassKind.ENUM_CLASS) {
|
||||||
|
val newGeneratedEntry = if (generateExpectClass) {
|
||||||
|
createSuperTypeEntry(generatedEntry.typeReference!!.text)
|
||||||
|
} else {
|
||||||
|
createSuperTypeCallEntry(generatedEntry.typeReference!!.text + "()")
|
||||||
|
}
|
||||||
|
generatedEntry.replace(newGeneratedEntry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (generatedClass.isAnnotation()) {
|
||||||
|
generatedClass.annotationEntries.zip(originalClass.annotationEntries).forEach { (generatedEntry, originalEntry) ->
|
||||||
|
val annotationDescriptor = context.get(BindingContext.ANNOTATION, originalEntry) ?: return@forEach
|
||||||
|
if (annotationDescriptor.fqName in forbiddenAnnotationFqNames) {
|
||||||
|
generatedEntry.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (generateExpectClass) {
|
||||||
|
if (outerClasses.isEmpty()) {
|
||||||
|
generatedClass.addModifier(KtTokens.EXPECT_KEYWORD)
|
||||||
|
} else {
|
||||||
|
generatedClass.makeNotActual()
|
||||||
|
}
|
||||||
|
generatedClass.removeModifier(KtTokens.DATA_KEYWORD)
|
||||||
|
} else {
|
||||||
|
if (generatedClass !is KtEnumEntry) {
|
||||||
|
generatedClass.addModifier(KtTokens.ACTUAL_KEYWORD)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
declLoop@ for (originalDeclaration in originalClass.declarations.filter { !it.exists() }) {
|
||||||
|
val descriptor = originalDeclaration.toDescriptor() ?: continue
|
||||||
|
if (generateExpectClass && !originalDeclaration.isEffectivelyActual()) continue
|
||||||
|
val generatedDeclaration: KtDeclaration = when (originalDeclaration) {
|
||||||
|
is KtClassOrObject -> {
|
||||||
|
generateClassOrObject(project, generateExpectClass, originalDeclaration, outerClasses + generatedClass)
|
||||||
|
}
|
||||||
|
is KtCallableDeclaration -> {
|
||||||
|
when (originalDeclaration) {
|
||||||
|
is KtFunction -> generateFunction(
|
||||||
|
project, generateExpectClass, originalDeclaration, descriptor as FunctionDescriptor, generatedClass, outerClasses
|
||||||
|
)
|
||||||
|
is KtProperty -> generateProperty(
|
||||||
|
project, generateExpectClass, originalDeclaration, descriptor as PropertyDescriptor, generatedClass, outerClasses
|
||||||
|
)
|
||||||
|
else -> continue@declLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> continue@declLoop
|
||||||
|
}
|
||||||
|
generatedClass.addDeclaration(generatedDeclaration)
|
||||||
|
}
|
||||||
|
if (!originalClass.isAnnotation()) {
|
||||||
|
for (originalProperty in originalClass.primaryConstructorParameters) {
|
||||||
|
if (!originalProperty.hasValOrVar() || !originalProperty.hasActualModifier()) continue
|
||||||
|
val descriptor = originalProperty.toDescriptor() as? PropertyDescriptor ?: continue
|
||||||
|
val generatedProperty = generateProperty(
|
||||||
|
project, generateExpectClass, originalProperty, descriptor, generatedClass, outerClasses
|
||||||
|
)
|
||||||
|
generatedClass.addDeclaration(generatedProperty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val originalPrimaryConstructor = originalClass.primaryConstructor
|
||||||
|
if (generatedClass is KtClass && originalPrimaryConstructor != null && !originalPrimaryConstructor.exists()) {
|
||||||
|
val descriptor = originalPrimaryConstructor.toDescriptor()
|
||||||
|
if (descriptor is FunctionDescriptor) {
|
||||||
|
val expectedPrimaryConstructor = generateFunction(
|
||||||
|
project, generateExpectClass, originalPrimaryConstructor, descriptor, generatedClass, outerClasses
|
||||||
|
)
|
||||||
|
generatedClass.createPrimaryConstructorIfAbsent().replace(expectedPrimaryConstructor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return generatedClass
|
||||||
|
}
|
||||||
|
|
||||||
|
private val forbiddenAnnotationFqNames = setOf(
|
||||||
|
ExpectedActualDeclarationChecker.OPTIONAL_EXPECTATION_FQ_NAME,
|
||||||
|
FqName("kotlin.ExperimentalMultiplatform"),
|
||||||
|
ExperimentalUsageChecker.USE_EXPERIMENTAL_FQ_NAME
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun generateFunction(
|
||||||
|
project: Project,
|
||||||
|
generateExpect: Boolean,
|
||||||
|
originalFunction: KtFunction,
|
||||||
|
descriptor: FunctionDescriptor,
|
||||||
|
generatedClass: KtClassOrObject? = null,
|
||||||
|
outerClasses: List<KtClassOrObject> = emptyList()
|
||||||
|
): KtFunction {
|
||||||
|
if (generateExpect) {
|
||||||
|
val accessibleClasses = outerClasses + listOfNotNull(generatedClass)
|
||||||
|
descriptor.checkTypeParameterBoundsAccessibility(accessibleClasses)
|
||||||
|
descriptor.returnType?.checkAccessibility(accessibleClasses)
|
||||||
|
descriptor.valueParameters.forEach {
|
||||||
|
it.type.checkAccessibility(accessibleClasses)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val memberChooserObject = create(
|
||||||
|
originalFunction, descriptor, descriptor,
|
||||||
|
if (generateExpect || descriptor.modality == Modality.ABSTRACT) NO_BODY else EMPTY_OR_TEMPLATE
|
||||||
|
)
|
||||||
|
return memberChooserObject.generateMember(
|
||||||
|
targetClass = generatedClass,
|
||||||
|
copyDoc = true,
|
||||||
|
project = project,
|
||||||
|
forceActual = !generateExpect,
|
||||||
|
forceExpect = generateExpect
|
||||||
|
) as KtFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun generateProperty(
|
||||||
|
project: Project,
|
||||||
|
generateExpect: Boolean,
|
||||||
|
originalProperty: KtNamedDeclaration,
|
||||||
|
descriptor: PropertyDescriptor,
|
||||||
|
generatedClass: KtClassOrObject? = null,
|
||||||
|
outerClasses: List<KtClassOrObject> = emptyList()
|
||||||
|
): KtProperty {
|
||||||
|
if (generateExpect) {
|
||||||
|
val accessibleClasses = outerClasses + listOfNotNull(generatedClass)
|
||||||
|
descriptor.checkTypeParameterBoundsAccessibility(accessibleClasses)
|
||||||
|
descriptor.type.checkAccessibility(accessibleClasses)
|
||||||
|
}
|
||||||
|
val memberChooserObject = create(
|
||||||
|
originalProperty, descriptor, descriptor,
|
||||||
|
if (generateExpect || descriptor.modality == Modality.ABSTRACT) NO_BODY else EMPTY_OR_TEMPLATE
|
||||||
|
)
|
||||||
|
return memberChooserObject.generateMember(
|
||||||
|
targetClass = generatedClass,
|
||||||
|
copyDoc = true,
|
||||||
|
project = project,
|
||||||
|
forceActual = !generateExpect,
|
||||||
|
forceExpect = generateExpect
|
||||||
|
) as KtProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CallableMemberDescriptor.checkTypeParameterBoundsAccessibility(accessibleClasses: List<KtClassOrObject>) {
|
||||||
|
typeParameters.forEach { typeParameter ->
|
||||||
|
typeParameter.upperBounds.forEach { upperBound ->
|
||||||
|
upperBound.checkAccessibility(accessibleClasses)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KotlinType.checkAccessibility(accessibleClasses: List<KtClassOrObject>) {
|
||||||
|
for (argument in arguments) {
|
||||||
|
if (argument.isStarProjection) continue
|
||||||
|
argument.type.checkAccessibility(accessibleClasses)
|
||||||
|
}
|
||||||
|
val classifierDescriptor = constructor.declarationDescriptor as? ClassifierDescriptorWithTypeParameters ?: return
|
||||||
|
val moduleDescriptor = classifierDescriptor.module
|
||||||
|
if (moduleDescriptor.getMultiTargetPlatform() == MultiTargetPlatform.Common) {
|
||||||
|
// Common classes are Ok
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val implementedDescriptors = moduleDescriptor.implementedDescriptors
|
||||||
|
if (implementedDescriptors.isEmpty()) {
|
||||||
|
// This happens now if we are not in sources, in this case yet we cannot answer question about accessibility
|
||||||
|
// Very rude check about JDK classes
|
||||||
|
if (!classifierDescriptor.fqNameSafe.toString().startsWith("java.")) return
|
||||||
|
}
|
||||||
|
if (implementedDescriptors.any { it.hasDeclarationOf(classifierDescriptor) }) {
|
||||||
|
// Platform classes with expected class are also Ok
|
||||||
|
return
|
||||||
|
}
|
||||||
|
accessibleClasses.forEach {
|
||||||
|
if (classifierDescriptor.name == it.nameAsName) return
|
||||||
|
}
|
||||||
|
if (this is AbbreviatedType) {
|
||||||
|
// For type aliases without expected class, check expansions instead
|
||||||
|
expandedType.checkAccessibility(accessibleClasses)
|
||||||
|
} else {
|
||||||
|
throw KotlinTypeInaccessibleException(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class KotlinTypeInaccessibleException(val type: KotlinType) : Exception() {
|
||||||
|
override val message: String
|
||||||
|
get() = "Type ${type.getJetTypeFqName(true)} is not accessible from common code"
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user