Create class quick fix: suggest on supertype entry

#KT-32511 Fixed
This commit is contained in:
Toshiaki Kameyama
2020-02-28 22:58:02 +09:00
committed by Yan Zhulanow
parent 4973d3c359
commit 21d6d50241
13 changed files with 107 additions and 19 deletions
@@ -542,7 +542,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
psiFactory.createEnumEntry("$safeName${if (hasParameters) "()" else " "}")
}
else -> {
val openMod = if (open) "open " else ""
val openMod = if (open && kind != ClassKind.INTERFACE) "open " else ""
val innerMod = if (inner || isInsideInnerOrLocalClass()) "inner " else ""
val typeParamList = when (kind) {
ClassKind.PLAIN_CLASS, ClassKind.INTERFACE -> "<>"
@@ -9,8 +9,10 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.types.KotlinType
@@ -28,16 +30,17 @@ object CreateClassFromTypeReferenceActionFactory : CreateClassFromUsageFactory<K
val typeRefParent = element.parent.parent
if (typeRefParent is KtConstructorCalleeExpression) return Collections.emptyList()
val interfaceExpected = typeRefParent is KtSuperTypeEntry
val isQualifier = (element.parent as? KtUserType)?.let { it.qualifier == element } ?: false
val typeReference = element.parent as? KtTypeReference
val isUpperBound =
typeReference?.getParentOfTypeAndBranch<KtTypeParameter> { extendsBound } != null || typeReference?.getParentOfTypeAndBranch<KtTypeConstraint> { boundTypeReference } != null
return when {
interfaceExpected -> Collections.singletonList(ClassKind.INTERFACE)
return when (typeRefParent) {
is KtSuperTypeEntry -> listOfNotNull(
ClassKind.INTERFACE,
if (typeRefParent.classExpected()) ClassKind.PLAIN_CLASS else null
)
else -> ClassKind.values().filter {
val noTypeArguments = element.typeArgumentsAsTypes.isEmpty()
when (it) {
@@ -51,6 +54,13 @@ object CreateClassFromTypeReferenceActionFactory : CreateClassFromUsageFactory<K
}
}
private fun KtSuperTypeEntry.classExpected(): Boolean {
val containingClass = getStrictParentOfType<KtClass>() ?: return false
return !containingClass.hasModifier(KtTokens.ANNOTATION_KEYWORD)
&& !containingClass.hasModifier(KtTokens.ENUM_KEYWORD)
&& !containingClass.hasModifier(KtTokens.INLINE_KEYWORD)
}
private fun getExpectedUpperBound(element: KtUserType, context: BindingContext): KotlinType? {
val projection = (element.parent as? KtTypeReference)?.parent as? KtTypeProjection ?: return null
val argumentList = projection.parent as? KtTypeArgumentList ?: return null
@@ -64,7 +74,8 @@ object CreateClassFromTypeReferenceActionFactory : CreateClassFromUsageFactory<K
override fun extractFixData(element: KtUserType, diagnostic: Diagnostic): ClassInfo? {
val name = element.referenceExpression?.getReferencedName() ?: return null
if (element.parent.parent is KtConstructorCalleeExpression) return null
val typeRefParent = element.parent.parent
if (typeRefParent is KtConstructorCalleeExpression) return null
val (context, module) = element.analyzeAndGetResult()
val qualifier = element.qualifier?.referenceExpression
@@ -79,6 +90,7 @@ object CreateClassFromTypeReferenceActionFactory : CreateClassFromUsageFactory<K
name = name,
targetParents = targetParents,
expectedTypeInfo = expectedUpperBound?.let { TypeInfo.ByType(it, Variance.INVARIANT) } ?: TypeInfo.Empty,
open = typeRefParent is KtSuperTypeEntry && typeRefParent.classExpected(),
typeArguments = element.typeArgumentsAsTypes.map {
if (it != null) TypeInfo(it, Variance.INVARIANT) else TypeInfo(anyType, Variance.INVARIANT)
}
@@ -1,10 +0,0 @@
// "Create class 'A'" "false"
// ACTION: Create interface 'A'
// ACTION: Create type parameter 'A' in class 'Foo'
// ACTION: Create test
// ERROR: Unresolved reference: A
package p
class Foo: <caret>A {
}
@@ -0,0 +1,4 @@
// "Create class 'Unknown'" "true"
class A : Unknown<caret> {
constructor()
}
@@ -0,0 +1,8 @@
// "Create class 'Unknown'" "true"
class A : Unknown {
constructor()
}
open class Unknown {
}
@@ -0,0 +1,6 @@
// "Create class 'Unknown'" "true"
// ACTION: Create interface 'Unknown'
// ACTION: Create type parameter 'Unknown' in class 'A'
// DISABLE-ERRORS
class A : Unknown<caret> {
}
@@ -0,0 +1,10 @@
// "Create class 'Unknown'" "true"
// ACTION: Create interface 'Unknown'
// ACTION: Create type parameter 'Unknown' in class 'A'
// DISABLE-ERRORS
class A : Unknown {
}
open class Unknown {
}
@@ -0,0 +1,7 @@
// "Create class 'Unknown'" "true"
// ACTION: Create interface 'Unknown'
// ACTION: Create type parameter 'Unknown' in class 'A'
// DISABLE-ERRORS
class A() : Unknown<caret> {
constructor(i: Int)
}
@@ -0,0 +1,11 @@
// "Create class 'Unknown'" "true"
// ACTION: Create interface 'Unknown'
// ACTION: Create type parameter 'Unknown' in class 'A'
// DISABLE-ERRORS
class A() : Unknown {
constructor(i: Int)
}
open class Unknown {
}
@@ -0,0 +1,5 @@
// "Create class 'Unknown'" "false"
// ACTION: Create interface 'Unknown'
// ACTION: Create type parameter 'Unknown' in class 'A'
// DISABLE-ERRORS
annotation class A : Unknown<caret>
@@ -0,0 +1,5 @@
// "Create class 'Unknown'" "false"
// ACTION: Create interface 'Unknown'
// ACTION: Create type parameter 'Unknown' in class 'A'
// DISABLE-ERRORS
enum class A : Unknown<caret>
@@ -0,0 +1,5 @@
// "Create class 'Unknown'" "false"
// ACTION: Create interface 'Unknown'
// ACTION: Create type parameter 'Unknown' in class 'A'
// DISABLE-ERRORS
inline class A(val a: String) : Unknown<caret>
@@ -3277,9 +3277,34 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true);
}
@TestMetadata("classDelegatorToSuperclass.kt")
public void testClassDelegatorToSuperclass() throws Exception {
runTest("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/classDelegatorToSuperclass.kt");
@TestMetadata("createSuperClassFromSuperTypeEntry.kt")
public void testCreateSuperClassFromSuperTypeEntry() throws Exception {
runTest("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/createSuperClassFromSuperTypeEntry.kt");
}
@TestMetadata("createSuperClassFromSuperTypeEntry2.kt")
public void testCreateSuperClassFromSuperTypeEntry2() throws Exception {
runTest("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/createSuperClassFromSuperTypeEntry2.kt");
}
@TestMetadata("createSuperClassFromSuperTypeEntry3.kt")
public void testCreateSuperClassFromSuperTypeEntry3() throws Exception {
runTest("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/createSuperClassFromSuperTypeEntry3.kt");
}
@TestMetadata("createSuperClassFromSuperTypeEntryForAnnotation.kt")
public void testCreateSuperClassFromSuperTypeEntryForAnnotation() throws Exception {
runTest("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/createSuperClassFromSuperTypeEntryForAnnotation.kt");
}
@TestMetadata("createSuperClassFromSuperTypeEntryForEnum.kt")
public void testCreateSuperClassFromSuperTypeEntryForEnum() throws Exception {
runTest("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/createSuperClassFromSuperTypeEntryForEnum.kt");
}
@TestMetadata("createSuperClassFromSuperTypeEntryForInline.kt")
public void testCreateSuperClassFromSuperTypeEntryForInline() throws Exception {
runTest("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/createSuperClassFromSuperTypeEntryForInline.kt");
}
@TestMetadata("createSuperclassInsideSubclass.kt")