Create From Usage: Fix StackOverflowException on recursive type parameter bounds
#EA-69240 Fixed
This commit is contained in:
+19
-11
@@ -56,7 +56,7 @@ private fun DeclarationDescriptor.render(
|
||||
}
|
||||
|
||||
private fun JetType.render(typeParameterNameMap: Map<TypeParameterDescriptor, String>, fq: Boolean): String {
|
||||
val arguments = getArguments().map { it.getType().render(typeParameterNameMap, fq) }
|
||||
val arguments = getArguments().map { if (it.isStarProjection) "*" else it.getType().render(typeParameterNameMap, fq) }
|
||||
val typeString = getConstructor().getDeclarationDescriptor()!!.render(typeParameterNameMap, fq)
|
||||
val typeArgumentString = if (arguments.isNotEmpty()) arguments.joinToString(", ", "<", ">") else ""
|
||||
val nullifier = if (isMarkedNullable()) "?" else ""
|
||||
@@ -73,20 +73,28 @@ private fun getTypeParameterNamesNotInScope(typeParameters: Collection<TypeParam
|
||||
}
|
||||
}
|
||||
|
||||
fun JetType.containsStarProjections(): Boolean = arguments.any { it.isStarProjection || it.type.containsStarProjections() }
|
||||
|
||||
fun JetType.getTypeParameters(): Set<TypeParameterDescriptor> {
|
||||
val visitedTypes = HashSet<JetType>()
|
||||
val typeParameters = LinkedHashSet<TypeParameterDescriptor>()
|
||||
val arguments = getArguments()
|
||||
if (arguments.isEmpty()) {
|
||||
val descriptor = getConstructor().getDeclarationDescriptor()
|
||||
if (descriptor is TypeParameterDescriptor) {
|
||||
typeParameters.add(descriptor)
|
||||
}
|
||||
}
|
||||
else {
|
||||
arguments.flatMapTo(typeParameters) { projection ->
|
||||
projection.getType().getTypeParameters()
|
||||
|
||||
fun traverseTypes(type: JetType) {
|
||||
if (!visitedTypes.add(type)) return
|
||||
|
||||
val arguments = type.arguments
|
||||
if (arguments.isEmpty()) {
|
||||
val descriptor = type.constructor.declarationDescriptor
|
||||
if (descriptor is TypeParameterDescriptor) {
|
||||
typeParameters.add(descriptor)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
arguments.forEach { traverseTypes(it.type) }
|
||||
}
|
||||
|
||||
traverseTypes(this)
|
||||
return typeParameters
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.core.refactoring.canRefactor
|
||||
import org.jetbrains.kotlin.idea.quickfix.DelegatingIntentionAction
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.containsStarProjections
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.guessTypes
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.noSubstitutions
|
||||
import org.jetbrains.kotlin.psi.Call
|
||||
@@ -39,9 +40,7 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.Qualifier
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind as ClassDescriptorKind
|
||||
|
||||
private fun String.checkClassName(): Boolean = isNotEmpty() && Character.isUpperCase(first())
|
||||
@@ -93,7 +92,7 @@ private fun JetExpression.getInheritableTypeInfo(
|
||||
val type = types.first()
|
||||
val descriptor = type.getConstructor().getDeclarationDescriptor() ?: return TypeInfo.Empty to { classKind -> false }
|
||||
|
||||
val canHaveSubtypes = TypeUtils.canHaveSubtypes(JetTypeChecker.DEFAULT, type)
|
||||
val canHaveSubtypes = !(type.constructor.isFinal || type.containsStarProjections())
|
||||
val isEnum = DescriptorUtils.isEnumClass(descriptor)
|
||||
|
||||
if (!(canHaveSubtypes || isEnum)
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Create object 'Foo'" "false"
|
||||
// ACTION: Create local variable 'Foo'
|
||||
// ACTION: Create parameter 'Foo'
|
||||
// ACTION: Create property 'Foo'
|
||||
// ACTION: Split property declaration
|
||||
// ERROR: Unresolved reference: Foo
|
||||
open class Cyclic<E : Cyclic<E>>
|
||||
|
||||
fun test() {
|
||||
val c : Cyclic<*> = <caret>Foo
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// "Create parameter 'foo'" "true"
|
||||
class Cyclic<E : Cyclic<E>>
|
||||
|
||||
fun test() {
|
||||
val c : Cyclic<*> = <caret>foo
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// "Create parameter 'foo'" "true"
|
||||
class Cyclic<E : Cyclic<E>>
|
||||
|
||||
fun test(foo: Cyclic<*>) {
|
||||
val c : Cyclic<*> = foo
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// "Create property 'foo'" "true"
|
||||
// ERROR: Property must be initialized
|
||||
class Cyclic<E : Cyclic<E>>
|
||||
|
||||
fun test() {
|
||||
val c : Cyclic<*> = <caret>foo
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Create property 'foo'" "true"
|
||||
// ERROR: Property must be initialized
|
||||
class Cyclic<E : Cyclic<E>>
|
||||
|
||||
val foo: Cyclic<*>
|
||||
|
||||
fun test() {
|
||||
val c : Cyclic<*> = foo
|
||||
}
|
||||
@@ -1304,6 +1304,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("recursiveBound.kt")
|
||||
public void testRecursiveBound() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/referenceExpression/recursiveBound.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("traitByQualifier.kt")
|
||||
public void testTraitByQualifier() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/referenceExpression/traitByQualifier.kt");
|
||||
@@ -2666,6 +2672,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("recursiveBound.kt")
|
||||
public void testRecursiveBound() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/parameter/recursiveBound.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("withPackageName.kt")
|
||||
public void testWithPackageName() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/parameter/withPackageName.kt");
|
||||
@@ -2747,6 +2759,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("recursiveBound.kt")
|
||||
public void testRecursiveBound() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/recursiveBound.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("refInImport.kt")
|
||||
public void testRefInImport() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/refInImport.kt");
|
||||
|
||||
Reference in New Issue
Block a user