diff --git a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRenderer.kt b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRenderer.kt index d11797e9385..8bd592701d7 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRenderer.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRenderer.kt @@ -188,7 +188,8 @@ interface DescriptorRendererOptions { var renderDefaultVisibility: Boolean var renderDefaultModality: Boolean var renderConstructorDelegation: Boolean - var renderActualAnnotationPropertiesInPrimaryConstructor: Boolean + var renderAnnotationPropertiesInPrimaryConstructor: Boolean + var actualPropertiesInPrimaryConstructor: Boolean var uninferredTypeParameterAsName: Boolean var overrideRenderingPolicy: OverrideRenderingPolicy var valueParametersHandler: DescriptorRenderer.ValueParametersHandler diff --git a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt index 970d35d0043..4e72a071651 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt @@ -810,8 +810,8 @@ internal class DescriptorRendererImpl( renderModifier(builder, valueParameter.isNoinline, "noinline") val containingDeclaration = valueParameter.containingDeclaration - if (renderActualAnnotationPropertiesInPrimaryConstructor && containingDeclaration.isAnnotationConstructor()) { - renderModifier(builder, true, "actual") + if (renderAnnotationPropertiesInPrimaryConstructor && containingDeclaration.isAnnotationConstructor()) { + renderModifier(builder, actualPropertiesInPrimaryConstructor, "actual") renderModifier(builder, true, "val") } diff --git a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererOptionsImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererOptionsImpl.kt index dbe8a7b4c03..c0e049f3cb7 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererOptionsImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererOptionsImpl.kt @@ -82,7 +82,8 @@ internal class DescriptorRendererOptionsImpl : DescriptorRendererOptions { override var renderDefaultVisibility by property(true) override var renderDefaultModality by property(true) override var renderConstructorDelegation by property(false) - override var renderActualAnnotationPropertiesInPrimaryConstructor by property(false) + override var renderAnnotationPropertiesInPrimaryConstructor by property(false) + override var actualPropertiesInPrimaryConstructor: Boolean by property(false) override var uninferredTypeParameterAsName by property(false) override var includePropertyConstant by property(false) override var withoutTypeParameters by property(false) diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt index ef903ab5c59..c46a3fc02a5 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt @@ -120,33 +120,48 @@ interface OverrideMemberChooserObject : ClassMember { fun OverrideMemberChooserObject.generateTopLevelActual( copyDoc: Boolean, project: Project -) = generateMember(null, copyDoc, project, forceActual = true) +) = generateMember(null, copyDoc, project, forceActual = true, forceExpect = false) fun OverrideMemberChooserObject.generateActualMember( targetClass: KtClassOrObject, copyDoc: Boolean -) = generateMember(targetClass, copyDoc, targetClass.project, forceActual = true) +) = generateMember(targetClass, copyDoc, targetClass.project, forceActual = true, forceExpect = false) + +fun OverrideMemberChooserObject.generateTopLevelExpect( + copyDoc: Boolean, + project: Project +) = 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( targetClass: KtClassOrObject, copyDoc: Boolean -) = generateMember(targetClass, copyDoc, targetClass.project, forceActual = false) +) = generateMember(targetClass, copyDoc, targetClass.project, forceActual = false, forceExpect = false) private fun OverrideMemberChooserObject.generateMember( targetClass: KtClassOrObject?, copyDoc: Boolean, project: Project, - forceActual: Boolean + forceActual: Boolean, + forceExpect: Boolean ): KtCallableDeclaration { val descriptor = immediateSuper val bodyType = when { targetClass?.hasExpectModifier() == true -> NO_BODY - descriptor.extensionReceiverParameter != null && !forceActual -> FROM_TEMPLATE + descriptor.extensionReceiverParameter != null && !forceActual && !forceExpect -> FROM_TEMPLATE else -> bodyType } - val renderer = if (forceActual) ACTUAL_RENDERER else OVERRIDE_RENDERER + val renderer = when { + forceActual -> ACTUAL_RENDERER + forceExpect -> EXPECT_RENDERER + else -> OVERRIDE_RENDERER + } if (preferConstructorParameter && descriptor is PropertyDescriptor) { return generateConstructorParameter(project, descriptor, renderer) @@ -158,21 +173,27 @@ private fun OverrideMemberChooserObject.generateMember( else -> error("Unknown member to override: $descriptor") } - if (forceActual) { - newMember.addModifier(KtTokens.ACTUAL_KEYWORD) - } else if (targetClass?.hasActualModifier() == true) { - val expectClassDescriptors = - targetClass.resolveToDescriptorIfAny()?.expectedDescriptors()?.filterIsInstance().orEmpty() - if (expectClassDescriptors.any { expectClassDescriptor -> - val expectMemberDescriptor = expectClassDescriptor.findCallableMemberBySignature(immediateSuper) - expectMemberDescriptor?.isExpect == true && expectMemberDescriptor.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE - } - ) { - newMember.addModifier(KtTokens.ACTUAL_KEYWORD) + when { + forceActual -> newMember.addModifier(KtTokens.ACTUAL_KEYWORD) + forceExpect -> if (targetClass == null) { + newMember.addModifier(KtTokens.EXPECT_KEYWORD) + } else { + newMember.makeNotActual() + } + targetClass?.hasActualModifier() == true -> { + val expectClassDescriptors = + targetClass.resolveToDescriptorIfAny()?.expectedDescriptors()?.filterIsInstance().orEmpty() + if (expectClassDescriptors.any { expectClassDescriptor -> + val expectMemberDescriptor = expectClassDescriptor.findCallableMemberBySignature(immediateSuper) + expectMemberDescriptor?.isExpect == true && expectMemberDescriptor.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE + } + ) { + newMember.addModifier(KtTokens.ACTUAL_KEYWORD) + } + } + else -> { + newMember.makeNotActual() } - } else { - newMember.removeModifier(KtTokens.IMPL_KEYWORD) - newMember.removeModifier(KtTokens.ACTUAL_KEYWORD) } if (copyDoc) { @@ -210,14 +231,18 @@ private val OVERRIDE_RENDERER = DescriptorRenderer.withOptions { presentableUnresolvedTypes = true } -private val ACTUAL_RENDERER = OVERRIDE_RENDERER.withOptions { +private val EXPECT_RENDERER = OVERRIDE_RENDERER.withOptions { modifiers = DescriptorRendererModifier.ALL renderConstructorKeyword = true secondaryConstructorsAsPrimary = false renderDefaultVisibility = false renderDefaultModality = false renderConstructorDelegation = true - renderActualAnnotationPropertiesInPrimaryConstructor = true + renderAnnotationPropertiesInPrimaryConstructor = true +} + +private val ACTUAL_RENDERER = EXPECT_RENDERER.withOptions { + actualPropertiesInPrimaryConstructor = true } private fun PropertyDescriptor.wrap(forceOverride: Boolean): PropertyDescriptor { @@ -237,7 +262,7 @@ private fun FunctionDescriptor.wrap(forceOverride: Boolean, forceAbstract: Boole override fun isExpect() = false override fun getModality() = if (forceAbstract) Modality.ABSTRACT else if (forceOverride) Modality.OPEN else this@wrap.modality override fun getReturnType() = this@wrap.returnType?.approximateFlexibleTypes(preferNotNull = true, preferStarForRaw = true) - override fun getOverriddenDescriptors() = if (forceOverride) listOf(this@wrap) else emptyList() + override fun getOverriddenDescriptors() = if (forceOverride) listOf(this@wrap) else this@wrap.overriddenDescriptors override fun accept(visitor: DeclarationDescriptorVisitor, data: D) = visitor.visitFunctionDescriptor(this, data) } @@ -260,8 +285,7 @@ private fun generateProperty( renderer: DescriptorRenderer, bodyType: OverrideMemberChooserObject.BodyType ): KtProperty { - val actualRendererUsed = renderer === ACTUAL_RENDERER - val newDescriptor = descriptor.wrap(forceOverride = !actualRendererUsed) + val newDescriptor = descriptor.wrap(forceOverride = renderer === OVERRIDE_RENDERER) val returnType = descriptor.returnType val returnsNotUnit = returnType != null && !KotlinBuiltIns.isUnit(returnType) @@ -281,8 +305,7 @@ private fun generateProperty( } private fun generateConstructorParameter(project: Project, descriptor: PropertyDescriptor, renderer: DescriptorRenderer): KtParameter { - val actualRendererUsed = renderer === ACTUAL_RENDERER - val newDescriptor = descriptor.wrap(forceOverride = !actualRendererUsed) + val newDescriptor = descriptor.wrap(forceOverride = renderer === OVERRIDE_RENDERER) newDescriptor.setSingleOverridden(descriptor) return KtPsiFactory(project).createParameter(renderer.render(newDescriptor)) } @@ -293,8 +316,9 @@ private fun generateFunction( renderer: DescriptorRenderer, bodyType: OverrideMemberChooserObject.BodyType ): KtFunction { - val actualRendererUsed = renderer === ACTUAL_RENDERER - val newDescriptor = descriptor.wrap(forceAbstract = actualRendererUsed && bodyType == NO_BODY, forceOverride = !actualRendererUsed) + val newDescriptor = descriptor.wrap( + forceAbstract = renderer === ACTUAL_RENDERER && bodyType == NO_BODY, forceOverride = renderer === OVERRIDE_RENDERER + ) val returnType = descriptor.returnType val returnsNotUnit = returnType != null && !KotlinBuiltIns.isUnit(returnType) @@ -363,4 +387,7 @@ fun generateUnsupportedOrSuperCall( } } - +fun KtNamedDeclaration.makeNotActual() { + removeModifier(KtTokens.ACTUAL_KEYWORD) + removeModifier(KtTokens.IMPL_KEYWORD) +} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 26711fcced4..c87c097f8af 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -38,6 +38,7 @@ import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateP import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterByRefActionFactory import org.jetbrains.kotlin.idea.quickfix.expectactual.AddActualFix import org.jetbrains.kotlin.idea.quickfix.expectactual.CreateActualFix +import org.jetbrains.kotlin.idea.quickfix.expectactual.CreateExpectedFix import org.jetbrains.kotlin.idea.quickfix.migration.MigrateExternalExtensionFix import org.jetbrains.kotlin.idea.quickfix.migration.MigrateTypeParameterListFix import org.jetbrains.kotlin.idea.quickfix.replaceWith.DeprecatedSymbolUsageFix @@ -510,6 +511,7 @@ class QuickFixRegistrar : QuickFixContributor { OVERLOADS_WITHOUT_DEFAULT_ARGUMENTS.registerFactory(RemoveAnnotationFix.JvmOverloads) ACTUAL_WITHOUT_EXPECT.registerFactory(RemoveModifierFix.createRemoveModifierFromListOwnerFactory(ACTUAL_KEYWORD)) + ACTUAL_WITHOUT_EXPECT.registerFactory(CreateExpectedFix) NO_ACTUAL_FOR_EXPECT.registerFactory(CreateActualFix) NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS.registerFactory(AddActualFix) 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 59567e7bc04..505c5f77447 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateActualFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateActualFix.kt @@ -22,7 +22,6 @@ import com.intellij.openapi.editor.Editor import com.intellij.openapi.module.Module import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.Project -import com.intellij.psi.JavaDirectoryService import com.intellij.psi.codeStyle.CodeStyleManager import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.descriptors.* @@ -31,18 +30,17 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent -import org.jetbrains.kotlin.idea.core.* +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.quickfix.KotlinQuickFixAction import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory -import org.jetbrains.kotlin.idea.refactoring.createKotlinFile import org.jetbrains.kotlin.idea.util.actualsForExpected import org.jetbrains.kotlin.idea.util.application.executeWriteCommand -import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* @@ -110,37 +108,7 @@ sealed class CreateActualFix( return actualDeclaration.containingKtFile } } - val name = declaration.name ?: return null - - val expectedDir = declaration.containingFile.containingDirectory - val expectedPackage = JavaDirectoryService.getInstance().getPackage(expectedDir) - - val actualDirectory = findOrCreateDirectoryForPackage( - actualModule, expectedPackage?.qualifiedName ?: "" - ) ?: return null - return runWriteAction { - val fileName = "$name.kt" - val existingFile = actualDirectory.findFile(fileName) - val packageDirective = declaration.containingKtFile.packageDirective - val packageName = - if (packageDirective?.packageNameExpression == null) actualDirectory.getPackage()?.qualifiedName - else packageDirective.fqName.asString() - if (existingFile is KtFile) { - val existingPackageDirective = existingFile.packageDirective - if (existingFile.declarations.isNotEmpty() && - existingPackageDirective?.fqName != packageDirective?.fqName - ) { - val newName = KotlinNameSuggester.suggestNameByName(name) { - actualDirectory.findFile("$it.kt") == null - } + ".kt" - createKotlinFile(newName, actualDirectory, packageName) - } else { - existingFile - } - } else { - createKotlinFile(fileName, actualDirectory, packageName) - } - } + return createFileForDeclaration(actualModule, declaration) } companion object : KotlinSingleIntentionActionFactory() { @@ -171,19 +139,7 @@ class CreateActualClassFix( generateClassOrObjectByExpectedClass(project, element, actualNeeded = true) }) { - override val elementType = run { - val element = element - when (element) { - is KtObjectDeclaration -> "object" - is KtClass -> when { - element.isInterface() -> "interface" - element.isEnum() -> "enum class" - element.isAnnotation() -> "annotation class" - else -> "class" - } - else -> "class" - } - } + override val elementType = element.getTypeDescription() } class CreateActualPropertyFix( @@ -242,16 +198,7 @@ internal fun KtPsiFactory.generateClassOrObjectByExpectedClass( } } - val expectedText = expectedClass.text - val actualClass = if (expectedClass is KtObjectDeclaration) { - if (expectedClass.isCompanion()) { - createCompanionObject(expectedText) - } else { - createObject(expectedText) - } - } else { - createClass(expectedText) - } + val actualClass = createClassCopyByText(expectedClass) actualClass.declarations.forEach { if (it.exists()) { it.delete() diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateExpectedFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateExpectedFix.kt new file mode 100644 index 00000000000..1b8d5476002 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateExpectedFix.kt @@ -0,0 +1,260 @@ +/* + * Copyright 2010-2018 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.idea.quickfix.expectactual + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.ide.util.EditorHelper +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.module.Module +import com.intellij.openapi.project.DumbService +import com.intellij.openapi.project.Project +import com.intellij.psi.codeStyle.CodeStyleManager +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.DiagnosticFactory +import org.jetbrains.kotlin.diagnostics.Errors +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.overrideImplement.* +import org.jetbrains.kotlin.idea.core.toDescriptor +import org.jetbrains.kotlin.idea.quickfix.KotlinIntentionActionsFactory +import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction +import org.jetbrains.kotlin.idea.util.application.executeWriteCommand +import org.jetbrains.kotlin.idea.util.liftToExpected +import org.jetbrains.kotlin.idea.util.module +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer +import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier +import org.jetbrains.kotlin.resolve.BindingContext + +sealed class CreateExpectedFix( + declaration: D, + targetExpectedClass: KtClassOrObject?, + private val commonModule: Module, + private val generateIt: KtPsiFactory.(Project, D) -> D? +) : KotlinQuickFixAction(declaration) { + + private val targetExpectedClassPointer = targetExpectedClass?.createSmartPointer() + + override fun getFamilyName() = text + + protected abstract val elementType: String + + override fun getText() = "Create expected $elementType in common module ${commonModule.name}" + + override fun startInWriteAction() = false + + final override fun invoke(project: Project, editor: Editor?, file: KtFile) { + val element = element ?: return + val factory = KtPsiFactory(project) + + val targetExpectedClass = targetExpectedClassPointer?.element + val expectedFile = targetExpectedClass?.containingKtFile ?: getOrCreateImplementationFile() ?: return + DumbService.getInstance(project).runWhenSmart { + val generated = factory.generateIt(project, element) ?: return@runWhenSmart + + project.executeWriteCommand("Create expected declaration") { + if (expectedFile.packageDirective?.fqName != file.packageDirective?.fqName && + expectedFile.declarations.isEmpty() + ) { + val packageDirective = file.packageDirective + packageDirective?.let { + val oldPackageDirective = expectedFile.packageDirective + val newPackageDirective = factory.createPackageDirective(it.fqName) + if (oldPackageDirective != null) { + oldPackageDirective.replace(newPackageDirective) + } else { + expectedFile.add(newPackageDirective) + } + } + } + val expectedDeclaration = when { + targetExpectedClass != null -> targetExpectedClass.addDeclaration(generated as KtNamedDeclaration) + else -> expectedFile.add(generated) as KtElement + } + val reformatted = CodeStyleManager.getInstance(project).reformat(expectedDeclaration) + val shortened = ShortenReferences.DEFAULT.process(reformatted as KtElement) + EditorHelper.openInEditor(shortened)?.caretModel?.moveToOffset(shortened.textRange.startOffset) + } + } + } + + private fun getOrCreateImplementationFile(): KtFile? { + val declaration = element as? KtNamedDeclaration ?: return null + val parent = declaration.parent + if (parent is KtFile) { + for (otherDeclaration in parent.declarations) { + if (otherDeclaration === declaration) continue + if (!otherDeclaration.hasActualModifier()) continue + val expectedDeclaration = otherDeclaration.liftToExpected() ?: continue + return expectedDeclaration.containingKtFile + } + } + return createFileForDeclaration(commonModule, declaration) + } + + companion object : KotlinIntentionActionsFactory() { + override fun doCreateActions(diagnostic: Diagnostic): List { + val d = DiagnosticFactory.cast(diagnostic, Errors.ACTUAL_WITHOUT_EXPECT) + val declaration = d.psiElement as? KtNamedDeclaration ?: return emptyList() + val compatibility = d.b + // For function we allow it, because overloads are possible + if (compatibility.isNotEmpty() && declaration !is KtFunction) return emptyList() + + val containingClass = declaration.containingClassOrObject + val expectedContainingClass = containingClass?.liftToExpected() as? KtClassOrObject + // If there is already an expected class, we suggest only for its module, + // otherwise we suggest for all relevant expected modules + val expectedModules = expectedContainingClass?.module?.let { listOf(it) } + ?: declaration.module?.implementedModules + ?: return emptyList() + return when (declaration) { + is KtClassOrObject -> expectedModules.map { CreateExpectedClassFix(declaration, expectedContainingClass, it) } + is KtFunction -> expectedModules.map { CreateExpectedFunctionFix(declaration, expectedContainingClass, it) } + is KtProperty, is KtParameter -> expectedModules.map { CreateExpectedPropertyFix(declaration, expectedContainingClass, it) } + else -> emptyList() + } + } + } +} + +class CreateExpectedClassFix( + klass: KtClassOrObject, + outerExpectedClass: KtClassOrObject?, + commonModule: Module +) : CreateExpectedFix(klass, outerExpectedClass, commonModule, { project, element -> + generateClassOrObjectByActualClass(project, element, isNested = outerExpectedClass != null) +}) { + + override val elementType = element.getTypeDescription() +} + +class CreateExpectedPropertyFix( + property: KtNamedDeclaration, + targetExpectedClass: KtClassOrObject?, + commonModule: Module +) : CreateExpectedFix(property, targetExpectedClass, commonModule, { project, element -> + val descriptor = element.toDescriptor() as? PropertyDescriptor + descriptor?.let { generateProperty(project, element, descriptor, targetExpectedClass) } +}) { + + override val elementType = "property" +} + +class CreateExpectedFunctionFix( + function: KtFunction, + targetExpectedClass: KtClassOrObject?, + commonModule: Module +) : CreateExpectedFix(function, targetExpectedClass, commonModule, { project, element -> + val descriptor = element.toDescriptor() as? FunctionDescriptor + descriptor?.let { generateFunction(project, element, descriptor, targetExpectedClass) } +}) { + + override val elementType = "function" +} + +private fun KtPsiFactory.generateClassOrObjectByActualClass( + project: Project, + actualClass: KtClassOrObject, + isNested: Boolean +): KtClassOrObject { + val expectedClass = createClassCopyByText(actualClass) + expectedClass.declarations.forEach { + when (it) { + is KtEnumEntry -> return@forEach + is KtClassOrObject -> it.delete() + is KtCallableDeclaration -> it.delete() + } + } + expectedClass.primaryConstructor?.delete() + + 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 (!isNested) { + expectedClass.addModifier(KtTokens.EXPECT_KEYWORD) + } else { + expectedClass.makeNotActual() + } + + declLoop@ for (actualDeclaration in actualClass.declarations) { + val descriptor = actualDeclaration.toDescriptor() ?: continue + val expectedDeclaration: KtDeclaration = when (actualDeclaration) { + is KtClassOrObject -> + if (actualDeclaration !is KtEnumEntry) { + generateClassOrObjectByActualClass(project, actualDeclaration, isNested = true) + } else { + continue@declLoop + } + is KtCallableDeclaration -> { + when (actualDeclaration) { + is KtFunction -> generateFunction(project, actualDeclaration, descriptor as FunctionDescriptor, expectedClass) + is KtProperty -> generateProperty(project, actualDeclaration, descriptor as PropertyDescriptor, expectedClass) + else -> continue@declLoop + } + } + else -> continue@declLoop + } + expectedClass.addDeclaration(expectedDeclaration) + } + 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) + expectedClass.createPrimaryConstructorIfAbsent().replace(expectedPrimaryConstructor) + } + } + + return expectedClass +} + +private fun generateFunction( + project: Project, + actualFunction: KtFunction, + descriptor: FunctionDescriptor, + targetClass: KtClassOrObject? = null +): KtFunction { + val memberChooserObject = OverrideMemberChooserObject.create( + actualFunction, descriptor, descriptor, + OverrideMemberChooserObject.BodyType.NO_BODY + ) + return if (targetClass != null) { + memberChooserObject.generateExpectMember(targetClass = targetClass, copyDoc = true) + } else { + memberChooserObject.generateTopLevelExpect(copyDoc = true, project = project) + } as KtFunction +} + +private fun generateProperty( + project: Project, + actualProperty: KtNamedDeclaration, + descriptor: PropertyDescriptor, + targetClass: KtClassOrObject? = null +): KtProperty { + val memberChooserObject = OverrideMemberChooserObject.create( + actualProperty, descriptor, descriptor, + OverrideMemberChooserObject.BodyType.NO_BODY + ) + return if (targetClass != null) { + memberChooserObject.generateExpectMember(targetClass = targetClass, copyDoc = true) + } else { + memberChooserObject.generateTopLevelExpect(copyDoc = true, project = project) + } as KtProperty +} + + + + diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/ExpectActualUtils.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/ExpectActualUtils.kt new file mode 100644 index 00000000000..c2cf98d63fa --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/ExpectActualUtils.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2010-2018 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.idea.quickfix.expectactual + +import com.intellij.openapi.module.Module +import com.intellij.psi.JavaDirectoryService +import org.jetbrains.kotlin.idea.core.KotlinNameSuggester +import org.jetbrains.kotlin.idea.core.findOrCreateDirectoryForPackage +import org.jetbrains.kotlin.idea.core.getPackage +import org.jetbrains.kotlin.idea.refactoring.createKotlinFile +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.psi.* + +fun createFileForDeclaration(module: Module, declaration: KtNamedDeclaration): KtFile? { + val fileName = declaration.name ?: return null + + val originalDir = declaration.containingFile.containingDirectory + val containerPackage = JavaDirectoryService.getInstance().getPackage(originalDir) + val packageDirective = declaration.containingKtFile.packageDirective + val directory = findOrCreateDirectoryForPackage( + module, containerPackage?.qualifiedName ?: "" + ) ?: return null + return runWriteAction { + val fileNameWithExtension = "$fileName.kt" + val existingFile = directory.findFile(fileNameWithExtension) + val packageName = + if (packageDirective?.packageNameExpression == null) directory.getPackage()?.qualifiedName + else packageDirective.fqName.asString() + if (existingFile is KtFile) { + val existingPackageDirective = existingFile.packageDirective + if (existingFile.declarations.isNotEmpty() && + existingPackageDirective?.fqName != packageDirective?.fqName + ) { + val newName = KotlinNameSuggester.suggestNameByName(fileName) { + directory.findFile("$it.kt") == null + } + ".kt" + createKotlinFile(newName, directory, packageName) + } else { + existingFile + } + } else { + createKotlinFile(fileNameWithExtension, directory, packageName) + } + } +} + +fun KtPsiFactory.createClassCopyByText(originalClass: KtClassOrObject): KtClassOrObject { + val text = originalClass.text + return if (originalClass is KtObjectDeclaration) { + if (originalClass.isCompanion()) { + createCompanionObject(text) + } else { + createObject(text) + } + } else { + createClass(text) + } +} + +fun KtClassOrObject?.getTypeDescription(): String = when (this) { + is KtObjectDeclaration -> "object" + is KtClass -> when { + isInterface() -> "interface" + isEnum() -> "enum class" + isAnnotation() -> "annotation class" + else -> "class" + } + else -> "class" +} diff --git a/idea/testData/multiModuleQuickFix/expectAnnotation/header/My.kt b/idea/testData/multiModuleQuickFix/expectAnnotation/header/My.kt new file mode 100644 index 00000000000..e8182b18100 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectAnnotation/header/My.kt @@ -0,0 +1,2 @@ +// My: to be implemented +// DISABLE-ERRORS \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectAnnotation/header/My.kt.after b/idea/testData/multiModuleQuickFix/expectAnnotation/header/My.kt.after new file mode 100644 index 00000000000..b6d5a150da6 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectAnnotation/header/My.kt.after @@ -0,0 +1,3 @@ +// My: to be implemented +// DISABLE-ERRORS +expect annotation class My(val x: Int, val y: Double) \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectAnnotation/jvm/My.kt b/idea/testData/multiModuleQuickFix/expectAnnotation/jvm/My.kt new file mode 100644 index 00000000000..8805f740092 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectAnnotation/jvm/My.kt @@ -0,0 +1,4 @@ +// "Create expected annotation class in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual annotation class My(actual val x: Int, actual val y: Double) \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectAnnotation/jvm/My.kt.after b/idea/testData/multiModuleQuickFix/expectAnnotation/jvm/My.kt.after new file mode 100644 index 00000000000..dccf7ca4ca8 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectAnnotation/jvm/My.kt.after @@ -0,0 +1,4 @@ +// "Create expected annotation class in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual annotation class My(actual val x: Int, actual val y: Double) \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClass/header/My.kt b/idea/testData/multiModuleQuickFix/expectClass/header/My.kt new file mode 100644 index 00000000000..e8182b18100 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClass/header/My.kt @@ -0,0 +1,2 @@ +// My: to be implemented +// DISABLE-ERRORS \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClass/header/My.kt.after b/idea/testData/multiModuleQuickFix/expectClass/header/My.kt.after new file mode 100644 index 00000000000..ff72f52305d --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClass/header/My.kt.after @@ -0,0 +1,13 @@ +// My: to be implemented +// DISABLE-ERRORS +expect class My { + fun foo(param: String): Int + fun String.bar(y: Double): Boolean + fun baz() + + constructor(flag: Boolean) + + val isGood: Boolean + var status: Int + +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClass/jvm/My.kt b/idea/testData/multiModuleQuickFix/expectClass/jvm/My.kt new file mode 100644 index 00000000000..9635b9ec1ae --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClass/jvm/My.kt @@ -0,0 +1,19 @@ +// "Create expected class in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual class My { + actual fun foo(param: String): Int = 42 + + actual fun String.bar(y: Double): Boolean = true + + actual fun baz() {} + + actual constructor(flag: Boolean) {} + + actual val isGood: Boolean + get() = true + actual var status: Int + get() = 0 + set(value) {} + +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClass/jvm/My.kt.after b/idea/testData/multiModuleQuickFix/expectClass/jvm/My.kt.after new file mode 100644 index 00000000000..ab24837d894 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClass/jvm/My.kt.after @@ -0,0 +1,19 @@ +// "Create expected class in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual class My { + actual fun foo(param: String): Int = 42 + + actual fun String.bar(y: Double): Boolean = true + + actual fun baz() {} + + actual constructor(flag: Boolean) {} + + actual val isGood: Boolean + get() = true + actual var status: Int + get() = 0 + set(value) {} + +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassFunction/common/My.kt b/idea/testData/multiModuleQuickFix/expectClassFunction/common/My.kt new file mode 100644 index 00000000000..21098fa5596 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassFunction/common/My.kt @@ -0,0 +1,3 @@ +// DISABLE-ERRORS + +expect class My \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassFunction/common/My.kt.after b/idea/testData/multiModuleQuickFix/expectClassFunction/common/My.kt.after new file mode 100644 index 00000000000..6f0c1a068a7 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassFunction/common/My.kt.after @@ -0,0 +1,5 @@ +// DISABLE-ERRORS + +expect class My { + fun foo(param: String): Int +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassFunction/jvm/My.kt b/idea/testData/multiModuleQuickFix/expectClassFunction/jvm/My.kt new file mode 100644 index 00000000000..48cb9b355b1 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassFunction/jvm/My.kt @@ -0,0 +1,6 @@ +// "Create expected function in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual class My { + actual fun foo(param: String) = param.length +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassFunction/jvm/My.kt.after b/idea/testData/multiModuleQuickFix/expectClassFunction/jvm/My.kt.after new file mode 100644 index 00000000000..48cb9b355b1 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassFunction/jvm/My.kt.after @@ -0,0 +1,6 @@ +// "Create expected function in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual class My { + actual fun foo(param: String) = param.length +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassProperty/common/My.kt b/idea/testData/multiModuleQuickFix/expectClassProperty/common/My.kt new file mode 100644 index 00000000000..21098fa5596 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassProperty/common/My.kt @@ -0,0 +1,3 @@ +// DISABLE-ERRORS + +expect class My \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassProperty/common/My.kt.after b/idea/testData/multiModuleQuickFix/expectClassProperty/common/My.kt.after new file mode 100644 index 00000000000..fb31a627db6 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassProperty/common/My.kt.after @@ -0,0 +1,5 @@ +// DISABLE-ERRORS + +expect class My { + var s: String +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassProperty/jvm/My.kt b/idea/testData/multiModuleQuickFix/expectClassProperty/jvm/My.kt new file mode 100644 index 00000000000..30969fbb3ba --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassProperty/jvm/My.kt @@ -0,0 +1,6 @@ +// "Create expected property in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual class My { + actual var s: String = "Hello" +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassProperty/jvm/My.kt.after b/idea/testData/multiModuleQuickFix/expectClassProperty/jvm/My.kt.after new file mode 100644 index 00000000000..30969fbb3ba --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassProperty/jvm/My.kt.after @@ -0,0 +1,6 @@ +// "Create expected property in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual class My { + actual var s: String = "Hello" +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassPropertyInConstructor/common/My.kt b/idea/testData/multiModuleQuickFix/expectClassPropertyInConstructor/common/My.kt new file mode 100644 index 00000000000..21098fa5596 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassPropertyInConstructor/common/My.kt @@ -0,0 +1,3 @@ +// DISABLE-ERRORS + +expect class My \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassPropertyInConstructor/common/My.kt.after b/idea/testData/multiModuleQuickFix/expectClassPropertyInConstructor/common/My.kt.after new file mode 100644 index 00000000000..575b2c3f50b --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassPropertyInConstructor/common/My.kt.after @@ -0,0 +1,5 @@ +// DISABLE-ERRORS + +expect class My { + val s: String +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassPropertyInConstructor/jvm/My.kt b/idea/testData/multiModuleQuickFix/expectClassPropertyInConstructor/jvm/My.kt new file mode 100644 index 00000000000..144c5eba8b3 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassPropertyInConstructor/jvm/My.kt @@ -0,0 +1,4 @@ +// "Create expected property in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual class My(actual val s: String) \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassPropertyInConstructor/jvm/My.kt.after b/idea/testData/multiModuleQuickFix/expectClassPropertyInConstructor/jvm/My.kt.after new file mode 100644 index 00000000000..144c5eba8b3 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassPropertyInConstructor/jvm/My.kt.after @@ -0,0 +1,4 @@ +// "Create expected property in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual class My(actual val s: String) \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassWithSupertype/common/My.kt b/idea/testData/multiModuleQuickFix/expectClassWithSupertype/common/My.kt new file mode 100644 index 00000000000..a21c8bb78a2 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassWithSupertype/common/My.kt @@ -0,0 +1,5 @@ +// DISABLE-ERRORS + +expect abstract class Base { + abstract fun foo(param: String): Int +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassWithSupertype/common/My.kt.after b/idea/testData/multiModuleQuickFix/expectClassWithSupertype/common/My.kt.after new file mode 100644 index 00000000000..d6134ceb05d --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassWithSupertype/common/My.kt.after @@ -0,0 +1,9 @@ +// DISABLE-ERRORS + +expect abstract class Base { + abstract fun foo(param: String): Int +} + +expect class My : Base { + override fun foo(param: String): Int +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassWithSupertype/jvm/My.kt b/idea/testData/multiModuleQuickFix/expectClassWithSupertype/jvm/My.kt new file mode 100644 index 00000000000..b63ec111b01 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassWithSupertype/jvm/My.kt @@ -0,0 +1,10 @@ +// "Create expected class in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual abstract class Base { + actual abstract fun foo(param: String): Int +} + +actual class My : Base() { + actual override fun foo(param: String) = param.length +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectClassWithSupertype/jvm/My.kt.after b/idea/testData/multiModuleQuickFix/expectClassWithSupertype/jvm/My.kt.after new file mode 100644 index 00000000000..34d56a123c4 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectClassWithSupertype/jvm/My.kt.after @@ -0,0 +1,10 @@ +// "Create expected class in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual abstract class Base { + actual abstract fun foo(param: String): Int +} + +actual class My : Base() { + actual override fun foo(param: String) = param.length +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectEnum/header/My.kt b/idea/testData/multiModuleQuickFix/expectEnum/header/My.kt new file mode 100644 index 00000000000..e8182b18100 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectEnum/header/My.kt @@ -0,0 +1,2 @@ +// My: to be implemented +// DISABLE-ERRORS \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectEnum/header/My.kt.after b/idea/testData/multiModuleQuickFix/expectEnum/header/My.kt.after new file mode 100644 index 00000000000..ded555abea5 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectEnum/header/My.kt.after @@ -0,0 +1,10 @@ +// My: to be implemented +// DISABLE-ERRORS +expect enum class My { + FIRST, + SECOND, + LAST; + + val num: Int + +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectEnum/jvm/My.kt b/idea/testData/multiModuleQuickFix/expectEnum/jvm/My.kt new file mode 100644 index 00000000000..24476becd62 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectEnum/jvm/My.kt @@ -0,0 +1,10 @@ +// "Create expected enum class in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual enum class My { + FIRST, + SECOND, + LAST; + + val num: Int get() = 42 +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectEnum/jvm/My.kt.after b/idea/testData/multiModuleQuickFix/expectEnum/jvm/My.kt.after new file mode 100644 index 00000000000..a3e2b441f3f --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectEnum/jvm/My.kt.after @@ -0,0 +1,10 @@ +// "Create expected enum class in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual enum class My { + FIRST, + SECOND, + LAST; + + val num: Int get() = 42 +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectFunction/common/foo.kt.after b/idea/testData/multiModuleQuickFix/expectFunction/common/foo.kt.after new file mode 100644 index 00000000000..ef2e2751b4e --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectFunction/common/foo.kt.after @@ -0,0 +1 @@ +expect fun foo(i: Int, d: Double, s: String): Boolean \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectFunction/jvm/Utils.kt b/idea/testData/multiModuleQuickFix/expectFunction/jvm/Utils.kt new file mode 100644 index 00000000000..1526c86dd3a --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectFunction/jvm/Utils.kt @@ -0,0 +1,4 @@ +// "Create expected function in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual fun foo(i: Int, d: Double, s: String) = s == "$i$d" \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectFunction/jvm/Utils.kt.after b/idea/testData/multiModuleQuickFix/expectFunction/jvm/Utils.kt.after new file mode 100644 index 00000000000..f2561ca2406 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectFunction/jvm/Utils.kt.after @@ -0,0 +1,4 @@ +// "Create expected function in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual fun foo(i: Int, d: Double, s: String) = s == "$i$d" \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectNestedClass/common/My.kt b/idea/testData/multiModuleQuickFix/expectNestedClass/common/My.kt new file mode 100644 index 00000000000..21098fa5596 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectNestedClass/common/My.kt @@ -0,0 +1,3 @@ +// DISABLE-ERRORS + +expect class My \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectNestedClass/common/My.kt.after b/idea/testData/multiModuleQuickFix/expectNestedClass/common/My.kt.after new file mode 100644 index 00000000000..588edd3b9bf --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectNestedClass/common/My.kt.after @@ -0,0 +1,13 @@ +// DISABLE-ERRORS + +expect class My { + inner class Nested(s: String) { + fun hello(): String + var ss: String + + class OtherNested(d: Double) { + val dd: Double + } + + } +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectNestedClass/jvm/My.kt b/idea/testData/multiModuleQuickFix/expectNestedClass/jvm/My.kt new file mode 100644 index 00000000000..8d3db470e17 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectNestedClass/jvm/My.kt @@ -0,0 +1,14 @@ +// "Create expected class in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual class My { + actual inner class Nested(actual val s: String) { + actual fun hello() = s + + actual var ss = s + + actual class OtherNested(actual var d: Double) { + actual val dd = d + } + } +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectNestedClass/jvm/My.kt.after b/idea/testData/multiModuleQuickFix/expectNestedClass/jvm/My.kt.after new file mode 100644 index 00000000000..8d3db470e17 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectNestedClass/jvm/My.kt.after @@ -0,0 +1,14 @@ +// "Create expected class in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual class My { + actual inner class Nested(actual val s: String) { + actual fun hello() = s + + actual var ss = s + + actual class OtherNested(actual var d: Double) { + actual val dd = d + } + } +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectProperty/common/foo.kt.after b/idea/testData/multiModuleQuickFix/expectProperty/common/foo.kt.after new file mode 100644 index 00000000000..a2361768d5a --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectProperty/common/foo.kt.after @@ -0,0 +1 @@ +expect var foo: String \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectProperty/jvm/Utils.kt b/idea/testData/multiModuleQuickFix/expectProperty/jvm/Utils.kt new file mode 100644 index 00000000000..89527491c8b --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectProperty/jvm/Utils.kt @@ -0,0 +1,6 @@ +// "Create expected property in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual var foo: String + get() = field * field + set(value) { field = value } \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/expectProperty/jvm/Utils.kt.after b/idea/testData/multiModuleQuickFix/expectProperty/jvm/Utils.kt.after new file mode 100644 index 00000000000..7f6dbe567ea --- /dev/null +++ b/idea/testData/multiModuleQuickFix/expectProperty/jvm/Utils.kt.after @@ -0,0 +1,6 @@ +// "Create expected property in common module testModule_Common" "true" +// DISABLE-ERRORS + +actual var foo: String + get() = field * field + set(value) { field = value } \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java index 86edca0edfb..ea77f6eeaa7 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java @@ -254,6 +254,56 @@ public class QuickFixMultiModuleTestGenerated extends AbstractQuickFixMultiModul runTest("idea/testData/multiModuleQuickFix/enum/"); } + @TestMetadata("expectAnnotation") + public void testExpectAnnotation() throws Exception { + runTest("idea/testData/multiModuleQuickFix/expectAnnotation/"); + } + + @TestMetadata("expectClass") + public void testExpectClass() throws Exception { + runTest("idea/testData/multiModuleQuickFix/expectClass/"); + } + + @TestMetadata("expectClassFunction") + public void testExpectClassFunction() throws Exception { + runTest("idea/testData/multiModuleQuickFix/expectClassFunction/"); + } + + @TestMetadata("expectClassProperty") + public void testExpectClassProperty() throws Exception { + runTest("idea/testData/multiModuleQuickFix/expectClassProperty/"); + } + + @TestMetadata("expectClassPropertyInConstructor") + public void testExpectClassPropertyInConstructor() throws Exception { + runTest("idea/testData/multiModuleQuickFix/expectClassPropertyInConstructor/"); + } + + @TestMetadata("expectClassWithSupertype") + public void testExpectClassWithSupertype() throws Exception { + runTest("idea/testData/multiModuleQuickFix/expectClassWithSupertype/"); + } + + @TestMetadata("expectEnum") + public void testExpectEnum() throws Exception { + runTest("idea/testData/multiModuleQuickFix/expectEnum/"); + } + + @TestMetadata("expectFunction") + public void testExpectFunction() throws Exception { + runTest("idea/testData/multiModuleQuickFix/expectFunction/"); + } + + @TestMetadata("expectNestedClass") + public void testExpectNestedClass() throws Exception { + runTest("idea/testData/multiModuleQuickFix/expectNestedClass/"); + } + + @TestMetadata("expectProperty") + public void testExpectProperty() throws Exception { + runTest("idea/testData/multiModuleQuickFix/expectProperty/"); + } + @TestMetadata("function") public void testFunction() throws Exception { runTest("idea/testData/multiModuleQuickFix/function/");