From 2f393cdd02e164ccc82ab704f25329bfe2fef5c8 Mon Sep 17 00:00:00 2001 From: Tianyu Geng Date: Tue, 7 Dec 2021 15:19:05 -0800 Subject: [PATCH] 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. --- .../KtFe10ExpressionTypeProvider.kt | 9 ++++++- .../KtFe10HLExpressionTypeTestGenerated.java | 24 +++++++++++++++++++ .../FirHLExpressionTypeTestGenerated.java | 24 +++++++++++++++++++ .../AbstractHLExpressionTypeTest.kt | 8 ++++++- .../expressionType/smartcast_asCallArg.kt | 7 ++++++ .../expressionType/smartcast_asCallArg.txt | 2 ++ .../expressionType/smartcast_asReceiver.kt | 5 ++++ .../expressionType/smartcast_asReceiver.txt | 2 ++ .../smartcast_multi.descriptors.txt | 2 ++ .../expressionType/smartcast_multi.kt | 10 ++++++++ .../expressionType/smartcast_multi.txt | 2 ++ .../smartcast_unused.descriptors.txt | 2 ++ .../expressionType/smartcast_unused.kt | 5 ++++ .../expressionType/smartcast_unused.txt | 2 ++ 14 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asCallArg.kt create mode 100644 analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asCallArg.txt create mode 100644 analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asReceiver.kt create mode 100644 analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asReceiver.txt create mode 100644 analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_multi.descriptors.txt create mode 100644 analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_multi.kt create mode 100644 analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_multi.txt create mode 100644 analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_unused.descriptors.txt create mode 100644 analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_unused.kt create mode 100644 analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_unused.txt diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10ExpressionTypeProvider.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10ExpressionTypeProvider.kt index da5a3d12af4..86e935516a1 100644 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10ExpressionTypeProvider.kt +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10ExpressionTypeProvider.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.inference.returnTypeOrNothing 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.getResolvedCall 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.types.ErrorUtils import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.checker.intersectWrappedTypes import org.jetbrains.kotlin.types.typeUtil.makeNullable class KtFe10ExpressionTypeProvider( @@ -54,7 +56,12 @@ class KtFe10ExpressionTypeProvider( } 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) } diff --git a/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/descriptors/test/components/expressionTypeProvider/KtFe10HLExpressionTypeTestGenerated.java b/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/descriptors/test/components/expressionTypeProvider/KtFe10HLExpressionTypeTestGenerated.java index ab2da09d056..2ea37a8a692 100644 --- a/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/descriptors/test/components/expressionTypeProvider/KtFe10HLExpressionTypeTestGenerated.java +++ b/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/descriptors/test/components/expressionTypeProvider/KtFe10HLExpressionTypeTestGenerated.java @@ -156,6 +156,30 @@ public class KtFe10HLExpressionTypeTestGenerated extends AbstractKtFe10HLExpress 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 @TestMetadata("stringLiteral.kt") public void testStringLiteral() throws Exception { diff --git a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/components/expressionTypeProvider/FirHLExpressionTypeTestGenerated.java b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/components/expressionTypeProvider/FirHLExpressionTypeTestGenerated.java index a319b3d5e9d..592a3ffbb44 100644 --- a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/components/expressionTypeProvider/FirHLExpressionTypeTestGenerated.java +++ b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/components/expressionTypeProvider/FirHLExpressionTypeTestGenerated.java @@ -156,6 +156,30 @@ public class FirHLExpressionTypeTestGenerated extends AbstractFirHLExpressionTyp 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 @TestMetadata("stringLiteral.kt") public void testStringLiteral() throws Exception { diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/expressionTypeProvider/AbstractHLExpressionTypeTest.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/expressionTypeProvider/AbstractHLExpressionTypeTest.kt index c1beefbb810..b356e17fde2 100644 --- a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/expressionTypeProvider/AbstractHLExpressionTypeTest.kt +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/components/expressionTypeProvider/AbstractHLExpressionTypeTest.kt @@ -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.psi.KtExpression import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtValueArgument import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.services.TestServices import org.jetbrains.kotlin.test.services.assertions abstract class AbstractHLExpressionTypeTest(configurator: FrontendApiTestConfiguratorService) : AbstractHLApiSingleFileTest(configurator) { 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 { analyseForTest(expression) { expression.getKtType()?.render() } } diff --git a/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asCallArg.kt b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asCallArg.kt new file mode 100644 index 00000000000..17b7b9b760d --- /dev/null +++ b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asCallArg.kt @@ -0,0 +1,7 @@ +fun test(a: Any) { + if (a is String) { + takeString(a) + } +} + +fun takeString(s: String) {} \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asCallArg.txt b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asCallArg.txt new file mode 100644 index 00000000000..693c21a0e31 --- /dev/null +++ b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asCallArg.txt @@ -0,0 +1,2 @@ +expression: a +type: kotlin.String diff --git a/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asReceiver.kt b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asReceiver.kt new file mode 100644 index 00000000000..6a07e02836d --- /dev/null +++ b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asReceiver.kt @@ -0,0 +1,5 @@ +fun test(a: Any) { + if (a is String) { + a.length + } +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asReceiver.txt b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asReceiver.txt new file mode 100644 index 00000000000..693c21a0e31 --- /dev/null +++ b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_asReceiver.txt @@ -0,0 +1,2 @@ +expression: a +type: kotlin.String diff --git a/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_multi.descriptors.txt b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_multi.descriptors.txt new file mode 100644 index 00000000000..044cafcec08 --- /dev/null +++ b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_multi.descriptors.txt @@ -0,0 +1,2 @@ +expression: a +type: kotlin.Any diff --git a/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_multi.kt b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_multi.kt new file mode 100644 index 00000000000..b9244d81b19 --- /dev/null +++ b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_multi.kt @@ -0,0 +1,10 @@ +interface A +interface B + +fun T.take() where T: A, T: B {} + +fun test(a: Any) { + if (a is A && a is B) { + a.take() + } +} diff --git a/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_multi.txt b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_multi.txt new file mode 100644 index 00000000000..e3a20081819 --- /dev/null +++ b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_multi.txt @@ -0,0 +1,2 @@ +expression: a +type: (A&B) diff --git a/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_unused.descriptors.txt b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_unused.descriptors.txt new file mode 100644 index 00000000000..044cafcec08 --- /dev/null +++ b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_unused.descriptors.txt @@ -0,0 +1,2 @@ +expression: a +type: kotlin.Any diff --git a/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_unused.kt b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_unused.kt new file mode 100644 index 00000000000..83b06340d2f --- /dev/null +++ b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_unused.kt @@ -0,0 +1,5 @@ +fun test(a: Any) { + if (a is String) { + a + } +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_unused.txt b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_unused.txt new file mode 100644 index 00000000000..693c21a0e31 --- /dev/null +++ b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/smartcast_unused.txt @@ -0,0 +1,2 @@ +expression: a +type: kotlin.String