Create From Usage: Add support of property delegates

This commit is contained in:
Alexey Sedunov
2014-10-08 17:46:19 +04:00
parent 2e6d4b3501
commit f0a0df94b5
13 changed files with 125 additions and 10 deletions
@@ -29,7 +29,7 @@ abstract class TypeInfo(val variance: Variance) {
}
override fun getPossibleTypes(builder: CallableBuilder): List<JetType> =
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) {
@@ -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<TypeParameterDescriptor> {
return typeParameters
}
fun JetExpression.guessTypes(context: BindingContext): Array<JetType> {
if (this !is JetDeclaration && isUsedAsStatement(context)) return array(KotlinBuiltIns.getInstance().getUnitType())
fun JetExpression.guessTypes(context: BindingContext, module: ModuleDescriptor?): Array<JetType> {
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<JetType> {
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<JetType> {
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<JetType> {
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
}
}
@@ -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])
@@ -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<JetSimpleNameExpression>()) ?: 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()
@@ -0,0 +1,11 @@
// "Create function 'foo' from usage" "true"
import kotlin.properties.ReadOnlyProperty
class A<T>(val t: T) {
val x: A<Int> by foo(t, "")
fun <T> foo(t: T, s: String): ReadOnlyProperty<A<T>, A<Int>> {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
@@ -0,0 +1,11 @@
// "Create function 'foo' from usage" "true"
import kotlin.properties.ReadWriteProperty
class A<T>(val t: T) {
var x: A<Int> by foo(t, "")
fun <T> foo(t: T, s: String): ReadWriteProperty<A<T>, A<Int>> {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
@@ -0,0 +1,5 @@
// "Create function 'foo' from usage" "true"
class A<T>(val t: T) {
val x: A<Int> by <caret>foo(t, "")
}
@@ -0,0 +1,5 @@
// "Create function 'foo' from usage" "true"
class A<T>(val t: T) {
var x: A<Int> by <caret>foo(t, "")
}
@@ -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<T> {
val <T> foo: ReadOnlyProperty<A<T>, A<Int>>
val x: A<Int> by foo
}
@@ -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<T> {
val <T> foo: ReadWriteProperty<A<T>, A<Int>>
var x: A<Int> by foo
}
@@ -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<T> {
val x: A<Int> by <caret>foo
}
@@ -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<T> {
var x: A<Int> by <caret>foo
}
@@ -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");