Specify Type Explicitly: Filter out types which can't be resolved in the target scope

#KT-10066 Fixed
This commit is contained in:
Alexey Sedunov
2015-12-24 14:51:44 +03:00
committed by Alexey
parent 21fd894d75
commit 00cc9d4150
8 changed files with 65 additions and 16 deletions
@@ -23,16 +23,15 @@ import com.intellij.psi.PsiDocumentManager
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.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.ShortenReferences
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import java.util.*
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
public class SpecifyTypeExplicitlyIntention : SelfTargetingIntention<KtCallableDeclaration>(KtCallableDeclaration::class.java, "Specify type explicitly"), LowPriorityAction {
override fun isApplicableTo(element: KtCallableDeclaration, caretOffset: Int): Boolean {
@@ -65,14 +64,34 @@ public class SpecifyTypeExplicitlyIntention : SelfTargetingIntention<KtCallableD
return type ?: ErrorUtils.createErrorType("null type")
}
public fun createTypeExpressionForTemplate(exprType: KotlinType): Expression {
val descriptor = exprType.constructor.declarationDescriptor
val isAnonymous = descriptor != null && DescriptorUtils.isAnonymousObject(descriptor)
public fun createTypeExpressionForTemplate(exprType: KotlinType, contextElement: KtElement): Expression {
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 allSupertypes = TypeUtils.getAllSupertypes(exprType)
val types = if (isAnonymous) ArrayList<KotlinType>() else arrayListOf(exprType)
types.addAll(allSupertypes)
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
}
}
object: DelegatingType() {
override fun getDelegate() = it
override fun getArguments() = newArguments
}
}
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)
@@ -103,7 +122,7 @@ public class SpecifyTypeExplicitlyIntention : SelfTargetingIntention<KtCallableD
assert(!exprType.isError) { "Unexpected error type, should have been checked before: " + declaration.getElementTextWithContext() + ", type = " + exprType }
val project = declaration.project
val expression = createTypeExpressionForTemplate(exprType)
val expression = createTypeExpressionForTemplate(exprType, declaration)
declaration.setType(KotlinBuiltIns.FQ_NAMES.any.asString())
@@ -356,7 +356,7 @@ public class KotlinInplaceVariableIntroducer<D extends KtCallableDeclaration> ex
if (typeReference != null) {
builder.replaceElement(typeReference,
TYPE_REFERENCE_VARIABLE_NAME,
SpecifyTypeExplicitlyIntention.Companion.createTypeExpressionForTemplate(myExprType),
SpecifyTypeExplicitlyIntention.Companion.createTypeExpressionForTemplate(myExprType, myDeclaration),
false);
}
}
@@ -109,7 +109,7 @@ public class KotlinVariableInplaceIntroducer(
addedVariable.typeReference?.let {
builder.replaceElement(it,
"TypeReferenceVariable",
SpecifyTypeExplicitlyIntention.createTypeExpressionForTemplate(expressionType!!),
SpecifyTypeExplicitlyIntention.createTypeExpressionForTemplate(expressionType!!, addedVariable),
false)
}
}
@@ -0,0 +1,3 @@
class A {
private fun foo() <caret>= { object {} }
}
@@ -0,0 +1,3 @@
class A {
private fun foo(): () -> Any<caret> = { object {} }
}
@@ -0,0 +1,6 @@
class A {
private fun bar() <caret>= {
class Local()
Local()
}
}
@@ -0,0 +1,6 @@
class A {
private fun bar(): () -> Any<caret> = {
class Local()
Local()
}
}
@@ -7906,6 +7906,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/specifyTypeExplicitly"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
@TestMetadata("anonymousObject.kt")
public void testAnonymousObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/anonymousObject.kt");
doTest(fileName);
}
@TestMetadata("badCaretPosition.kt")
public void testBadCaretPosition() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/badCaretPosition.kt");
@@ -7942,6 +7948,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName);
}
@TestMetadata("localClass.kt")
public void testLocalClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/localClass.kt");
doTest(fileName);
}
@TestMetadata("loopParameter.kt")
public void testLoopParameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/loopParameter.kt");