Create from Usage: Approximate unresolvable types
#KT-7722 Fixed (cherry picked from commit 917cd22)
This commit is contained in:
@@ -117,6 +117,7 @@ These artifacts include extensions for the types available in the latter JDKs, s
|
||||
|
||||
###### Issues fixed
|
||||
|
||||
- [`KT-7722`](https://youtrack.jetbrains.com/issue/KT-7722) Approximate unresolvable types in "Create from Usage" quickfixes
|
||||
- [`KT-11115`](https://youtrack.jetbrains.com/issue/KT-11115) Implement Members: Fix base member detection when abstract and non-abstract members with matching signatures are inherited from an interface
|
||||
|
||||
###### New features
|
||||
|
||||
@@ -31,6 +31,8 @@ import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.*
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
|
||||
|
||||
fun KotlinType.approximateFlexibleTypes(preferNotNull: Boolean = false): KotlinType {
|
||||
if (isDynamic()) return this
|
||||
@@ -100,4 +102,31 @@ fun KotlinType.anonymousObjectSuperTypeOrNull(): KotlinType? {
|
||||
return immediateSupertypes().firstOrNull() ?: classDescriptor.builtIns.anyType
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun KotlinType.getResolvableApproximations(scope: LexicalScope?, checkTypeParameters: Boolean): Sequence<KotlinType> {
|
||||
return (singletonList() + TypeUtils.getAllSupertypes(this))
|
||||
.asSequence()
|
||||
.filter { it.isResolvableInScope(scope, checkTypeParameters) }
|
||||
.mapNotNull mapArgs@ {
|
||||
val resolvableArgs = it.arguments.filterTo(SmartSet.create()) { it.type.isResolvableInScope(scope, checkTypeParameters) }
|
||||
if (resolvableArgs.containsAll(it.arguments)) return@mapArgs it
|
||||
|
||||
val newArguments = (it.arguments zip it.constructor.parameters).map {
|
||||
val (arg, param) = it
|
||||
when {
|
||||
arg in resolvableArgs -> arg
|
||||
|
||||
arg.projectionKind == Variance.OUT_VARIANCE ||
|
||||
param.variance == Variance.OUT_VARIANCE -> TypeProjectionImpl(
|
||||
arg.projectionKind,
|
||||
arg.type.approximateWithResolvableType(scope, checkTypeParameters)
|
||||
)
|
||||
|
||||
else -> return@mapArgs null
|
||||
}
|
||||
}
|
||||
|
||||
it.replace(newArguments)
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,8 @@ import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.idea.util.approximateWithResolvableType
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.idea.util.isResolvableInScope
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.idea.util.getResolvableApproximations
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
@@ -40,9 +42,9 @@ import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
import org.jetbrains.kotlin.utils.ifEmpty
|
||||
|
||||
class SpecifyTypeExplicitlyIntention :
|
||||
@@ -112,28 +114,7 @@ class SpecifyTypeExplicitlyIntention :
|
||||
val resolutionFacade = contextElement.getResolutionFacade()
|
||||
val bindingContext = resolutionFacade.analyze(contextElement, BodyResolveMode.PARTIAL)
|
||||
val scope = contextElement.getResolutionScope(bindingContext, resolutionFacade)
|
||||
val types = (exprType.singletonList() + TypeUtils.getAllSupertypes(exprType))
|
||||
.filter { it.isResolvableInScope(scope, true) }
|
||||
.mapNotNull mapArgs@ {
|
||||
val resolvableArgs = it.arguments.filterTo(SmartSet.create()) { it.type.isResolvableInScope(scope, true) }
|
||||
if (resolvableArgs.containsAll(it.arguments)) return@mapArgs it
|
||||
|
||||
val newArguments = (it.arguments zip it.constructor.parameters).map {
|
||||
val (arg, param) = it
|
||||
when {
|
||||
arg in resolvableArgs -> arg
|
||||
arg.projectionKind == Variance.OUT_VARIANCE ||
|
||||
param.variance == Variance.OUT_VARIANCE -> TypeProjectionImpl(
|
||||
arg.projectionKind,
|
||||
arg.type.approximateWithResolvableType(scope, true)
|
||||
)
|
||||
else -> return@mapArgs null
|
||||
}
|
||||
}
|
||||
|
||||
it.replace(newArguments)
|
||||
}
|
||||
.ifEmpty { return null }
|
||||
val types = exprType.getResolvableApproximations(scope, true).toList().ifEmpty { return null }
|
||||
return object : ChooseValueExpression<KotlinType>(types, types.first()) {
|
||||
override fun getLookupString(element: KotlinType) = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(element)
|
||||
override fun getResult(element: KotlinType) = IdeDescriptorRenderers.SOURCE_CODE.renderType(element)
|
||||
|
||||
+1
-1
@@ -146,7 +146,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
|
||||
private val typeCandidates = HashMap<TypeInfo, List<TypeCandidate>>()
|
||||
|
||||
var placement: CallablePlacement by Delegates.notNull()
|
||||
var placement: CallablePlacement? = null
|
||||
|
||||
private val elementsToShorten = ArrayList<KtElement>()
|
||||
|
||||
|
||||
+34
-7
@@ -18,17 +18,20 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.ArrayUtil
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.ClassInfo
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtTypeReference
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.idea.util.getResolvableApproximations
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@@ -86,14 +89,38 @@ abstract class TypeInfo(val variance: Variance) {
|
||||
open fun getPossibleNamesFromExpression(bindingContext: BindingContext): Array<String> = ArrayUtil.EMPTY_STRING_ARRAY
|
||||
abstract fun getPossibleTypes(builder: CallableBuilder): List<KotlinType>
|
||||
|
||||
private fun getScopeForTypeApproximation(config: CallableBuilderConfiguration, placement: CallablePlacement?): LexicalScope? {
|
||||
if (placement == null) return config.originalElement.getResolutionScope()
|
||||
|
||||
val containingElement = when (placement) {
|
||||
is CallablePlacement.NoReceiver -> {
|
||||
placement.containingElement
|
||||
}
|
||||
is CallablePlacement.WithReceiver -> {
|
||||
val receiverClassDescriptor =
|
||||
placement.receiverTypeCandidate.theType.constructor.declarationDescriptor
|
||||
val classDeclaration = receiverClassDescriptor?.let { DescriptorToSourceUtils.getSourceFromDescriptor(it) }
|
||||
if (!config.isExtension && classDeclaration != null) classDeclaration else config.currentFile
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unexpected placement: $placement")
|
||||
}
|
||||
return when (containingElement) {
|
||||
is KtClassOrObject -> (containingElement.resolveToDescriptor() as? ClassDescriptorWithResolutionScopes)?.scopeForMemberDeclarationResolution
|
||||
is KtBlockExpression -> (containingElement.statements.firstOrNull() ?: containingElement).getResolutionScope()
|
||||
is KtElement -> containingElement.getContainingKtFile().getResolutionScope()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
protected fun KotlinType?.getPossibleSupertypes(variance: Variance, callableBuilder: CallableBuilder): List<KotlinType> {
|
||||
if (this == null || ErrorUtils.containsErrorType(this)) {
|
||||
return Collections.singletonList(callableBuilder.currentFileModule.builtIns.anyType)
|
||||
}
|
||||
val single = Collections.singletonList(this)
|
||||
val scope = getScopeForTypeApproximation(callableBuilder.config, callableBuilder.placement)
|
||||
val approximations = getResolvableApproximations(scope, false)
|
||||
return when (variance) {
|
||||
Variance.IN_VARIANCE -> single + supertypes()
|
||||
else -> single
|
||||
Variance.IN_VARIANCE -> approximations.toList()
|
||||
else -> listOf(approximations.firstOrNull() ?: this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
// "Create extension function 'List<Int>.foo'" "true"
|
||||
|
||||
open class A
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val list = listOf(1, 2, 4, 5)
|
||||
list.<caret>foo { object : A() {} }
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Create extension function 'List<Int>.foo'" "true"
|
||||
|
||||
open class A
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val list = listOf(1, 2, 4, 5)
|
||||
list.foo { object : A() {} }
|
||||
}
|
||||
|
||||
fun <E> List<E>.foo(function: () -> A) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Create extension function 'List<Int>.foo'" "true"
|
||||
|
||||
open class A
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
class Local : A()
|
||||
|
||||
val list = listOf(1, 2, 4, 5)
|
||||
list.<caret>foo { Local() }
|
||||
}
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
// "Create extension function 'List<Int>.foo'" "true"
|
||||
|
||||
open class A
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
class Local : A()
|
||||
|
||||
val list = listOf(1, 2, 4, 5)
|
||||
list.foo { Local() }
|
||||
}
|
||||
|
||||
fun <E> List<E>.foo(function: () -> A) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
@@ -1956,6 +1956,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createFunction/call"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("approximateAnonymousObjectRuntime.kt")
|
||||
public void testApproximateAnonymousObjectRuntime() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/approximateAnonymousObjectRuntime.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("approximateLocalClassRuntime.kt")
|
||||
public void testApproximateLocalClassRuntime() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/approximateLocalClassRuntime.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("argumentTypeMismatch.kt")
|
||||
public void testArgumentTypeMismatch() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/argumentTypeMismatch.kt");
|
||||
|
||||
Reference in New Issue
Block a user