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 {
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
}
@@ -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)
@@ -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);
}
@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");