From 42ef9d634a2b16ba7af5704f53df0988998e14cf Mon Sep 17 00:00:00 2001 From: Dmitry Gridin Date: Thu, 22 Aug 2019 14:38:53 +0700 Subject: [PATCH] ExpectActualUtils: merge generateFunction with generateProperty to generateCallable & remove old checker --- .../quickfix/expectactual/AddActualFix.kt | 12 +- .../quickfix/expectactual/CreateActualFix.kt | 29 ++- .../expectactual/CreateExpectedFix.kt | 45 ++--- .../expectactual/ExpectActualUtils.kt | 165 +++--------------- 4 files changed, 48 insertions(+), 203 deletions(-) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/AddActualFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/AddActualFix.kt index db5bd3c2ee1..6ea8430861a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/AddActualFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/AddActualFix.kt @@ -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()?.let { - generateFunction(project, false, missedDeclaration, it, element, checker = checker) - } - is KtProperty -> missedDeclaration.toDescriptor()?.safeAs()?.let { - generateProperty(project, false, missedDeclaration, it, element, checker = checker) + is KtClassOrObject -> factory.generateClassOrObject(project, false, missedDeclaration, checker) + is KtFunction, is KtProperty -> missedDeclaration.toDescriptor()?.safeAs()?.let { + generateCallable(project, false, missedDeclaration, it, element, checker = checker) } else -> null } ?: continue diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateActualFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateActualFix.kt index 3c9659780f7..c86db87a9da 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateActualFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateActualFix.kt @@ -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( 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(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(function, actualModule, actualPlatform, { project, checker, element -> - val descriptor = element.toDescriptor() as? FunctionDescriptor - descriptor?.let { generateFunction(project, false, element, descriptor, checker = checker) } +) : CreateActualFix(declaration, actualModule, actualPlatform, { project, checker, element -> + val descriptor = element.toDescriptor() as? CallableMemberDescriptor + descriptor?.let { generateCallable(project, false, element, descriptor, checker = checker) } }) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateExpectedFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateExpectedFix.kt index 6205aedfa60..6e67333385b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateExpectedFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateExpectedFix.kt @@ -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( ?: 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): HashSet { @@ -294,42 +292,19 @@ private fun KtNamedDeclaration.selected(): Sequence { return sequenceOf(this) + additionalSequence + containingClassOrObject?.selected().orEmpty() } -class CreateExpectedPropertyFix( - property: KtNamedDeclaration, +class CreateExpectedCallableMemberFix( + declaration: KtCallableDeclaration, targetExpectedClass: KtClassOrObject?, commonModule: Module -) : CreateExpectedFix(property, targetExpectedClass, commonModule, block@{ project, checker, element -> +) : CreateExpectedFix(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(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, diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/ExpectActualUtils.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/ExpectActualUtils.kt index b58dd9003bf..10b4c96a578 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/ExpectActualUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/ExpectActualUtils.kt @@ -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 = 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().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 = 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 = 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 = 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) { - typeParameters.forEach { typeParameter -> - typeParameter.upperBounds.forEach { upperBound -> - upperBound.checkAccessibility(accessibleClasses) - } - } -} - -private fun KotlinType.checkAccessibility(accessibleClasses: List) { - 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) : Exception() { + override val message: String = + "${StringUtil.pluralize("Type", fqNames.size)} ${fqNames.joinToString()} is not accessible from common code" } fun KtNamedDeclaration.isAlwaysActual(): Boolean = safeAs()?.parent?.parent?.safeAs()