From b16ebe2dc4df6f145760471e1e6c4de44568201b Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Sun, 13 Dec 2020 12:55:08 +0100 Subject: [PATCH] FIR IDE: consider if & while conditions expected type as boolean --- .../api/fir/components/KtFirTypeProvider.kt | 50 ++++++++++++++----- .../expectedExpressionType/ifCondition.kt | 5 ++ .../ifConditionQualified.kt | 5 ++ .../expectedExpressionType/whileCondition.kt | 5 ++ .../whileConditionQualified.kt | 5 ++ .../ExpectedExpressionTypeTestGenerated.java | 20 ++++++++ 6 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 idea/idea-frontend-fir/testData/components/expectedExpressionType/ifCondition.kt create mode 100644 idea/idea-frontend-fir/testData/components/expectedExpressionType/ifConditionQualified.kt create mode 100644 idea/idea-frontend-fir/testData/components/expectedExpressionType/whileCondition.kt create mode 100644 idea/idea-frontend-fir/testData/components/expectedExpressionType/whileConditionQualified.kt diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirTypeProvider.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirTypeProvider.kt index 20dd0dd1c39..674bbb05689 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirTypeProvider.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirTypeProvider.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.idea.frontend.api.fir.components import com.intellij.psi.PsiElement -import com.intellij.psi.util.parentOfType import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType @@ -21,12 +20,10 @@ import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.fir.types.KtFirType import org.jetbrains.kotlin.idea.frontend.api.types.KtType import org.jetbrains.kotlin.idea.frontend.api.withValidityAssertion -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtQualifiedExpression -import org.jetbrains.kotlin.psi.KtReturnExpression +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.AbstractTypeCheckerContext +import kotlin.reflect.KClass internal class KtFirTypeProvider( override val analysisSession: KtFirAnalysisSession, @@ -43,6 +40,7 @@ internal class KtFirTypeProvider( override fun getExpectedType(expression: PsiElement): KtType? = getExpectedTypeByReturnExpression(expression) + ?: getExpressionTypeByIfOrBooleanCondition(expression) private fun getExpectedTypeByReturnExpression(expression: PsiElement): KtType? { val returnParent = expression.getReturnExpressionWithThisType() ?: return null @@ -50,15 +48,20 @@ internal class KtFirTypeProvider( return targetSymbol.type } - private fun PsiElement.getReturnExpressionWithThisType(): KtReturnExpression? { - val parent = parent - return when { - parent is KtReturnExpression && parent.returnedExpression == this -> parent - parent is KtQualifiedExpression && parent.selectorExpression == this -> parent.getReturnExpressionWithThisType() - else -> null - } + private fun PsiElement.getReturnExpressionWithThisType(): KtReturnExpression? = + unwrapQualified { returnExpr, target -> returnExpr.returnedExpression == target } + + private fun getExpressionTypeByIfOrBooleanCondition(expression: PsiElement): KtType? = when { + expression.isWhileLoopCondition() || expression.isIfCondition() -> builtinTypes.BOOLEAN + else -> null } + private fun PsiElement.isWhileLoopCondition() = + unwrapQualified { whileExpr, cond -> whileExpr.condition == cond } != null + + private fun PsiElement.isIfCondition() = + unwrapQualified { ifExpr, cond -> ifExpr.condition == cond } != null + override fun isEqualTo(first: KtType, second: KtType): Boolean = withValidityAssertion { second.assertIsValid() check(first is KtFirType) @@ -94,4 +97,25 @@ internal class KtFirTypeProvider( isStubTypeEqualsToAnything = true, analysisSession.firResolveState.rootModuleSession //TODO use correct session here ) -} \ No newline at end of file +} + +private inline fun PsiElement.unwrapQualified(check: (R, PsiElement) -> Boolean): R? { + val parent = nonContainerParent + return when { + parent is R && check(parent, this) -> parent + parent is KtQualifiedExpression && parent.selectorExpression == this -> { + val grandParent = parent.nonContainerParent + when { + grandParent is R && check(grandParent, parent) -> grandParent + else -> null + } + } + else -> null + } +} + +private val PsiElement.nonContainerParent: PsiElement? + get() = when (val parent = parent) { + is KtContainerNode -> parent.parent + else -> parent + } \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/expectedExpressionType/ifCondition.kt b/idea/idea-frontend-fir/testData/components/expectedExpressionType/ifCondition.kt new file mode 100644 index 00000000000..d375d652d08 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/expectedExpressionType/ifCondition.kt @@ -0,0 +1,5 @@ +fun x() { + if(xy){ +} + +// EXPECTED_TYPE: kotlin/Boolean \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/expectedExpressionType/ifConditionQualified.kt b/idea/idea-frontend-fir/testData/components/expectedExpressionType/ifConditionQualified.kt new file mode 100644 index 00000000000..a7153790034 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/expectedExpressionType/ifConditionQualified.kt @@ -0,0 +1,5 @@ +fun x() { + if(x.fdfd){ +} + +// EXPECTED_TYPE: kotlin/Boolean \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/expectedExpressionType/whileCondition.kt b/idea/idea-frontend-fir/testData/components/expectedExpressionType/whileCondition.kt new file mode 100644 index 00000000000..3dadaff806a --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/expectedExpressionType/whileCondition.kt @@ -0,0 +1,5 @@ +fun x() { + while(xy){ +} + +// EXPECTED_TYPE: kotlin/Boolean \ No newline at end of file diff --git a/idea/idea-frontend-fir/testData/components/expectedExpressionType/whileConditionQualified.kt b/idea/idea-frontend-fir/testData/components/expectedExpressionType/whileConditionQualified.kt new file mode 100644 index 00000000000..080a09ca2b0 --- /dev/null +++ b/idea/idea-frontend-fir/testData/components/expectedExpressionType/whileConditionQualified.kt @@ -0,0 +1,5 @@ +fun x() { + while(x.fdfd){ +} + +// EXPECTED_TYPE: kotlin/Boolean \ No newline at end of file diff --git a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/components/ExpectedExpressionTypeTestGenerated.java b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/components/ExpectedExpressionTypeTestGenerated.java index 42c954b9116..d10b77e8801 100644 --- a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/components/ExpectedExpressionTypeTestGenerated.java +++ b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/components/ExpectedExpressionTypeTestGenerated.java @@ -28,6 +28,16 @@ public class ExpectedExpressionTypeTestGenerated extends AbstractExpectedExpress KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/testData/components/expectedExpressionType"), Pattern.compile("^(.+)\\.kt$"), null, true); } + @TestMetadata("ifCondition.kt") + public void testIfCondition() throws Exception { + runTest("idea/idea-frontend-fir/testData/components/expectedExpressionType/ifCondition.kt"); + } + + @TestMetadata("ifConditionQualified.kt") + public void testIfConditionQualified() throws Exception { + runTest("idea/idea-frontend-fir/testData/components/expectedExpressionType/ifConditionQualified.kt"); + } + @TestMetadata("returnFromFunction.kt") public void testReturnFromFunction() throws Exception { runTest("idea/idea-frontend-fir/testData/components/expectedExpressionType/returnFromFunction.kt"); @@ -47,4 +57,14 @@ public class ExpectedExpressionTypeTestGenerated extends AbstractExpectedExpress public void testReturnFromLambda() throws Exception { runTest("idea/idea-frontend-fir/testData/components/expectedExpressionType/returnFromLambda.kt"); } + + @TestMetadata("whileCondition.kt") + public void testWhileCondition() throws Exception { + runTest("idea/idea-frontend-fir/testData/components/expectedExpressionType/whileCondition.kt"); + } + + @TestMetadata("whileConditionQualified.kt") + public void testWhileConditionQualified() throws Exception { + runTest("idea/idea-frontend-fir/testData/components/expectedExpressionType/whileConditionQualified.kt"); + } }