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 106397d5bee..ab2da09d056 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 @@ -120,6 +120,24 @@ public class KtFe10HLExpressionTypeTestGenerated extends AbstractKtFe10HLExpress runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/nonExpression.kt"); } + @Test + @TestMetadata("plusAssign.kt") + public void testPlusAssign() throws Exception { + runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/plusAssign.kt"); + } + + @Test + @TestMetadata("postfixDec.kt") + public void testPostfixDec() throws Exception { + runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/postfixDec.kt"); + } + + @Test + @TestMetadata("prefixInc.kt") + public void testPrefixInc() throws Exception { + runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/prefixInc.kt"); + } + @Test @TestMetadata("property.kt") public void testProperty() throws Exception { diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirExpressionTypeProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirExpressionTypeProvider.kt index acb4eca869d..e873ba568aa 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirExpressionTypeProvider.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirExpressionTypeProvider.kt @@ -54,6 +54,13 @@ internal class KtFirExpressionTypeProvider( fir.typeRef.coneType.asKtType() } } + is FirVariableAssignment -> { + if (expression is KtUnaryExpression && expression.operationToken in KtTokens.INCREMENT_AND_DECREMENT) { + fir.rValue.typeRef.coneType.asKtType() + } else { + analysisSession.builtinTypes.UNIT + } + } is FirExpression -> fir.typeRef.coneType.asKtType() is FirNamedReference -> fir.getReferencedElementType(firResolveState).asKtType() is FirStatement -> with(analysisSession) { builtinTypes.UNIT } 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 a0441b57d83..a319b3d5e9d 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 @@ -120,6 +120,24 @@ public class FirHLExpressionTypeTestGenerated extends AbstractFirHLExpressionTyp runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/nonExpression.kt"); } + @Test + @TestMetadata("plusAssign.kt") + public void testPlusAssign() throws Exception { + runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/plusAssign.kt"); + } + + @Test + @TestMetadata("postfixDec.kt") + public void testPostfixDec() throws Exception { + runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/postfixDec.kt"); + } + + @Test + @TestMetadata("prefixInc.kt") + public void testPrefixInc() throws Exception { + runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/prefixInc.kt"); + } + @Test @TestMetadata("property.kt") public void testProperty() throws Exception { diff --git a/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/plusAssign.kt b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/plusAssign.kt new file mode 100644 index 00000000000..3cb8cc0543e --- /dev/null +++ b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/plusAssign.kt @@ -0,0 +1,4 @@ +fun test() { + var i = 1 + i += 1 +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/plusAssign.txt b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/plusAssign.txt new file mode 100644 index 00000000000..676bde2f069 --- /dev/null +++ b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/plusAssign.txt @@ -0,0 +1,2 @@ +expression: i += 1 +type: kotlin.Unit diff --git a/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/postfixDec.kt b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/postfixDec.kt new file mode 100644 index 00000000000..76d41a12467 --- /dev/null +++ b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/postfixDec.kt @@ -0,0 +1,4 @@ +fun test() { + var i = 1 + i-- +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/postfixDec.txt b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/postfixDec.txt new file mode 100644 index 00000000000..443397f1229 --- /dev/null +++ b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/postfixDec.txt @@ -0,0 +1,2 @@ +expression: i-- +type: kotlin.Int diff --git a/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/prefixInc.kt b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/prefixInc.kt new file mode 100644 index 00000000000..35e77c3cfa7 --- /dev/null +++ b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/prefixInc.kt @@ -0,0 +1,4 @@ +fun test() { + var i = 1 + ++i +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/prefixInc.txt b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/prefixInc.txt new file mode 100644 index 00000000000..4642c598569 --- /dev/null +++ b/analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/prefixInc.txt @@ -0,0 +1,2 @@ +expression: ++i +type: kotlin.Int diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/FirElementsRecorder.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/FirElementsRecorder.kt index 68221902e2c..5ddf01430b9 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/FirElementsRecorder.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/FirElementsRecorder.kt @@ -7,9 +7,12 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.KtRealPsiSourceElement +import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.DuplicatedFirSourceElementsException import org.jetbrains.kotlin.analysis.low.level.api.fir.util.isErrorElement import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment import org.jetbrains.kotlin.fir.references.* import org.jetbrains.kotlin.fir.types.FirErrorTypeRef @@ -19,8 +22,11 @@ import org.jetbrains.kotlin.fir.types.FirUserTypeRef import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl import org.jetbrains.kotlin.fir.visitors.FirVisitor import org.jetbrains.kotlin.psi +import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtTypeReference +import org.jetbrains.kotlin.psi.KtUnaryExpression +import org.jetbrains.kotlin.util.OperatorNameConventions internal open class FirElementsRecorder : FirVisitor>() { private fun cache(psi: KtElement, fir: FirElement, cache: MutableMap) { @@ -95,12 +101,50 @@ internal open class FirElementsRecorder : FirVisitor fir.isWriteInCompoundCall() + parentPsi is KtBinaryExpression && psi == parentPsi.left -> fir.isReadInCompoundCall() + parentPsi is KtUnaryExpression && psi == parentPsi.baseExpression -> fir.isReadInCompoundCall() + else -> false + } + } + + private fun FirElement.isReadInCompoundCall(): Boolean { + if (this is FirPropertyAccessExpression) return true + if (this !is FirFunctionCall) return false + val name = (calleeReference as? FirResolvedNamedReference)?.name + return name == OperatorNameConventions.GET + } + + private fun FirElement.isWriteInCompoundCall(): Boolean { + if (this is FirVariableAssignment) return true + if (this !is FirFunctionCall) return false + val name = (calleeReference as? FirResolvedNamedReference)?.name + return name == OperatorNameConventions.SET || name in OperatorNameConventions.ASSIGNMENT_OPERATIONS + } + companion object { @OptIn(ExperimentalStdlibApi::class) fun recordElementsFrom(firElement: FirElement, recorder: FirElementsRecorder): Map = diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVal.kt b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVal.kt new file mode 100644 index 00000000000..ec2346c1678 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVal.kt @@ -0,0 +1,6 @@ +interface A { + operator fun plusAssign(i: Int) +} +fun test(l: A) { + l += 1 +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVal.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVal.txt new file mode 100644 index 00000000000..1ec7965e805 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVal.txt @@ -0,0 +1,5 @@ +KT element: KtBinaryExpression +FIR element: FirFunctionCallImpl + +FIR element rendered: +R|/l|.R|/A.plusAssign|(Int(1)) \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVal_lhs.kt b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVal_lhs.kt new file mode 100644 index 00000000000..31dc6e74c8b --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVal_lhs.kt @@ -0,0 +1,6 @@ +interface A { + operator fun plusAssign(i: Int) +} +fun test(l: A) { + l += 1 +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVal_lhs.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVal_lhs.txt new file mode 100644 index 00000000000..36544d1c6aa --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVal_lhs.txt @@ -0,0 +1,5 @@ +KT element: KtNameReferenceExpression +FIR element: FirPropertyAccessExpressionImpl + +FIR element rendered: +R|/l| \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVar.kt b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVar.kt new file mode 100644 index 00000000000..3cb8cc0543e --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVar.kt @@ -0,0 +1,4 @@ +fun test() { + var i = 1 + i += 1 +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVar.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVar.txt new file mode 100644 index 00000000000..53a2f07c103 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVar.txt @@ -0,0 +1,5 @@ +KT element: KtBinaryExpression +FIR element: FirVariableAssignmentImpl + +FIR element rendered: +R|/i| = R|/i|.R|kotlin/Int.plus|(Int(1)) \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVar_lhs.kt b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVar_lhs.kt new file mode 100644 index 00000000000..25c9720b9b8 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVar_lhs.kt @@ -0,0 +1,4 @@ +fun test() { + var i = 1 + i += 1 +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVar_lhs.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVar_lhs.txt new file mode 100644 index 00000000000..4dedf12a952 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVar_lhs.txt @@ -0,0 +1,5 @@ +KT element: KtNameReferenceExpression +FIR element: FirResolvedNamedReferenceImpl + +FIR element rendered: +R|/i| \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayAccessConvention.kt b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayAccessConvention.kt new file mode 100644 index 00000000000..cc9dd612c1e --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayAccessConvention.kt @@ -0,0 +1,8 @@ +interface MyMap { + operator fun get(k: K): V + operator fun set(k: K, v: V) +} + +fun test(m: MyMap) { + m["a"] += 1 +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayAccessConvention.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayAccessConvention.txt new file mode 100644 index 00000000000..d22baa85427 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayAccessConvention.txt @@ -0,0 +1,5 @@ +KT element: KtBinaryExpression +FIR element: FirFunctionCallImpl + +FIR element rendered: +R|/<>|.R|SubstitutionOverride|(R|/<>|, R|/<>|.R|SubstitutionOverride|(R|/<>|).R|kotlin/Int.plus|(Int(1))) \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayAccessConvention_lhs.kt b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayAccessConvention_lhs.kt new file mode 100644 index 00000000000..534f949ccc9 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayAccessConvention_lhs.kt @@ -0,0 +1,8 @@ +interface MyMap { + operator fun get(k: K): V + operator fun set(k: K, v: V) +} + +fun test(m: MyMap) { + m["a"] += 1 +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayAccessConvention_lhs.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayAccessConvention_lhs.txt new file mode 100644 index 00000000000..584a3315b93 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayAccessConvention_lhs.txt @@ -0,0 +1,5 @@ +KT element: KtArrayAccessExpression +FIR element: FirFunctionCallImpl + +FIR element rendered: +R|/<>|.R|SubstitutionOverride|(R|/<>|) \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayGetConvention.kt b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayGetConvention.kt new file mode 100644 index 00000000000..640e0be0537 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayGetConvention.kt @@ -0,0 +1,11 @@ +interface A { + operator fun plusAssign(i: Int) +} + +interface MyMap { + operator fun get(k: K): V +} + +fun test(m: MyMap) { + m["a"] += 1 +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayGetConvention.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayGetConvention.txt new file mode 100644 index 00000000000..af7b2a9a7fa --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayGetConvention.txt @@ -0,0 +1,5 @@ +KT element: KtBinaryExpression +FIR element: FirFunctionCallImpl + +FIR element rendered: +R|/m|.R|SubstitutionOverride|(String(a)).R|/A.plusAssign|(Int(1)) \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayGetConvention_lhs.kt b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayGetConvention_lhs.kt new file mode 100644 index 00000000000..497f14e34a4 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayGetConvention_lhs.kt @@ -0,0 +1,11 @@ +interface A { + operator fun plusAssign(i: Int) +} + +interface MyMap { + operator fun get(k: K): V +} + +fun test(m: MyMap) { + m["a"] += 1 +} diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayGetConvention_lhs.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayGetConvention_lhs.txt new file mode 100644 index 00000000000..e36b2b0715c --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayGetConvention_lhs.txt @@ -0,0 +1,5 @@ +KT element: KtArrayAccessExpression +FIR element: FirFunctionCallImpl + +FIR element rendered: +R|/m|.R|SubstitutionOverride|(String(a)) \ No newline at end of file diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/GetOrBuildFirTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/GetOrBuildFirTestGenerated.java index b089704eeef..da80c66189f 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/GetOrBuildFirTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/GetOrBuildFirTestGenerated.java @@ -97,6 +97,54 @@ public class GetOrBuildFirTestGenerated extends AbstractGetOrBuildFirTest { runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/calls/calllTypeArguments.kt"); } + @Test + @TestMetadata("compoundAssignOnVal.kt") + public void testCompoundAssignOnVal() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVal.kt"); + } + + @Test + @TestMetadata("compoundAssignOnVal_lhs.kt") + public void testCompoundAssignOnVal_lhs() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVal_lhs.kt"); + } + + @Test + @TestMetadata("compoundAssignOnVar.kt") + public void testCompoundAssignOnVar() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVar.kt"); + } + + @Test + @TestMetadata("compoundAssignOnVar_lhs.kt") + public void testCompoundAssignOnVar_lhs() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignOnVar_lhs.kt"); + } + + @Test + @TestMetadata("compoundAssignWithArrayAccessConvention.kt") + public void testCompoundAssignWithArrayAccessConvention() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayAccessConvention.kt"); + } + + @Test + @TestMetadata("compoundAssignWithArrayAccessConvention_lhs.kt") + public void testCompoundAssignWithArrayAccessConvention_lhs() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayAccessConvention_lhs.kt"); + } + + @Test + @TestMetadata("compoundAssignWithArrayGetConvention.kt") + public void testCompoundAssignWithArrayGetConvention() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayGetConvention.kt"); + } + + @Test + @TestMetadata("compoundAssignWithArrayGetConvention_lhs.kt") + public void testCompoundAssignWithArrayGetConvention_lhs() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/calls/compoundAssignWithArrayGetConvention_lhs.kt"); + } + @Test @TestMetadata("constructorDelegationSuperCall.kt") public void testConstructorDelegationSuperCall() throws Exception {