Create Class from Usage: Support nested classes

This covers the case when original expression doesn't contains qualifier
Also for local or inner containing classes:
  - forbid nested objects
  - add 'inner' to nested class declaration

 #KT-16404 Fixed
This commit is contained in:
Alexey Sedunov
2017-07-21 14:09:43 +03:00
parent d30461afc6
commit 97a3d343f7
21 changed files with 252 additions and 94 deletions
@@ -413,6 +413,11 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
typeCandidates[typeInfo]?.forEach { it.render(typeParameterNameMap, fakeFunction) } typeCandidates[typeInfo]?.forEach { it.render(typeParameterNameMap, fakeFunction) }
} }
private fun isInsideInnerOrLocalClass(): Boolean {
val classOrObject = containingElement.getNonStrictParentOfType<KtClassOrObject>()
return classOrObject is KtClass && (classOrObject.isInner() || classOrObject.isLocal)
}
private fun createDeclarationSkeleton(): KtNamedDeclaration { private fun createDeclarationSkeleton(): KtNamedDeclaration {
with (config) { with (config) {
val assignmentToReplace = val assignmentToReplace =
@@ -490,14 +495,14 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val safeName = name.quoteIfNeeded() val safeName = name.quoteIfNeeded()
when (kind) { when (kind) {
ClassKind.ENUM_ENTRY -> { ClassKind.ENUM_ENTRY -> {
val targetParent = targetParents.singleOrNull() val targetParent = applicableParents.singleOrNull()
if (!(targetParent is KtClass && targetParent.isEnum())) throw AssertionError("Enum class expected: ${targetParent?.text}") if (!(targetParent is KtClass && targetParent.isEnum())) throw AssertionError("Enum class expected: ${targetParent?.text}")
val hasParameters = targetParent.primaryConstructorParameters.isNotEmpty() val hasParameters = targetParent.primaryConstructorParameters.isNotEmpty()
psiFactory.createEnumEntry("$safeName${if (hasParameters) "()" else " "}") psiFactory.createEnumEntry("$safeName${if (hasParameters) "()" else " "}")
} }
else -> { else -> {
val openMod = if (open) "open " else "" val openMod = if (open) "open " else ""
val innerMod = if (inner) "inner " else "" val innerMod = if (inner || isInsideInnerOrLocalClass()) "inner " else ""
val typeParamList = when (kind) { val typeParamList = when (kind) {
ClassKind.PLAIN_CLASS, ClassKind.INTERFACE -> "<>" ClassKind.PLAIN_CLASS, ClassKind.INTERFACE -> "<>"
else -> "" else -> ""
@@ -53,7 +53,6 @@ object CreateClassFromCallWithConstructorCalleeActionFactory : CreateClassFromUs
val isAnnotation = element is KtAnnotationEntry val isAnnotation = element is KtAnnotationEntry
val callee = element.calleeExpression as? KtConstructorCalleeExpression ?: return null val callee = element.calleeExpression as? KtConstructorCalleeExpression ?: return null
val calleeRef = callee.constructorReferenceExpression ?: return null val calleeRef = callee.constructorReferenceExpression ?: return null
val file = element.containingFile as? KtFile ?: return null
val typeRef = callee.typeReference ?: return null val typeRef = callee.typeReference ?: return null
val userType = typeRef.typeElement as? KtUserType ?: return null val userType = typeRef.typeElement as? KtUserType ?: return null
@@ -62,7 +61,7 @@ object CreateClassFromCallWithConstructorCalleeActionFactory : CreateClassFromUs
val qualifier = userType.qualifier?.referenceExpression val qualifier = userType.qualifier?.referenceExpression
val qualifierDescriptor = qualifier?.let { context[BindingContext.REFERENCE_TARGET, it] } val qualifierDescriptor = qualifier?.let { context[BindingContext.REFERENCE_TARGET, it] }
val targetParents = getTargetParentsByQualifier(file, qualifier != null, qualifierDescriptor).ifEmpty { return null } val targetParents = getTargetParentsByQualifier(element, qualifier != null, qualifierDescriptor).ifEmpty { return null }
val anyType = module.builtIns.nullableAnyType val anyType = module.builtIns.nullableAnyType
val valueArguments = element.valueArguments val valueArguments = element.valueArguments
@@ -42,9 +42,8 @@ object CreateClassFromConstructorCallActionFactory: CreateClassFromUsageFactory<
val inAnnotationEntry = diagnostic.psiElement.getNonStrictParentOfType<KtAnnotationEntry>() != null val inAnnotationEntry = diagnostic.psiElement.getNonStrictParentOfType<KtAnnotationEntry>() != null
val (context, moduleDescriptor) = element.analyzeFullyAndGetResult() val (context, moduleDescriptor) = element.analyzeFullyAndGetResult()
val file = element.containingFile as? KtFile ?: return emptyList()
val call = element.getCall(context) ?: return emptyList() val call = element.getCall(context) ?: return emptyList()
val targetParents = getTargetParentsByCall(call, file, context).ifEmpty { return emptyList() } val targetParents = getTargetParentsByCall(call, context).ifEmpty { return emptyList() }
val classKind = if (inAnnotationEntry) ClassKind.ANNOTATION_CLASS else ClassKind.PLAIN_CLASS val classKind = if (inAnnotationEntry) ClassKind.ANNOTATION_CLASS else ClassKind.PLAIN_CLASS
val fullCallExpr = element.getQualifiedExpressionForSelectorOrThis() val fullCallExpr = element.getQualifiedExpressionForSelectorOrThis()
@@ -72,12 +71,10 @@ object CreateClassFromConstructorCallActionFactory: CreateClassFromUsageFactory<
val fullCallExpr = val fullCallExpr =
if (callParent is KtQualifiedExpression && callParent.selectorExpression == callExpr) callParent else callExpr if (callParent is KtQualifiedExpression && callParent.selectorExpression == callExpr) callParent else callExpr
val file = fullCallExpr.containingFile as? KtFile ?: return null
val (context, moduleDescriptor) = callExpr.analyzeFullyAndGetResult() val (context, moduleDescriptor) = callExpr.analyzeFullyAndGetResult()
val call = callExpr.getCall(context) ?: return null val call = callExpr.getCall(context) ?: return null
val targetParents = getTargetParentsByCall(call, file, context).ifEmpty { return null } val targetParents = getTargetParentsByCall(call, context).ifEmpty { return null }
val inner = isInnerClassExpected(call) val inner = isInnerClassExpected(call)
val valueArguments = callExpr.valueArguments val valueArguments = callExpr.valueArguments
@@ -57,8 +57,6 @@ object CreateClassFromReferenceExpressionActionFactory : CreateClassFromUsageFac
} }
} }
val file = element.containingFile as? KtFile ?: return Collections.emptyList()
val name = element.getReferencedName() val name = element.getReferencedName()
val (context, moduleDescriptor) = element.analyzeFullyAndGetResult() val (context, moduleDescriptor) = element.analyzeFullyAndGetResult()
@@ -70,11 +68,8 @@ object CreateClassFromReferenceExpressionActionFactory : CreateClassFromUsageFac
val receiverSelector = (fullCallExpr as? KtQualifiedExpression)?.receiverExpression?.getQualifiedElementSelector() as? KtReferenceExpression val receiverSelector = (fullCallExpr as? KtQualifiedExpression)?.receiverExpression?.getQualifiedElementSelector() as? KtReferenceExpression
val qualifierDescriptor = receiverSelector?.let { context[BindingContext.REFERENCE_TARGET, it] } val qualifierDescriptor = receiverSelector?.let { context[BindingContext.REFERENCE_TARGET, it] }
val targetParents = getTargetParentsByQualifier( val targetParents = getTargetParentsByQualifier(element, receiverSelector != null, qualifierDescriptor)
element.containingKtFile, .ifEmpty { return emptyList() }
receiverSelector != null,
qualifierDescriptor
).ifEmpty { return emptyList() }
targetParents.forEach { targetParents.forEach {
if (element.getCreatePackageFixIfApplicable(it) != null) return emptyList() if (element.getCreatePackageFixIfApplicable(it) != null) return emptyList()
@@ -100,7 +95,7 @@ object CreateClassFromReferenceExpressionActionFactory : CreateClassFromUsageFac
if (fullCallExpr.getAssignmentByLHS() != null) return Collections.emptyList() if (fullCallExpr.getAssignmentByLHS() != null) return Collections.emptyList()
val call = element.getCall(context) ?: return Collections.emptyList() val call = element.getCall(context) ?: return Collections.emptyList()
val targetParents = getTargetParentsByCall(call, file, context).ifEmpty { return emptyList() } val targetParents = getTargetParentsByCall(call, context).ifEmpty { return emptyList() }
if (isInnerClassExpected(call)) return Collections.emptyList() if (isInnerClassExpected(call)) return Collections.emptyList()
val allKinds = Arrays.asList(ClassKind.OBJECT, ClassKind.ENUM_ENTRY) val allKinds = Arrays.asList(ClassKind.OBJECT, ClassKind.ENUM_ENTRY)
@@ -119,8 +114,6 @@ object CreateClassFromReferenceExpressionActionFactory : CreateClassFromUsageFac
} }
override fun extractFixData(element: KtSimpleNameExpression, diagnostic: Diagnostic): ClassInfo? { override fun extractFixData(element: KtSimpleNameExpression, diagnostic: Diagnostic): ClassInfo? {
val file = element.containingFile as? KtFile ?: return null
val name = element.getReferencedName() val name = element.getReferencedName()
val (context, moduleDescriptor) = element.analyzeFullyAndGetResult() val (context, moduleDescriptor) = element.analyzeFullyAndGetResult()
@@ -131,11 +124,8 @@ object CreateClassFromReferenceExpressionActionFactory : CreateClassFromUsageFac
val receiverSelector = (fullCallExpr as? KtQualifiedExpression)?.receiverExpression?.getQualifiedElementSelector() as? KtReferenceExpression val receiverSelector = (fullCallExpr as? KtQualifiedExpression)?.receiverExpression?.getQualifiedElementSelector() as? KtReferenceExpression
val qualifierDescriptor = receiverSelector?.let { context[BindingContext.REFERENCE_TARGET, it] } val qualifierDescriptor = receiverSelector?.let { context[BindingContext.REFERENCE_TARGET, it] }
val targetParents = getTargetParentsByQualifier( val targetParents = getTargetParentsByQualifier(element, receiverSelector != null, qualifierDescriptor)
element.containingKtFile, .ifEmpty { return null }
receiverSelector != null,
qualifierDescriptor
).ifEmpty { return null }
return ClassInfo( return ClassInfo(
name = name, name = name,
@@ -145,7 +135,7 @@ object CreateClassFromReferenceExpressionActionFactory : CreateClassFromUsageFac
} }
val call = element.getCall(context) ?: return null val call = element.getCall(context) ?: return null
val targetParents = getTargetParentsByCall(call, file, context).ifEmpty { return null } val targetParents = getTargetParentsByCall(call, context).ifEmpty { return null }
val expectedTypeInfo = fullCallExpr.guessTypeForClass(context, moduleDescriptor)?.toClassTypeInfo() ?: TypeInfo.Empty val expectedTypeInfo = fullCallExpr.guessTypeForClass(context, moduleDescriptor)?.toClassTypeInfo() ?: TypeInfo.Empty
@@ -77,13 +77,11 @@ object CreateClassFromTypeReferenceActionFactory : CreateClassFromUsageFactory<K
val name = element.referenceExpression?.getReferencedName() ?: return null val name = element.referenceExpression?.getReferencedName() ?: return null
if (element.parent.parent is KtConstructorCalleeExpression) return null if (element.parent.parent is KtConstructorCalleeExpression) return null
val file = element.containingFile as? KtFile ?: return null
val (context, module) = element.analyzeAndGetResult() val (context, module) = element.analyzeAndGetResult()
val qualifier = element.qualifier?.referenceExpression val qualifier = element.qualifier?.referenceExpression
val qualifierDescriptor = qualifier?.let { context[BindingContext.REFERENCE_TARGET, it] } val qualifierDescriptor = qualifier?.let { context[BindingContext.REFERENCE_TARGET, it] }
val targetParents = getTargetParentsByQualifier(file, qualifier != null, qualifierDescriptor).ifEmpty { return null } val targetParents = getTargetParentsByQualifier(element, qualifier != null, qualifierDescriptor).ifEmpty { return null }
val expectedUpperBound = getExpectedUpperBound(element, context) val expectedUpperBound = getExpectedUpperBound(element, context)
val anyType = module.builtIns.anyType val anyType = module.builtIns.anyType
@@ -23,10 +23,7 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass import com.intellij.psi.*
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiPackage
import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
import org.jetbrains.kotlin.idea.quickfix.IntentionActionPriority import org.jetbrains.kotlin.idea.quickfix.IntentionActionPriority
@@ -38,8 +35,10 @@ import org.jetbrains.kotlin.idea.refactoring.chooseContainerElementIfNecessary
import org.jetbrains.kotlin.idea.refactoring.getOrCreateKotlinFile import org.jetbrains.kotlin.idea.refactoring.getOrCreateKotlinFile
import org.jetbrains.kotlin.idea.util.application.executeCommand import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.allChildren
import java.util.* import java.util.*
enum class ClassKind(val keyword: String, val description: String) { enum class ClassKind(val keyword: String, val description: String) {
@@ -58,13 +57,20 @@ val ClassKind.actionPriority: IntentionActionPriority
data class ClassInfo( data class ClassInfo(
val kind: ClassKind = ClassKind.DEFAULT, val kind: ClassKind = ClassKind.DEFAULT,
val name: String, val name: String,
val targetParents: List<PsiElement>, private val targetParents: List<PsiElement>,
val expectedTypeInfo: TypeInfo, val expectedTypeInfo: TypeInfo,
val inner: Boolean = false, val inner: Boolean = false,
val open: Boolean = false, val open: Boolean = false,
val typeArguments: List<TypeInfo> = Collections.emptyList(), val typeArguments: List<TypeInfo> = Collections.emptyList(),
val parameterInfos: List<ParameterInfo> = Collections.emptyList() val parameterInfos: List<ParameterInfo> = Collections.emptyList()
) ) {
val applicableParents by lazy {
targetParents.filter {
if (kind == ClassKind.OBJECT && it is KtClass && (it.isInner() || it.isLocal)) return@filter false
true
}
}
}
open class CreateClassFromUsageFix<E : KtElement> protected constructor ( open class CreateClassFromUsageFix<E : KtElement> protected constructor (
element: E, element: E,
@@ -76,7 +82,8 @@ open class CreateClassFromUsageFix<E : KtElement> protected constructor (
if (!super.isAvailable(project, editor, file)) return false if (!super.isAvailable(project, editor, file)) return false
with(classInfo) { with(classInfo) {
if (kind == DEFAULT) return false if (kind == DEFAULT) return false
targetParents.forEach { if (applicableParents.isEmpty()) return false
applicableParents.forEach {
if (it is PsiClass) { if (it is PsiClass) {
if (kind == OBJECT || kind == ENUM_ENTRY) return false if (kind == OBJECT || kind == ENUM_ENTRY) return false
if (it.isInterface && inner) return false if (it.isInterface && inner) return false
@@ -90,60 +97,75 @@ open class CreateClassFromUsageFix<E : KtElement> protected constructor (
override fun startInWriteAction() = false override fun startInWriteAction() = false
override fun invoke(project: Project, editor: Editor?, file: KtFile) { override fun invoke(project: Project, editor: Editor?, file: KtFile) {
fun createFileByPackage(psiPackage: PsiPackage): KtFile? {
val directories = psiPackage.directories.filter { it.canRefactor() }
assert (directories.isNotEmpty()) { "Package '${psiPackage.qualifiedName}' must be refactorable" }
val currentModule = ModuleUtilCore.findModuleForPsiElement(file)
val preferredDirectory =
directories.firstOrNull { ModuleUtilCore.findModuleForPsiElement(it) == currentModule }
?: directories.firstOrNull()
val targetDirectory = if (directories.size > 1 && !ApplicationManager.getApplication().isUnitTestMode) {
DirectoryChooserUtil.chooseDirectory(directories.toTypedArray(), preferredDirectory, project, HashMap())
}
else {
preferredDirectory
} ?: return null
val fileName = "${classInfo.name}.${KotlinFileType.INSTANCE.defaultExtension}"
val targetFile = getOrCreateKotlinFile(fileName, targetDirectory)
if (targetFile == null) {
val filePath = "${targetDirectory.virtualFile.path}/$fileName"
CodeInsightUtils.showErrorHint(
targetDirectory.project,
editor!!,
"File $filePath already exists but does not correspond to Kotlin file",
"Create file",
null
)
}
return targetFile
}
if (editor == null) return if (editor == null) return
with (classInfo) { if (ApplicationManager.getApplication().isUnitTestMode) {
chooseContainerElementIfNecessary(targetParents, editor, "Choose class container", true, { it }) { val targetParent = classInfo.applicableParents.firstOrNull {
runWriteAction { it.allChildren.any { it is PsiComment && it.text == "// TARGET_PARENT:" }
val targetParent = } ?: classInfo.applicableParents.last()
when (it) { return doInvoke(targetParent, editor, file)
is KtElement, is PsiClass -> it }
is PsiPackage -> createFileByPackage(it)
else -> throw AssertionError("Unexpected element: " + it.text) chooseContainerElementIfNecessary(classInfo.applicableParents, editor, "Choose class container", true, { it }) {
} ?: return@runWriteAction doInvoke(it, editor, file)
val constructorInfo = PrimaryConstructorInfo(classInfo, expectedTypeInfo) }
val builder = CallableBuilderConfiguration( }
Collections.singletonList(constructorInfo),
element as KtElement, private fun createFileByPackage(
file, psiPackage: PsiPackage,
editor, editor: Editor,
false, originalFile: KtFile
kind == PLAIN_CLASS || kind == INTERFACE ): KtFile? {
).createBuilder() val directories = psiPackage.directories.filter { it.canRefactor() }
builder.placement = CallablePlacement.NoReceiver(targetParent) assert (directories.isNotEmpty()) { "Package '${psiPackage.qualifiedName}' must be refactorable" }
project.executeCommand(text) { builder.build() }
} val currentModule = ModuleUtilCore.findModuleForPsiElement(originalFile)
val preferredDirectory =
directories.firstOrNull { ModuleUtilCore.findModuleForPsiElement(it) == currentModule }
?: directories.firstOrNull()
val targetDirectory = if (directories.size > 1 && !ApplicationManager.getApplication().isUnitTestMode) {
DirectoryChooserUtil.chooseDirectory(directories.toTypedArray(), preferredDirectory, originalFile.project, HashMap())
}
else {
preferredDirectory
} ?: return null
val fileName = "${classInfo.name}.${KotlinFileType.INSTANCE.defaultExtension}"
val targetFile = getOrCreateKotlinFile(fileName, targetDirectory)
if (targetFile == null) {
val filePath = "${targetDirectory.virtualFile.path}/$fileName"
CodeInsightUtils.showErrorHint(
targetDirectory.project,
editor,
"File $filePath already exists but does not correspond to Kotlin file",
"Create file",
null
)
}
return targetFile
}
private fun doInvoke(selectedParent: PsiElement, editor: Editor, file: KtFile) {
runWriteAction {
with(classInfo) {
val targetParent =
when (selectedParent) {
is KtElement, is PsiClass -> selectedParent
is PsiPackage -> createFileByPackage(selectedParent, editor, file)
else -> throw AssertionError("Unexpected element: " + selectedParent.text)
} ?: return@runWriteAction
val constructorInfo = PrimaryConstructorInfo(classInfo, expectedTypeInfo)
val builder = CallableBuilderConfiguration(
Collections.singletonList(constructorInfo),
element as KtElement,
file,
editor,
false,
kind == PLAIN_CLASS || kind == INTERFACE
).createBuilder()
builder.placement = CallablePlacement.NoReceiver(targetParent)
file.project.executeCommand(text) { builder.build() }
} }
} }
} }
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.guessT
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.noSubstitutions import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.noSubstitutions
import org.jetbrains.kotlin.idea.refactoring.canRefactor import org.jetbrains.kotlin.idea.refactoring.canRefactor
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -51,14 +52,15 @@ internal fun String.checkClassName(): Boolean = isNotEmpty() && Character.isUppe
private fun String.checkPackageName(): Boolean = isNotEmpty() && Character.isLowerCase(first()) private fun String.checkPackageName(): Boolean = isNotEmpty() && Character.isLowerCase(first())
internal fun getTargetParentsByQualifier( internal fun getTargetParentsByQualifier(
file: KtFile, element: KtElement,
isQualified: Boolean, isQualified: Boolean,
qualifierDescriptor: DeclarationDescriptor? qualifierDescriptor: DeclarationDescriptor?
): List<PsiElement> { ): List<PsiElement> {
val file = element.containingKtFile
val project = file.project val project = file.project
val targetParents: List<PsiElement> = when { val targetParents: List<PsiElement> = when {
!isQualified -> !isQualified ->
listOf(file) element.parents.filterIsInstance<KtClassOrObject>().toList() + file
qualifierDescriptor is ClassDescriptor -> qualifierDescriptor is ClassDescriptor ->
listOfNotNull(DescriptorToSourceUtilsIde.getAnyDeclaration(project, qualifierDescriptor)) listOfNotNull(DescriptorToSourceUtilsIde.getAnyDeclaration(project, qualifierDescriptor))
qualifierDescriptor is PackageViewDescriptor -> qualifierDescriptor is PackageViewDescriptor ->
@@ -72,12 +74,13 @@ internal fun getTargetParentsByQualifier(
return targetParents.filter { it.canRefactor() } return targetParents.filter { it.canRefactor() }
} }
internal fun getTargetParentsByCall(call: Call, file: KtFile, context: BindingContext): List<PsiElement> { internal fun getTargetParentsByCall(call: Call, context: BindingContext): List<PsiElement> {
val callElement = call.callElement
val receiver = call.explicitReceiver val receiver = call.explicitReceiver
return when (receiver) { return when (receiver) {
null -> getTargetParentsByQualifier(file, false, null) null -> getTargetParentsByQualifier(callElement, false, null)
is Qualifier -> getTargetParentsByQualifier(file, true, context[BindingContext.REFERENCE_TARGET, receiver.referenceExpression]) is Qualifier -> getTargetParentsByQualifier(callElement, true, context[BindingContext.REFERENCE_TARGET, receiver.referenceExpression])
is ReceiverValue -> getTargetParentsByQualifier(file, true, receiver.type.constructor.declarationDescriptor) is ReceiverValue -> getTargetParentsByQualifier(callElement, true, receiver.type.constructor.declarationDescriptor)
else -> throw AssertionError("Unexpected receiver: $receiver") else -> throw AssertionError("Unexpected receiver: $receiver")
} }
} }
@@ -40,7 +40,7 @@ object CreateTypeAliasFromTypeReferenceActionFactory : KotlinSingleIntentionActi
if (!element.languageVersionSettings.supportsFeature(LanguageFeature.TypeAliases)) return null if (!element.languageVersionSettings.supportsFeature(LanguageFeature.TypeAliases)) return null
val classInfo = CreateClassFromTypeReferenceActionFactory.extractFixData(element, diagnostic) ?: return null val classInfo = CreateClassFromTypeReferenceActionFactory.extractFixData(element, diagnostic) ?: return null
val targetParent = classInfo.targetParents.singleOrNull { it !is KtDeclaration && it !is PsiPackage } ?: return null val targetParent = classInfo.applicableParents.singleOrNull { it !is KtDeclaration && it !is PsiPackage } ?: return null
val expectedType = getTypeConstraintInfo(element)?.upperBound val expectedType = getTypeConstraintInfo(element)?.upperBound
if (expectedType != null && expectedType.containsError()) return null if (expectedType != null && expectedType.containsError()) return null
@@ -0,0 +1,7 @@
// "Create class 'Nested'" "true"
class A {
// TARGET_PARENT:
class B {
val a = <caret>Nested()
}
}
@@ -0,0 +1,11 @@
// "Create class 'Nested'" "true"
class A {
// TARGET_PARENT:
class B {
val a = Nested()
class Nested {
}
}
}
@@ -0,0 +1,7 @@
// "Create class 'Nested'" "true"
class A {
// TARGET_PARENT:
inner class B {
val a = <caret>Nested()
}
}
@@ -0,0 +1,11 @@
// "Create class 'Nested'" "true"
class A {
// TARGET_PARENT:
inner class B {
val a = Nested()
inner class Nested {
}
}
}
@@ -0,0 +1,7 @@
// "Create object 'Nested'" "true"
class A {
// TARGET_PARENT:
class B {
val a = <caret>Nested
}
}
@@ -0,0 +1,11 @@
// "Create object 'Nested'" "true"
class A {
// TARGET_PARENT:
class B {
val a = Nested
object Nested {
}
}
}
@@ -0,0 +1,7 @@
// "Create object 'Nested'" "true"
class A {
// TARGET_PARENT:
inner class B {
val a = <caret>Nested
}
}
@@ -0,0 +1,11 @@
// "Create object 'Nested'" "true"
class A {
// TARGET_PARENT:
inner class B {
val a = Nested
}
}
object Nested {
}
@@ -0,0 +1,7 @@
// "Create class 'Nested'" "true"
class A {
// TARGET_PARENT:
class B {
val a: <caret>Nested = Nested()
}
}
@@ -0,0 +1,11 @@
// "Create class 'Nested'" "true"
class A {
// TARGET_PARENT:
class B {
val a: Nested = Nested()
class Nested {
}
}
}
@@ -0,0 +1,7 @@
// "Create class 'Nested'" "true"
class A {
// TARGET_PARENT:
inner class B {
val a: <caret>Nested = Nested()
}
}
@@ -0,0 +1,11 @@
// "Create class 'Nested'" "true"
class A {
// TARGET_PARENT:
inner class B {
val a: Nested = Nested()
inner class Nested {
}
}
}
@@ -1647,6 +1647,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("createNestedClass.kt")
public void testCreateNestedClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/callExpression/createNestedClass.kt");
doTest(fileName);
}
@TestMetadata("createNestedClassInInner.kt")
public void testCreateNestedClassInInner() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/callExpression/createNestedClassInInner.kt");
doTest(fileName);
}
@TestMetadata("expectedTypeBySuperFunction.kt") @TestMetadata("expectedTypeBySuperFunction.kt")
public void testExpectedTypeBySuperFunction() throws Exception { public void testExpectedTypeBySuperFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/callExpression/expectedTypeBySuperFunction.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/callExpression/expectedTypeBySuperFunction.kt");
@@ -1959,6 +1971,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("createNestedObject.kt")
public void testCreateNestedObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/referenceExpression/createNestedObject.kt");
doTest(fileName);
}
@TestMetadata("createNestedObjectInInner.kt")
public void testCreateNestedObjectInInner() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/referenceExpression/createNestedObjectInInner.kt");
doTest(fileName);
}
@TestMetadata("enumByClassLiteral.kt") @TestMetadata("enumByClassLiteral.kt")
public void testEnumByClassLiteral() throws Exception { public void testEnumByClassLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/referenceExpression/enumByClassLiteral.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/referenceExpression/enumByClassLiteral.kt");
@@ -2196,6 +2220,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("createNestedClass.kt")
public void testCreateNestedClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/createNestedClass.kt");
doTest(fileName);
}
@TestMetadata("createNestedClassInInner.kt")
public void testCreateNestedClassInInner() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/createNestedClassInInner.kt");
doTest(fileName);
}
@TestMetadata("enumEntryNotQualifierNoTypeArgs.kt") @TestMetadata("enumEntryNotQualifierNoTypeArgs.kt")
public void testEnumEntryNotQualifierNoTypeArgs() throws Exception { public void testEnumEntryNotQualifierNoTypeArgs() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/enumEntryNotQualifierNoTypeArgs.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/enumEntryNotQualifierNoTypeArgs.kt");