Skip to content

Commit 00cc9d4

Browse files
asedunovAlexey
authored and
Alexey
committed
Specify Type Explicitly: Filter out types which can't be resolved in the target scope
#KT-10066 Fixed
1 parent 21fd894 commit 00cc9d4

File tree

8 files changed

+67
-18
lines changed

8 files changed

+67
-18
lines changed

idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyIntention.kt

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,15 @@ import com.intellij.psi.PsiDocumentManager
2323
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
2424
import org.jetbrains.kotlin.descriptors.CallableDescriptor
2525
import org.jetbrains.kotlin.idea.caches.resolve.analyze
26-
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
27-
import org.jetbrains.kotlin.idea.util.ShortenReferences
26+
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
27+
import org.jetbrains.kotlin.idea.util.*
2828
import org.jetbrains.kotlin.psi.*
2929
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
3030
import org.jetbrains.kotlin.resolve.BindingContext
31-
import org.jetbrains.kotlin.resolve.DescriptorUtils
32-
import org.jetbrains.kotlin.types.ErrorUtils
33-
import org.jetbrains.kotlin.types.KotlinType
34-
import org.jetbrains.kotlin.types.TypeUtils
35-
import java.util.*
31+
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
32+
import org.jetbrains.kotlin.types.*
33+
import org.jetbrains.kotlin.utils.SmartSet
34+
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
3635

3736
public class SpecifyTypeExplicitlyIntention : SelfTargetingIntention<KtCallableDeclaration>(KtCallableDeclaration::class.java, "Specify type explicitly"), LowPriorityAction {
3837
override fun isApplicableTo(element: KtCallableDeclaration, caretOffset: Int): Boolean {
@@ -65,14 +64,34 @@ public class SpecifyTypeExplicitlyIntention : SelfTargetingIntention<KtCallableD
6564
return type ?: ErrorUtils.createErrorType("null type")
6665
}
6766

68-
public fun createTypeExpressionForTemplate(exprType: KotlinType): Expression {
69-
val descriptor = exprType.constructor.declarationDescriptor
70-
val isAnonymous = descriptor != null && DescriptorUtils.isAnonymousObject(descriptor)
71-
72-
val allSupertypes = TypeUtils.getAllSupertypes(exprType)
73-
val types = if (isAnonymous) ArrayList<KotlinType>() else arrayListOf(exprType)
74-
types.addAll(allSupertypes)
75-
67+
public fun createTypeExpressionForTemplate(exprType: KotlinType, contextElement: KtElement): Expression {
68+
val resolutionFacade = contextElement.getResolutionFacade()
69+
val bindingContext = resolutionFacade.analyze(contextElement, BodyResolveMode.PARTIAL)
70+
val scope = contextElement.getResolutionScope(bindingContext, resolutionFacade)
71+
val types = (exprType.singletonList() + TypeUtils.getAllSupertypes(exprType))
72+
.filter { it.isResolvableInScope(scope, true) }
73+
.mapNotNull mapArgs@ {
74+
val resolvableArgs = it.arguments.filterTo(SmartSet.create()) { it.type.isResolvableInScope(scope, true) }
75+
if (resolvableArgs.containsAll(it.arguments)) return@mapArgs it
76+
77+
val newArguments = (it.arguments zip it.constructor.parameters).map {
78+
val (arg, param) = it
79+
when {
80+
arg in resolvableArgs -> arg
81+
arg.projectionKind == Variance.OUT_VARIANCE ||
82+
param.variance == Variance.OUT_VARIANCE -> TypeProjectionImpl(
83+
arg.projectionKind,
84+
arg.type.approximateWithResolvableType(scope, true)
85+
)
86+
else -> return@mapArgs null
87+
}
88+
}
89+
90+
object: DelegatingType() {
91+
override fun getDelegate() = it
92+
override fun getArguments() = newArguments
93+
}
94+
}
7695
return object : ChooseValueExpression<KotlinType>(types, types.first()) {
7796
override fun getLookupString(element: KotlinType) = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(element)
7897
override fun getResult(element: KotlinType) = IdeDescriptorRenderers.SOURCE_CODE.renderType(element)
@@ -103,7 +122,7 @@ public class SpecifyTypeExplicitlyIntention : SelfTargetingIntention<KtCallableD
103122
assert(!exprType.isError) { "Unexpected error type, should have been checked before: " + declaration.getElementTextWithContext() + ", type = " + exprType }
104123

105124
val project = declaration.project
106-
val expression = createTypeExpressionForTemplate(exprType)
125+
val expression = createTypeExpressionForTemplate(exprType, declaration)
107126

108127
declaration.setType(KotlinBuiltIns.FQ_NAMES.any.asString())
109128

idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinInplaceVariableIntroducer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ protected void addTypeReferenceVariable(TemplateBuilderImpl builder) {
356356
if (typeReference != null) {
357357
builder.replaceElement(typeReference,
358358
TYPE_REFERENCE_VARIABLE_NAME,
359-
SpecifyTypeExplicitlyIntention.Companion.createTypeExpressionForTemplate(myExprType),
359+
SpecifyTypeExplicitlyIntention.Companion.createTypeExpressionForTemplate(myExprType, myDeclaration),
360360
false);
361361
}
362362
}

idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinVariableInplaceIntroducer.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public class KotlinVariableInplaceIntroducer(
109109
addedVariable.typeReference?.let {
110110
builder.replaceElement(it,
111111
"TypeReferenceVariable",
112-
SpecifyTypeExplicitlyIntention.createTypeExpressionForTemplate(expressionType!!),
112+
SpecifyTypeExplicitlyIntention.createTypeExpressionForTemplate(expressionType!!, addedVariable),
113113
false)
114114
}
115115
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class A {
2+
private fun foo() <caret>= { object {} }
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class A {
2+
private fun foo(): () -> Any<caret> = { object {} }
3+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class A {
2+
private fun bar() <caret>= {
3+
class Local()
4+
Local()
5+
}
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class A {
2+
private fun bar(): () -> Any<caret> = {
3+
class Local()
4+
Local()
5+
}
6+
}

idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7906,6 +7906,12 @@ public void testAllFilesPresentInSpecifyTypeExplicitly() throws Exception {
79067906
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/specifyTypeExplicitly"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
79077907
}
79087908

7909+
@TestMetadata("anonymousObject.kt")
7910+
public void testAnonymousObject() throws Exception {
7911+
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/anonymousObject.kt");
7912+
doTest(fileName);
7913+
}
7914+
79097915
@TestMetadata("badCaretPosition.kt")
79107916
public void testBadCaretPosition() throws Exception {
79117917
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/badCaretPosition.kt");
@@ -7942,6 +7948,12 @@ public void testLambdaParam() throws Exception {
79427948
doTest(fileName);
79437949
}
79447950

7951+
@TestMetadata("localClass.kt")
7952+
public void testLocalClass() throws Exception {
7953+
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/localClass.kt");
7954+
doTest(fileName);
7955+
}
7956+
79457957
@TestMetadata("loopParameter.kt")
79467958
public void testLoopParameter() throws Exception {
79477959
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/loopParameter.kt");

0 commit comments

Comments
 (0)