Introduce quick-fix "create expected class / function / property"
#KT-27075 Fixed
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
+57
-30
@@ -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<ClassDescriptor>().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<ClassDescriptor>().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 <R : Any?, D : Any?> accept(visitor: DeclarationDescriptorVisitor<R, D>, 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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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<out D : KtNamedDeclaration>(
|
||||
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()
|
||||
|
||||
@@ -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<out D : KtNamedDeclaration>(
|
||||
declaration: D,
|
||||
targetExpectedClass: KtClassOrObject?,
|
||||
private val commonModule: Module,
|
||||
private val generateIt: KtPsiFactory.(Project, D) -> D?
|
||||
) : KotlinQuickFixAction<D>(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<IntentionAction> {
|
||||
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<KtClassOrObject>(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<KtNamedDeclaration>(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<KtFunction>(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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// My: to be implemented
|
||||
// DISABLE-ERRORS
|
||||
@@ -0,0 +1,3 @@
|
||||
// My: to be implemented
|
||||
// DISABLE-ERRORS
|
||||
expect annotation class My(val x: Int, val y: Double)
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Create expected annotation class in common module testModule_Common" "true"
|
||||
// DISABLE-ERRORS
|
||||
|
||||
actual annotation class <caret>My(actual val x: Int, actual val y: Double)
|
||||
@@ -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)
|
||||
@@ -0,0 +1,2 @@
|
||||
// My: to be implemented
|
||||
// DISABLE-ERRORS
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// "Create expected class in common module testModule_Common" "true"
|
||||
// DISABLE-ERRORS
|
||||
|
||||
actual class <caret>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) {}
|
||||
|
||||
}
|
||||
@@ -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) {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// DISABLE-ERRORS
|
||||
|
||||
expect class My
|
||||
@@ -0,0 +1,5 @@
|
||||
// DISABLE-ERRORS
|
||||
|
||||
expect class My {
|
||||
fun foo(param: String): Int
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Create expected function in common module testModule_Common" "true"
|
||||
// DISABLE-ERRORS
|
||||
|
||||
actual class My {
|
||||
actual fun <caret>foo(param: String) = param.length
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Create expected function in common module testModule_Common" "true"
|
||||
// DISABLE-ERRORS
|
||||
|
||||
actual class My {
|
||||
actual fun <caret>foo(param: String) = param.length
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// DISABLE-ERRORS
|
||||
|
||||
expect class My
|
||||
@@ -0,0 +1,5 @@
|
||||
// DISABLE-ERRORS
|
||||
|
||||
expect class My {
|
||||
var s: String
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Create expected property in common module testModule_Common" "true"
|
||||
// DISABLE-ERRORS
|
||||
|
||||
actual class My {
|
||||
actual var <caret>s: String = "Hello"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Create expected property in common module testModule_Common" "true"
|
||||
// DISABLE-ERRORS
|
||||
|
||||
actual class My {
|
||||
actual var <caret>s: String = "Hello"
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// DISABLE-ERRORS
|
||||
|
||||
expect class My
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// DISABLE-ERRORS
|
||||
|
||||
expect class My {
|
||||
val s: String
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Create expected property in common module testModule_Common" "true"
|
||||
// DISABLE-ERRORS
|
||||
|
||||
actual class My(actual val <caret>s: String)
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// "Create expected property in common module testModule_Common" "true"
|
||||
// DISABLE-ERRORS
|
||||
|
||||
actual class My(actual val <caret>s: String)
|
||||
@@ -0,0 +1,5 @@
|
||||
// DISABLE-ERRORS
|
||||
|
||||
expect abstract class Base {
|
||||
abstract fun foo(param: String): Int
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 <caret>My : Base() {
|
||||
actual override fun foo(param: String) = param.length
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// My: to be implemented
|
||||
// DISABLE-ERRORS
|
||||
@@ -0,0 +1,10 @@
|
||||
// My: to be implemented
|
||||
// DISABLE-ERRORS
|
||||
expect enum class My {
|
||||
FIRST,
|
||||
SECOND,
|
||||
LAST;
|
||||
|
||||
val num: Int
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Create expected enum class in common module testModule_Common" "true"
|
||||
// DISABLE-ERRORS
|
||||
|
||||
actual enum class <caret>My {
|
||||
FIRST,
|
||||
SECOND,
|
||||
LAST;
|
||||
|
||||
val num: Int get() = 42
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
expect fun foo(i: Int, d: Double, s: String): Boolean
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Create expected function in common module testModule_Common" "true"
|
||||
// DISABLE-ERRORS
|
||||
|
||||
actual fun <caret>foo(i: Int, d: Double, s: String) = s == "$i$d"
|
||||
@@ -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"
|
||||
@@ -0,0 +1,3 @@
|
||||
// DISABLE-ERRORS
|
||||
|
||||
expect class My
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Create expected class in common module testModule_Common" "true"
|
||||
// DISABLE-ERRORS
|
||||
|
||||
actual class My {
|
||||
actual inner class <caret>Nested(actual val s: String) {
|
||||
actual fun hello() = s
|
||||
|
||||
actual var ss = s
|
||||
|
||||
actual class OtherNested(actual var d: Double) {
|
||||
actual val dd = d
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Create expected class in common module testModule_Common" "true"
|
||||
// DISABLE-ERRORS
|
||||
|
||||
actual class My {
|
||||
actual inner class <caret>Nested(actual val s: String) {
|
||||
actual fun hello() = s
|
||||
|
||||
actual var ss = s
|
||||
|
||||
actual class OtherNested(actual var d: Double) {
|
||||
actual val dd = d
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
expect var foo: String
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Create expected property in common module testModule_Common" "true"
|
||||
// DISABLE-ERRORS
|
||||
|
||||
actual var <caret>foo: String
|
||||
get() = field * field
|
||||
set(value) { field = value }
|
||||
@@ -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 }
|
||||
+50
@@ -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/");
|
||||
|
||||
Reference in New Issue
Block a user