ExpectActualUtils: merge generateFunction with generateProperty to generateCallable & remove old checker

This commit is contained in:
Dmitry Gridin
2019-08-22 14:38:53 +07:00
parent 90b82c96eb
commit 42ef9d634a
4 changed files with 48 additions and 203 deletions
@@ -21,8 +21,7 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.codeStyle.CodeStyleManager
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.Errors
@@ -60,12 +59,9 @@ class AddActualFix(
val checker = TypeAccessibilityChecker.create(project, module)
for (missedDeclaration in missedDeclarationPointers.mapNotNull { it.element }) {
val actualDeclaration = when (missedDeclaration) {
is KtClassOrObject -> factory.generateClassOrObject(project, false, missedDeclaration, checker, listOf(element))
is KtFunction -> missedDeclaration.toDescriptor()?.safeAs<FunctionDescriptor>()?.let {
generateFunction(project, false, missedDeclaration, it, element, checker = checker)
}
is KtProperty -> missedDeclaration.toDescriptor()?.safeAs<PropertyDescriptor>()?.let {
generateProperty(project, false, missedDeclaration, it, element, checker = checker)
is KtClassOrObject -> factory.generateClassOrObject(project, false, missedDeclaration, checker)
is KtFunction, is KtProperty -> missedDeclaration.toDescriptor()?.safeAs<CallableMemberDescriptor>()?.let {
generateCallable(project, false, missedDeclaration, it, element, checker = checker)
}
else -> null
} ?: continue
@@ -21,8 +21,7 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.Errors
@@ -74,8 +73,11 @@ sealed class CreateActualFix<D : KtNamedDeclaration>(
val actualPlatform = actualModuleDescriptor.platform ?: return null
return when (declaration) {
is KtClassOrObject -> CreateActualClassFix(declaration, actualModule, actualPlatform)
is KtFunction -> CreateActualFunctionFix(declaration, actualModule, actualPlatform)
is KtProperty -> CreateActualPropertyFix(declaration, actualModule, actualPlatform)
is KtFunction, is KtProperty -> CreateActualCallableMemberFix(
declaration as KtCallableDeclaration,
actualModule,
actualPlatform
)
else -> null
}
}
@@ -90,21 +92,12 @@ class CreateActualClassFix(
generateClassOrObject(project, false, element, checker = checker)
})
class CreateActualPropertyFix(
property: KtProperty,
class CreateActualCallableMemberFix(
declaration: KtCallableDeclaration,
actualModule: Module,
actualPlatform: TargetPlatform
) : CreateActualFix<KtProperty>(property, actualModule, actualPlatform, { project, checker, element ->
val descriptor = element.toDescriptor() as? PropertyDescriptor
descriptor?.let { generateProperty(project, false, element, descriptor, checker = checker) }
})
class CreateActualFunctionFix(
function: KtFunction,
actualModule: Module,
actualPlatform: TargetPlatform
) : CreateActualFix<KtFunction>(function, actualModule, actualPlatform, { project, checker, element ->
val descriptor = element.toDescriptor() as? FunctionDescriptor
descriptor?.let { generateFunction(project, false, element, descriptor, checker = checker) }
) : CreateActualFix<KtCallableDeclaration>(declaration, actualModule, actualPlatform, { project, checker, element ->
val descriptor = element.toDescriptor() as? CallableMemberDescriptor
descriptor?.let { generateCallable(project, false, element, descriptor, checker = checker) }
})
@@ -13,10 +13,9 @@ import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.showOkNoDialog
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.Errors
@@ -90,10 +89,9 @@ sealed class CreateExpectedFix<D : KtNamedDeclaration>(
?: return emptyList()
return when (actualDeclaration) {
is KtClassOrObject -> expectedModules.map { CreateExpectedClassFix(actualDeclaration, expectedContainingClass, it) }
is KtFunction -> expectedModules.map { CreateExpectedFunctionFix(actualDeclaration, expectedContainingClass, it) }
is KtProperty, is KtParameter -> expectedModules.map {
CreateExpectedPropertyFix(
actualDeclaration,
is KtProperty, is KtParameter, is KtFunction -> expectedModules.map {
CreateExpectedCallableMemberFix(
actualDeclaration as KtCallableDeclaration,
expectedContainingClass,
it
)
@@ -163,7 +161,7 @@ class CreateExpectedClassFix(
repairActualModifiers(originalElements + klass, resultDeclarations.toSet())
}
generateClassOrObject(project, true, element, checker, listOfNotNull(outerExpectedClass))
generateClassOrObject(project, true, element, checker)
})
private tailrec fun TypeAccessibilityChecker.findAndApplyExistingClasses(elements: Collection<KtNamedDeclaration>): HashSet<String> {
@@ -294,42 +292,19 @@ private fun KtNamedDeclaration.selected(): Sequence<KtNamedDeclaration> {
return sequenceOf(this) + additionalSequence + containingClassOrObject?.selected().orEmpty()
}
class CreateExpectedPropertyFix(
property: KtNamedDeclaration,
class CreateExpectedCallableMemberFix(
declaration: KtCallableDeclaration,
targetExpectedClass: KtClassOrObject?,
commonModule: Module
) : CreateExpectedFix<KtNamedDeclaration>(property, targetExpectedClass, commonModule, block@{ project, checker, element ->
) : CreateExpectedFix<KtNamedDeclaration>(declaration, targetExpectedClass, commonModule, block@{ project, checker, element ->
if (!checker.checkAccessibility(element)) {
showUnknownTypesError(element)
return@block null
}
val descriptor = element.toDescriptor() as? PropertyDescriptor
val descriptor = element.toDescriptor() as? CallableMemberDescriptor
checker.existingFqNames = targetExpectedClass?.getSuperNames()?.toSet().orEmpty()
descriptor?.let {
generateProperty(
project,
true,
element,
descriptor,
targetExpectedClass,
checker = checker
)
}
})
class CreateExpectedFunctionFix(
function: KtFunction,
targetExpectedClass: KtClassOrObject?,
commonModule: Module
) : CreateExpectedFix<KtFunction>(function, targetExpectedClass, commonModule, block@{ project, checker, element ->
if (!checker.checkAccessibility(element)) {
showUnknownTypesError(element)
return@block null
}
val descriptor = element.toDescriptor() as? FunctionDescriptor
checker.existingFqNames = targetExpectedClass?.getSuperNames()?.toSet().orEmpty()
descriptor?.let {
generateFunction(
generateCallable(
project,
true,
element,
@@ -7,10 +7,10 @@ package org.jetbrains.kotlin.idea.quickfix.expectactual
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.JavaDirectoryService
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.idea.caches.project.implementedDescriptors
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToBeShortenedDescendantsToWaitingSet
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
@@ -23,25 +23,21 @@ import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObj
import org.jetbrains.kotlin.idea.core.overrideImplement.generateMember
import org.jetbrains.kotlin.idea.core.overrideImplement.makeNotActual
import org.jetbrains.kotlin.idea.core.toDescriptor
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
import org.jetbrains.kotlin.idea.util.hasInlineModifier
import org.jetbrains.kotlin.idea.util.isEffectivelyActual
import org.jetbrains.kotlin.idea.util.mustHaveValOrVar
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
import org.jetbrains.kotlin.types.AbbreviatedType
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -111,8 +107,7 @@ internal fun KtPsiFactory.generateClassOrObject(
project: Project,
generateExpectClass: Boolean,
originalClass: KtClassOrObject,
checker: TypeAccessibilityChecker,
outerClasses: List<KtClassOrObject> = emptyList()
checker: TypeAccessibilityChecker
): KtClassOrObject {
val generatedClass = createClassHeaderCopyByText(originalClass)
val context = originalClass.analyzeWithContent()
@@ -132,7 +127,7 @@ internal fun KtPsiFactory.generateClassOrObject(
}
if (generateExpectClass) {
if (outerClasses.isEmpty()) {
if (originalClass.isTopLevel()) {
generatedClass.addModifier(KtTokens.EXPECT_KEYWORD)
} else {
generatedClass.makeNotActual()
@@ -144,10 +139,6 @@ internal fun KtPsiFactory.generateClassOrObject(
}
}
val existingClasses = originalClass.declarations.asSequence().filterIsInstance<KtClassOrObject>().filter {
it.isEffectivelyActual(false)
}.toList() + generatedClass + outerClasses
val existingFqNamesWithSuperTypes = (checker.existingFqNames + superNames).toSet()
declLoop@ for (originalDeclaration in originalClass.declarations) {
val descriptor = originalDeclaration.toDescriptor() ?: continue
@@ -157,17 +148,15 @@ internal fun KtPsiFactory.generateClassOrObject(
project,
generateExpectClass,
originalDeclaration,
checker,
existingClasses
checker
)
is KtFunction, is KtProperty -> checker.runInContext(existingFqNamesWithSuperTypes) {
generateCallable(
project,
generateExpectClass,
originalDeclaration as KtCallableDeclaration,
descriptor as CallableDescriptor,
descriptor as CallableMemberDescriptor,
generatedClass,
existingClasses,
this
)
}
@@ -180,13 +169,12 @@ internal fun KtPsiFactory.generateClassOrObject(
if (!originalProperty.hasValOrVar() || !originalProperty.hasActualModifier()) continue
val descriptor = originalProperty.toDescriptor() as? PropertyDescriptor ?: continue
checker.runInContext(existingFqNamesWithSuperTypes) {
val generatedProperty = generateProperty(
val generatedProperty = generateCallable(
project,
generateExpectClass,
originalProperty,
descriptor,
generatedClass,
outerClasses,
this
)
generatedClass.addDeclaration(generatedProperty)
@@ -202,13 +190,12 @@ internal fun KtPsiFactory.generateClassOrObject(
val descriptor = originalPrimaryConstructor.toDescriptor()
if (descriptor is FunctionDescriptor) {
checker.runInContext(existingFqNamesWithSuperTypes) {
val expectedPrimaryConstructor = generateFunction(
val expectedPrimaryConstructor = generateCallable(
project,
generateExpectClass,
originalPrimaryConstructor,
descriptor,
generatedClass,
outerClasses,
this
)
generatedClass.createPrimaryConstructorIfAbsent().replace(expectedPrimaryConstructor)
@@ -266,52 +253,14 @@ private val forbiddenAnnotationFqNames = setOf(
internal fun generateCallable(
project: Project,
generateExpect: Boolean,
originalDeclaration: KtCallableDeclaration,
descriptor: CallableDescriptor,
originalDeclaration: KtDeclaration,
descriptor: CallableMemberDescriptor,
generatedClass: KtClassOrObject? = null,
outerClasses: List<KtClassOrObject> = emptyList(),
checker: TypeAccessibilityChecker
) = when (originalDeclaration) {
is KtFunction -> generateFunction(
project,
generateExpect,
originalDeclaration,
descriptor as FunctionDescriptor,
generatedClass,
outerClasses,
checker
)
is KtProperty -> generateProperty(
project,
generateExpect,
originalDeclaration,
descriptor as PropertyDescriptor,
generatedClass,
outerClasses,
checker
)
else -> error("Unsupported operation for $originalDeclaration")
}
internal fun generateFunction(
project: Project,
generateExpect: Boolean,
originalFunction: KtFunction,
descriptor: FunctionDescriptor,
generatedClass: KtClassOrObject? = null,
outerClasses: List<KtClassOrObject> = emptyList(),
checker: TypeAccessibilityChecker
): KtFunction {
if (generateExpect) {
val accessibleClasses = outerClasses + listOfNotNull(generatedClass)
descriptor.checkTypeParameterBoundsAccessibility(accessibleClasses)
descriptor.returnType?.checkAccessibility(accessibleClasses)
descriptor.valueParameters.forEach {
it.type.checkAccessibility(accessibleClasses)
}
}
): KtCallableDeclaration {
if (generateExpect) descriptor.checkAccessibility(checker)
val memberChooserObject = create(
originalFunction, descriptor, descriptor,
originalDeclaration, descriptor, descriptor,
if (generateExpect || descriptor.modality == Modality.ABSTRACT) NO_BODY else EMPTY_OR_TEMPLATE
)
return memberChooserObject.generateMember(
@@ -321,35 +270,12 @@ internal fun generateFunction(
mode = if (generateExpect) MemberGenerateMode.EXPECT else MemberGenerateMode.ACTUAL
).apply {
repair(generatedClass, descriptor, checker)
} as KtFunction
}
}
internal fun generateProperty(
project: Project,
generateExpect: Boolean,
originalProperty: KtNamedDeclaration,
descriptor: PropertyDescriptor,
generatedClass: KtClassOrObject? = null,
outerClasses: List<KtClassOrObject> = emptyList(),
checker: TypeAccessibilityChecker
): 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,
mode = if (generateExpect) MemberGenerateMode.EXPECT else MemberGenerateMode.ACTUAL
).apply {
repair(generatedClass, descriptor, checker)
} as KtProperty
private fun CallableMemberDescriptor.checkAccessibility(checker: TypeAccessibilityChecker) {
val errors = checker.incorrectTypes(this).ifEmpty { return }
throw KotlinTypeInaccessibleException(errors.toSet())
}
private fun KtCallableDeclaration.repair(
@@ -383,54 +309,9 @@ private fun AnnotationDescriptor.isValidInModule(checker: TypeAccessibilityCheck
return fqName !in forbiddenAnnotationFqNames && checker.checkAccessibility(type)
}
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.platform.isCommon()) {
// Common classes are Ok; unfortunately this check does not work correctly for simple (non-expect) classes from common module
return
}
val declaration = DescriptorToSourceUtils.descriptorToDeclaration(classifierDescriptor)
if (declaration?.module?.platform?.isCommon() == true) {
// Common classes are Ok again
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"
class KotlinTypeInaccessibleException(fqNames: Collection<FqName?>) : Exception() {
override val message: String =
"${StringUtil.pluralize("Type", fqNames.size)} ${fqNames.joinToString()} is not accessible from common code"
}
fun KtNamedDeclaration.isAlwaysActual(): Boolean = safeAs<KtParameter>()?.parent?.parent?.safeAs<KtPrimaryConstructor>()