Create From Usage: Fix StackOverflowException on recursive type parameter bounds

#EA-69240 Fixed
This commit is contained in:
Alexey Sedunov
2015-08-18 15:57:32 +03:00
parent 69218e2647
commit d18a9f6d8a
8 changed files with 78 additions and 14 deletions
@@ -56,7 +56,7 @@ private fun DeclarationDescriptor.render(
} }
private fun JetType.render(typeParameterNameMap: Map<TypeParameterDescriptor, String>, fq: Boolean): String { 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 typeString = getConstructor().getDeclarationDescriptor()!!.render(typeParameterNameMap, fq)
val typeArgumentString = if (arguments.isNotEmpty()) arguments.joinToString(", ", "<", ">") else "" val typeArgumentString = if (arguments.isNotEmpty()) arguments.joinToString(", ", "<", ">") else ""
val nullifier = if (isMarkedNullable()) "?" 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> { fun JetType.getTypeParameters(): Set<TypeParameterDescriptor> {
val visitedTypes = HashSet<JetType>()
val typeParameters = LinkedHashSet<TypeParameterDescriptor>() val typeParameters = LinkedHashSet<TypeParameterDescriptor>()
val arguments = getArguments()
if (arguments.isEmpty()) { fun traverseTypes(type: JetType) {
val descriptor = getConstructor().getDeclarationDescriptor() if (!visitedTypes.add(type)) return
if (descriptor is TypeParameterDescriptor) {
typeParameters.add(descriptor) val arguments = type.arguments
} if (arguments.isEmpty()) {
} val descriptor = type.constructor.declarationDescriptor
else { if (descriptor is TypeParameterDescriptor) {
arguments.flatMapTo(typeParameters) { projection -> typeParameters.add(descriptor)
projection.getType().getTypeParameters() }
return
} }
arguments.forEach { traverseTypes(it.type) }
} }
traverseTypes(this)
return typeParameters return typeParameters
} }
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.refactoring.canRefactor import org.jetbrains.kotlin.idea.core.refactoring.canRefactor
import org.jetbrains.kotlin.idea.quickfix.DelegatingIntentionAction import org.jetbrains.kotlin.idea.quickfix.DelegatingIntentionAction
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo 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.guessTypes
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.noSubstitutions import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.noSubstitutions
import org.jetbrains.kotlin.psi.Call 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.DescriptorUtils
import org.jetbrains.kotlin.resolve.scopes.receivers.Qualifier import org.jetbrains.kotlin.resolve.scopes.receivers.Qualifier
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue 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.Variance
import org.jetbrains.kotlin.types.checker.JetTypeChecker
import org.jetbrains.kotlin.descriptors.ClassKind as ClassDescriptorKind import org.jetbrains.kotlin.descriptors.ClassKind as ClassDescriptorKind
private fun String.checkClassName(): Boolean = isNotEmpty() && Character.isUpperCase(first()) private fun String.checkClassName(): Boolean = isNotEmpty() && Character.isUpperCase(first())
@@ -93,7 +92,7 @@ private fun JetExpression.getInheritableTypeInfo(
val type = types.first() val type = types.first()
val descriptor = type.getConstructor().getDeclarationDescriptor() ?: return TypeInfo.Empty to { classKind -> false } 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) val isEnum = DescriptorUtils.isEnumClass(descriptor)
if (!(canHaveSubtypes || isEnum) if (!(canHaveSubtypes || isEnum)
@@ -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
}
@@ -0,0 +1,6 @@
// "Create parameter 'foo'" "true"
class Cyclic<E : Cyclic<E>>
fun test() {
val c : Cyclic<*> = <caret>foo
}
@@ -0,0 +1,6 @@
// "Create parameter 'foo'" "true"
class Cyclic<E : Cyclic<E>>
fun test(foo: Cyclic<*>) {
val c : Cyclic<*> = foo
}
@@ -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
}
@@ -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); 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") @TestMetadata("traitByQualifier.kt")
public void testTraitByQualifier() throws Exception { public void testTraitByQualifier() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/referenceExpression/traitByQualifier.kt"); String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/referenceExpression/traitByQualifier.kt");
@@ -2666,6 +2672,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest(fileName); 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") @TestMetadata("withPackageName.kt")
public void testWithPackageName() throws Exception { public void testWithPackageName() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/parameter/withPackageName.kt"); String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/parameter/withPackageName.kt");
@@ -2747,6 +2759,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest(fileName); 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") @TestMetadata("refInImport.kt")
public void testRefInImport() throws Exception { public void testRefInImport() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/refInImport.kt"); String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/refInImport.kt");