Create From Usage: Add support of property delegates
This commit is contained in:
+1
-1
@@ -29,7 +29,7 @@ abstract class TypeInfo(val variance: Variance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getPossibleTypes(builder: CallableBuilder): List<JetType> =
|
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) {
|
class ByType(val theType: JetType, variance: Variance, val keepUnsubstituted: Boolean = false): TypeInfo(variance) {
|
||||||
|
|||||||
+27
-6
@@ -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.resolve.bindingContextUtil.isUsedAsStatement
|
||||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||||
import org.jetbrains.jet.lang.psi.JetDeclaration
|
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 {
|
private fun JetType.contains(inner: JetType): Boolean {
|
||||||
return JetTypeChecker.DEFAULT.equalTypes(this, inner) || getArguments().any { inner in it.getType() }
|
return JetTypeChecker.DEFAULT.equalTypes(this, inner) || getArguments().any { inner in it.getType() }
|
||||||
@@ -74,8 +81,10 @@ fun JetType.getTypeParameters(): Set<TypeParameterDescriptor> {
|
|||||||
return typeParameters
|
return typeParameters
|
||||||
}
|
}
|
||||||
|
|
||||||
fun JetExpression.guessTypes(context: BindingContext): Array<JetType> {
|
fun JetExpression.guessTypes(context: BindingContext, module: ModuleDescriptor?): Array<JetType> {
|
||||||
if (this !is JetDeclaration && isUsedAsStatement(context)) return array(KotlinBuiltIns.getInstance().getUnitType())
|
val builtIns = KotlinBuiltIns.getInstance()
|
||||||
|
|
||||||
|
if (this !is JetDeclaration && isUsedAsStatement(context)) return array(builtIns.getUnitType())
|
||||||
|
|
||||||
// if we know the actual type of the expression
|
// if we know the actual type of the expression
|
||||||
val theType1 = context[BindingContext.EXPRESSION_TYPE, this]
|
val theType1 = context[BindingContext.EXPRESSION_TYPE, this]
|
||||||
@@ -89,15 +98,16 @@ fun JetExpression.guessTypes(context: BindingContext): Array<JetType> {
|
|||||||
return array(theType2)
|
return array(theType2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val parent = getParent()
|
||||||
return when {
|
return when {
|
||||||
this is JetTypeConstraint -> {
|
this is JetTypeConstraint -> {
|
||||||
// expression itself is a type assertion
|
// expression itself is a type assertion
|
||||||
val constraint = (this as JetTypeConstraint)
|
val constraint = (this as JetTypeConstraint)
|
||||||
array(context[BindingContext.TYPE, constraint.getBoundTypeReference()]!!)
|
array(context[BindingContext.TYPE, constraint.getBoundTypeReference()]!!)
|
||||||
}
|
}
|
||||||
getParent() is JetTypeConstraint -> {
|
parent is JetTypeConstraint -> {
|
||||||
// expression is on the left side of a type assertion
|
// 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()]!!)
|
array(context[BindingContext.TYPE, constraint.getBoundTypeReference()]!!)
|
||||||
}
|
}
|
||||||
this is JetMultiDeclarationEntry -> {
|
this is JetMultiDeclarationEntry -> {
|
||||||
@@ -124,9 +134,9 @@ fun JetExpression.guessTypes(context: BindingContext): Array<JetType> {
|
|||||||
guessType(context)
|
guessType(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getParent() is JetVariableDeclaration -> {
|
parent is JetVariableDeclaration -> {
|
||||||
// the expression is the RHS of a variable assignment with a specified type
|
// 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()
|
val typeRef = variable.getTypeReference()
|
||||||
if (typeRef != null) {
|
if (typeRef != null) {
|
||||||
// and has a specified type
|
// and has a specified type
|
||||||
@@ -137,6 +147,17 @@ fun JetExpression.guessTypes(context: BindingContext): Array<JetType> {
|
|||||||
variable.guessType(context)
|
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
|
else -> array() // can't infer anything
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ object CreateIteratorFunctionActionFactory : JetSingleIntentionActionFactory() {
|
|||||||
val returnJetType = KotlinBuiltIns.getInstance().getIterator().getDefaultType()
|
val returnJetType = KotlinBuiltIns.getInstance().getIterator().getDefaultType()
|
||||||
|
|
||||||
val context = file.getBindingContext()
|
val context = file.getBindingContext()
|
||||||
val returnJetTypeParameterTypes = variableExpr.guessTypes(context)
|
val returnJetTypeParameterTypes = variableExpr.guessTypes(context, null)
|
||||||
if (returnJetTypeParameterTypes.size != 1) return null
|
if (returnJetTypeParameterTypes.size != 1) return null
|
||||||
|
|
||||||
val returnJetTypeParameterType = TypeProjectionImpl(returnJetTypeParameterTypes[0])
|
val returnJetTypeParameterType = TypeProjectionImpl(returnJetTypeParameterTypes[0])
|
||||||
|
|||||||
+4
-2
@@ -40,6 +40,7 @@ import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
|
|||||||
import org.jetbrains.jet.lang.psi.JetNamedFunction
|
import org.jetbrains.jet.lang.psi.JetNamedFunction
|
||||||
import org.jetbrains.jet.lang.psi.psiUtil.getAssignmentByLHS
|
import org.jetbrains.jet.lang.psi.psiUtil.getAssignmentByLHS
|
||||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.getExpressionForTypeGuess
|
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.getExpressionForTypeGuess
|
||||||
|
import org.jetbrains.jet.plugin.caches.resolve.getAnalysisResults
|
||||||
|
|
||||||
object CreateParameterActionFactory: JetSingleIntentionActionFactory() {
|
object CreateParameterActionFactory: JetSingleIntentionActionFactory() {
|
||||||
private fun JetType.hasTypeParametersToAdd(functionDescriptor: FunctionDescriptor, context: BindingContext): Boolean {
|
private fun JetType.hasTypeParametersToAdd(functionDescriptor: FunctionDescriptor, context: BindingContext): Boolean {
|
||||||
@@ -65,14 +66,15 @@ object CreateParameterActionFactory: JetSingleIntentionActionFactory() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
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
|
val refExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetSimpleNameExpression>()) ?: return null
|
||||||
if (refExpr.getQualifiedElement() != refExpr) return null
|
if (refExpr.getQualifiedElement() != refExpr) return null
|
||||||
|
|
||||||
val varExpected = refExpr.getAssignmentByLHS() != 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) {
|
when (it.size) {
|
||||||
0 -> KotlinBuiltIns.getInstance().getAnyType()
|
0 -> KotlinBuiltIns.getInstance().getAnyType()
|
||||||
1 -> it.first()
|
1 -> it.first()
|
||||||
|
|||||||
+11
@@ -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.
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -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.
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
// "Create function 'foo' from usage" "true"
|
||||||
|
|
||||||
|
class A<T>(val t: T) {
|
||||||
|
val x: A<Int> by <caret>foo(t, "")
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
// "Create function 'foo' from usage" "true"
|
||||||
|
|
||||||
|
class A<T>(val t: T) {
|
||||||
|
var x: A<Int> by <caret>foo(t, "")
|
||||||
|
}
|
||||||
+11
@@ -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
|
||||||
|
}
|
||||||
+11
@@ -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
|
||||||
|
}
|
||||||
+7
@@ -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
|
||||||
|
}
|
||||||
+7
@@ -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);
|
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")
|
@TestMetadata("beforeObjectMemberFunNoReceiver.kt")
|
||||||
public void testObjectMemberFunNoReceiver() throws Exception {
|
public void testObjectMemberFunNoReceiver() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/beforeObjectMemberFunNoReceiver.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/beforeObjectMemberFunNoReceiver.kt");
|
||||||
@@ -1541,12 +1553,24 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("beforeMemberValNoReceiver.kt")
|
||||||
public void testMemberValNoReceiver() throws Exception {
|
public void testMemberValNoReceiver() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/beforeMemberValNoReceiver.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/beforeMemberValNoReceiver.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("beforeObjectMemberValNoReceiver.kt")
|
||||||
public void testObjectMemberValNoReceiver() throws Exception {
|
public void testObjectMemberValNoReceiver() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/beforeObjectMemberValNoReceiver.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/beforeObjectMemberValNoReceiver.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user