FE1.0 Analysis API: get smartcast if available with getKtExpressionType

The current implementation still does not work with multicast. In
addition, it appears FE1.0 does not attempt smart cast if it's not used.
This commit is contained in:
Tianyu Geng
2021-12-07 15:19:05 -08:00
committed by teamcity
parent a5844b754e
commit 2f393cdd02
14 changed files with 102 additions and 2 deletions
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.inference.returnTypeOrNothing import org.jetbrains.kotlin.resolve.calls.inference.returnTypeOrNothing
import org.jetbrains.kotlin.resolve.calls.smartcasts.MultipleSmartCasts import org.jetbrains.kotlin.resolve.calls.smartcasts.MultipleSmartCasts
import org.jetbrains.kotlin.resolve.calls.smartcasts.SingleSmartCast
import org.jetbrains.kotlin.resolve.calls.util.getParameterForArgument import org.jetbrains.kotlin.resolve.calls.util.getParameterForArgument
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.util.getType import org.jetbrains.kotlin.resolve.calls.util.getType
@@ -29,6 +30,7 @@ import org.jetbrains.kotlin.resolve.sam.SamConstructorDescriptor
import org.jetbrains.kotlin.resolve.sam.getFunctionTypeForAbstractMethod import org.jetbrains.kotlin.resolve.sam.getFunctionTypeForAbstractMethod
import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.checker.intersectWrappedTypes
import org.jetbrains.kotlin.types.typeUtil.makeNullable import org.jetbrains.kotlin.types.typeUtil.makeNullable
class KtFe10ExpressionTypeProvider( class KtFe10ExpressionTypeProvider(
@@ -54,7 +56,12 @@ class KtFe10ExpressionTypeProvider(
} }
val bindingContext = analysisContext.analyze(unwrapped, AnalysisMode.PARTIAL) val bindingContext = analysisContext.analyze(unwrapped, AnalysisMode.PARTIAL)
val kotlinType = expression.getType(bindingContext) ?: analysisContext.builtIns.unitType val smartCastType = when (val smartCastType = bindingContext[BindingContext.SMARTCAST, expression]) {
is SingleSmartCast -> smartCastType.type
is MultipleSmartCasts -> intersectWrappedTypes(smartCastType.map.values)
else -> null
}
val kotlinType = smartCastType ?: expression.getType(bindingContext) ?: analysisContext.builtIns.unitType
return kotlinType.toKtType(analysisContext) return kotlinType.toKtType(analysisContext)
} }
@@ -156,6 +156,30 @@ public class KtFe10HLExpressionTypeTestGenerated extends AbstractKtFe10HLExpress
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/returnExpression.kt"); runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/returnExpression.kt");
} }
@Test
@TestMetadata("smartcast_asCallArg.kt")
public void testSmartcast_asCallArg() throws Exception {
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asCallArg.kt");
}
@Test
@TestMetadata("smartcast_asReceiver.kt")
public void testSmartcast_asReceiver() throws Exception {
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asReceiver.kt");
}
@Test
@TestMetadata("smartcast_multi.kt")
public void testSmartcast_multi() throws Exception {
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_multi.kt");
}
@Test
@TestMetadata("smartcast_unused.kt")
public void testSmartcast_unused() throws Exception {
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_unused.kt");
}
@Test @Test
@TestMetadata("stringLiteral.kt") @TestMetadata("stringLiteral.kt")
public void testStringLiteral() throws Exception { public void testStringLiteral() throws Exception {
@@ -156,6 +156,30 @@ public class FirHLExpressionTypeTestGenerated extends AbstractFirHLExpressionTyp
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/returnExpression.kt"); runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/returnExpression.kt");
} }
@Test
@TestMetadata("smartcast_asCallArg.kt")
public void testSmartcast_asCallArg() throws Exception {
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asCallArg.kt");
}
@Test
@TestMetadata("smartcast_asReceiver.kt")
public void testSmartcast_asReceiver() throws Exception {
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asReceiver.kt");
}
@Test
@TestMetadata("smartcast_multi.kt")
public void testSmartcast_multi() throws Exception {
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_multi.kt");
}
@Test
@TestMetadata("smartcast_unused.kt")
public void testSmartcast_unused() throws Exception {
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_unused.kt");
}
@Test @Test
@TestMetadata("stringLiteral.kt") @TestMetadata("stringLiteral.kt")
public void testStringLiteral() throws Exception { public void testStringLiteral() throws Exception {
@@ -10,13 +10,19 @@ import org.jetbrains.kotlin.analysis.api.impl.barebone.test.expressionMarkerProv
import org.jetbrains.kotlin.analysis.api.impl.base.test.test.framework.AbstractHLApiSingleFileTest import org.jetbrains.kotlin.analysis.api.impl.base.test.test.framework.AbstractHLApiSingleFileTest
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtValueArgument
import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions import org.jetbrains.kotlin.test.services.assertions
abstract class AbstractHLExpressionTypeTest(configurator: FrontendApiTestConfiguratorService) : AbstractHLApiSingleFileTest(configurator) { abstract class AbstractHLExpressionTypeTest(configurator: FrontendApiTestConfiguratorService) : AbstractHLApiSingleFileTest(configurator) {
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) { override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
val expression = testServices.expressionMarkerProvider.getSelectedElement(ktFile) as KtExpression val selected = testServices.expressionMarkerProvider.getSelectedElement(ktFile)
val expression = when (selected) {
is KtExpression -> selected
is KtValueArgument -> selected.getArgumentExpression()
else -> null
} ?: error("expect an expression but got ${selected.text}")
val type = executeOnPooledThreadInReadAction { val type = executeOnPooledThreadInReadAction {
analyseForTest(expression) { expression.getKtType()?.render() } analyseForTest(expression) { expression.getKtType()?.render() }
} }
@@ -0,0 +1,7 @@
fun test(a: Any) {
if (a is String) {
takeString(<expr>a</expr>)
}
}
fun takeString(s: String) {}
@@ -0,0 +1,2 @@
expression: a
type: kotlin.String
@@ -0,0 +1,5 @@
fun test(a: Any) {
if (a is String) {
<expr>a</expr>.length
}
}
@@ -0,0 +1,2 @@
expression: a
type: kotlin.String
@@ -0,0 +1,2 @@
expression: a
type: kotlin.Any
@@ -0,0 +1,10 @@
interface A
interface B
fun <T> T.take() where T: A, T: B {}
fun test(a: Any) {
if (a is A && a is B) {
<expr>a</expr>.take()
}
}
@@ -0,0 +1,2 @@
expression: a
type: (A&B)
@@ -0,0 +1,2 @@
expression: a
type: kotlin.Any
@@ -0,0 +1,5 @@
fun test(a: Any) {
if (a is String) {
<expr>a</expr>
}
}
@@ -0,0 +1,2 @@
expression: a
type: kotlin.String