From f0a0df94b5ab44a2bfdb22399b0b7e9a5941421c Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Wed, 8 Oct 2014 17:46:19 +0400 Subject: [PATCH] Create From Usage: Add support of property delegates --- .../callableBuilder/CallableInfo.kt | 2 +- .../callableBuilder/typeUtils.kt | 33 +++++++++++++++---- .../CreateIteratorFunctionActionFactory.kt | 2 +- .../CreateParameterActionFactory.kt | 6 ++-- .../call/afterMemberValDelegateRuntime.kt | 11 +++++++ .../call/afterMemberVarDelegateRuntime.kt | 11 +++++++ .../call/beforeMemberValDelegateRuntime.kt | 5 +++ .../call/beforeMemberVarDelegateRuntime.kt | 5 +++ .../property/afterMemberValDelegateRuntime.kt | 11 +++++++ .../property/afterMemberVarDelegateRuntime.kt | 11 +++++++ .../beforeMemberValDelegateRuntime.kt | 7 ++++ .../beforeMemberVarDelegateRuntime.kt | 7 ++++ .../quickfix/QuickFixTestGenerated.java | 24 ++++++++++++++ 13 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/afterMemberValDelegateRuntime.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/afterMemberVarDelegateRuntime.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/beforeMemberValDelegateRuntime.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/beforeMemberVarDelegateRuntime.kt create mode 100644 idea/testData/quickfix/createFromUsage/createVariable/property/afterMemberValDelegateRuntime.kt create mode 100644 idea/testData/quickfix/createFromUsage/createVariable/property/afterMemberVarDelegateRuntime.kt create mode 100644 idea/testData/quickfix/createFromUsage/createVariable/property/beforeMemberValDelegateRuntime.kt create mode 100644 idea/testData/quickfix/createFromUsage/createVariable/property/beforeMemberVarDelegateRuntime.kt diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/CallableInfo.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/CallableInfo.kt index f8ea38c0dcc..08e722af81f 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/CallableInfo.kt +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/CallableInfo.kt @@ -29,7 +29,7 @@ abstract class TypeInfo(val variance: Variance) { } override fun getPossibleTypes(builder: CallableBuilder): List = - expression.guessTypes(builder.currentFileContext).flatMap { it.getPossibleSupertypes(variance) } + expression.guessTypes(builder.currentFileContext, builder.currentFileModule).flatMap { it.getPossibleSupertypes(variance) } } class ByType(val theType: JetType, variance: Variance, val keepUnsubstituted: Boolean = false): TypeInfo(variance) { diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/typeUtils.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/typeUtils.kt index 8bb2083f665..6748c72252d 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/typeUtils.kt +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/typeUtils.kt @@ -26,6 +26,13 @@ import org.jetbrains.jet.lang.psi.psiUtil.getAssignmentByLHS import org.jetbrains.jet.lang.resolve.bindingContextUtil.isUsedAsStatement import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns import org.jetbrains.jet.lang.psi.JetDeclaration +import org.jetbrains.jet.lang.psi.JetPropertyDelegate +import org.jetbrains.jet.lang.psi.JetProperty +import org.jetbrains.jet.lang.descriptors.ModuleDescriptor +import org.jetbrains.jet.lang.resolve.lazy.ResolveSessionUtils +import org.jetbrains.jet.lang.resolve.name.FqName +import kotlin.properties.Delegates +import org.jetbrains.jet.lang.descriptors.PropertyDescriptor private fun JetType.contains(inner: JetType): Boolean { return JetTypeChecker.DEFAULT.equalTypes(this, inner) || getArguments().any { inner in it.getType() } @@ -74,8 +81,10 @@ fun JetType.getTypeParameters(): Set { return typeParameters } -fun JetExpression.guessTypes(context: BindingContext): Array { - if (this !is JetDeclaration && isUsedAsStatement(context)) return array(KotlinBuiltIns.getInstance().getUnitType()) +fun JetExpression.guessTypes(context: BindingContext, module: ModuleDescriptor?): Array { + val builtIns = KotlinBuiltIns.getInstance() + + if (this !is JetDeclaration && isUsedAsStatement(context)) return array(builtIns.getUnitType()) // if we know the actual type of the expression val theType1 = context[BindingContext.EXPRESSION_TYPE, this] @@ -89,15 +98,16 @@ fun JetExpression.guessTypes(context: BindingContext): Array { return array(theType2) } + val parent = getParent() return when { this is JetTypeConstraint -> { // expression itself is a type assertion val constraint = (this as JetTypeConstraint) array(context[BindingContext.TYPE, constraint.getBoundTypeReference()]!!) } - getParent() is JetTypeConstraint -> { + parent is JetTypeConstraint -> { // expression is on the left side of a type assertion - val constraint = (getParent() as JetTypeConstraint) + val constraint = (parent as JetTypeConstraint) array(context[BindingContext.TYPE, constraint.getBoundTypeReference()]!!) } this is JetMultiDeclarationEntry -> { @@ -124,9 +134,9 @@ fun JetExpression.guessTypes(context: BindingContext): Array { guessType(context) } } - getParent() is JetVariableDeclaration -> { + parent is JetVariableDeclaration -> { // the expression is the RHS of a variable assignment with a specified type - val variable = getParent() as JetVariableDeclaration + val variable = parent as JetVariableDeclaration val typeRef = variable.getTypeReference() if (typeRef != null) { // and has a specified type @@ -137,6 +147,17 @@ fun JetExpression.guessTypes(context: BindingContext): Array { variable.guessType(context) } } + parent is JetPropertyDelegate && module != null -> { + val property = context[BindingContext.DECLARATION_TO_DESCRIPTOR, parent.getParent() as JetProperty] as PropertyDescriptor + val delegateClassName = if (property.isVar() ) "ReadWriteProperty" else "ReadOnlyProperty" + val delegateClass = + ResolveSessionUtils.getClassDescriptorsByFqName(module, FqName("kotlin.properties.$delegateClassName")).firstOrNull() + ?: return array(builtIns.getAnyType()) + val receiverType = (property.getExtensionReceiverParameter() ?: property.getDispatchReceiverParameter())?.getType() + ?: builtIns.getNullableNothingType() + val typeArguments = listOf(TypeProjectionImpl(receiverType), TypeProjectionImpl(property.getType())) + array(TypeUtils.substituteProjectionsForParameters(delegateClass, typeArguments)) + } else -> array() // can't infer anything } } diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateIteratorFunctionActionFactory.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateIteratorFunctionActionFactory.kt index c7a2488a72b..75f10e93bf6 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateIteratorFunctionActionFactory.kt +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateIteratorFunctionActionFactory.kt @@ -25,7 +25,7 @@ object CreateIteratorFunctionActionFactory : JetSingleIntentionActionFactory() { val returnJetType = KotlinBuiltIns.getInstance().getIterator().getDefaultType() val context = file.getBindingContext() - val returnJetTypeParameterTypes = variableExpr.guessTypes(context) + val returnJetTypeParameterTypes = variableExpr.guessTypes(context, null) if (returnJetTypeParameterTypes.size != 1) return null val returnJetTypeParameterType = TypeProjectionImpl(returnJetTypeParameterTypes[0]) diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createVariable/CreateParameterActionFactory.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createVariable/CreateParameterActionFactory.kt index 2c486ddcc8d..38575f790b6 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createVariable/CreateParameterActionFactory.kt +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createVariable/CreateParameterActionFactory.kt @@ -40,6 +40,7 @@ import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils import org.jetbrains.jet.lang.psi.JetNamedFunction import org.jetbrains.jet.lang.psi.psiUtil.getAssignmentByLHS import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.getExpressionForTypeGuess +import org.jetbrains.jet.plugin.caches.resolve.getAnalysisResults object CreateParameterActionFactory: JetSingleIntentionActionFactory() { private fun JetType.hasTypeParametersToAdd(functionDescriptor: FunctionDescriptor, context: BindingContext): Boolean { @@ -65,14 +66,15 @@ object CreateParameterActionFactory: JetSingleIntentionActionFactory() { } override fun createAction(diagnostic: Diagnostic): IntentionAction? { - val context = (diagnostic.getPsiFile() as? JetFile)?.getBindingContext() ?: return null + val exhaust = (diagnostic.getPsiFile() as? JetFile)?.getAnalysisResults() ?: return null + val context = exhaust.getBindingContext() val refExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) ?: return null if (refExpr.getQualifiedElement() != refExpr) return null val varExpected = refExpr.getAssignmentByLHS() != null - val paramType = refExpr.getExpressionForTypeGuess().guessTypes(context).let { + val paramType = refExpr.getExpressionForTypeGuess().guessTypes(context, exhaust.getModuleDescriptor()).let { when (it.size) { 0 -> KotlinBuiltIns.getInstance().getAnyType() 1 -> it.first() diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/afterMemberValDelegateRuntime.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/afterMemberValDelegateRuntime.kt new file mode 100644 index 00000000000..3247ba61116 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/afterMemberValDelegateRuntime.kt @@ -0,0 +1,11 @@ +// "Create function 'foo' from usage" "true" + +import kotlin.properties.ReadOnlyProperty + +class A(val t: T) { + val x: A by foo(t, "") + + fun foo(t: T, s: String): ReadOnlyProperty, A> { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/afterMemberVarDelegateRuntime.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/afterMemberVarDelegateRuntime.kt new file mode 100644 index 00000000000..e0cf488ec55 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/afterMemberVarDelegateRuntime.kt @@ -0,0 +1,11 @@ +// "Create function 'foo' from usage" "true" + +import kotlin.properties.ReadWriteProperty + +class A(val t: T) { + var x: A by foo(t, "") + + fun foo(t: T, s: String): ReadWriteProperty, A> { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/beforeMemberValDelegateRuntime.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/beforeMemberValDelegateRuntime.kt new file mode 100644 index 00000000000..cf0e461daa4 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/beforeMemberValDelegateRuntime.kt @@ -0,0 +1,5 @@ +// "Create function 'foo' from usage" "true" + +class A(val t: T) { + val x: A by foo(t, "") +} diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/beforeMemberVarDelegateRuntime.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/beforeMemberVarDelegateRuntime.kt new file mode 100644 index 00000000000..b2376d9c559 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/beforeMemberVarDelegateRuntime.kt @@ -0,0 +1,5 @@ +// "Create function 'foo' from usage" "true" + +class A(val t: T) { + var x: A by foo(t, "") +} diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/afterMemberValDelegateRuntime.kt b/idea/testData/quickfix/createFromUsage/createVariable/property/afterMemberValDelegateRuntime.kt new file mode 100644 index 00000000000..68ada12997a --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/afterMemberValDelegateRuntime.kt @@ -0,0 +1,11 @@ +// "Create property 'foo' from usage" "true" +// ERROR: Property must be initialized or be abstract +// ERROR: Variable 'foo' must be initialized + +import kotlin.properties.ReadOnlyProperty + +class A { + val foo: ReadOnlyProperty, A> + + val x: A by foo +} diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/afterMemberVarDelegateRuntime.kt b/idea/testData/quickfix/createFromUsage/createVariable/property/afterMemberVarDelegateRuntime.kt new file mode 100644 index 00000000000..f12fe45cc61 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/afterMemberVarDelegateRuntime.kt @@ -0,0 +1,11 @@ +// "Create property 'foo' from usage" "true" +// ERROR: Property must be initialized or be abstract +// ERROR: Variable 'foo' must be initialized + +import kotlin.properties.ReadWriteProperty + +class A { + val foo: ReadWriteProperty, A> + + var x: A by foo +} diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/beforeMemberValDelegateRuntime.kt b/idea/testData/quickfix/createFromUsage/createVariable/property/beforeMemberValDelegateRuntime.kt new file mode 100644 index 00000000000..3d53929b975 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/beforeMemberValDelegateRuntime.kt @@ -0,0 +1,7 @@ +// "Create property 'foo' from usage" "true" +// ERROR: Property must be initialized or be abstract +// ERROR: Variable 'foo' must be initialized + +class A { + val x: A by foo +} diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/beforeMemberVarDelegateRuntime.kt b/idea/testData/quickfix/createFromUsage/createVariable/property/beforeMemberVarDelegateRuntime.kt new file mode 100644 index 00000000000..58128d2d772 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/beforeMemberVarDelegateRuntime.kt @@ -0,0 +1,7 @@ +// "Create property 'foo' from usage" "true" +// ERROR: Property must be initialized or be abstract +// ERROR: Variable 'foo' must be initialized + +class A { + var x: A by foo +} diff --git a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java index d493588a33f..ed2a64d6d46 100644 --- a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java @@ -844,6 +844,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("beforeMemberValDelegateRuntime.kt") + public void testMemberValDelegateRuntime() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/beforeMemberValDelegateRuntime.kt"); + doTest(fileName); + } + + @TestMetadata("beforeMemberVarDelegateRuntime.kt") + public void testMemberVarDelegateRuntime() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/beforeMemberVarDelegateRuntime.kt"); + doTest(fileName); + } + @TestMetadata("beforeObjectMemberFunNoReceiver.kt") public void testObjectMemberFunNoReceiver() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/beforeObjectMemberFunNoReceiver.kt"); @@ -1541,12 +1553,24 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("beforeMemberValDelegateRuntime.kt") + public void testMemberValDelegateRuntime() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/beforeMemberValDelegateRuntime.kt"); + doTest(fileName); + } + @TestMetadata("beforeMemberValNoReceiver.kt") public void testMemberValNoReceiver() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/beforeMemberValNoReceiver.kt"); doTest(fileName); } + @TestMetadata("beforeMemberVarDelegateRuntime.kt") + public void testMemberVarDelegateRuntime() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/beforeMemberVarDelegateRuntime.kt"); + doTest(fileName); + } + @TestMetadata("beforeObjectMemberValNoReceiver.kt") public void testObjectMemberValNoReceiver() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/beforeObjectMemberValNoReceiver.kt");