Create from Usage: Fix "Create class" applicability check for when entries
#KT-22329 Fixed
This commit is contained in:
+20
-9
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoAfter
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement
|
||||
@@ -148,22 +149,32 @@ fun KtExpression.guessTypes(
|
||||
&& isUsedAsStatement(context)
|
||||
&& getNonStrictParentOfType<KtAnnotationEntry>() == null) return arrayOf(module.builtIns.unitType)
|
||||
|
||||
// if we know the actual type of the expression
|
||||
val theType1 = context.getType(this)
|
||||
if (theType1 != null && isAcceptable(theType1)) {
|
||||
val dataFlowInfo = context.getDataFlowInfoAfter(this)
|
||||
val dataFlowValueFactory = this.getResolutionFacade().frontendService<DataFlowValueFactory>()
|
||||
val dataFlowValue = dataFlowValueFactory.createDataFlowValue(this, theType1, context, module)
|
||||
val parent = parent
|
||||
|
||||
val possibleTypes = dataFlowInfo.getCollectedTypes(dataFlowValue, languageVersionSettings)
|
||||
return if (possibleTypes.isNotEmpty()) possibleTypes.toTypedArray() else arrayOf(theType1)
|
||||
// Type/Expected type may be wrong for the expression of KtWhenEntry when some branches have unresolved expressions
|
||||
if (parent is KtWhenEntry && parent.expression == this) {
|
||||
return parent
|
||||
.getStrictParentOfType<KtWhenExpression>()
|
||||
?.guessTypes(context, module, pseudocode, coerceUnusedToUnit, allowErrorTypes) ?: arrayOf()
|
||||
}
|
||||
|
||||
if (this !is KtWhenExpression) {
|
||||
// if we know the actual type of the expression
|
||||
val theType1 = context.getType(this)
|
||||
if (theType1 != null && isAcceptable(theType1)) {
|
||||
val dataFlowInfo = context.getDataFlowInfoAfter(this)
|
||||
val dataFlowValueFactory = this.getResolutionFacade().frontendService<DataFlowValueFactory>()
|
||||
val dataFlowValue = dataFlowValueFactory.createDataFlowValue(this, theType1, context, module)
|
||||
|
||||
val possibleTypes = dataFlowInfo.getCollectedTypes(dataFlowValue, languageVersionSettings)
|
||||
return if (possibleTypes.isNotEmpty()) possibleTypes.toTypedArray() else arrayOf(theType1)
|
||||
}
|
||||
}
|
||||
|
||||
// expression has an expected type
|
||||
val theType2 = context[BindingContext.EXPECTED_EXPRESSION_TYPE, this]
|
||||
if (theType2 != null && isAcceptable(theType2)) return arrayOf(theType2)
|
||||
|
||||
val parent = parent
|
||||
return when {
|
||||
this is KtTypeConstraint -> {
|
||||
// expression itself is a type assertion
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.intellij.psi.PsiElement
|
||||
import com.intellij.slicer.*
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.guessTypes
|
||||
@@ -31,6 +32,7 @@ import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isPlainWithEscapes
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.isError
|
||||
import org.jetbrains.kotlin.types.isNullabilityFlexible
|
||||
@@ -53,10 +55,7 @@ class KotlinSliceProvider : SliceLanguageSupportProvider, SliceUsageTransformer
|
||||
val types = when (element) {
|
||||
is KtCallableDeclaration -> listOfNotNull((element.resolveToDescriptorIfAny() as? CallableDescriptor)?.returnType)
|
||||
is KtDeclaration -> emptyList()
|
||||
is KtExpression -> {
|
||||
val (bindingContext, moduleDescriptor) = element.analyzeAndGetResult()
|
||||
element.guessTypes(bindingContext, moduleDescriptor).toList()
|
||||
}
|
||||
is KtExpression -> listOfNotNull(element.analyze(BodyResolveMode.PARTIAL).getType(element))
|
||||
else -> emptyList()
|
||||
}
|
||||
return when {
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// "Create class 'AAA'" "true"
|
||||
// ERROR: Unresolved reference: BBB
|
||||
|
||||
abstract class I
|
||||
|
||||
fun test(n: Int): I? {
|
||||
return if (n > 0) {
|
||||
when (n) {
|
||||
1 -> <caret>AAA("1")
|
||||
2 -> BBB("2")
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
else null
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// "Create class 'AAA'" "true"
|
||||
// ERROR: Unresolved reference: BBB
|
||||
|
||||
abstract class I
|
||||
|
||||
fun test(n: Int): I? {
|
||||
return if (n > 0) {
|
||||
when (n) {
|
||||
1 -> AAA("1")
|
||||
2 -> BBB("2")
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
else null
|
||||
}
|
||||
|
||||
class AAA(s: String) : I() {
|
||||
|
||||
}
|
||||
@@ -1933,6 +1933,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("callInWhenEntry.kt")
|
||||
public void testCallInWhenEntry() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/callExpression/callInWhenEntry.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("callNoReceiver.kt")
|
||||
public void testCallNoReceiver() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/callExpression/callNoReceiver.kt");
|
||||
|
||||
Reference in New Issue
Block a user