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