diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt index f212d60c8cc..5e88306649d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt @@ -56,7 +56,7 @@ private fun DeclarationDescriptor.render( } private fun JetType.render(typeParameterNameMap: Map, 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 { + val visitedTypes = HashSet() val typeParameters = LinkedHashSet() - 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 } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/createClassUtils.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/createClassUtils.kt index c5daf3a671b..221f630d05b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/createClassUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/createClassUtils.kt @@ -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) diff --git a/idea/testData/quickfix/createFromUsage/createClass/referenceExpression/recursiveBound.kt b/idea/testData/quickfix/createFromUsage/createClass/referenceExpression/recursiveBound.kt new file mode 100644 index 00000000000..104b2f25906 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createClass/referenceExpression/recursiveBound.kt @@ -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> + +fun test() { + val c : Cyclic<*> = Foo +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/parameter/recursiveBound.kt b/idea/testData/quickfix/createFromUsage/createVariable/parameter/recursiveBound.kt new file mode 100644 index 00000000000..97fe1a5169c --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/parameter/recursiveBound.kt @@ -0,0 +1,6 @@ +// "Create parameter 'foo'" "true" +class Cyclic> + +fun test() { + val c : Cyclic<*> = foo +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/parameter/recursiveBound.kt.after b/idea/testData/quickfix/createFromUsage/createVariable/parameter/recursiveBound.kt.after new file mode 100644 index 00000000000..e8994a09bc8 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/parameter/recursiveBound.kt.after @@ -0,0 +1,6 @@ +// "Create parameter 'foo'" "true" +class Cyclic> + +fun test(foo: Cyclic<*>) { + val c : Cyclic<*> = foo +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/recursiveBound.kt b/idea/testData/quickfix/createFromUsage/createVariable/property/recursiveBound.kt new file mode 100644 index 00000000000..4c9f67493ea --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/recursiveBound.kt @@ -0,0 +1,7 @@ +// "Create property 'foo'" "true" +// ERROR: Property must be initialized +class Cyclic> + +fun test() { + val c : Cyclic<*> = foo +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/recursiveBound.kt.after b/idea/testData/quickfix/createFromUsage/createVariable/property/recursiveBound.kt.after new file mode 100644 index 00000000000..b129959307c --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/recursiveBound.kt.after @@ -0,0 +1,9 @@ +// "Create property 'foo'" "true" +// ERROR: Property must be initialized +class Cyclic> + +val foo: Cyclic<*> + +fun test() { + val c : Cyclic<*> = foo +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 3cc14a50109..e087b5a32a3 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -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");