Create from Usage: Approximate unresolvable types

#KT-7722 Fixed
(cherry picked from commit 917cd22)
This commit is contained in:
Alexey Sedunov
2016-06-29 13:06:13 +03:00
parent a1e86e8bfa
commit ae06f01c95
10 changed files with 127 additions and 33 deletions
@@ -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)
@@ -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>()
@@ -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)
}
}
}